Skip to content

CLI

aiur exists so an agent can run Aiur on your behalf.

Design goalResult
Executor accessAn agent can operate a run from a terminal without asking a human to type commands.
Surface parityThe CLI targets feature parity with the GUI and TUI.

A human can of course type any of it. Most humans will not: they watch the TUI or the dashboard and let their Executor agent drive.

Run the command from the repository that owns the run. An instance is keyed to that project, so control commands address that repository's daemon.

What the CLI does

JobCommandsNotes
Start or attachaiur, aiur runBare aiur attaches to this repository's live session when one exists; otherwise foreground gives the TUI board and chat panes. Background is headless unless launched with --interactive.
Inspect live statestatus, agents, watch, alerts, usage, github-cost, github-usageRead-only reports from the running daemon.
Operate the fleetset max-agents, pause, resume, message, reset-budget, stop, restartSteers a live run.
Mirror a dashboard pageunits, commands, build-orders, analyticsRead-only terminal forms of the dashboard pages.
Act on durable recordsask, asks, executor-answer, executor-escalate, executor-moot, executor-emit, executor-listen, findingsDecision inbox, Executor events, and findings ledger.
Guard a repositoryguard-pr-deletionsRefuses a PR with excessive untouched deletions.

Background mode is the shape that matters for an agent Executor. aiur --bg starts the daemon with no board and no panes, the dashboard stays up, and every command below reads and writes the same live state through that detached daemon.

Start, initialize, and queue

SyntaxDefault or important interactionRunnable example
aiurAttaches to this repository's live tmux session when one exists; otherwise starts a foreground interactive run. Attachment does not create a second run or take teardown ownership, so detaching leaves the daemon healthy.aiur
aiur runExplicit foreground launch form. --bg makes it headless; --interactive restores terminal panes in a background session.aiur run --bg
aiur initInteractive setup detects the tracker and toolchain, writes .aiur/config, .aiur/hooks, .aiur/prompt.md, .aiur/alerts, and prewarm support when selected, then creates the repository state-node tree and warms the base build. For GitHub trackers it verifies repository access before offering CI and label setup, with token-specific recovery guidance when an organization-owned repository is hidden by GitHub's authorization-masked 404; it then asks once whether to use an App for the daemon and defaults to No (GITHUB_TOKEN), and choosing Yes points to the existing App setup guide. It also asks whether to enable Stream Deck voice input with ElevenLabs speech-to-text; answering yes writes the elevenlabs section, defaulting the key to the $ELEVENLABS_API_KEY environment reference. A resumed aiur init offers the same question when the saved config predates the section.aiur init
aiur init --forceRecreates generated configuration. Re-running without it preserves existing scaffold files.aiur init --force
aiur --todo 142 143Requires a running daemon and one or more numeric IDs, with commas also accepted. A stopped daemon exits nonzero.aiur --todo 142,143
aiur --todo 142 --onlyQueues the named IDs and asks GitHub to remove agent:todo from other pending tickets. It is GitHub-only, is bounded to 50 cleanup targets, and stops after three consecutive rate-limit failures. Cleanup is skipped if a requested ID fails, so the operation does not silently dequeue work after a bad request.aiur --todo 142 --only
aiur --bgStarts detached headless execution. Against an existing live session it exits successfully and names bare aiur as the attach command. A default headless session has no agent-list or chat panes; use the dashboard or control commands.aiur --bg
aiur --debugEnables debug logs and durable chat-pane recording for this run.aiur --debug
aiur --pauseCold-starts with the global provisioning switch paused.aiur --pause
aiur --max-agents 6Launch-only session cap. It wins over agent.max_concurrent_agents; Aiur warns when it exceeds that setting. status identifies the active binding.aiur --max-agents 6
aiur --interactiveRequests the terminal UI, including from a background launch.aiur --bg --interactive
aiur --headlessRequests no terminal UI. Background launch injects it unless --interactive is present.aiur run --headless
aiur --executorMarks the run as Executor-owned and registers its renewing principal in executor-roster. Recording is not gated on this flag: every run arms the supervised executor.# listener and the wake inbox, so PR-lifecycle, CI and attention records exist for a later agent to replay. What the flag adds is authority — created and deferred Commands are raised as needs-attention alerts only on an Executor-owned run. LISTENER absent is therefore always a fault.aiur --bg --executor
aiur --no-dashboardSuppresses the dashboard listener in foreground or background mode. It is rejected for Remote Control because its lifecycle hooks need the listener.aiur --bg --no-dashboard
aiur --host 127.0.0.1Overrides the dashboard bind host. A non-loopback host requires dashboard credentials.aiur --host 127.0.0.1
aiur --port 4000Overrides the HTTP port. 0 lets the OS choose a free port.aiur --port 4000
aiur --logs-root /var/log/aiurOverrides the daemon log root for this launch.aiur --logs-root /var/log/aiur
aiur --i-understand-that-this-will-be-running-without-the-usual-guardrailsRequired by the release parser; the launcher inserts it for normal run commands.aiur run --i-understand-that-this-will-be-running-without-the-usual-guardrails
aiur --versionPrints both the release version and shell dispatcher version without contacting or claiming a running daemon. If they differ, update aiur-cli before trusting that newer subcommands are available.aiur --version
Launch choiceBehavior
ForegroundShows the terminal board and chat panes. A later bare aiur from the same repository reattaches to that session.
--bgRuns headlessly but keeps the dashboard unless paired with --no-dashboard.
Host precedence--host wins over server.host, which wins over the loopback or safe Tailscale default.
Startup outputReports the usable dashboard URL and effective bind host and port.

