About wokitoki
WokiToki (`toki`) is a local, blocking feedback CLI invoked by an AI during a conversation. It suits multi-part questions and feedback tied to specific text, presenting forms in a browser and returning block-mapped Result JSON on stdout.
Key features
- 01Local browser-based feedback
- 02Mix questions and report blocks
- 03Map answers by block ID
- 04Capture quotes, text, and images
- 05Write Result JSON to stdout
Use cases
- 01Collect feedback across multiple decisions
- 02Get responses to individual report passages
- 03Anchor replies to their source text
- 04Keep interaction files outside a repository
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ upex-galaxy/ agentic-user-skills --skill wokitokiAfter installing, type/wokitokiin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
cli/assets.d.tsindex.tsmarkdown.tsREADME.mdrender.tsschema.tsserver.tscli/ui/app.cssapp.jsreferences/examples.mdschema.mdsetup.mdDetails
From the project READMEWokiToki (toki)
WokiToki is a local, blocking feedback CLI the AI invokes mid-conversation. It serves a dark web UI, opens the browser, waits for the user to answer, and on submit prints a single Result JSON to stdout that the AI parses the same turn. The branding is "walkie-talkie": back-and-forth, one answer per thing. Command = toki; all tool output lands under ~/.toki/.
This skill is installed user-level (global): it loads automatically in every project, with no per-repo wiring. Because every file it writes goes under ~/.toki/ (never the cwd), running toki inside any repo leaves zero footprint in that repo — no .gitignore edits needed.
Everything is one engine: a spec is a list of blocks. A block with controls is a question; a block without controls is a report paragraph; mixing them in one spec is a hybrid form — for free.
Phase 0 — first-run binary check (before the first invocation in a session)
toki ships as source and is compiled to a single global binary on first use. Before invoking it the first time, ensure the binary exists; build it if missing:
command -v toki >/dev/null 2>&1 || bun build --compile "<skill-dir>/cli/index.ts" --outfile "$HOME/.bun/bin/toki"
<skill-dir>is this skill's install directory — user-level that is~/.claude/skills/wokitoki.~/.bun/binis already on PATH for Bun installs, so the freshly builttokiis immediately runnable.- Fallback (if
--compileis unavailable or the binary can't be written): run from source withbun "<skill-dir>/cli/index.ts" <specPath>— identical behavior, just a slower cold start.
Full first-run details (sizes, PATH, rebuild) → references/setup.md.
When to use vs AskUserQuestion vs inline prose
| Situation | Use |
|---|---|
| 1–2 option pick, single yes/no, ≤3 simple decisions | AskUserQuestion |
| >3 decision points at once (mix of single/multi/toggle) | WokiToki |
| A long multi-section explanation/report the user may want to react to one point at a time | WokiToki |
| You need the reply ANCHORED to the exact phrase it is about | WokiToki (highlight-to-quote) |
| Non-interactive / CI / no human at a browser | neither — emit plain output |
Why not the alternatives:
AskUserQuestionis capped (~4 questions × ~4 options), terminal-bound, has no rich free-text, and cannot display the reference content while the user answers.- Inline prose questionnaire produces unanchored replies: the AI has to guess which paragraph/point each sentence of the reply maps to. WokiToki returns per-block answers keyed by a stable
id, so the mapping is exact.
Spec schema in brief
{
"title": "Auth plan — decisions", // required, non-empty
"intro": "Optional markdown at top", // optional
"submitLabel": "Submit answers", // optional (default "Submit")
"blocks": [ // required, non-empty
{
"id": "q1", // required, unique within the spec
"content": "Which token strategy?", // markdown string (may be empty "")
"controls": { // OMIT for a report paragraph
"type": "single", // single | multi | toggle
"options": [ // required for single/multi, FORBIDDEN for toggle
{ "value": "jwt", "label": "JWT stateless" },
{ "value": "session", "label": "Server-side session" }
],
"required": true // optional (default false)
},
"text": { "required": true, "placeholder": "Justify" } // text ALWAYS present at runtime; required decided per-block
}
]
}
controlsabsent → report paragraph. Present → question. Mixed list → hybrid.controls.type:single(radio),multi(checkbox),toggle(boolean switch, nooptions).controls.requiredandtext.requiredare independent — either, both, or neither can be required per block.idis the stable AI-assigned key the answer is mapped back to.
Full contract (every field, defaults, validation rules) → references/schema.md. Worked copy-pasteable specs → references/examples.md.
Exact invocation
- Write the spec to a JSON file. Convention:
~/.toki/spec-<name>.json(aspec-<name>.jsonfilename makes the backup land at~/.toki/result-<name>.json). Keeping the spec under~/.toki/— not the repo — keeps the whole footprint out of whatever repo you are working in. - Run it (blocking):
toki <specPath>. This serves the UI, opens the browser, and waits. (First run in a session: do the Phase 0 binary check above.)- Flags:
--port <n>(default 4747, auto-increments if busy),--timeout <min>(default 1440 = 24h, fractional ok),--no-open(print the URL but do not open the browser),--help.
- Flags:
- Parse stdout. stdout carries ONLY the Result JSON — one object, no banner. All progress lines, the waiting URL, and errors go to stderr. Read stdout,
JSON.parseit, continue the same turn.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | submitted OK — Result JSON is on stdout |
| 1 | timeout (no submit within --timeout) or runtime error — nothing usable on stdout |
| 2 | bad spec (file unreadable or failed validation) — [toki] invalid spec at <path>: … on stderr |
A backup of the Result is also written to ~/.toki/result-<name>.json, but the stdout copy is authoritative for the AI.
User-absence protocol
A long session is expected — the default --timeout is 24h (1440 min), adjustable via --timeout <min>. A person may step away and leave the session open, so a non-zero exit is not necessarily an error:
| Exit | Meaning | What the AI does |
|---|---|---|
| 0 | submitted — Result JSON on stdout | parse it, continue the same turn |
| 1 | timeout elapsed with NO submission (or runtime error) | treat as the user being AWAY, not a failure — see below |
| 2 | bad spec (unreadable / failed validation) | fix the spec, then re-run |
| 130 | Ctrl-C (SIGINT) | the user cancelled — ask what they want next |
On exit code 1, do not error out or move on silently. Treat it as the user being away from the keyboard: post a brief standby message and offer to relaunch — e.g. "Dejé toki esperando, no llegó respuesta; sigo disponible — decime y lo relanzo." Keep toki ready to re-run with the same spec the moment the user is back.
Reading the Result
{
"submittedAt": "2026-05-29T12:34:56.000Z",
"blocks": [
{ "id": "q1", "controlAnswer": "jwt", "text": "Prefer JWT for scale", "quotes": ["token strategy"] }
],
"meta": { "answered": 5, "total": 8 }
}
Per ResultBlock, decode controlAnswer by the block's control type:
single→string(the chosen optionvalue) ornullif unanswered.multi→string[](chosen optionvalues;[]if none).toggle→boolean.- report block / no controls →
null.
Other fields:
text— the user's free-text for that block (empty string""if untouched).quotes—string[]of exact phrases the user highlighted from that block'scontent. These tell you precisely which words the reply emphasizes; weight them when interpretingtext.images— present only when the user pasted at least one image onto that block (or table row). Each entry is an absolute file path under~/.toki/the AI canRead— never inline base64.meta.answered— count of blocks with any answer (a control selection and/or non-empty text);meta.total— block count.
Match each blocks[].id back to the id you assigned in the spec to anchor every answer to its decision/paragraph.
Notes
- UI chrome is English (repo-artifact rule); the block
contentis whatever language you authored it in. - The CLI is decoupled (Bun built-ins only, zero external deps) so it ships as a standalone global binary — see
cli/README.md.