Help us improve
Share bugs, ideas, or general feedback.
From humanloop
Pauses agent execution to collect human input: structured decisions, free text, or markdown document review. Use for approval gates, design tradeoffs, and multi-option picks where human judgment is required.
How this skill is triggered — by the user, by Claude, or both
Slash command
/humanloop:humanloopThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use the `hl` CLI to ask the human a structured set of questions (a *deck*) or get freeform comments on a markdown doc (a *review*). It opens a TUI (auto-splits a tmux pane when `$TMUX` is set), persists progress to disk, and returns JSON.
Share bugs, ideas, or general feedback.
Use the hl CLI to ask the human a structured set of questions (a deck) or get freeform comments on a markdown doc (a review). It opens a TUI (auto-splits a tmux pane when $TMUX is set), persists progress to disk, and returns JSON.
Every interaction is a kickoff: the launch call (hl deck ask, hl review open) spawns the human's TUI in a detached pane and returns a job_id in well under a second — it does not wait for the human. You collect the answer separately with hl job result. See Long-running: kick off, then collect — this is the single most important thing to get right, because a human may take many minutes or step away entirely.
Reach for hl when the next step materially depends on a human judgment you cannot make alone:
hl is for batched, structured review.The deck is read by a busy, technical human. Write with progressive disclosure so the reader can stop at any layer:
| Field | Role | Guidance |
|---|---|---|
title | Inbox label | Noun-phrase topic (≤4 words). The thing being decided, not the decision. Database, not Use Postgres. |
subtitle | TL;DR | One plain-English sentence framing the choice or stakes. Action-ready if the call is obvious. No jargon, no library names without context. |
body | ELI12 explanation | Plain language up top — audience is a smart engineer joining the codebase. Tuck anything denser (technical specifics, alternatives considered, edge cases) under a heading like ## Details or ## Alternatives so the reader can skip past. Every layer below the TL;DR is optional reading. |
options[] | Genuine alternatives | Two or more real picks. Empty array = freetext-only. |
allowFreetext | Comment + escape hatch | Set true when you want a comment alongside a choice, or to let the human write their own answer. |
Avoid: walls of jargon, raw schema dumps or stack traces in body, titles that bury the topic, subtitles that restate the title, options that are not real alternatives.
Every leaf reads one JSON object from stdin and writes one JSON object to stdout. There are no file-path arguments — pipe the input in.
hl deck validate if unsure.echo '{"deck":{…}}' | hl deck ask → returns {job_id, dir, follow_up} immediately. The human's TUI is now open in a pane; you are not blocked.echo '{"job_id":"…","wait":true}' | hl job result blocks until the human finishes, then prints the resolution. Run this backgrounded.id — never by index, since the human can skip questions.The human is slow and may walk away. Treat every interaction as fire-and-forget plus a deferred collect:
hl deck ask and hl review open return in <1s with a job_id. They never wait for the human. The follow_up string in their output tells you the exact collect call.hl job result + {"wait":true}. This is the call that blocks until the human finishes (or {"wait":false} to poll once — returns {error:"not_ready"} exit 1 if they're not done).run_in_background; you will be notified when it completes, and you stay free to do other work meanwhile. Blocking the foreground on a human who might take 20 minutes (and overrunning the 10-minute command ceiling) is the failure this design exists to prevent.hl job status → {state: live|done|failed|canceled, kind, age_seconds, last_event}. hl job logs streams JSONL events. hl job cancel is best-effort.hl deck update rewrites its questions and the pane reloads within ~1s (answers to surviving ids are kept).{
"title": "Capture pipeline decisions",
"interactions": [
{
"id": "db",
"title": "Database",
"subtitle": "Postgres or SQLite for the new capture store?",
"body": "Two services will write at the same time, which is the crux.\n\nPostgres handles concurrent writes natively. SQLite serializes them — fine at low traffic, but we expect bursts.\n\n## Details\nSQLite WAL still serializes writers; Postgres uses MVCC.",
"options": [
{"id": "pg", "label": "Postgres"},
{"id": "sqlite", "label": "SQLite"}
],
"allowFreetext": true
},
{
"id": "retry",
"title": "Retry policy",
"subtitle": "How aggressively should we retry publish failures?",
"body": "Affects the reliability budget. Too aggressive and we hammer downstream during outages; too lax and transient blips become user-visible.",
"options": [],
"allowFreetext": true
}
]
}
hl job result for a deck job returns a resolution envelope; the responses array is what you act on:
{
"responses": [
{ "id": "db", "selectedOptionId": "pg", "freetext": "Yes — concurrent writes are non-negotiable" },
{ "id": "retry", "freetext": "Exponential backoff capped at 5 attempts, then DLQ" }
],
"completedAt": "2026-04-20T15:23:00.000Z"
}
selectedOptionId is present when the human picked one of the listed options.freetext is present when the human typed a comment or freetext answer.responses may be shorter than interactions. Always look up by id.Every leaf is hl <noun> <verb>, reads one JSON object on stdin, writes one on stdout. -h on any node is the full spec.
# Deck (structured questions)
echo '{"deck":{…}}' | hl deck ask # kickoff → {job_id, dir, follow_up}
echo '{"deck":{…}}' | hl deck validate # preflight, no side effects
echo '{"job_id":"…","deck":{…}}' | hl deck update # rewrite a live deck; pane reloads
# Review (markdown doc feedback)
echo '{"file":"/abs/doc.md"}' | hl review open # kickoff → {job_id, output, follow_up}
# Collect / inspect any job
echo '{"job_id":"…","wait":true}' | hl job result # block for the human, then print result
echo '{"job_id":"…"}' | hl job status # state snapshot, never blocks
echo '{"job_id":"…","follow":true}'| hl job logs # stream JSONL events
echo '{"kind":"deck"}' | hl schema show # JSON Schema for an input type
Typical end-to-end flow:
# 1. Kick off — returns immediately with a job_id
JOB=$(echo '{"deck":{"interactions":[{"id":"db","title":"Database",
"subtitle":"Postgres or SQLite for the capture store?",
"body":"Concurrent writes are the crux. Postgres handles them natively; SQLite serializes.",
"options":[{"id":"pg","label":"Postgres"},{"id":"sqlite","label":"SQLite"}],
"allowFreetext":true}]}}' | hl deck ask | jq -r .job_id)
# 2. Collect — BACKGROUND this (the human may take many minutes)
echo "{\"job_id\":\"$JOB\",\"wait\":true}" | hl job result > /tmp/answers.json
# 3. Look up the answer by id
jq '.responses[] | select(.id=="db")' /tmp/answers.json
$TMUX is set, the TUI/editor opens in a detached pane; the caller keeps focus and returns immediately. Pass {"tmux":false} to force the current pane (degraded: the launch call then blocks in-process).dir in the kickoff output): deck.json/review.vim, the live progress.json/autosaved feedback, the terminal response.json/feedback.json sidecar, and job.log (JSONL). A killed job is recoverable — re-collect by job_id; appearance of the sidecar is the done signal.hl review open opens the doc read-only; if the file is rewritten on disk while the human reviews, the pane reloads it automatically (their line-anchored comments are kept). Edit the doc and they see the latest without reopening.sessionId), Haiku generates a short ANSI context block per interaction. Pass {"visuals":false} to skip it.0 — success; one JSON object (or JSONL for job logs) on stdout.{error, message, next}. Codes: bad_stdin_json | bad_input | deck_invalid | file_not_found | editor_not_found | job_not_found | not_ready | not_in_tmux | internal. next tells you how to recover.If the launch call can't open a pane (no $TMUX), it falls back to running in-process and blocks; run inside tmux so it can auto-dispatch to a new pane and return immediately.
Database ✓, Use Postgres ✗. Helps the human scan the inbox.## Details (or ## Alternatives) heading so it is skippable.npx claudepluginhub crouton-labs/crouton-kit --plugin humanloopGuides how to ask users questions interactively: which tool to use, batching strategy, and when to ask serially. Useful for collecting requirements, confirming decisions, or pausing for input.
Orchestrates subagents, task planning, and human-in-the-loop approval in Deep Agents. Covers SubAgentMiddleware, TodoListMiddleware, and HITL interrupts for delegated execution and workflow control.