junco

junco · coding-agent worker · v0.11.0 · MIT

Issues in.
Pull requests out.

Label a GitHub issue junco and a plan appears as a comment. Approve it, and junco drives a coding agent in a sandboxed worktree until a draft PR arrives — watched from a dashboard built for the whole loop.

npm install -g @ironforgesoftware/junco

MIT · runs entirely on your machine · local endpoint or hosted provider

designation
harness-agnostic coding-agent worker · local daemon
input
GitHub issues you label · Markdown tickets (YAML frontmatter)
output
draft pull requests · plan comments · in-place answers
cockpit
fullscreen terminal dashboard · junco dashboard
endpoint
any OpenAI-compatible endpoint · or a hosted provider from the built-in catalog
sandbox
OS-level isolation of agent tools · on by default · fails closed
identity
your gh auth · optional dedicated bot account for daemon traffic
data root
one dataDir · queue, clones, worktrees, transcripts, parked reviews
spend
per-session cost ledger · optional daily budget cap
runtime
Node ≥ 22.19 · git · gh
records
per-ticket JSONL transcripts · structured worker.log

The dashboard

junco dashboard — repos, issues, plans, the queue, and every junco-authored PR one keystroke apart.

The junco dashboard: three panes side by side. Pane one lists watched repositories with a queue card showing the running ticket and waiting count. Pane two lists the selected repository's junco-labeled issues with lifecycle glyphs and state badges. Pane three lists junco-authored pull requests with check counts. A header line shows review count, PRs needing attention, daemon uptime, and queue depth; a footer line shows key bindings.
  1. Watched repos and the queue card; the repo's junco-labeled issues; junco's PRs, attention-first. 1/2/3 jump panes; enter opens detail.
  2. Every action is a label mutation through your own gh auth; the daemon's sweep does the rest.
  3. A command palette (:) runs the actual junco CLI — no reimplementation, no drift.

The loop

You label an issue junco. Junco verifies the labeler has write access, plans in a read-only session, and posts the plan as an issue comment, flipping the label to junco:plan-ready. You read the plan comment, optionally edit it, and apply junco:approved. Junco verifies the approval — write access, and newer than the plan — then runs the agent in a git worktree with verification and a critic, opens a draft pull request whose description says Closes acme/reef-api#52, and flips the issue to junco:done.
$ junco logs -f09:14:07 INFO  github bridge: dispatched issue {"nwo":"acme/reef-api","issue":52,"id":"gh-acme-reef-api-52-plan","kind":"plan"}09:14:52 INFO  [gh-acme-reef-api-52-plan] finalized {"dst":"done/gh-acme-reef-api-52-plan.md","status":"completed"}── plan on #52 · junco:plan-ready · a human reads it, applies junco:approved ──09:31:02 INFO  github bridge: approved plan dispatched for execution {"nwo":"acme/reef-api","issue":52,"id":"gh-acme-reef-api-52"}09:31:05 INFO  claimed {"src":"inbox/gh-acme-reef-api-52.md","dst":"processing/gh-acme-reef-api-52.md"}09:42:31 INFO  [gh-acme-reef-api-52] spec verification: 2/2 checks passed09:42:58 INFO  [gh-acme-reef-api-52] critic: pass09:43:41 INFO  [gh-acme-reef-api-52] pushed junco/gh-acme-reef-api-52 (3 new commits)09:43:56 INFO  [gh-acme-reef-api-52] opened PR https://github.com/acme/reef-api/pull/5709:43:57 INFO  [gh-acme-reef-api-52] finalized (pr-flow) {"dst":"done/gh-acme-reef-api-52.md","status":"completed"}09:43:58 INFO  idle
  1. Approval is verified — write access, applied after the plan comment; junco fails closed otherwise.
  2. The plan is an editable issue comment; what it says at approval time is what executes.
  3. junco:ask skips planning — read-only Q&A answered as a comment; no branch, no PR.

It files its own issues

junco assess audits a repo — npm audit plus a read-only agent pass — and parks every finding for review. Nothing reaches a tracker until you confirm; filed issues arrive fingerprinted, severity-labeled, deduped. junco analyze drafts one issue's investigation as a comment, posted only on your say-so.

