Set up AKA Security — calibrate notifications and detection posture from Antigravity's real activity.
Install to Claude Code
npx -y skills add akasecurity/ai-tc --skill setup --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Setup?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/akasecurity-setup)More formats (shields.io, HTML) on the badges page.
---
name: aka-setup
description: Set up AKA Security — calibrate notifications and detection posture from Antigravity's real activity.
---
# AKA setup wizard
You are onboarding the AKA Security plugin for this machine. AKA works
fully locally with **zero backend and zero Docker**: detection runs in-process
and findings persist to a local SQLite store at `~/.aka/data/aka.db`.
This wizard tells a **calibration story**: introduce AKA → show what it does →
offer one retroactive scan → report the real numbers it found and the posture it
recommends → apply on confirmation → show the installed summary → hand off to the
dashboard. Everything the user sees is derived from their _actual_ history — never
a fabricated or demo number. When there isn't enough history to judge, the wizard
falls back to a conservative severity-derived floor instead of guessing.
The false-positive/severity judgment needs the raw (unmasked) findings to rate
them accurately, so it **sends them to the model API** through separate `agy`
subprocesses (a large history is judged in several batches). Two things
cross for each finding: its **raw value**, and about **120 characters of the
surrounding transcript text** on either side of it — re-masked first, so any
_other_ detectable secret in that window never leaves raw. The transcript
file's path, the value's fingerprint, and the fingerprint key version are
**dropped before egress**. A copy of the raw value **leaves the machine**, sent
to the model provider like any other Antigravity prompt. You act only on the
raw-free plan the subprocesses print back.
Two limits of this host are worth knowing before you consent, because they are
weaker than the Claude Code and Codex plugins' equivalents:
- **The `agy` CLI documents no ephemeral mode.** Every run is written to your
conversation store under `~/.gemini/antigravity/brain/`, raw values and
all — the same store this backfill scans. AKA therefore **deletes the judge's
own conversation itself** as soon as the run ends. That deletion is best
effort: if the process is killed between the write and the cleanup, the
conversation stays on disk until you remove it.
- **Deleting the conversation is not network isolation.** It is a local-write
cleanup only; it cannot recall what was already sent.
Reading history is granted in step 1; **sending findings to the model is a
distinct consent, collected in step 3 before the judgment pipe runs** — the
judge refuses to run without it.
Follow the steps below **in order**. Nothing is written to the policy store
until step 5 (or a floor fallback in step 3 if the calibration can't complete).
## Execution contract (read before step 0)
Every script prints output in three region kinds. Your job for each is fixed:
- **`<<<AKA_SHOW … AKA_SHOW>>>`** — relay every AKA_SHOW region verbatim as your
next message: paste the content _between_ the markers exactly — a card region
carries its own code fence, a plain confirmation line does not, but either way
you paste exactly what's between the markers — never the marker lines, never a
paraphrase or summary.
- **`<<<AKA_FRAME_JSON … AKA_FRAME_JSON>>>`** — machine-only. Parse it if a step
tells you to read a value from it; never display it.
- **Anything else on stdout** — status for you (paths like `Plan saved to:`,
errors, exit signals). Act on it; never relay it.
**Collecting a decision.** Wherever a step asks the user a question, ask it as a
normal conversational turn with numbered options for the user to pick from. This
wizard depends on no specific interactive-picker tool being available — present
each option clearly and wait for the user's reply before moving on. If Antigravity
exposes a native multi-choice picker in your environment, prefer that over plain
text; otherwise the numbered-list form is the fallback. Never answer a question
on the user's behalf.
Invariants:
- **Never write a confirmation or acknowledgement the wizard did not emit** — the
script's AKA_SHOW line is the confirmation.
- **Each step's AKA_SHOW regions must be relayed before you advance.**
- **One question per decision; never re-ask a decision already collected.**
## 0. Show the intro card
Run the intro script and relay its AKA_SHOW region per the execution contract:
paste the content between the markers verbatim, never the marker lines. It
prints a single space-aligned monospace card — name, repository, version, and
what AKA adds — inside a Markdown code fence that is part of that pasted
content. Keep the fence as printed and do **not** add another code fence, strip
the fence, or reformat it (unfenced, Markdown collapses the indentation and
mangles the `●` lines).
```bash
node "${PLUGIN_ROOT}/scripts/intro.js" "${PLUGIN_ROOT}/.antigravity-plugin/plugin.json"
```
## 0b. Repo-aware posture check — tighten-only, working-tree only
Before showing any recommended posture — the start-light default table in
step 2 or the calibrated posture in step 4 — look at the **current project's**
working tree yourself, with your own file-reading tools. There is no script for
this: it is your own reasoning over facts you read directly, not the triage
subprocess's raw-free plan, and it needs no user interaction.
**In scope:** the manifest's declared frameworks/dependencies
(`package.json` or equivalent), payment or other third-party API SDKs among
them, CI config (`.github/workflows/`, etc.), and the **presence and names**
of `.env*`/config files as a signal that secrets live on disk here — never
their contents; a secret-bearing file's contents are exactly the kind of raw
value this wizard never reads. **Out of scope:** Antigravity's own history and the
local AKA store (that is the separate, consent-gated scan in steps 1/3) — no
historical read, and no question or other consent interaction of any kind.
The severity-floor default map (secret/pii/financial/phi/code_flaw/custom at
`warn`, code_context/config at `monitor` — the table step 2's start-light card
prints) is both the **floor** this check is measured against and its
**fallback**. From what you directly observe you may **tighten** individual
categories above that floor — raise the level, never lower one below it —
each tightened category carrying a one-line rationale naming the concrete
evidence you found, e.g.:
> Stripe + a `Customer` model here — financial → redact
Present the tightening on whichever recommended posture is about to render, the
same "recommended base + changed-packs overlay" shape step 4b's adjust fork
uses: the tightened categories raise, every other category keeps its existing
recommended level, each carrying its rationale line. Where you compose the view
yourself (the adjust fork, the calibrated result) this tightened recommendation
IS that view; where the view is a script's AKA_SHOW card (the step-2
start-light card, relayed per the execution contract), show the tightened
recommendation and its rationale lines adjacent to that card rather than
rewriting the card's own printed levels.
This tightening is a **display-time recommendation**: it shapes the recommended
posture the user reads, not a separate write. Persisting a tightened level happens
only where the wizard already writes a per-category override — the adjust fork's
`onboard.js --posture` write (step 4b), where the user picks each category's level
explicitly. The keep-defaults path writes the severity floor (`--floor`, step 2)
and the calibrated accept path applies the triage subprocess's saved plan
verbatim (`--confirmed --plan`, step 5); neither carries the tightening on its
own, so a tightened level the user wants persisted is chosen through the adjust
fork. Do **not** bolt on an extra `onboard.js --posture` overlay to auto-re-persist
the tightening across the other paths: it would overwrite — and so could silently
**downgrade** — a category the user had hardened out of band (a tightening is only
guaranteed to raise above the severity floor, not above the user's stored level),
with no downgrade-approval gate. So the tightening is not auto-persisted across
those paths — a tightened level the user wants kept is set through the adjust fork.
**When nothing in the working tree is inferable, change nothing.** Render the
recommended posture exactly as the static frame already gives it — no rationale
line and no tightened category (fail-open).
## 1. Offer the retroactive scan
Ask this **before** anything about detection posture — the posture
recommendation in step 4 is _derived from_ the answer to this question, so it
has to come first. Ask it per the execution contract's decision rules: a
conversational turn with numbered options, waiting for the user's reply.
**Disclose the model-API egress plainly before you show the options — this is what
the user is consenting to, so it must be visible before they choose.** State it in
your own words, without softening it: if they say yes, AKA scans the last 30 days
of Antigravity history, and to rate what it finds it **sends the raw, unmasked values —
including any secrets — to the model API through the `antigravity` CLI**. Name what
travels with each one: **its raw value, and about 120 characters of the
surrounding transcript text on either side** (any other secrets detected in
that window are masked; the rollout file's path stays local). That is real
network egress to the model provider (the same one your
Antigravity session already uses), not a purely local review. **A copy of each value
leaves the machine.** The rollouts those values came from stay on disk
untouched — nothing here removes them; that is the separate redaction step in
step 6. The judge's own conversation IS written to your local Antigravity
history under `~/.gemini/antigravity/brain/` — this host has no ephemeral
mode — and AKA deletes that conversation itself as soon as the run ends, best
effort; a killed process can leave it behind. That cleanup is a local-write
guard only, not network isolation. The findings are also
recorded, masked, to the local store. The grant is revocable from the
dashboard's **Settings → Historical access**, which stops future scans — it
cannot recall anything already sent. Do not present the options until you have
said this.
**Want me to look over what Antigravity has been up to?** — "I'll scan Antigravity's recent work — session rollouts, temp files, agent memory — and send what I find to the model to rate it, so I can tune what I bring you next."
Offer exactly two options:
1. **Yes, take a look** — "scan my real work here; raw findings go to the model to be rated, then tune what you bring me"
2. **Not now** — "start light and I'll learn as we go"
Choosing **Yes, take a look** records the same historical-review consent the wizard has
always recorded — the identical scope (which includes the model-API judgment disclosed
above), the one-time grant, and the same revocation semantics — so the simpler question
broadens nothing about what AKA may access. Those granular scope and revocation details
stay inspectable on request and in the dashboard, under **Settings → Historical access**,
whose own copy repeats the model-API disclosure.
## 2. Save the answer, then branch
Branch on the answer from step 1. On the **Yes, take a look** path the onboarding
writer runs, and it must run **before** the backfill (step 3), because the
backfill script reads `historicalAccess` from the saved settings to decide
whether it's allowed to run. Omitting `--policy` is deliberate — the old global
redact/warn toggle no longer drives enforcement (posture is per-category now);
its field is kept for backward compatibility but this wizard doesn't ask about
it.
**Branch on the choice:**
- **If the user chose "Yes, take a look"** — record the historical-review consent and
continue to step 3 (which runs the scan and leads to the calibrated result in
step 4). "Yes, take a look" maps to the existing full historical-review path — no
access is granted beyond what that path already granted:
```bash
node "${PLUGIN_ROOT}/scripts/onboard.js" --historical full
```
- **If the user chose "Not now"** — take the **start-light** path.
This path takes **zero historical access**: do **not** read any history, do
**not** run the backfill, and do **not** record consent — nothing about the
machine's past is touched.
Instead present the start-light posture card, write the posture the user picks
(this write **is** the applying frame — it stands in for step 5, which never
runs here because there is no scan plan to apply), and rejoin the spine at the
installed summary (step 6). **Skip steps 3, 4, and 5 entirely** — there is no
scan to triage, no calibrated result to confirm, and no suppression plan to
write. Do the following in order:
1. **Show the start-light card.** Run the start-light script and relay its
AKA_SHOW card per the execution contract — the
`● Starting light — your detection categories` heading, the full 8-pack ×
4-level default posture table, the per-pack rationale, and the re-tune
hint, pasted between the markers exactly as printed, fence included. It
reads no history and writes nothing; it only prints the card
(the severity-floor default map — secret, pii, financial, phi, code_flaw, custom at
`warn`; code_context, config at `monitor`).
```bash
node "${PLUGIN_ROOT}/scripts/start-light.js"
```
2. **Confirm or adjust.** Ask — per the execution contract's decision rules —
whether to keep the recommended defaults or tune individual packs:
**Set your detection categories** — "Keep the defaults I'd recommend, or adjust any of them?"
1. **Keep defaults** _(recommended)_ — "the careful defaults shown above"
2. **Adjust** — "change one or more levels, keep the rest as I recommend"
If they choose **Adjust**, ask again — same decision rules — to collect the
new level (monitor/warn/redact/block) for each pack they want to change,
then merge those overrides over the severity-floor defaults to form the
full 8-pack map.
3. **Write the chosen posture.** The default map is the severity floor,
so when the user keeps the defaults, write the floor directly; when they
adjusted packs, write the merged 8-pack map:
```bash
# Kept the recommended defaults
node "${PLUGIN_ROOT}/scripts/onboard.js" --floor
# Adjusted one or more packs — <json> is the merged 8-pack map
node "${PLUGIN_ROOT}/scripts/onboard.js" --posture '<json>'
```
Either write prints only `✓ Set all K detection categories` (the `--floor`
write appends ` — safe defaults`) — which is the honest confirmation here,
because nothing was scanned or suppressed. Show that line to the user; do
**not** invent a dismissed count or any calibration counts.
4. **Rejoin the spine at the installed summary (step 6).** Continue to step 6
to show the installed summary and hand off to the dashboard, using honest
no-scan copy. No scan ran, so there is **no surfaced count** — call
`firstrun.js` with **no `--surfaced` flag** (the same floor-fallback rule
step 6 already follows when no calibration frame was emitted). Step 7
then runs as written.
## 3. Run the evidence triage — off-transcript judgment, nothing written yet
**Model-judge consent — a distinct opt-in, asked here before the pipe.** The
false-positive/severity judgment runs by sending each finding to the model API
through `antigravity`. That is a separate egress from the historical-read consent
collected in step 1 (which only let AKA _read_ the local rollouts), so it needs
its own explicit grant. Before running the pipe, restate plainly what leaves the
machine (the step-1 disclosure's payload: each raw value plus its masked context
window; the file path stays local), then ask as a normal conversational turn
with numbered options — and present both options flat: this grant sends the
user's data off the machine, so do **not** mark either one recommended.
**Send findings to the model to sort real leaks from noise?** — "I'll send each
detected value, plus a bit of surrounding context with any secrets in it masked,
to the model to tell real leaks from routine noise. The file path stays local.
This consent is saved machine-wide in AKA's settings, so it also covers the
same triage step in AKA's other harness plugins on this machine — revoke it
anytime from the dashboard."
1. **Yes, send them** — "let the model triage what I found"
2. **No, keep it local** — "skip the model triage and start from the safe defaults"
**Branch on the choice:**
- **If the user chose "Yes, send them"** — record the model-judge consent, then
run the pipe below:
```bash
node "${PLUGIN_ROOT}/scripts/onboard.js" --model-judge-consent
```
- **If the user chose "No, keep it local"** — do **not** run the pipe. The judge
refuses to run without consent (it would only print a clean skip line and a
zero-count frame), so there is no calibrated plan to confirm. Fall back to the
conservative severity floor exactly as step 2's start-light path does — write
the floor, tell the user the model triage was skipped, and continue to step 6
with **no `--surfaced` flag**:
```bash
node "${PLUGIN_ROOT}/scripts/onboard.js" --floor
```
Pipe the backfill's triage stream straight into the `apply-suppressions`
adapter in **PREVIEW** mode (no `--confirmed`):
```bash
node "${PLUGIN_ROOT}/scripts/backfill.js" --triage | node "${PLUGIN_ROOT}/scripts/apply-suppressions.js"
```
The backfill sweeps prior Antigravity CLI conversation transcripts (last 30
days, all conversations) and streams one masked-plus-raw triage hit per line;
masked findings are recorded to the local store as a side effect. The adapter
runs the false-positive/severity **judgment in separate `agy` subprocesses
that send each hit's raw value and masked context window — never its source
path — to the model API** (a large history is split into several batches; each
run persists a conversation under `~/.gemini/antigravity/brain/`, which AKA
then deletes best effort — this host has no ephemeral mode), then prints back a
**raw-free plan** you can safely show the user:
the calibrated-result card (the real-count headline and the recommended posture),
the per-category reasoning, the masked false positives it would suppress, any
categories it skipped, and its notes.
The preview also **persists that exact raw-free plan to a temp file and prints
its path** — a line beginning `Plan saved to: <path>`. Capture that path: step 5
applies **this saved plan verbatim**, so the confirm step performs no second scan
and no second judgment. (The plan file carries only masked/fingerprint/enum data;
it is deleted after a successful apply.)
Alongside the human copy, the preview also emits a **machine-readable calibration
frame** — a single JSON block delimited by `<<<AKA_FRAME_JSON` … `AKA_FRAME_JSON>>>`
carrying the raw-free calibration counts and categories, plus (when the scan
surfaced any) a `maskedFindings` array of raw-free secret-leak summaries. Do
**not** show this block to the user (it is additive to the human copy above).
Capture its `counts.important` value — the **surfaced count** — and pass it to the
first-run summary in step 6 as `--surfaced <count>` — but **only when the preview
also printed a `Plan saved to:` path** (a real calibrated plan to confirm in step
4). The `Plan saved to:` line is the completion signal: a preview that omits it did
not calibrate a plan you can confirm. The fallback branches below carry no
surfaced count; the scan-ran-clean empty state (a scan that completed but
surfaced nothing) emits a zero-count frame but **no plan path**, so it too routes
to the floor branch below rather than step 4.
Also **retain the block's full text verbatim** (not just the counts you read out
of it) — step 6's "Review leaked keys" branch feeds this same text to the
secret-leak remediation entry, which reads its own `maskedFindings` from it,
and step 4's finding narration and step 6's secret-leak narration (both below)
read the same `maskedFindings` array off it too. When present, the block's
`falsePositivePatterns` array is what step 4's fixture/exception offer (below)
names its pattern and count from — never invent either off-signal.
Everything you show the user in step 4 comes from **this command's output**. You
never read the raw finding values yourself — do not echo, quote, or reconstruct
them; the judge subprocesses send them to the model API and return only the
raw-free plan, so they never enter this conversation.
**Failed or truncated triage — never proceed silently (fallback).** If this
command exits non-zero, or the adapter reports a truncated / sentinel-less
stream, the calibration could **not** complete. Do not guess a posture and do
not leave setup half-applied. Apply the conservative severity floor, **tell the
user it happened**, and continue to step 6:
```bash
node "${PLUGIN_ROOT}/scripts/onboard.js" --floor
```
Say plainly: the historical scan couldn't finish, so AKA is starting from the
conservative severity floor (high-impact categories at `warn`, observe-only at
`monitor`) instead of a calibrated posture, and it can be re-run any time with
the aka-setup skill.
**Nothing to calibrate.** A scan that **completes but surfaces nothing** prints
the honest **scan-ran-clean** card — `I looked over Antigravity's recent work —
nothing needs your attention right now. You're starting clean; here's what I'd
recommend:` over the recommended posture — with a zero-count calibration frame
(its `counts.important` is `0`) and **no `Plan saved to:` path**. A machine with
no history to scan instead prints the no-history card (`Nothing to learn from
yet — Antigravity hasn't left any work on this machine.` over the start-light table),
and a scan skipped for lack of consent prints `I didn't review anything —
historical access wasn't granted.`. In every one of these cases there's no
evidence to calibrate from and no plan to confirm: show the card the adapter
printed, take the floor branch (`onboard.js --floor`), tell the user the scan
found nothing to calibrate from, and skip to step 6 (with **no `--surfaced`**,
the floor-fallback rule there — nothing was surfaced to carry over). Do **not**
continue to step 4.
Otherwise (the preview printed a `Plan saved to:` path) continue to step 4.
## 4. Show the calibrated result and get explicit confirmation — before any write
The preview output is raw-free. Lead with the **calibrated-result card** it
printed and show it in full:
1. **The calibrated headline.** The `I went through Antigravity's recent work — N
detections, M results worth a look.` line — every count templated over the
real scan (surfaced findings are the `M results` worth a look; the rest are
routine noise a plain scanner would have screamed about). Show it verbatim;
never substitute a demo number.
2. **The recommended posture.** The condensed one-row-per-pack recommended view
the card printed — the level AKA would set for each category. Show it in full.
- **Surface the downgrades the preview flags — this is not optional.** For the
recommended posture it is about to write, the preview compares each category
against its stored setting and flags any that would be **LOWERED** from a
stronger existing one (e.g. an existing `block`/`redact` dropping to
`warn`/`monitor`), printing a `WARNING` line summarizing them. Call these out
prominently: a user who hardened a category must **explicitly approve weakening
it** before applying.
3. **The false positives to be suppressed (the human gate).** The masked value,
rule, and masked context for each detection the writeback would suppress —
the routine noise being dismissed. This is the checkpoint that stops a genuine
secret being silenced: the user reads the masked evidence and approves it.
4. **Explain what surfaced, in plain language.** When the frame carries
`maskedFindings`, walk through them — what each one is, where it showed up,
and why it matters — grounded entirely in that array: every count you speak
equals the frame's own count for it (`counts.important`/`counts.total`, or
a specific finding kind's count), and every value you reference appears
masked, exactly as the frame gives it — never a raw value, never an
invented one. This is an actual explanation of the known findings, not a
restatement of the headline's counts. When the frame carries no
`maskedFindings` (nothing surfaced, or a fallback floor ran), skip this —
the calibrated-result card already said so; do not invent narration over a
missing signal.
5. **Offer an exception for a grounded false-positive pattern.** When the
frame carries `falsePositivePatterns`, name each group's pattern and count
**strictly from that signal** — never invent a pattern name or fabricate a
count. For each group, ask — per the execution contract's decision rules —
whether to set a pre-filled exception, with a duration choice (the exception
scope axis — `once` / `temporary` / `permanent`):
**Make an exception for `<pattern>` (×N)?** — "This `<pattern>` looks like a
test fixture — want me to set an exception so it stops popping up?"
1. **Once** — just this once — expires in 30 minutes
2. **Temporary** — for a set window, then I'll check it again
3. **Permanent** — stays until you revoke it
4. **Not now** — skip — I won't write anything
**Temporary needs a concrete window.** `once` and `permanent` fully determine
the scope on their own, but `temporary` does not — resolving it into the
stored `{scope, expiresAt, maxUses}` triple requires an actual duration, and
you must **never** invent or default one. When the user picks **Temporary**,
follow up with a second question that offers concrete windows only
— `30m` / `1h` / `24h` (the exception scope resolver accepts `<n>m`/`<n>h`,
capped at 24h; a longer bypass is a `permanent` grant, not a forgotten timer)
— and resolve the exact chosen string through that resolver. **Once** and
**Permanent** take no follow-up.
Accepting surfaces the exact pre-filled exception — one **per distinct value
identity** (`ruleId`/`valueFingerprint`/`keyVersion`) at the chosen
`{scope, expiresAt, maxUses}` — for review, and the marked pattern is
suppressed as part of the calibration plan confirmed below (the same store
the aka-exceptions skill reads). A group whose displayed pattern covers more
than one distinct value surfaces one exception per distinct value — never a
single grant collapsing them — and a value missing its exact identity is not
offered for. Declining surfaces nothing; this offer is separate from the
calibration plan's suppressions confirmed below, so declining here changes
nothing about that confirmation. When the frame carries no
`falsePositivePatterns` (nothing was marked a likely false positive, or the
scan was declined), skip this entirely — no offer, nothing invented
(fail-open).
Then ask — per the execution contract's decision rules, never answering for the
user — to confirm:
**Want me to apply this?** — "I'll set these levels and suppress the false
positives above."
1. **Yes, apply** _(recommended)_ — write the posture and suppressions exactly as
previewed.
2. **Adjust a category** — "change one or more first; keep the rest as I
recommend"
Do **not** proceed until the user picks one. On **Yes, apply**, continue to
step 5 and apply the previewed plan verbatim — that is the confirm spine,
unchanged. On **Adjust a category**, take the **adjust fork** (step 4b), which
applies within the fork and rejoins the spine at the installed summary (step 6).
## 4b. Adjust a category — the override fork
The **adjust base is the calibrated recommended posture the preview just
printed** — the condensed one-row-per-pack view from step 4, not the cold-start
severity floor. The user changes the packs they want and keeps the rest as
recommended.
1. **Collect the changes.** Ask — per the execution contract's decision rules —
which packs to change and to which level (monitor/warn/redact/block), one
clear numbered-option question at a time.
2. **Show the adjust-confirm table.** Compose the merged 8-pack map — the
recommended base with the user's picks overlaid — and render the adjust-confirm
card by passing the calibrated recommended posture as `--recommended` and that
merged map as `--posture`. Relay its AKA_SHOW region per the execution
contract — the fenced `category │ recommended │ yours` table, pasted between
the markers exactly as printed (it is space-aligned monospace; do **not** add
another code fence, strip the fence, or reformat it):
```bash
node "${PLUGIN_ROOT}/scripts/start-light.js" --adjust-confirm --recommended '<recommended-json>' --posture '<merged-json>' --current '<current-json>'
```
`<recommended-json>` is the calibrated recommended posture the preview printed
(the adjust base) — so the `recommended` column shows each pack's calibrated
level, and a pack calibration escalated above the floor never renders as a
spurious change. `<merged-json>` is the full 8-pack map — that same recommended
base with the user's overrides overlaid — so a changed pack reads as a different
`yours` value and every untouched pack repeats its recommended level.
`<current-json>` is the `current` object from the plan file at the path step 3
printed (`Plan saved to: <path>`) — the store's per-category action at preview
time, the baseline the downgrade check compares against. Pass it verbatim; do
not retype or summarize it.
3. **Surface any downgrade — the card computes this, you do not.** With
`--current` passed, the card itself appends the `WARNING: N categories … would
be LOWERED from a stronger existing setting` footer whenever a pick weakens
enforcement — the same rule and the same wording as the confirm gate above,
from the same code. Show the card in full, footer included, and when that
footer is present get explicit approval before saving. Never let an enforcement
downgrade through without the user having seen it.
4. **Save or back out.** Ask — per the execution contract's decision rules —
with **N** the number of packs the user changed and **M** the number kept as
recommended (`M = 8 − N`), both real — never a placeholder:
**Save your adjustments?**
1. **Save adjusted — N changed, M as recommended** — apply with the adjusted
posture.
2. **Back to recommended** — discard the changes and apply the recommended
posture instead.
5. **On "Save adjusted" — produce the applying frame here, carrying the adjusted
posture, then rejoin the spine at the installed summary (step 6).** This fork
applies within itself and **stands in for step 5**, so step 5 never runs on
this path. First apply the previewed plan with the **unchanged confirm spine**,
so the reviewed false positives are dismissed and the recommended base is
written (the reviewed evidence packs overwrite; the severity floor fill-gaps the
rest, so a pack hardened out of band is never downgraded):
```bash
node "${PLUGIN_ROOT}/scripts/apply-suppressions.js" --confirmed --plan <path>
```
If that `--confirmed` run exits non-zero (or `--plan` is missing/unreadable),
handle it exactly as step 5 does: tell the user the write did not complete, fall
back to the floor (`onboard.js --floor`), and continue to step 6. Do **not** run
the overlay below on a failed spine — nothing was written, so there is no
recommended base to overlay the changes onto.
Then overwrite **only the packs the user changed** with their chosen levels:
```bash
node "${PLUGIN_ROOT}/scripts/onboard.js" --posture '<changed-packs-json>'
```
`<changed-packs-json>` carries **only** the packs the user adjusted (not the
full 8-pack map), so the packs kept as recommended keep the fill-gaps-safe
values the spine wrote and only the user's explicit, downgrade-approved changes
overwrite.
If that overlay exits non-zero, the spine already wrote the recommended base, so
the store holds a valid posture — but **not** the user's overrides. Tell the user
their adjustments did not save and the store holds the recommended posture, then
continue to step 6. Do **not** report the adjusted posture as saved on a failed
overlay.
On success, present the applying-frame confirmation the **spine** printed —
`✓ Set all 8 detection categories · set aside N routine results · Ready: …` —
the store now holds the adjusted posture. The overlay's own smaller
`✓ Set all N detection categories` line (the count of just the changed packs)
is bookkeeping — **do not show it**; the applying frame reports the full
8-pack posture. Then continue to step 6.
**On "Back to recommended"** — take the **Yes, apply** path instead: continue
to step 5 and apply the previewed plan verbatim with no override.
Do **not** write anything until the user has explicitly confirmed at step 4
(Yes, apply) or saved at step 4b (Save adjusted).
## 5. Write the posture and suppressions
On confirmation, run the adapter again with `--confirmed --plan <path>`, passing
the **plan-file path the preview printed in step 3** (`Plan saved to: <path>`). It
reads that saved plan back and applies it **exactly as previewed** — establishing
the **full 8-pack posture** the recommended view showed (the reviewed
evidence packs overwrite; the conservative severity floor fill-gaps the remaining
packs, so a pack the user had already hardened out of band is never downgraded) and
writing one 30-day suppression per confirmed false positive **without re-running the
backfill or the judge**. There is deliberately **no `backfill.js` pipe here**:
re-scanning and re-judging would produce a fresh, non-deterministic plan and
silently defeat the human gate the user just approved.
The posture overwrite and the suppression writes are applied as a **single
all-or-nothing transaction**: a mid-batch failure rolls back the posture change
too, so the store is never left half-applied. That is why the floor fallback below
is safe — a non-zero exit means **nothing** persisted, so re-applying the
conservative floor cannot collide with a partially-written posture.
```bash
node "${PLUGIN_ROOT}/scripts/apply-suppressions.js" --confirmed --plan <path>
```
The script prints the applying confirmation — `✓ Set all K detection categories
· set aside N routine results · Ready: …` — with both counts threaded from the
real write. Show that line to the user.
If `--plan` is missing or the file is unreadable/invalid, the adapter **fails loud
(non-zero) and writes nothing** — it never falls back to a re-judge. Treat that
like the `--confirmed` failure below: tell the user the write did not complete,
fall back to the floor, and continue to step 6.
If the `--confirmed` run exits non-zero, tell the user the write did not
complete, fall back to the floor (`onboard.js --floor`), and continue to step 6
so setup still finishes.
## 6. Show the installed summary and hand off to the dashboard
Run the first-run script and relay its **install-complete summary** AKA_SHOW
region per the execution contract (live findings/recommendation counts, the
health score, and the per-category posture just written or floored). The
script wraps that summary in a Markdown code fence; paste it between the
markers exactly as printed and do **not** add another code fence, strip the
fence, or reformat it (it is space-aligned monospace that Markdown would
otherwise collapse).
Pass the **surfaced count** captured from step 3's calibration frame
(`counts.important`) as `--surfaced <count>` — this is the 'N worth a look' figure
the script emits in its own machine-readable handoff payload — but only when step 3
carried a surfaced count forward (it printed a `Plan saved to:` path). If the
calibration fell back to the floor (no plan path in step 3 — a fallback branch, or
the scan-ran-clean empty state whose zero-count frame carries nothing to look at),
**omit `--surfaced` entirely** — the script then withholds that payload rather than
fabricating a count.
Alongside it, pass the **surfaced live-key count** — the number of surfaced
live-key secret findings, which is the length of the calibration frame's
`maskedFindings` array (absent ⇒ 0) — as `--live-keys <count>`. This is the
narrower secret subset of the surfaced count; it gates the remediation
chain-entry the handoff offers, so a calibration that surfaced only non-secret
findings passes `--live-keys 0` and offers no remediation.
When `--surfaced` is passed, the script appends that handoff payload as a single
JSON block delimited by `<<<AKA_FRAME_JSON` … `AKA_FRAME_JSON>>>` after the fenced
card. Like step 3's calibration frame, do **not** show this block to the user — it
is additive and machine-only; only the fenced install summary above is
user-facing.
```bash
node "${PLUGIN_ROOT}/scripts/firstrun.js" --surfaced <count> --live-keys <count>
```
**Then hand off to the dashboard.** When the payload carries a positive
`worthALook` count, ask explicitly — per the execution contract's decision rules
— using that count for `N`:
**N worth a look — want to see them in the browser?**
1. **Review leaked keys** — "let's deal with the exposed keys I found" —
_(offer this option first only when the payload's `options` include
`enter-remediation`, i.e. `liveKeys > 0`)_; entering it starts the
secret-leak remediation chain on the surfaced live keys. This composes with —
never replaces — the dashboard handoff below, so both stay reachable.
2. **Open dashboard** — "open the local dashboard on what I found"
3. **Not now** — "stay here — you can open it anytime"
Use the payload's `worthALook` value for `N` verbatim — do not invent or round it.
Offer **Review leaked keys** exactly when the payload's `options` carry the
`enter-remediation` entry (never otherwise); the **Open dashboard** / **Not now**
handoff is always present. If the payload was withheld (the floor fallback, or
nothing surfaced), skip this handoff question rather than inventing a count.
**If they choose "Review leaked keys"** — run the secret-leak remediation entry's
**present** mode, feeding it the calibration frame block you captured in step 3
(the same text `maskedFindings` came from) on stdin:
```bash
node "${PLUGIN_ROOT}/scripts/remediate.js" <<'AKA_FRAME'
<the <<<AKA_FRAME_JSON … AKA_FRAME_JSON>>> block captured in step 3, verbatim>
AKA_FRAME
```
It prints the decision as human-facing text, then a machine-readable block
delimited by `<<<AKA_FRAME_JSON` … `AKA_FRAME_JSON>>>` carrying the same decision
structured (do **not** show that block to the user). The human text has three
parts, all of which you **show to the user verbatim, in order**:
1. the templated count line ("I found N exposed secret keys sitting in old
transcripts."),
2. the fenced finding table (provider, masked token, where, state), and
3. inside that same fence, a most-exposed-first recommendation line and a
secret-scan chaining line.
This entire human-text block is the entry's AKA_SHOW region — relay it per the
execution contract, pasting it between the markers exactly as printed — do not
drop the recommendation or chaining lines, and do not paraphrase.
Alongside that fenced block, explain the findings in plain language grounded
in the same `maskedFindings` array the block came from — what each finding is
and why it matters, not a bare recital of the count line above it. The same
grounding discipline as step 4's narration applies here: every count you
speak matches the frame's own count, and every value you reference stays
masked. With no `maskedFindings` present there is nothing to narrate beyond
the count line and table already shown — do not invent an explanation.
Then ask — per the execution contract's decision rules — offering exactly these
four options, in order (each option's label maps to the `--option` id shown in
parentheses):
1. **Redact + rotation checklist** (`redact-rotation-checklist`)
2. **Redact only** (`redact-only`)
3. **Set 'secret' to redact** (`set-secret-redact`)
4. **Leave** (`leave`)
**If they chose "Redact + rotation checklist" or "Redact only"** — before running
the route, ask a second question presenting the standing-posture prompt,
offering exactly these four options, in order (each option's label maps to the
`--posture` level in parentheses):
**Set the 'secret' detection level**
1. **Redact** (`redact`)
2. **Warn** (`warn`)
3. **Block** (`block`)
4. **Monitor** (`monitor`)
Then run the entry's **route** mode ONCE with the chosen redact option's id AND
the chosen posture level, feeding it the SAME calibration frame block again on
stdin:
```bash
node "${PLUGIN_ROOT}/scripts/remediate.js" --option <id> --posture <level> <<'AKA_FRAME'
<the same block>
AKA_FRAME
```
Never run the route a second time for this choice — a repeat call would strike
the already-redacted keys again and corrupt the reported count. Show its printed
result verbatim, in order. For "Redact only" that is the redaction confirmation
then the standing-posture confirmation. For "Redact + rotation checklist" it is
the standing-posture confirmation then the resolved rotation-checklist summary —
which reports the redaction itself, so the script does not print a separate
redaction confirmation ahead of it.
**If they chose "Set 'secret' to redact" or "Leave"** — run the entry's **route**
mode with the chosen option's id (the id in parentheses above, e.g. **Leave** →
`leave`), feeding it the SAME calibration frame block again on stdin:
```bash
node "${PLUGIN_ROOT}/scripts/remediate.js" --option <id> <<'AKA_FRAME'
<the same block>
AKA_FRAME
```
Show its printed result verbatim — a standing-posture confirmation, or (choosing
"Leave") a plain note that nothing changed. This entry reads its findings from the
calibration frame alone and holds no wizard state of its own, so it works
identically from any caller.
## 7. Offer the AKA CLI + local dashboard (opt-in)
Now that the plugin is set up, offer the optional **AKA CLI + local dashboard** —
a richer, still-fully-local surface over the same `~/.aka` store this plugin
writes. The plugin works completely on its own; this is additive (and the path to
future multi-agent support). Ask — per the execution contract's decision rules:
**Want the AKA CLI + dashboard too?** — "The `aka` CLI adds a local user
interface + terminal dashboard and on-demand scans."
1. **Yes, add it** _(recommended)_ — "adds the `aka` command: stats, a terminal
dashboard, a local user interface, and on-demand scans"
2. **Not now** — "skip — you can add it anytime with the one-liner below"
**Yes, add it** is the install authorization — run the bootstrap installer
directly, with no second question (it downloads the self-contained `aka` binary for
their platform — no Node.js or npm required — and links it onto PATH). Run the
line for their OS:
```bash
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/akasecurity/ai-tc/bin-latest/tools/installer/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/akasecurity/ai-tc/bin-latest/tools/installer/install.ps1 | iex
```
The one-liner pins to the latest published binary release **tag** (`bin-latest`),
never `main` — each binary release (`bin-v*`) moves that tag to its commit, and
the installer verifies the downloaded binary against the release's `SHA256SUMS`
(fail-closed), so a corrupted or tampered download is refused. To pin an exact
version instead, set `AKA_INSTALL_REF=bin-v<version>` before running the line. If
no `bin-v*` release exists yet the one-liner fails closed rather than guessing.
After it completes, point them at `aka init` then `aka dashboard`. If they chose
**Not now**, show the one-liner once so they can add it later, and move on —
declining keeps the plugin fully functional standalone.
**Fail open.** This install is optional, so it must never derail the session. If
the installer fails for any reason — Node missing, network/registry error, a
non-zero exit, or the checksum check rejecting a bad download — briefly report
what happened, show the one-liner so they can retry later, and continue the
wizard normally. The plugin is already fully set up and works on its own; a
failed CLI install changes nothing about that.
**Close the wizard.** The first-run summary already confirmed the saved posture
and pointed at the aka-health skill. Whichever way the CLI offer went —
installed, declined, or a failed install you already reported — end with one
warm close: "That's it — I'm watching out for Antigravity going forward."
Before you finish, confirm every AKA_SHOW region on the path you took was
relayed to the user. If you summarized one instead of pasting it, paste it now.
## Known limitations
Antigravity's hook contract is narrower than Claude Code's in several ways that
change what AKA can actually do. Be honest about every one of these if the user
asks why something wasn't caught — never imply coverage this plugin does not
have.
**The IDE does not run plugin hooks.** Skills and rules from a plugin load in
the Antigravity IDE and show under Customizations, but the `hooks.json` entries
appear never to fire there; hooks execute in the `agy` CLI. Google has not
documented this either way, so treat it as observed behaviour rather than a
guarantee. The practical consequence is the important part: **AKA's live
detection and blocking only apply to CLI sessions.** Work done in the IDE is not
scanned as it happens. The AKA skills still work in the IDE (they read the same
local store), and the worktree scan (`aka scan`) and the dashboard cover IDE
work after the fact.
**Prompts cannot be blocked or redacted.** Antigravity has no `UserPromptSubmit`
equivalent, and its `PreInvocation` event — the one that fires before the model
is called — carries no prompt text. No event on this host hands a hook the
user's message before the model sees it, so a secret pasted into a prompt
reaches the model. AKA records it (so it appears in findings and the dashboard)
but cannot stop it. This is the single biggest difference from the Claude Code
plugin, where a flagged prompt is blocked outright.
**A redact policy blocks instead of masking.** Claude Code's PreToolUse can hand
back rewritten tool arguments; Antigravity's cannot — its output is
`{ decision, reason, permissionOverrides }` with no field for modified args. So
when a policy says "redact", AKA denies the tool call and explains what to
remove, rather than masking the value and letting the call run. This applies to
file writes too, not just shell commands.
**A warn is recorded but not shown inline.** PreToolUse's output has no
message channel (its `reason` accompanies a deny), so a `warn` policy captures
and ledgers the finding without printing anything in the session. Point the user
at the aka-findings skill or the dashboard to see warns.
**Tool results are not scanned live.** Antigravity's PostToolUse payload does
not include the tool's output at all — it carries the tool call again plus a
step index and an optional error. There is no way for a hook to read, rewrite,
or withhold a tool result on this host. Secrets that appear in command output
are therefore caught after the fact, not before the model sees them.
**History is scanned; token usage is not.** Because of the two gaps above, the
only place prompts and tool results exist for AKA to read is the session
transcript — and the historical scan reads Antigravity's real record shape, so
secrets in past prompts and model messages **are** found by this wizard's
calibration and by the backfill. Three things in that same file are still not
read, and each under-reports rather than mis-reports:
- **Token usage and cost produce nothing.** The transcript carries no token,
model or cost field at any depth, so the reconcile worker recognizes nothing
and usage reporting stays empty for Antigravity sessions. Do not tell the user
their token spend is being tracked here.
- **Tool-call arguments are not scan input.** A secret sitting in a
`run_command` command line or a `write_to_file` body is not found by the
_historical_ scan. The live PreToolUse hook does scan those, so this gap is
about past sessions, not current ones.
- **The truncated transcript copy is skipped.** Each conversation stores a
complete file and a truncated one; AKA reads the complete file, so nothing is
double-counted. A conversation that has only the truncated copy is still
scanned, but its longer records were already capped on disk and whatever was
cut is not recoverable.
Live prompt capture remains impossible for the reason given above — no event on
this host hands a hook the prompt text — so what the historical scan finds is
what had already been written to disk.
**`multi_replace_file_content` is not scanned.** Its arguments nest the edits in
a container whose shape is not documented, so AKA does not guess at it. Single
-edit writes (`write_to_file`, `replace_file_content`) and shell commands
(`run_command`) are scanned.
**A hook that gets stuck denies the tool call, and it will not look like AKA.**
This host treats a hook that prints nothing as a `deny`, so AKA's hooks always
print an explicit allow — on a crash, on bad input, and on their own 8-second
watchdog. That watchdog is a timer, so it cannot interrupt work that blocks the
thread: if the local store is heavily contended (several AKA processes writing
at once), a hook can be killed by Antigravity's own 10-second timeout before it
prints anything, and the user sees "Tool call denied by <hook>". If a user
reports a denial with no AKA reason attached, that is the likely cause — it is
not a policy decision and there is nothing wrong with their command. Retrying
usually succeeds, since the contention that caused it is transient. Do not
describe AKA's fail-open guarantee to a user as absolute; it covers every hook
that gets a turn on the event loop, which is everything except a thread that is
already blocked.
**If the plugin's hooks do not seem to fire at all**, the likely cause is the
`${PLUGIN_ROOT}` variable in `hooks.json`. It is not documented for Antigravity,
so each command falls back to the default CLI install location
(`~/.gemini/config/plugins/aka-antigravity`). If the plugin was
installed somewhere else, edit `hooks.json` and replace `${PLUGIN_ROOT:-…}` with
the absolute path to the plugin directory.
**The reversible secret vault is not wired for Antigravity sessions**, so this
wizard does not offer the vault-consent step: everything AKA redacts here is
one-way (the safe direction), and nothing this plugin captures is ever vaulted —
even when vault consent was granted through the Claude Code wizard on the same
machine. Vault pointers minted elsewhere render as literal `[[aka:...]]` tokens
in Antigravity output (there is no display-side hook to badge them), and a
pointer inside a shell command is denied rather than executed or substituted.
If the user asks about recovering a redacted value, point them at the vault
surfaces that do exist on this machine (`aka vault show`, the dashboard's
Vault page) rather than implying this plugin can reveal anything.
**The model-judge step needs an `agy` with the streaming stdin interface.** It
runs `agy --input-format stream-json --output-format stream-json` and writes the
prompt to the CLI's standard input, rather than putting it on the command line.
An older `agy` that does not accept those flags exits non-zero and the step
reports `agy judge subprocess failed`, with no detail beyond the exit status —
the subprocess's own output is deliberately discarded, because it can echo the
raw findings. If a user hits that, the first thing to check is the `agy`
version. Everything before the model pass is unaffected: the calibration
questions, the consent-gated historical read and the local ruleset scan all run,
and every finding is still detected and redacted locally.
**On Windows, a stalled model call may not be bounded.** Where `agy` is a batch
shim (`agy.cmd`, what `npm i -g` writes), AKA has to reach it through
`cmd.exe`. The 180-second limit applies to `cmd.exe`, not to the `agy` process
it starts, so a model call that hangs can hold the step open past that limit
instead of failing cleanly. If the step appears stuck with no output, that is
what it looks like; interrupting it is safe, and nothing is written until the
plan is confirmed. This is not specific to this plugin — the Claude Code and
Codex wizards reach their own CLIs the same way — and it does not arise where
`agy` is installed as a real executable, which AKA then runs directly.
Scanned 8/31/2026
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!