How do you assign GitHub issues to AI agents?

Drop your repo folder into kanbots, switch to GitHub mode, and drag the issue card to In progress. kanbots creates a kanbots/issue-N-runId branch in a per-card git worktree, spawns claude -p against it, streams the run into the card, and finishes with a one-click Open draft PR.

The literal click path

kanbots turns “assign this issue to an AI” into a drag. There is no separate dispatch screen, no “agent assignment wizard”, no API to call. You move the card; the agent starts. Everything else — the branch name, the worktree path, the stream parsing, the decision UI — falls out of that move.

Setup: connect the repo

The repo connection depends on which edition you’re running.

OSS desktop

  1. Install gh (the GitHub CLI) and run gh auth login once. kanbots’s resolveGitHubToken() shells out to gh auth token first. Alternatives: GITHUB_TOKEN env var, or a token in ~/.kanbots/token. PATs need repo scope; fine-grained tokens need Issues, Contents, and Pull requests read/write.
  2. Open kanbots, pick your folder. The first time you flip Settings → Workspace to GitHub mode, kanbots writes {"mode":"github","owner":...,"repo":...} into .kanbots/config.json and runs the initial sync.

Cloud

  1. From Settings → Integrations → GitHub, click Connect GitHub. The KanBots GitHub App install page opens; the org admin picks the GH org and the repos to expose (granular, not all-or-nothing).
  2. The callback drops you back on /o/<org>/settings/integrations?integration_installed=github. Per-installation tokens (1h TTL) are minted on demand from the App’s private key; no long-lived PAT in the database.

Initial sync: open issues land in Backlog

kanbots paginates GET /repos/{owner}/{repo}/issues?state=all&per_page=100and creates one card per issue. Labels are mapped to kanbots labels (auto-created if absent). Status comes from the status:* label on the issue if one exists; otherwise the card lands in the Inbox column waiting for triage. The card-to-issue link is stored in card_external_links with a unique (link_kind, external_id) constraint, so webhook redeliveries upsert the same card.

After the initial sync, GitHub webhooks (cloud) or a polling Octokit client (OSS) keep the board in sync as issues are opened, edited, labeled, or closed.

The dispatch: what a drag does

When you drag issue #142 to In progress, kanbots runs the following sequence:

  1. Column move → label edit. kanbots issues a PATCH /repos/{owner}/{repo}/issues/142 that removes status:backlog and adds status:in-progress. Drag-to-move is the GitHub edit.
  2. Worktree. A fresh worktree is created at .kanbots/worktrees/issue-142-<runId>/ on a new branch kanbots/issue-142-<runId>, branched from the repo’s default branch. A .kanbots-identity.json file is dropped at the worktree root so an orphaned run can be recovered later.
  3. Pre-push hook. A pre-push hook is installed in the worktree that exits non-zero. The agent can run git pushall it wants — it will fail. Pushes go through your hands (or through the GitHub App, in cloud) only.
  4. Agent spawn. kanbots execs Claude Code:
    claude -p \
      --output-format stream-json --verbose \
      --permission-mode bypassPermissions \
      --append-system-prompt "<issue title + body>" \
      --model <chosen model>
    For Codex, the adapter swaps in codex exec with the equivalent flags. Both stream one JSON object per line.
  5. Live thread. Every line is parsed by stream-parser.ts into text, tool_use, tool_result, decision, result, or rate_limit, persisted to the agent_events table, and broadcast to the run drawer. If you reload the desktop window, the thread replays from the table.
  6. Agent state label. While the run is live, the issue gets an agent:running label. On a decision pause it becomes agent:blocked; on completion awaiting review, agent:review; on failure, agent:failed. Teammates on the GitHub issue see the state without opening kanbots.

Mid-run: decisions and replies

Claude or Codex can emit a decision stream event with a question and a list of options. The dispatcher marks the card awaiting_input, stops feeding stdin, and surfaces the prompt in the run drawer with numbered buttons. Click a button; the handler cards:resolvewrites the chosen value back into the agent’s stdin and the run continues. Close the window mid-decision and reopen later — the prompt is persisted on the card, not lost in memory.

The Reply box on the card takes slash commands too: /spec asks the agent to produce a spec for the issue; /review reviews its own diff; /split proposes child issues. The replies go through the same stream-parsing path, so the response shows up as new events in the thread.

Finishing: promote, PR, or discard

When the agent finishes (a result event with ok:true), the worktree stays on disk. You pick one of four actions:

  • Open draft PR (GitHub mode). kanbots pushes the branch and opens a draft PR via Octokit (OSS) or the GitHub App POST /repos/{owner}/{repo}/pulls (cloud). The PR body links back to the issue and lists the run’s commits.
  • Promote commit.Stage the tip of the worktree’s branch and cherry-pick onto your main checkout’s active branch. Worktree is removed.
  • Reveal worktree. Opens the worktree path in your file manager so you can hand-edit before promoting.
  • Discard. Stops the run if active, removes the worktree, deletes the branch.

Failure modes and fixes

“Branch not found on remote.”

The pre-push hook is in your worktree; if you bypass kanbots and manually run a git pushelsewhere, you might think the branch is on origin when it isn’t. Open draft PR fails with branch_not_found. Fix: use the kanbots promote-to-PR button; it pushes from your real checkout (cloud) or prompts you to push manually (OSS).

“Repo isn’t in the GitHub App’s selected list.”

On cloud, the GitHub App is installed with granular repo selection. If you forget to include the repo, PR creation fails with repo_not_in_installation. Fix: go to github.com/organizations/<org>/settings/installations, click the KanBots App, add the repo. The webhook fires and kanbots picks up the new repo within a few seconds.

“Decision timed out.”

Default decision timeout is 30 minutes. If the assignee is asleep when the agent pauses, the run can time out and fail. Fix: raise the timeout in Settings → Decisions (range 60s to 24h), or set a default_value on common decisions so the cloud auto-answers if no human responds in time. The agent sees the system answer the same way as a human one.

When this is the wrong fit

If your GitHub issues are bug reports without enough context to act on, an agent will spin and ask three decision questions before getting useful work done. Triage first: either flesh out the issue manually, or run a triage persona (see how triage autopilot works) that splits and clarifies before you dispatch a coding agent.

If your “issue” spans multiple repos or requires infrastructure changes outside the codebase, the worktree-per-issue model is not the right shape — kanbots agents only see the files inside their worktree.

Related reading

Same workflow from a kanban-board angle: what is an AI agent kanban board. The branch and worktree mechanics in isolation: how do you use git worktrees with Claude Code. Background, sleep-mode operation: run AI agents in the background on GitHub issues.

Try it on your own folder

Drop a folder, get a board, dispatch parallel agents. The desktop runs locally on macOS, Linux, and Windows.