When an unknown subcommand is routed through a release built from a checkout, Aiur also compares the dispatcher and checkout package versions. If the dispatcher is older, the error tells you to update aiur-cli instead of presenting the command as simply unavailable.

Inspect and operate a running daemon

SyntaxDefault or important interactionRunnable example
aiur helpPrints the current launcher usage.aiur help
aiur statusShows daemon and capacity status, including WAKES CURSOR <id> PENDING <count> for the durable Executor inbox and AGENTS occupied/max (binding: ...). A CPU-corroborated load or run-queue hold includes both the pressure and reclaimable-CPU thresholds; a high local load sample alone says the daemon still corroborates CPU contention. Every admission measurement carries sampled=<n>s ago, the age of the reading it was taken from, and gains a trailing STALE once the daemon has not refreshed it within two poll intervals (never sooner than a minute, never later than five) — so a load= figure minutes older than the live LOAD line beside it is visible as such rather than reading as current. A GitHub quota hold includes its resource, measured remaining/limit, and observation time, and becomes github_quota stale after two missed probes. Other bindings include config max_concurrent_agents, AIMD envelope, paused reservations, ticket supply, session max_concurrent_agents, or none; ticket supply means a recent poll found no queued ticket, while has not polled yet (with the POLL backoff countdown) is shown when the fleet is idle-backed-off or the last tracker fetch failed. When slots are free, the binding also names the effective ceiling's source (ticket supply; ceiling: config max_concurrent_agents vs has not polled yet (...; ceiling: session max_concurrent_agents)) so a restart that dropped a live set max-agents reads as config-sourced rather than as the operator's last command. When a build-gate lease is held or queued, status also prints BUILD GATE HOLDER slot=… pid=… command="…" held=… (and BUILD GATE QUEUED … waiting=…) so a pinned lease is attributable without reading process trees.aiur status
aiur usagePrints the current provider-meter observations and their known headroom.aiur usage
aiur github-costRanks GitHub API spend by the call site that caused it, in points and points per hour, and prints the reconciliation against the credential's own window beside it. Prints the admission ledger's retention window (one rolling hour) on the same screen, so the ledger is never reconciled against a longer /rate_limit span. Reads the meter the daemon already keeps and issues no GitHub request of its own. Defaults to the graphql budget.aiur github-cost
aiur github-cost --budget coreSelects one budget: graphql, core, or all. The two budgets are never summed into one number because GitHub bills them separately, on separate windows.aiur github-cost --budget core
aiur github-cost --format recordsChooses auto, table, or line-oriented records output.aiur github-cost --format records
aiur github-cost --jsonEmits the ranking as one versioned envelope.aiur github-cost --json
aiur github-usagePrints per-actor (daemon vs each agent workspace) GitHub usage: Core, GraphQL and search used/limit with reset times, read from the shared admission broker's admissions. Limits are request-count ceilings (the broker sees requests, not GraphQL points); 0 in the config means no ceiling. Issues no GitHub request of its own.aiur github-usage
aiur github-usage --jsonEmits the per-actor usage as one versioned envelope.aiur github-usage --json
aiur agentsPrints each active agent's state and current activity.aiur agents
aiur unitsReads the Dashboard Units projection. Choose --scope live|unfinished|all|none, repeat --condition active|alert|paused|queued|finished, choose --format auto|table|records, or add --json.aiur units --scope unfinished --condition active
aiur units --condition alertRepeats to require any of the selected Unit conditions.aiur units --condition alert --condition paused
aiur units --format recordsChooses auto, table, or line-oriented records output.aiur units --format records
aiur watchShows changed fleet rows by default.aiur watch
aiur watch --fullPrints all fleet rows.aiur watch --full
aiur watch --changesMakes the changed-rows default explicit.aiur watch --changes
aiur watch --onceRequests the one-shot form.aiur watch --once
aiur watch --interval 5Re-renders until interrupted. The interval must be a positive number of seconds.aiur watch --interval 5
aiur alertsShows the structured alert feed.aiur alerts
aiur alerts --needs-attentionFilters to unresolved alerts requiring Executor action.aiur alerts --needs-attention
aiur set max-agents 6Changes the live session cap without editing config. The new cap applies to live state at once (status reflects it), and dispatch reconciles to it on the next poll cadence. It does not rewrite the next launch's config; a restart drops it, and aiur status then shows the ceiling as config max_concurrent_agents rather than as the operator's last command.aiur set max-agents 6
aiur pauseTurns on the global pause switch. It stops new provisioning and cooperatively holds the fleet. The switch is persisted with its source and survives restart; a failed persisted-state read starts paused.aiur pause
aiur resumeTurns off that global switch. Lifting the pause schedules a prompt poll, so a ramp resumes dispatch within one base interval rather than waiting out the idle poll backoff.aiur resume
aiur pause 142 143Requests a safe-boundary pause for named tickets.aiur pause 142,143
aiur pause --allRequests a pause for every active ticket.aiur pause --all
aiur resume 142Resumes a paused ticket, or starts an idle eligible ticket.aiur resume 142
aiur resume --allResumes every individually paused ticket.aiur resume --all
aiur reset-budget 142Clears a named ticket's dispatch-budget latch. It does not accept --all; resume cannot clear this latch.aiur reset-budget 142
aiur message 142 "Check review"Enqueues Executor text on the native agent queue. Aiur may interrupt at a safe point, queue it for the next turn, auto-resume a paused entry, or reactivate a deactivated entry. Text must be nonblank and at most 8,000 characters. The command reports what it observed: delivered message to #142 once the agent has claimed it, otherwise queued message for #142 (request N); delivery is unconfirmed. Both are successful enqueues and exit 0 — a queued message is normally claimed at the agent's next checkpoint.aiur message 142 "Check the latest review"
aiur stopGracefully stops the BEAM and its tmux lifetime session, reaping agent process trees and workspace-rooted descendants before a final launcher backstop removes stragglers. A stopped daemon makes stop and --todo exit nonzero.aiur stop
aiur restartStops the running session, refreshes the release, and starts it again detached. See Restart semantics.aiur restart
aiur restart --no-buildBounces the daemon on whatever release is already on disk. Use it for a fast restart, or to bounce without taking uncommitted source edits live. It has no effect on the installed aiur, which never builds. It cannot rescue a failed development rebuild: that removes the incomplete release, so there is nothing left to start, and restart says so instead of suggesting it.aiur restart --no-build
aiur upgradeInstalls the newer aiur-cli on your channel (latest, next, or nightly) and reports the version before and after, with the restart step to actually pick it up. Refuses under the aiurdev development launcher, for Homebrew installs (releases are npm-only right now), and while a daemon is running — pass --force to upgrade a live install anyway. It never downgrades: a nightly or next user is measured against their own channel, never against a lower latest.aiur upgrade
aiur upgrade --forceUpgrades even while a daemon is running. The running daemon keeps the old code until you restart it; in-flight agents are unaffected until then.aiur upgrade --force
aiur cleanup-staleLists and reaps stale manual-smoke processes and sockets.aiur cleanup-stale
aiur cleanup-stale --dry-runReports stale leftovers without reaping them.aiur cleanup-stale --dry-run
aiur guard-pr-deletions mainRefuses a PR that deletes more than 50 files the branch never touched. Reads the base branch from the argument or AIUR_BASE_BRANCH, and the branch start from AIUR_BRANCH_START_SHA or refs/aiur/branch-start. Exit 1 is a refusal, exit 2 is an unusable input such as a dirty tree, an unfetchable base, or a missing branch-start ref.aiur guard-pr-deletions main