[high] Unsanitized template in exportCsv (sql-injection)
  junco:finding · severity/high · junco

Under the hood: every issue becomes a ticket

The bridge writes execution tickets into the same inbox/ as junco submit — one queue, one pipeline, wherever work comes from.

A ticket lands in the inbox directory, is claimed into the processing directory by an atomic rename, runs through an agent in an isolated git worktree with verification and a critic review, is pushed as a draft pull request, and is finalized into the done directory. A transient failure with no commits requeues the ticket to the inbox with backoff.
---
id: fix-tide-rounding
priority: normal
timeout_minutes: 30
repo: ~/code/reef-api
base_branch: main
pr_title: "fix: round tide heights to one decimal"
draft: true
---

# Round tide heights to one decimal

`TideTable.render()` prints raw floats. Round display values
to one decimal; leave the underlying data untouched.

## Steps

- [ ] Add `formatHeight()` in src/tideTable.ts; use it in `render()`.
- [ ] Commit: `git add -A && git commit -m "fix: round tide heights"`

## Verification

```bash
npm test
```

## Done when

- [ ] 1 commit on the branch; `npm test` green.

No repo: field? Then it's a Q&A ticket — answered in place, read-only. junco schema prints the full contract.

Capabilities

Atomic claiming
A ticket moves inbox/ → processing/ in one rename; ownership is never ambiguous.
Plans before code
In GitHub mode the plan is an issue comment; nothing runs until a verified junco:approved label.
Supervised sessions
Four loop guards on the event stream: nudge → escalate → kill. Every run leaves a JSONL transcript.
Timeout salvage
Commits made before the cutoff are pushed and opened as a draft PR instead of thrown away.
Requeue with backoff
Transient endpoint failures set retry_count and not_before, then return the ticket to the inbox.
Offline outbox
Comments, labels, and PR pushes queue durably while GitHub is unreachable, then drain on reconnect.
Sandboxed by default
Agent tools run OS-confined: writes jailed to the worktree, network denied unless a ticket opts in, credentials scrubbed. Fails closed.
Provider gate
A bad key, empty quota, or rate limit pauses claiming loudly — and never burns a ticket's retry budget.
Spend you can see
Every session's cost lands in a per-day ledger; daily_budget_usd stops the queue at the cap until midnight.
Local-first
Your machine, your git, your gh auth. No third service in the loop unless you choose a hosted provider.
PR monitor
junco-authored PRs sorted attention-first — failing checks and requested changes surface at the top. junco prs prints the same list.
Verify, then critique
Verification blocks run in the worktree; a critic reads the diff against the spec — a MISSING verdict buys exactly one corrective pass.
Bot identity
Daemon GitHub traffic can run as a dedicated bot account you grant per-repo access with junco auth grant; interactive commands stay under your own gh auth.
One data root
Queue, clones, worktrees, transcripts, and parked reviews resolve under a single dataDir. junco data prints the tree; junco data migrate unifies a legacy layout.
Stay current
A newer release surfaces in the dashboard, junco status, and junco doctor; junco update installs it and drain-restarts the supervised daemon.

Sixty seconds to a running worker

Node ≥ 22.19 · git · an authenticated gh for PR flows.

npx @ironforgesoftware/junco   # first run → guided setup walkthrough
junco                          # ensures the daemon is up, then the cockpit: watch, dispatch, approve, monitor
junco submit my-task.md        # or feed it a Markdown ticket directly
{
  "github": {
    "enabled": true,
    "repos": [{ "nwo": "acme/reef-api", "path": "~/code/reef-api" }]
  }
}

Contribute

junco is young; the codebase still fits in your head. ~3,200 tests run in under a minute; ARCHITECTURE.md is accurate and maintained. For features, open an issue first — plans are cheap, rework isn't. junco dogfoods: it submits tickets against its own repository.

git clone https://github.com/ironforgesoftware/junco && cd junco
npm install && npm test       # vitest, ~3,200 tests, under a minute

Label an issue. Get a pull request.

npm install -g @ironforgesoftware/junco