CLI reference
Every command the installed junco answers to, generated from its own help surface at the
version stamped below and annotated by hand. Synopsis conventions:
<required>, [optional], a|b alternatives.
Global flags work on every command; per-command flags are covered with their command.
global flags
--once- (
start) Process one task then exit --platform<name>- (
service) Target platform: launchd | systemd default launchd on macOS, systemd elsewhere --plan- (
unwatch) Print what would be deleted as JSON; delete nothing --help, -h- Show this help message
--version- Print junco's version and exit
junco
junco
The bare command is the daily driver: on an interactive terminal it makes sure the
supervised daemon is running, then opens the dashboard. With no config yet, it opens the
guided setup walkthrough instead. It never starts a daemon on a non-interactive terminal —
services and scripts use junco start.
junco
see also: start · dashboard · config init
junco analyze
junco analyze <owner/repo#N|url>
Resolves one GitHub issue — owner/repo#N or a full issue URL; there is no local-path form — and queues a read-only investigation ticket: root cause, file:line evidence, reproduction steps, a suggested fix direction. The daemon parks the result as a comment draft, sanitized before it lands (HTML comments and control characters stripped, capped at 60,000 characters); nothing posts until the separate junco analyze post confirm. Works on owned and unowned repos alike — an unwatched repo is auto-forked, cloned into a managed directory, and added to the watchlist. One pending draft per issue: re-analyzing overwrites the old draft, and queuing while the same issue's ticket still waits in the inbox fails loud.
junco analyze acme/reef-api#52
junco analyze https://github.com/upstream/reef-api/issues/52
see also: analyze review · analyze post · analyze guide
junco analyze edit
junco analyze edit <id>
Opens the pending draft in $VISUAL (falling back to $EDITOR) as a temporary Markdown file, git-commit-message style. On save the text is re-sanitized — the same HTML-comment and control-character stripping and length cap the original draft got — and written back to the store. The disclosure footer is never part of the file; it's composed at post/preview time, not stored in the draft text. An editor that exits nonzero, or a draft that's empty after sanitizing, leaves the draft unchanged. With no editor configured, the command prints the draft's JSON file path instead of guessing.
junco analyze edit analyze-acme-reef-api-52
draft updated — junco analyze review analyze-acme-reef-api-52 to preview
see also: analyze review · analyze post · analyze guide
junco analyze post
junco analyze post <id> [--no-footer]
The human confirm gate, and the only outward write in the analyze flow: composes the final comment — draft text plus the disclosure footer unless the draft has it off or --no-footer is passed — posts it under your own gh auth via gh issue comment (or queues it to the durable outbox when GitHub is unreachable), and archives the draft to posted/ on either outcome. One draft, one deliberate action — there is no separate --yes flag. A non-network failure (locked issue, permission error) leaves the draft pending, so the same id can be retried after the fix.
junco analyze post analyze-acme-reef-api-52
junco analyze post analyze-acme-reef-api-52 --no-footer
see also: analyze review · outbox · analyze guide
junco analyze review
junco analyze review [<id>]
With no id: one line per pending draft — id, nwo#issue, (owned)/(external), the parked timestamp, and the draft's first non-empty line truncated at 60 characters. With an id: the draft's nwo#issue, scope, and issue title, then exactly what junco analyze post would post — draft text plus the disclosure footer when the draft carries one — followed by a post: hint. Read-only; nothing posts from here. The same review is available in the dashboard under v: enter previews, f/enter posts, x discards.
junco analyze review
junco analyze review analyze-acme-reef-api-52
see also: analyze edit · analyze post · analyze guide
junco assess
junco assess <path|owner/repo|owner/repo#N> [--auto-plan]
Queues a read-only audit ticket: npm audit over the dependency tree plus a read-only agent pass over the code, with findings validated, severity-filtered, deduped against your own most recent 500 issues on the repo, and parked for review — nothing is filed until you confirm with junco assess file. The target is a local path, an already-watched owner/repo (an unwatched one errors out rather than guessing a clone path), or owner/repo#N / an issue URL, which scopes the audit to the code that issue implicates and auto-provisions an unwatched repo. --auto-plan stamps the configured trigger label on every issue the batch later files — effective only on owned, bridge-watched repos; an external batch forces it off.
junco assess acme/reef-api --auto-plan
junco assess acme/reef-api#52
see also: assess review · assess file · assess discard · assess guide
junco assess discard
junco assess discard <id>
Archives a pending review batch without filing any of its findings. Filing
(junco assess file) no longer auto-archives the batch it draws from — a batch you
reviewed and decided not to act on needs this explicit step, or it keeps showing up in
junco assess review. The same action is bound to x in the dashboard's
review view.
junco assess discard 20260718-093042
see also: assess · assess review · assess file · assess guide
junco assess file
junco assess file <id> --all | --only <fp,...>
The confirmation half of an audit: files reviewed findings from a parked batch as GitHub
issues. There is no bare default — you choose
--all or an explicit --only list of fingerprints from
junco assess review <id>. Filed issues arrive fingerprinted,
severity-labeled, and deduped against your recent findings. Filing stamps per-finding
accounting (created/queued/deduped, plus a timestamp and URL) but no longer auto-archives the
batch — it stays in junco assess review, filed findings and all, until you run
junco assess discard on it.
junco assess file 20260718-093042 --all
junco assess file 20260718-093042 --only 9f2c01ab54ce77d1,4a11beef00c2d9e3
<!-- junco:finding --> marker line.see also: assess · assess review · assess discard · assess guide
junco assess review
junco assess review [<id>]
Read-only inspection of the parked-findings store. With no id: one line per pending batch — id, owner/repo, (owned)/(external), finding count, and the audit's timestamp. With an id: the batch's repo and scope, then each finding's fingerprint, severity, and title, closing with two ready-to-run junco assess file hints (--all, and a --only example built from the first two fingerprints). Nothing is written; filing is the separate junco assess file confirm step. The same checklist review is available in the dashboard under v.
junco assess review
junco assess review 20260718-093042
see also: assess · assess file · assess discard · assess guide
junco auth grant
junco auth grant <owner/repo>
Grants the bot push access to a repo by driving both identities junco holds: invites the bot as a collaborator with permission=push under your own ambient login (you need admin on the repo — anything less fails with a message saying so), switches to the bot's isolated GH_CONFIG_DIR to accept the matching pending invitation (bounded retry, since invitation propagation can lag), then verifies under the bot's identity that it now has push before reporting success. Idempotent — re-running on an already-granted repo skips straight to verification. The dashboard runs the same grant in the background after you watch a repo with the bot enabled; a failure there shows a toast naming this command as the fix.
junco auth grant acme/reef-api
✓ junco-agent has write on acme/reef-api
gh OAuth token for the org — junco recognizes the error and prints this guidance.see also: auth login · doctor · bot account guide
junco auth login
junco auth login
Logs the daemon's dedicated bot account in: runs gh auth login's own device-flow into an isolated GH_CONFIG_DIR (default ~/.junco/gh, fully separate from your personal gh config; a legacy login at ~/.config/junco/gh keeps working until junco data migrate moves it), verifies the resulting identity, and flips botAccount.enabled to true in config.json on success. It prints who junco now acts as and reminds you to run junco restart to apply it. gh owns token refresh — nothing secret lands in config.json or the daemon's environment; child processes only ever receive the config-dir path.
junco auth login
botAccount.enabled set but the bot's login missing or expired, junco start and junco run-once refuse to run at all — there is no silent fallback to your personal identity. Re-run junco auth login.see also: auth grant · restart · bot account guide
junco config get
junco config get <path>
Reads one lever by dotted path from the resolved config.json and prints its value. Read-only; nothing on disk changes.
junco config get worker.maxConcurrent
see also: config set · config list · configuration reference
junco config init
junco config init
Headless default-config scaffold for scripting and CI: writes ~/.junco/config.json with safe defaults and creates the queue directories, no prompts. With a config already present it only ensures the directories exist — it never overwrites an existing file. A fresh scaffold writes no legacy path keys, and writes dataDir only when it differs from the default. The interactive equivalent is the setup walkthrough in the dashboard.
junco config init
see also: config path · doctor · configuration reference
junco config list
junco config list
Prints the full annotated reference: every config lever with its default, type, and one-line explanation. This is the always-current source of truth for the lever set. Levers marked editable: false (e.g. tools) are structural and require a direct edit of config.json rather than config set. Read-only.
junco config list
see also: config get · config set · configuration reference
junco config path
junco config path
Prints the resolved config file location and exits: ~/.junco/config.json, or the legacy ~/.config/junco/config.json (respects XDG_CONFIG_HOME) while it exists and no canonical file does. Resolution is environment-only — the working directory never matters, and the deprecated --config flag is ignored. Reads nothing else, writes nothing — the target for direct edits of structural fields.
junco config path
see also: config list · config init · configuration reference
junco config set
junco config set <path> <value>
Writes one lever by dotted path through a validated read/mutate/write of config.json. The daemon re-reads the file on change, so live-safe levers apply at the next poll with no restart.
junco config set model.id myprovider/my-model
junco config set sandbox.enabled false
dataDir, worker.maxConcurrent) take effect only after restart: with the daemon up, set prints a restart hint, and the pending change is reported in pendingRestartFields (junco status, /health) rather than silently ignored.see also: config get · restart · configuration reference
junco dashboard
junco dashboard
The fullscreen terminal UI, drawn in the terminal's alternate buffer and restored exactly on exit. Two modes, swapped with m: GITHUB — watched repos, their trigger-labeled issues, and junco-authored PRs — and LOCAL — the queue, the GitHub outbox, watched repos on disk, ticket worktrees, and the daemon itself. It observes without starting anything — the daemon runs separately — and the command palette (:) runs junco CLI subcommands without leaving it. With no config yet, the first run opens the guided setup walkthrough (re-run anytime from the palette's "setup").
junco dashboard
junco also lands here after ensuring the supervised daemon is running.see also: junco · status · dashboard guide
junco data
junco data [--json]
Prints the resolved data tree under dataDir: every path with live counts, legacy-override provenance, and any pending migration. A pure read-only view — it never creates or moves anything. Absent nodes are still listed ((absent) is normal before the first daemon start); a legacy-overridden root prints its config key with a deprecation tag, and an unmigrated old-name directory prints the pending rename. --json emits the same information as { root, layout, paths, counts, legacy, pendingMigrations, deprecations } for scripts.
junco data
junco data --json
junco data — root: /Users/you/.junco queue /Users/you/.junco/queue inbox 2 · processing 1 · done 148 · failed 9
see also: data migrate · doctor · configuration reference
junco data migrate
junco data migrate [--dry-run] [--force]
The opt-in full unification of everything junco owns under the single ~/.junco root — the ticket queue never moves automatically. Moves the queue into <dataDir>/queue (rename, falling back to copy+verify+delete across filesystems), restructures a flat legacy tree into the v2 layout (data/, cache/, logs/), relocates the bot's gh credentials to ~/.junco/gh and the config file itself to ~/.junco/config.json, rewrites the config to drop vaultRoot / juncoSubdir / observability.stateDir, and removes the emptied legacy root. Every step is journaled to migrated.json. Holds migrate.lock at every root it may touch so two migrations can't race; nothing already moved is ever rolled back, so fixing a conflict by hand and re-running picks up where it left off. --dry-run prints the full plan and changes nothing.
junco data migrate --dry-run
junco data migrate
/health response (even non-200) counts, as does a live-held worker.lock next to config.json. --force skips both checks. A state-tree name collision where the destination holds real files is left untouched and exits non-zero — file-holding data is never deleted.see also: data · restart · configuration reference
--dry-run- Print every move without touching anything
--force- Skip the daemon-up refusal checks
junco dispatch
junco dispatch <ref>
Fetches a GitHub issue — owner/repo#N or a full issue URL — and queues a ticket for it; the daemon works the issue in an isolated worktree and opens a draft PR. Access decides the mode: with push access the clone is fork-less, branches land on the repo itself, and the repo is auto-onboarded to the watchlist as a first-class entry with no confirmation step; a public repo without push access is auto-forked, cloned into a managed directory, and watched as an external entry, with the draft PR opened against upstream — no labels or comments ever touch the upstream issue. A private repo without push access fails loud before anything is cloned. Ticket frontmatter is machine-built, never model output; the issue text rides in the ticket body as untrusted data, not instructions.
junco dispatch acme/reef-api#52
junco dispatch https://github.com/upstream/reef-api/issues/52
amends_pr ticket instead.see also: prs · submit · github loop guide
junco doctor
junco doctor
Preflight for the whole install: config parses, node/git/gh are present, gh is authenticated, the inference endpoint is reachable, the configured model is advertised, and the queue/worktree/data directories are writable. Also preflights the execution sandbox's backend binary.
junco doctor
bwrap, or set sandbox.backend: "none" / sandbox.enabled: false.see also: status · config list · operations guide
junco inbox-path
junco inbox-path
Prints the resolved inbox directory path and exits — the directory the daemon watches, resolved from the active config.json. Useful for tools that write ticket files directly instead of going through submit.
junco inbox-path
see also: submit · list · tickets guide
junco list
junco list [box]
Lists tickets newest-first per queue box — inbox | processing | done | failed — with terminal statuses; pass a box name to show just that one. Read-only: nothing moves on disk.
junco list
junco list failed
junco logs
junco logs [-f] [-n N] [--json|--human]
Tails the worker log at ~/.junco/logs/worker.log (<dataDir>/worker.log on a not-yet-migrated flat tree; rotated at 10 MB). -f follows, -n N bounds the tail. Output is human-readable on a TTY and raw JSON when piped or with --json; --human forces the readable format even when piped.
junco logs -f
junco logs -n 200 --json
09:31:05 INFO claimed {"src":"inbox/gh-acme-reef-api-52.md","dst":"processing/gh-acme-reef-api-52.md"}
09:43:56 INFO [gh-acme-reef-api-52] opened PR https://github.com/acme/reef-api/pull/57
09:43:58 INFO idle
see also: status · dashboard · operations guide
junco outbox
junco outbox [flush]
Lists the offline GitHub backlog: when GitHub is unreachable, lifecycle label flips, issue comments, filed assess findings, analyze comment posts, and the whole PR push+create sequence queue as durable per-op JSON files under <dataDir>/outbox/. The listing shows each op's type, target issue/branch, age, and attempt count, plus any dead-letter count; junco outbox flush pushes the backlog now instead of waiting for the next bridge sweep. Both forms work with the daemon down. Replay is FIFO and idempotent — a crash mid-flush won't double-post comments or duplicate labels — and a non-network failure dead-letters an op after 3 attempts to <dataDir>/outbox/dead/.
junco outbox
junco outbox flush
junco doctor warns while dead-lettered ops exist. To retry one: edit it by hand, move it from outbox/dead/ back into outbox/, then run junco outbox flush.see also: status · doctor · github loop guide
junco prs
junco prs
Lists junco-authored pull requests across watched repos, sorted attention-first — checks-failing and changes-requested surface at the top, the same order as the dashboard's PRs view. External fork-PR entries count: a repo watched as external: true is excluded from the bridge's label sweep but stays watched for PR listing, so upstream draft PRs opened in fork-PR mode appear alongside PRs on repos you own. Read-only — it lists, nothing is posted or flipped.
junco prs
see also: dispatch · outbox · github loop guide
junco replay
junco replay <ticket-id|path.jsonl> [--budget-per-kind
Re-runs a recorded per-ticket event transcript through a fresh guard manager and reports what the guards would decide under a chosen policy — a what-if report, never a live rerun: no endpoint traffic, no tool execution, no queue changes. Each of the four supervisor knobs resolves independently by precedence: an explicit flag, then the policy recorded in the transcript's run frames, then the loaded config, then the built-in defaults. The report compares recorded decisions against replayed ones per run and ends in a verdict with caveats; --json prints the raw report instead.
junco replay gh-acme-reef-api-52
junco replay ~/.junco/data/transcripts/gh-acme-reef-api-52.jsonl --budget-per-kind 2 --json
junco_run_start / junco_guard_decision) that transcripts carry from 0.11.0 on; a bare ticket id resolves against <dataDir>/data/transcripts/.see also: logs · operations guide
junco restart
junco restart
Restarts the supervised daemon so it picks up config and code changes. It validates the config first — refusing to bounce the daemon onto a config it can't parse — then finds the launchd plist / systemd user unit referencing the config path, kicks it with the platform-correct verb (launchctl kickstart -k / systemctl --user restart, the verbs that relaunch unconditionally), and confirms the pid changed before reporting success.
junco restart
SuccessfulExit=false keep-alive, a graceful exit stays down.see also: service · update · operations guide
junco retry
junco retry <name…|--all>
Moves failed tickets back to the inbox for a fresh run — the claim stamp, appended result blocks, and retry bookkeeping are stripped, so the ticket re-enters the queue as if newly submitted. Takes one or more ticket names, or --all to requeue everything in failed/.
junco retry gh-acme-reef-api-52.md
junco retry --all
<!-- junco-result separator, so a ticket body containing that literal line would lose its tail on retry.see also: list · rm · tickets guide
junco rm
junco rm <name>
Best-effort delete of a queued ticket from inbox/ only — it never touches processing/ (the daemon owns it) and refuses any name that resolves outside the inbox. The name is fuzzy-matched against inbox/*.md, the same way retry matches.
junco rm add-util-2026-05-31.md
processing/ between your listing and the delete, so "not present in inbox" exits 0. This is a best-effort inbox delete, not an authoritative kill.junco run-once
junco run-once
Processes a single available task and exits — the one-shot form of the worker loop. Takes no lock, so it is safe for cron or by-hand runs alongside a running daemon.
junco run-once
see also: start · submit · operations guide
junco schema
junco schema
Prints the ticket-frontmatter JSON Schema — the typed contract for every frontmatter field — and exits. Read-only; nothing on disk changes. The reference for writing tickets by hand or validating them from other tools.
junco schema
see also: submit · ticket schema reference
junco service
junco service
Renders a platform-native service file to stdout — a launchd plist or a systemd unit, defaulting to launchd on macOS and systemd elsewhere; --platform launchd|systemd overrides. Pipe it to the right location and load it; once supervised, restart knows how to bounce the daemon.
junco service --platform launchd > ~/Library/LaunchAgents/com.junco.worker.plist
junco service --platform systemd > ~/.config/systemd/user/junco.service
launchctl load then launchctl start com.junco.worker on macOS; systemctl --user daemon-reload then systemctl --user enable --now junco on Linux.see also: start · restart · operations guide
junco skill install
junco skill install [--harness <name|path>]...
Links the junco-dispatch skill into agent-harness skills directories through the <dataDir>/skills mount. A harness passed by name or by path is written to skills.harnessDirs — standing consent junco honors from then on: the daemon re-creates and repairs the links at every start and after junco update, and junco never writes into a directory not on that list. With no arguments the command re-ensures the configured links and prints what it created, repaired, or skipped.
junco skill install --harness codex
junco skill install --harness ~/.config/opencode/skills
junco skill install
junco doctor names the blocked path.see also: doctor · update · tickets guide
junco start
junco start
Runs the daemon in the foreground: it polls the inbox forever, claiming and processing tickets, until Ctrl-C. On startup it acquires a single-instance lock (worker.lock, next to config.json); if another instance already holds the lock it exits 0 instead of erroring, so a supervisor (launchd, systemd) never enters a restart loop after a double start. --once processes one task and exits.
junco start
junco start --once
worker.startupWait = true) startup blocks and retries every 30 s until the inference endpoint responds; set it to false to start immediately and fail individual tickets while the endpoint is down.see also: run-once · service · restart · operations guide
junco status
junco status
One-glance view of the runtime: daemon pid and uptime, inference-endpoint readiness, in-flight tickets, processed counts, and queue sizes — the full record behind the dashboard header's condensed pulse.
junco status
see also: logs · doctor · dashboard · operations guide
junco submit
junco submit <file|->
Places a ticket into the inbox with an atomic write — the daemon can never claim a
half-written file. The filename comes from the ticket's id (falling back to
the source filename); - reads the ticket from stdin, which is how other tools
pipe work in.
junco submit fix-tide-rounding.md
cat ticket.md | junco submit -
queued inbox/fix-tide-rounding.md
git.allowedRepoRoots.see also: list · retry · schema · tickets guide
junco unwatch
junco unwatch <owner/repo> [--plan]
Stops watching a repository and deletes its junco-owned operational state: queued inbox tickets, the worktree namespace, and outbox, review, history, mirror, and cache traces. --plan prints the itemized deletion as JSON and deletes nothing. Re-running after a partial failure sweeps the leftover residue idempotently. The dashboard's unwatch runs the same core behind an itemized confirmation.
junco unwatch acme/reef-api --plan
junco unwatch acme/reef-api
--plan first. A ticket for the repo currently in processing/ blocks the run until it finishes.see also: data · github loop guide
junco update
junco update
Installs the latest npm release of junco, then drains and restarts the supervised daemon — a graceful stop drains in-flight work before the new version takes over.
junco update
see also: restart · service · status · operations guide
junco worktree prune
junco worktree prune <path>
Deletes a stale or backup ticket worktree by path (a failed ticket's preserved worktree lives at <worktreeRoot>/<id>). The prune runs under the daemon's own shared worktree lock with an in-lock liveness gate, so it can never race a ticket that just started using the tree — and it refuses a live worktree outright: the daemon owns those.
junco worktree prune <worktreeRoot>/gh-acme-reef-api-52
x) runs this same command under the hood — no separate implementation.see also: dashboard · status · operations guide