Restart semantics

Restart caseResult
Installed aiurBounces the pinned release without building.
Development releaseRefreshes between stop and start, then boots current code.
Daemon already stoppedStarts it.
Daemon still answers after stopAborts rather than rebuilding underneath it.

Any failure after the stop, whether a failed rebuild, a failed start, or an interrupt, reports that the daemon is stopped and was not restarted. Restart uses the same graceful agent-tree and workspace-descendant reap as stop before refreshing or starting the release.

Under scripts/aiurdev, restart verifies that the refreshed release came from the expected checkout and commit.

Development refresh evidenceResult
Rebuild verified against the expected checkout and commitStarts the rebuilt release.
Rebuild cannot be verifiedLeaves the daemon stopped, exits with code 70, and names the unconfirmed builder.
Custom build command without verification supportStarts and reports the result as unverified.

Dashboard page commands

aiur units, aiur commands, aiur build-orders, and aiur analytics are read-only terminal forms of the corresponding Dashboard pages and show the same data.

CommandPage view and important inputsExample
aiur unitsFiltered Units catalog; use --scope, repeated --condition, --format, or --json.aiur units --scope unfinished --condition alert --json
aiur commands [decision-id]Durable decision inbox, or one selected decision. Use --filter all|open|blocking|resolved, --blocking, --ticket, --search, --cursor, --limit, and --json. --ticket and --search require --filter all.aiur commands --filter blocking --json
aiur build-orders [root]Build Order catalog without a root; one root adds graph, execution, and activity detail.aiur build-orders 1567 --json
aiur analyticsAnalytics snapshot, including whole-host fleet/build pressure. Human output reports peaks, latest measured capacities, the binding admission signal with measured load, and longest live build wait; --json includes the timestamped pressure series. Choose --range run|full, an ISO-8601 --since/--until window, an optional numeric --build-order, or --json.aiur analytics --range full --build-order 1567 --json
aiur analytics --range fullSelects the current run or all retained analytics observations.aiur analytics --range full
aiur analytics --since 2026-08-01T00:00:00ZSets the inclusive ISO-8601 start of an analytics window.aiur analytics --since 2026-08-01T00:00:00Z
aiur analytics --until 2026-08-02T00:00:00ZSets the exclusive ISO-8601 end of an analytics window.aiur analytics --until 2026-08-02T00:00:00Z
aiur analytics --build-order 1567Limits analytics to one numeric Build Order root.aiur analytics --build-order 1567

