Skip to content
Back to skills

Cactus

ASecurity

Ask the human without stopping work. Use cactus instead of AskUserQuestion whenever a question can be posted now and collected later, whenever several decisions belong to one thread, whenever you are announcing what you will do anyway and only need a veto, whenever a command needs approval, or whenever a verify block or an ordered checklist should stay on the human's board across sessions. Triggers on "cactus", "ask the human", "park this question", "put it on the board", "I need a decision b...

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 5, 2026
ai-agentsgoshellbashsqlgitbackend

Works with

  • claude code
  • claude desktop
  • cursor
  • terminal
  • cli
  • mcp

Security analysis

A100/100

Pro scans all 7 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add eighteyes/cactus --skill cactus --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cactus?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cactus
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/eighteyes-cactus-cactus/badge)](https://www.skillsdirectory.com/skills/eighteyes-cactus-cactus)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cactus
description: Ask the human without stopping work. Use cactus instead of AskUserQuestion whenever a question can be posted now and collected later, whenever several decisions belong to one thread, whenever you are announcing what you will do anyway and only need a veto, whenever a command needs approval, or whenever a verify block or an ordered checklist should stay on the human's board across sessions. Triggers on "cactus", "ask the human", "park this question", "put it on the board", "I need a decision but keep going". Do NOT use for a question whose answer you need before the next keystroke and that has no sensible default; that is AskUserQuestion.
---

# cactus — the durable inbox between you and the human

## Choose your runtime instructions

This file is the shared Cactus workflow. Read the instructions for the host
running you:

- [Claude Code](CLAUDE.md) — Claude plugin hooks and Herdr identity.
- [Codex](CODEX.md) — Codex plugin hooks and the Codex session identity.
- [Grok](GROK.md) — webhook wake-up.
- [Claude Desktop and other MCP hosts](DESKTOP.md) — the verbs are
  `cactus_*` tools, and answers are read at the fork.

The host-specific file changes only identity and wake-up mechanics. The row
semantics, authoring rules, and ownership rules below apply everywhere.

cactus is a SQLite inbox. You post a row, get a key back, and keep working. The
human answers in `cactus --tui` on their own schedule, from any project, and you
read the answer when you reach the fork. Rows survive the session, so the next
agent in this repository sees them too.

Full syntax lives in the tool, not here. Read it once per session before any
unfamiliar verb:

    cactus --agent-help

## Preflight

    command -v cactus >/dev/null && cactus where

`cactus where` prints the project the row will be filed under. Agent verbs see
only that project; if it is wrong, `cd` before asking.

## Workflow, required

    1  ask       post every decision the human makes here, not in chat;
                 --recommend LABEL --confidence L when you have a pick, -f
                 for every file the question is about, --agent on every row
    2  wait      a blocking ask (ask, run) waits for the human by default.
                 Post it as ONE backgrounded command (Bash run_in_background);
                 it waits, and its exit is your wake-up. No monitor.
    3  work      do everything the answer does not block while it waits
    4  inspect   on wake, `cactus get` the row (the background output holds
                 the answer); the next-turn frontier lists the rest
    5  clear     your own rows, by key, once acted on

`steer`, `notify`, `review`, `plan` and `data` never wait. `--no-wait` posts a
blocking row and returns at once; `--no-block` makes the row non-blocking.
A foreground `cactus ask`/`run` that would wait is refused by the plugin's
PreToolUse hook: rerun it with `run_in_background: true`.

## Blocked by a permission prompt

Do not stop and do not ask in chat. In Claude Code auto mode the plugin's
`PermissionDenied` hook has already posted the denied command as a `cactus run`
row in thread `denied`. Do not post it again: a second row is a second
approval, and the second run fails. Find the hook's row and wait on it with
one backgrounded call; its exit wakes you:

    cactus list -s open -t denied --agent "$AGENT"
    cactus get KEY --wait --agent "$AGENT" --json

Post the row yourself only when no `denied` row holds the command (a host
without the hook, or a prompt the hook did not see), also backgrounded:

    cactus run "pnpm exec playwright install" --agent "$AGENT" --why "denied in auto mode"

`--why` becomes the row's context. The human approves or denies it from the
TUI; the wait exits with the answer, `approve` or `deny`. On approve, re-read
the row: `cactus get KEY --json | jq '.[0].result'`. A non-null `result`
means the TUI already ran it (exit code, a 50-line tail, a log path); act on
that. A null `result` after `approve` means the human approved from the CLI
and you run it yourself. Never run it before `approve`.

## Identity

`cactus ask` refuses a row without `--agent`. The session-start hook prints
the value to pass, resolved through herdr when the session runs in one. Outside
herdr, choose one stable value for the session and use it on every ask and
clear. A bare pane id is not an identity: it outlives the session and the next
occupant inherits your rows.

Clears are scoped by owner. `--thread`, `--here` and `--all` clear only rows
posted under the `--agent` you pass; another agent's key is refused; a row with
no owner clears by explicit key only.

## When cactus, when AskUserQuestion

    AskUserQuestion   the answer gates the next action and no default is safe
    cactus ask        the answer gates a later action; post now, work until then
    cactus steer      no gate: say what you will do, proceed, let a tap redirect
    cactus run        a command needs a yes before it runs
    cactus review     a verify block the human re-checks as the work changes
    cactus plan       an ordered checklist both sides tick
    cactus notify     an FYI the human dismisses; no answer expected
    cactus data       chunks the human copies; each copy is a verdict

There is no fork act. A fork in the conversation is an `ask` with two or three
`-c` options, one per direction, and it becomes a `steer` with `--chosen` the
moment one direction is the sensible default.

Parking the human on a question you could have answered yourself is the failure
mode. It is measured: `cursor.blocked` in `cactus feed` counts open blocking
rows per agent, for rows posted with `--agent`. Reach for `steer` first, `ask`
when proceeding under any assumption would waste the work, a waiting ask only
when the very next step depends on it; otherwise post it `--no-wait`.

## Batching

Post every question you can see now, under one thread, with `--no-wait`. Follow
up in the same thread with `-p KEY` when an answer opens a new question. A set
of taps is cheaper for the human than a drip of interrupts across an afternoon.

When work blocks on the batch, wait on all of it with one backgrounded call;
it exits once every key has settled (a blocking row leaves `open`; a
review/plan/data/steer/notify row gets a new verdict, tap, or clear), and that
exit is your wake-up:

    cactus get q7 q8 q9 --wait --agent "$AGENT" --json

Otherwise leave the batch to the next-turn frontier.

## Authoring a row

Options are mile posts: two or three, mutually exclusive, each one a thing that
actually happens. Never "other". Split each on the first colon, label then
description.

Format the question itself for an 80-column terminal: it may use two or three
rendered lines, but no more. Cactus warns when an ask or edit wraps past three
lines; decompose a larger decision into follow-ups instead. Hard-wrap context
and choice descriptions at 80 columns, with blank lines between paragraphs.

    cactus ask "Which auth backend?" \
      -c "oidc: existing IdP" \
      -c "local: bcrypt table" \
      --context "Staging tenant is provisioned. Local means owning password reset. Default if unanswered: oidc." \
      -f docs/auth-spec.md \
      -t auth --agent "$AGENT" --recommend oidc --confidence high --why "tenant already exists"

A description may span lines. A line starting `+ ` is a pro, `- ` a con; the
rest is the summary. The card shows them as green and red marks under the choice.

    -c $'oidc: existing IdP\n+ tenant already provisioned\n- couples us to IdP uptime'

`--context` carries what the human cannot see from the labels: what you tried
and what it cost, the numbers, what breaks under each option, what happens if
nobody answers. Never a restatement of the question, never reassurance.

`--recommend` (with `--confidence`) preselects the pick, so enter alone
submits it; the row still waits for the human. `--chosen` on a steer does not
wait: you proceed with it.

`--word SHORT` gives boards a stable label. Set it when a project has many rows
whose text starts the same way.

`-f PATH` attaches a file. Attach every file the question is about: the plan,
spec, diff, config, or draft the human would otherwise have to go find. They
open it from the card with `f` (view) or `F` (edit), or preview it inline
with `o` (a diff vs git HEAD when changed). Repeat for more;
`edit -f` replaces the list. A row about a file with no `-f` is a row the
human answers blind.

`--site URL` (http or https) gives the human a `w` key that opens the URL.
`edit --site ""` clears it.

## Steer: proceed, invite a veto

    cactus ask "Using the staging tenant for the migration dry run" \
      --act steer --chosen staging -t auth --agent "$AGENT" \
      -c "staging: provisioned, disposable" \
      -c "prod-shadow: real data, read only"

Then keep going. Before the irreversible step, re-read the row: a tap on the
other option redirects you.

## Block only when blocked

    cactus ask "Safe to drop the legacy column?" --confirm --agent "$AGENT" --timeout 600

Run it with `run_in_background: true`. It prints the key at once, waits (default
timeout 3600s, `--timeout` overrides), and prints the answer on exit: that exit
is your wake-up. Exit 2 is the timeout: proceed on the default you stated in
`--context`, do not treat it as an error. A row the human
clears returns exit 0 with status `cleared`, which is a decline: check the
status, not just the exit code.

## Approve a command

    cactus run "alembic upgrade head" --agent "$AGENT" -t ship --why "schema is one revision behind" --timeout 900 --json

Run it with `run_in_background: true`; it waits for the verdict and its exit
wakes you. `cactus run` posts the command as the row text, `--why` as its context. The
human sees the command and runs it from the TUI with `R` or `y`, which
records `result` on the row. The verdict labels are `approve` and `deny`.
Read `result` before doing anything: non-null means it already ran, null
after `approve` means run it yourself. `review --run` still attaches a
command to any other row when a verify block needs one.

## Persistent rows: review and plan

Both are born `live`, take a verdict every time the work is re-checked, and stay
on the board until cleared. Post them once at the start of a piece of work and
update them as it moves.

    cactus ask "Does the build verify?" --act review -t ship --agent "$AGENT"
    cactus review q8 --look-at "the diff" --run "pytest -q" --pass "0 failures" --fail "any failure" --then "tag the release"

    cactus ask "Release steps" --act plan -t ship --agent "$AGENT"
    cactus plan q9 --step build --step test --step tag
    cactus plan q9 --done 1          # 1-based at the CLI

The latest verdict is `answer`; the full log is `answers`. Re-read rather than
cache: a human can undo a verdict and the row reads `open` again.

Read a review or plan row with `cactus get KEY --agent "$AGENT"`: that read is
what tells the human you heard the verdict (the card shows `sent`, then
`heard ✓`). After acting on a verdict, respond with `plan`, `review`, or `edit`
and `--agent "$AGENT"` (the card returns to normal), and `clear` the row when
the work is done.

## Data: hand over chunks

Also persistent. Each `-c` is one chunk — SQL, a command, a snippet — label
then body, split on the first colon; the body may be multi-line.

    cactus ask "Backfill queries" --act data -t db --agent "$AGENT" \
      -c "count: SELECT count(*) FROM orders WHERE backfilled IS NULL" \
      -c "run: UPDATE orders SET backfilled = now() WHERE backfilled IS NULL"

In the TUI, a digit copies that chunk to the clipboard; each copy appends a
verdict naming the chunk's label, readable with `cactus get`. `d` retires the
row.

## Collect on the next turn

A backgrounded waiting ask wakes you when it exits, even on an idle session.
Rows posted `--no-wait`, and review/plan rows, come back through the
next-turn frontier, which shows answered, elaborated, and open rows. Use
`cactus get KEY --agent "$AGENT"` before acting on a review or plan verdict.

## Elaborate or decompose: the human wants the question changed

`e` on a row moves it to status `elaborate`. The next-turn frontier and
`cactus get` show its `hint` (what the human typed, or null) and
`instruction` (the hint, else: rewrite plainly, no jargon, add what you
tried, the numbers, what each option costs, what happens if nobody
answers). Rewrite the row in place; the key stays:

    cactus edit q7 --agent "$AGENT" --context "..." [--text "..."] [-c "label: desc"]...

The row returns to `open`; your own `--agent` stream does not echo the
`edited` event back at you (q334). `edit` also works on any open or live row
you own without a request, so fix a typo or add a fact the moment you notice
it. `-c` replaces the choices and drops a `--recommend` that no longer names
one. A human `u` on an elaborate request reads as `withdrawn`: re-read the
row before rewriting.

`D` in the TUI asks for decomposition through the same `elaborate` event.
When its instruction says to decompose, do not edit the original row. Post
each smaller, independently answerable question as a follow-up (`-p q7
--no-wait`) with the same `--agent`; the follow-ups inherit its thread. Once
they are posted, clear the original row with `cactus clear q7 --agent
"$AGENT"`, then wait on the follow-ups as a batch (see Batching).

## Read back

    cactus get q7 --json | jq '.[0]'              get returns a list, one row per key
    cactus list -s answered -t auth --json        a thread's verdicts
    cactus feed --json --here                     the whole actionable inbox, one document

The answer lives under `.answer`, not at the top level: `.answer.selected[]`,
`.answer.text`, `.answer.skipped`, `.answer.created_at`. The top-level `text`
is the question. `skipped` means the human saw it and chose not to decide;
act on your stated default.

Keys are `qN` within the project. Across projects (`--all`) use the row's
`ref`, `LABEL:qN`, which every verb accepts as a key.

## Decision records

Every answer, undo, verdict and clear rewrites one file per row in the
project, so the decision travels with the code. Commit it with the change it
governed.

    .ai/cactus/qN-ID-SLUG.md    decision record, rewritten on each answer, undo, verdict, clear; commit it

Set `CACTUS_RECORDS=0` in a test harness that runs from a real repository, or
the scratch rows write records into it.

## Retire

    cactus clear q7                                retire, keep the transcript
    cactus clear --thread auth --agent "$AGENT"    retire your rows in a thread
    cactus clear --purge q7                        delete

Clear what you have acted on. A stale open row is a question the human answers
for nothing.

## Exit codes

    0  ok      1  error      2  --wait timed out      3  nothing matched

`cactus ask --wait` on a `--no-block` row, `steer`, `notify`, `review`,
`plan`, or `data` is exit 1. `cactus get KEY --wait` on one returns on the next
change: a new verdict, a tap, a clear.

## Webhook owners

Agents that use external webhook delivery need a per-agent webhook wake-up.
Read [GROK.md](GROK.md) for the external-agent recipe and
[WEBHOOK_SETUP.md](WEBHOOK_SETUP.md) for the map, smoke test, and
transport details. Do not set global `CACTUS_POKE` in a normal shell profile:
it overrides Herdr delivery for every agent.

Files in this skill

  • CLAUDE.md1.1 KB
  • CODEX.md2 KB
  • DESKTOP.md3.1 KB
  • GROK.md816 B
  • GROK_SETUP.md394 B
  • SKILL.md15 KB
  • WEBHOOK_SETUP.md8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…