Focused context
Structured answers, not free-form prose
A CLI returns one shape, every time. --json with stable error codes means the agent doesn't need to read paragraphs to know what happened — it branches on a field.
Open source · MIT
contextq is a tiny local CLI for coordinating agent work: named queues with built-in context, FIFO keys, and a durable journal on disk. No daemon, no server — just files and a small binary. Built for narrow, agent-facing tooling that stays predictable and out of the context window.
→ go install github.com/norlinga/contextq/cmd/contextq@latest
The premise
Once an agent is the one invoking your tool, the shape of its output, its error model, and the protocol it enforces all become first-class design surfaces. Modern command-line tooling has to consider agent-native workflows, not just human ergonomics.
Core thesis
"Good agent-facing tools are narrow, predictable, and deterministic. They give agents structured answers instead of ambiguous prose — keeping context windows focused, response times short, and behavior on a known path."
contextq is one small experiment in that direction: a CLI shaped for how an agent reads a queue, claims FIFO work, and reports outcomes — paired with a tightly-coupled Agent Skill the agent reads first.
Focused context
A CLI returns one shape, every time. --json with stable error codes means the agent doesn't need to read paragraphs to know what happened — it branches on a field.
Predictable cost
Agent-facing tools should be cheap to call and small in their output. A queue read is a few lines, not a Markdown wall the agent has to re-parse on every turn.
On the rails
contextq rejects races, duplicate work, and invalid state transitions at the CLI boundary — so the agent's plan can't drift into shapes the system was never built to handle.
What contextq is
contextq keeps project-specific named queues on disk to coordinate multi-agent work — or single-agent work split across time — without each turn rebuilding context from Markdown sprawl.
Every queue carries a required block of guidance text written for the agent. The first thing any consumer does is read the queue and follow its instructions.
Work items are bare string keys — file paths, issue IDs, task IDs. item pop is the only way to take work: it atomically claims the oldest available item (FIFO). There is no cherry-picking from item list or counts from queue read. A key can be re-enqueued for a fresh lifecycle.
Everything is appended to a per-queue events.jsonl under ./contextq/. Inspect it with cat, recover state from disk, and trust the history.
Local-first
No daemon, no server, no network. Just files on disk under ./contextq/.
Inspectable
Open events.jsonl in any editor. Replay the journal to derive current state.
Concurrency-safe
Per-queue file lock serializes mutations. item pop runs under the lock — the sole dequeue/claim path, with no races.
Quickstart
Install, create a queue with agent-facing context, push some work, take the next item with item pop, and complete it.
Build from source. contextq is a single Go binary with no runtime dependencies.
The context string is required — it's the agent-facing brief every consumer reads first.
Items are stable string keys. File paths work well; so do issue IDs or task IDs.
item pop is the only way to take work from the queue. It atomically claims the oldest available item under the queue's file lock — no races, no manual CLAIMED.
Every transition is in the event journal. history shows every lifecycle for a key, including retries.
Core concepts
Three nouns, five states. Everything else falls out of those.
Noun · 1
A durable container with a required context string for agents. Optionally named (names don't have to be unique; UUIDs disambiguate).
Noun · 2
A stable string identifier — file path, issue ID, anything meaningful. Unique per queue at any moment, but the same key can be re-enqueued for a fresh lifecycle.
Noun · 3
One pass through the state machine for one enqueue of a key. Each lifecycle gets its own item_id and its own history.
Five states, five legal transitions. AVAILABLE → CLAIMED happens only via item pop — you cannot set CLAIMED with item update. Terminal states are terminal — to retry, push the key again.
AVAILABLE · eligible to be claimed via item pop only
CLAIMED · owned by a consumer after a successful pop
DONE · completed successfully (terminal)
FAILED · attempted, didn't succeed (terminal)
CANCELED · intentionally abandoned (terminal)
Order is determined by enqueue time of each item lifecycle, not by first appearance of a key. Re-pushing an old key puts a new lifecycle at the back of the queue.
A push is rejected (duplicate_available_key) if the key already has an AVAILABLE lifecycle. Once that lifecycle terminates, the key can be pushed again.
CLI reference
Everything contextq can do in v1. Add --json to any command for machine-readable output and stable error codes.
Taking work: use item pop only. item list, item read, and queue read may show available keys or counts, but they do not reserve or claim work — another agent may pop between your inspection and your next command.
contextq queue list
List all queues, most-recently-modified first.
contextq queue create "<context>" --name <name>
Create a queue. The context string is required and immutable.
contextq queue read <queue>
Show context, counts, and last-modified info. Counts are informational only — refresh before item pop, not to pick a key.
contextq queue destroy <queue> -f
Hard delete the queue and its journal. Non-interactive; requires -f.
contextq item push <queue> <key>
Enqueue a key as a new AVAILABLE lifecycle.
contextq item pop <queue>
Only way to take work. Atomically pops the oldest available item (FIFO) under the queue lock. Returns CLAIMED — that state is not set via update.
contextq item list <queue> [--state S] [--key K] [--limit N]
Observation only — inspect backlog with optional filters. Do not use to choose work to claim; always item pop.
contextq item read <queue> <key>
Show the most recent lifecycle state and metadata for a key. For debugging or after pop — not a substitute for claiming.
contextq item update <queue> <key> DONE|FAILED|CANCELED
Transition the active lifecycle for a key to a terminal state. Only after you own the key via pop (CLAIMED is never an update target).
contextq item history <queue> <key>
Show every lifecycle and transition recorded for a key.
Agent skill
contextq ships with a canonical AGENT_SKILL.md — the contract every agent reads before invoking the CLI. The skill is the missing link between a generic LLM and a piece of project-aware tooling.
Why this matters: the skill plus the CLI plus the project-local contextq.config form a tight loop. The agent doesn't need to invent a coordination protocol every session — the tool teaches it the protocol on first read, including the rule that item pop is the sole path from available work to owned work.