Output contract

Every --json result is one versioned envelope with schema_version, page, snapshot.captured_at, request, sources, data, and auxiliary. snapshot.captured_at is when the command ran; it is not a claim that every source was observed then.

aiur build-orders --json uses schema version 2. Its completion objects report progress_resolution: "empty" with progress: null when a Build Order has no members, distinguishing an observed empty plan from 0% progress.

Each source reports state, observed_at, age_ms, freshness, partial, and machine-readable reasons, while human output prints the same labelled state and age because a number without observation age is not actionable.

Fleet-capacity and build-gate evidence have independent source states: stale fleet samples do not erase current build measurements; unavailable daemon process metrics do not erase whole-host pressure; and missing values remain null in JSON and unavailable in human output rather than becoming zero.

Source conditionOutput contract
Unavailable, stale, partial, invalid, or unknown observation timeRemains null or field-level status in JSON and a labelled warning in human output; it never becomes 0, [], or {} merely because the command could not measure it.
Observed empty source or valid zero-result filterMay return an empty collection.
Separately derived value, such as provider spendLives under auxiliary with its own source metadata.

Decisions, Executor events, and findings

SyntaxDefault or important interactionRunnable example
aiur ask "Title"Creates a durable operator request for the current repository. Add --body or --body-file, --urgency low|normal|high, and --blocking as needed.aiur ask "Approve production cutover" --blocking --urgency high
aiur ask "Title" --body "Context"Stores short supporting Markdown in the ask.aiur ask "Approve production cutover" --body "Production freeze ends today."
aiur ask "Title" --body-file request.mdReads the supporting body from a UTF-8 file.aiur ask "Approve production cutover" --body-file request.md
aiur ask "Title" --urgency highClassifies the request as low, normal, or high.aiur ask "Approve production cutover" --urgency high
aiur ask --done ASK-IDResolves one open ask; --note records the resolution context.aiur ask --done ask_123 --note "Approved by release manager"
aiur ask --done ASK-ID --note "Approved"Saves the optional resolution context with a completed ask.aiur ask --done ask_123 --note "Approved by release manager"
aiur asksLists open asks by default. Use --all to include resolved records and --json for the durable rows.aiur asks --all --json
aiur asks --openMakes the open-asks default explicit.aiur asks --open --json
aiur commandsLists durable Executor decisions. An optional decision ID selects one record.aiur commands
aiur commands --filter openAccepts all, open, blocking, or resolved. --ticket and --search require --filter all.aiur commands --filter open
aiur commands --blockingLimits the decision list to blocking records.aiur commands --blocking
aiur commands --ticket 142Narrows an all decision query to a ticket.aiur commands --filter all --ticket 142
aiur commands --search timeoutSearches an all decision query.aiur commands --filter all --search timeout
aiur commands --cursor TOKENContinues a paginated decision query.aiur commands --cursor TOKEN
aiur commands --limit 20Sets a positive result limit.aiur commands --limit 20
aiur commands --jsonEmits machine-readable decision rows.aiur commands --json
aiur executor-answer DECISION-IDRecords a supervising-Executor answer to one open decision. It requires --expected-version, --rationale, --idempotency-key, and exactly one of --option or --custom-response.aiur executor-answer dec_123 --expected-version 1 --option morning --rationale "Lowest risk" --idempotency-key run-1
aiur executor-answer DECISION-ID --expected-version 1Optimistic-concurrency guard. It must be a positive integer and must match the decision's current version, so a stale answer is rejected instead of overwriting a newer one.aiur executor-answer dec_123 --expected-version 1 --option morning --rationale "Lowest risk" --idempotency-key run-1
aiur executor-answer DECISION-ID --option morningSelects one of the decision's offered option IDs. It is mutually exclusive with --custom-response.aiur executor-answer dec_123 --expected-version 1 --option morning --rationale "Lowest risk" --idempotency-key run-1
aiur executor-answer DECISION-ID --custom-response "Hold"Supplies a bounded free-text answer instead of an offered option. It is mutually exclusive with --option.aiur executor-answer dec_123 --expected-version 1 --custom-response "Hold until Monday" --rationale "Freeze" --idempotency-key run-2
aiur executor-answer DECISION-ID --rationale "Why"Required. It records why the answer was chosen, so decision history is auditable.aiur executor-answer dec_123 --expected-version 1 --option morning --rationale "Lowest risk" --idempotency-key run-1
aiur executor-answer DECISION-ID --idempotency-key run-1Required. Replaying the same key does not record a second answer, so a retried command is safe.aiur executor-answer dec_123 --expected-version 1 --option morning --rationale "Lowest risk" --idempotency-key run-1
aiur executor-answer DECISION-ID --executor-id exec-1Optional attribution for the answering Executor. It must not be empty when present.aiur executor-answer dec_123 --expected-version 1 --option morning --rationale "Lowest risk" --idempotency-key run-1 --executor-id exec-1
aiur executor-escalate DECISION-IDHands a decision back to the human Executor instead of answering it. It requires --expected-version and --reason.aiur executor-escalate dec_123 --expected-version 1 --reason "Needs the release owner"
aiur executor-escalate DECISION-ID --reason "Text"Required. It records why the supervising Executor declined to answer.aiur executor-escalate dec_123 --expected-version 1 --reason "Needs the release owner"
aiur executor-moot DECISION-IDRetires a Command whose question is void — its ticket closed or its originating agent is gone — recording why and by whom, without fabricating an answer. It requires --expected-version and --reason-class.aiur executor-moot dec_123 --expected-version 1 --reason-class ticket_closed
aiur executor-moot DECISION-ID --reason-class ticket_closedRequired. A bounded class such as ticket_closed or origin_agent_gone that explains why the Command was voided.aiur executor-moot dec_123 --expected-version 1 --reason-class origin_agent_gone --reason "Agent no longer runs"
aiur executor-moot DECISION-ID --reason "Text"Optional free-text detail explaining why the Command was retired.aiur executor-moot dec_123 --expected-version 1 --reason-class ticket_closed --reason "Folded into #2073"
aiur executor-moot DECISION-ID --executor-id exec-1Optional attribution for the Executor retiring the Command. It must not be empty when present.aiur executor-moot dec_123 --expected-version 1 --reason-class ticket_closed --executor-id exec-1

A mooted Command leaves aiur commands --blocking and the open/blocking counts, stays visible under --filter resolved, and keeps its answer unset in the audit history.

Executor mutation failures include a remedy on stderr. Supply any named missing flag and retry; when --expected-version is stale, read the current version from the error and retry only after confirming the Command has not changed unexpectedly.

If executor-answer says a field is outside Executor scope, use aiur executor-escalate with the same decision ID and current version.

A stopped daemon is reported separately with the command needed to start it; a live but silent or unreachable daemon reports the attempted decision ID, expected version, and daemon endpoint so the same call can be diagnosed without guessing.

| aiur executor-listen | Persists the requested subscription, then streams all persisted-pattern events after the saved cursor before live events as JSON lines. It intentionally does not use the ten-second one-shot RPC timeout. | aiur executor-listen | | aiur executor-listen --topic 'executor.#' | Adds that validated AMQP topic pattern before listening; the default is executor.#. Empty segments and malformed patterns are rejected. | aiur executor-listen --topic 'executor.#' | | aiur executor-wait | Returns immediately when durable Executor wake records are pending; otherwise blocks for up to 300 seconds. Exit 0 covers both outcomes that lose nothing: wakes were returned and acknowledged, or the wait timed out quietly having consumed nothing. It auto-claims the wake stream when nobody holds it, and the shared cursor advances only for the owner. | aiur executor-wait | | aiur executor-wait --timeout 60 --json | Sets the positive timeout in seconds and emits the identifier-only wake batch as JSON, alongside this consumer's role and a status of woken, timeout or error. Non-Executor wakes contain validated IDs and typed flags, never source free text. | aiur executor-wait --timeout 60 --json | | aiur executor-wait --as agent-b | Names the consumer explicitly instead of using AIUR_EXECUTOR_ID or the derived host identity. Refused by a live owner, it reads the same records as an observer and does not advance the cursor. | aiur executor-wait --as agent-b | | aiur executor-fast-forward <wake-id> | Explicitly acknowledges an exact durable wake prefix that this Executor demonstrably covered by another complete means. It acquires the owner lease, refuses a live peer owner, rejects an absent or future id, reports before/through/acknowledged/pending counts, and preserves newer wakes. This is recovery, not the normal consumption loop. | aiur executor-fast-forward 2832 --as agent-a | | aiur executor-roster | Lists every Executor consumer with the evidence behind its state: role, host, pid, claimed_at, last_renewed_at, last_acknowledged_at, pending_count, cursor position, and whether the cursor moved since the previous observation. | aiur executor-roster --json | | aiur executor-claim | Claims the wake stream. Refused when a live, renewing owner holds it; the refusal names that owner so a takeover is an operator decision, never a silent steal. | aiur executor-claim --as agent-a | | aiur executor-release | Gives up this consumer's claim so a successor can take over immediately. | aiur executor-release --as agent-a | | aiur executor-revoke <consumer-id> | Operator-only revoke of the named live owner. Requires the owner's id, read from the roster first. | aiur executor-revoke agent-a | | aiur executor-emit executor.note --payload '{"text":"ready"}' | Publishes JSON on a nonempty executor. topic. Empty segments and other namespaces are rejected. | aiur executor-emit executor.note --payload '{"text":"ready"}' | | aiur executor-subscribe 'executor.#' | Adds a persistent Executor event binding. Bindings accept executor.* and reviewed ticket/system patterns only; broader wildcards are rejected. | aiur executor-subscribe 'executor.#' | | aiur executor-unsubscribe 'executor.#' | Removes that exact persistent binding. | aiur executor-unsubscribe 'executor.#' | | aiur executor-subscriptions | Lists persistent Executor event bindings. | aiur executor-subscriptions | | aiur findings | Reads the host-local findings ledger. | aiur findings | | aiur findings --unfiled | Filters ledger entries that have no filed ticket. | aiur findings --unfiled | | aiur findings --slugs | Emits only finding slugs. | aiur findings --slugs | | aiur findings --scope repo | Filters to aiur or repo scope. | aiur findings --scope repo | | aiur findings --record JSON --repo owner/repo | Appends one validated finding to the named repository ledger. Both options are required together. | aiur findings --record '{"slug":"example"}' --repo aiur-team/aiur | | aiur findings --digest | Generates the Markdown projection, optionally scoped. | aiur findings --digest --scope repo |

executor-wait outcomes

A quiet timeout is a successful empty result, not a failure: nothing was pending, so nothing was consumed and nothing was lost. Plain mode prints NO-WAKES role=<role> timeout_ms=<ms> nothing pending, nothing consumed; --json returns {"status":"timeout","role":...,"records":[]}. Both exit 0.

Under --json every outcome carries a status: woken for a returned batch, timeout for a quiet wait, error for a failure. Branch on that field rather than on the presence of records.

Every nonzero exit names the stage that failed — claim, wait or acknowledge — on stderr, and as a status: "error" envelope with that stage under --json.

ExitMeaning
0Wakes were delivered (and acknowledged, when this consumer is the owner), or the wait timed out quietly.
69Contention on the shared claim: the cross-process claims lock was still held after its bounded retry, or the claim moved to another consumer before the batch could be acknowledged. Nothing was consumed and the cursor did not move, so the same call is safe to retry.
1Daemon or store failure — an unreadable wake ledger, or a claims store that cannot be written. Retrying repeats it.
64Invalid usage.

The 69 diagnostic reports the retry bounds actually spent, read from the live configuration: by default the claims lock is retried every 25ms for 5 seconds, and a lock older than 60 seconds is broken as stale.

A batch that could not be acknowledged is still printed — losing a wake is a worse failure than announcing a redelivery — so an acknowledge-stage failure emits the wake envelope first and the status: "error" envelope after it.

Read the last line for the outcome: its unconsumed_wake_ids name the records whose cursor did not advance, and they will be delivered again to whichever consumer holds the claim next.

Wake ledger bound and lease TTL

The wake ledger is capped at 10,000 records. Consumed records are evicted first.

Past the cap the oldest unread wakes are evicted too. The shared cursor is advanced past them and an executor.wakes.overflow alert names the count and id range; those wakes are never delivered.

In practice that only happens when a run records for a long time with no consumer, or with a stalled one. The roster's stalled state is the earlier warning.

A claim is a lease with a 10-minute TTL. An --executor run registers and renews its principal below that TTL; executor-wait also renews while it blocks, and every claim or acknowledgement renews. A consumer that stops renewing is reported expired after the TTL lapses, and a successor may take over with no operator action.

Executor roster states

A stalled consumer still holds a claim and still renews its lease, so a presence-based list reports it as fine.

aiur executor-roster derives state from evidence, never from presence.

StateMeaning
activePositive evidence of consumption: it acknowledged inside the stall window, or the shared cursor moved since the previous observation.
idleLease live, nothing pending, cursor legitimately still.
stalledLease renewing, but acknowledgements are frozen while pending_count grows. The "backgrounded stuck" case.
expiredLease lapsed. A successor may take over with no operator action.
unknownThe evidence needed to decide is missing. Never reported as active.

Multiple executors are a supported configuration, so a healthy peer is listed plainly and is not a fault. An agent reports what it finds and recommends; it never revokes a live peer's claim on its own.

Records — the journal, wake inbox, cursor, and subscriptions — are created automatically on first use beneath the per-repository state node (~/.aiur/repo/<owner>/<repo>/executor), so they survive a daemon restart and a successor resumes from the durable cursor.

A tail -F -n0 follower is a low-latency notification aid only. It skips the existing prefix, may filter wake classes, has no consumer identity or lease, and never advances the durable cursor.

executor-wait is the normal discovery and consumption path. Use executor-fast-forward only after independently verifying the exact prefix already covered. The 10,000-record bound remains an emergency disk bound, not evidence of consumption.

AIUR_EXECUTOR_ID names this consumer when --as is omitted. Nothing infers consumer identity from the terminal, parent process, or any other environment signal.

Executor subscriptions are the Executor's half of the event system; see Message Bus. Agents do not need these commands: every agent is auto-subscribed to its own comment, review, and CI topics, and to both directions of every blocker edge.

Normal control commands use a bounded RPC. After ten seconds the launcher terminates its helper and descendants, exits 124, and reports the timeout. A crash marker distinguishes a known crashed daemon from a stopped one; run aiur stop to reap possible orphaned agents before restarting.

An open blocking ask is also printed by plain aiur status; no extra flag is required. That keeps a durable request in the normal operating view instead of hiding it in a ledger that nobody reads.

--blocking and --filter blocking mean dispatch is held until you answer this, which is also what the dispatch gate reads.

An agent attention is a visibility signal rather than a gate — the agent keeps running after it opens one — so attentions are listed by aiur commands but are not counted as blocking.

Answering a Command also dismisses every other unanswered Command — open or deferred — asking the same question on the same ticket, so a question filed more than once clears in one answer. A blocking Command an agent is genuinely waiting on is never swept up this way, because dismissing it would deliver nothing to that agent.

Dismissing a Command closes it and moves it to history. If the Command's agent is still live, it is told to use its judgement and proceed; an agent that is gone is not notified.

Operational facts that change an incident response

FactResponse
Dispatch needs agent:todo.AGENTS 0/32 (binding: ticket supply) means a recent poll found no queued ticket. If it instead reads has not polled yet (POLL backed off...), the daemon has not looked recently — run aiur --todo <id>, add the label, or trigger a refresh so the work is seen.
Global pause is durable.Use aiur status and aiur resume before treating a silent restarted fleet as broken.
CI readiness uses an operator-only token.Put AIUR_CI_READINESS_TOKEN with GitHub workflow scope in the daemon environment, restart, and never expose it to agent workspaces.
A base refresh affects approval ownership.With require_last_push_approval, route a base refresh through the ticket agent so the Executor does not become the ineligible last pusher.

aiurdev, for developing Aiur itself

scripts/aiurdev is only for working on Aiur's own source. It builds and runs a local development release from a checkout, entirely separate from the aiur you installed with npm, bun, or Homebrew. If you are using Aiur rather than changing it, you never need this section.

Apart from the commands below, aiurdev executes the same launcher engine as aiur; it is one CLI surface, not two products.

SyntaxDefault or important interactionRunnable example
scripts/aiurdev buildRebuilds the local release before the next run. The installed aiur does not have this command.scripts/aiurdev build
scripts/aiurdev build --depsRebuilds dependencies as part of the development build.scripts/aiurdev build --deps
scripts/aiurdev --testResets the single sandbox ticket, first stopping the keyed live daemon, then starts the foreground smoke harness. It is blocked from agent workspaces.scripts/aiurdev --test --force
scripts/aiurdev --test3Resets the three-ticket blocker-chain harness, stops the keyed live daemon first, and permits the remote scenario. It is blocked from agent workspaces.scripts/aiurdev --test3
scripts/aiurdev --clearRequires debug mode and deletes every entry under ~/.aiur/logs/ before the smoke run, not merely debug logs.scripts/aiurdev --debug --clear
scripts/aiurdev --allow-remotePermits the remote test scenario.scripts/aiurdev --test3 --allow-remote

aiurdev refuses every invocation from an existing tmux session because Aiur starts its own tmux socket. It pins the opencode binary selected by the release and preserves a complete existing release for control commands, so a control command does not rebuild a daemon it is about to contact.

Which checkout aiurdev targets

aiurdev resolves its target repository from its own path, following symlinks first, so a globally symlinked aiurdev works from any directory. Set AIUR_REPO_ROOT to name a target explicitly and that choice always wins.

When the script path and current directory point at different checkouts, command intent decides the outcome.

Command typeOutcome
build, restart, or bare runRefuses with exit code 64 and prints both roots plus the two ways to resolve the mismatch.
Control or RPC command, including status, stop, watch, units, and executor-answerRuns against the script's checkout, names that checkout, and reuses its release without rebuilding.
AIUR_BUILD_STAMP fieldWhy it matters
Repository root and source commitA commit mismatch marks the release stale after operations such as git switch.
Dirty-tree flag and build timeRecords the exact development build provenance.

aiur accepts a path to a workflow configuration as the final run argument. Every fresh aiur or aiurdev launch prints Config: /absolute/path after startup, naming the configuration selected by discovery or that explicit argument. An already-running background no-op does not load or print a configuration.

Command macro, delegate micro, maximize APM.