Skip to content

programmism/junior-swarm

Repository files navigation

junior-swarm

Autonomous software engineering agent powered by Claude AI. Monitors your GitHub repositories, picks up issues labeled agent-ready, implements fixes, opens PRs, waits for CI, merges, and cuts releases — without human intervention.

Install

curl -fsSL https://raw.githubusercontent.com/programmism/junior-swarm/main/install.sh | bash

Or pull the Docker image directly:

docker pull ghcr.io/programmism/junior-swarm:latest

Quick start

Choose the Claude backend that fits your setup:

Mode When to use Billing
API key (backend: api) You have an Anthropic API key Per token
CLI (backend: cli) You have a Claude.ai subscription (Pro/Team/Max) Flat monthly

Option A — API key mode (default)

1. Create a GitHub token

Go to GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens and create a token with:

Permission Access
Contents Read and write
Issues Read and write
Pull requests Read and write
Workflows Read and write
Metadata Read-only (auto-selected)

2. Create config.yaml

global:
  backend: api                    # use ANTHROPIC_API_KEY

projects:
  - name: "my-app"
    repo: "myorg/my-app"
    github_token_env: "GITHUB_TOKEN"
    workdir: "/workspace/my-app"

3. Create .env

ANTHROPIC_API_KEY=sk-ant-...
GITHUB_TOKEN=ghp_...

4. Start

docker run -d \
  --name junior-swarm \
  --env-file .env \
  -v $(pwd)/config.yaml:/config/config.yaml:ro \
  -v junior-swarm-workspace:/workspace \
  -p 8080:8080 \
  ghcr.io/programmism/junior-swarm:latest

Option B — CLI mode (Claude.ai subscription)

CLI mode runs Claude through the claude CLI subprocess, which uses the credentials stored by claude login. No API key needed — billing goes through your Claude.ai subscription.

1. Install the claude CLI and log in

npm install -g @anthropic-ai/claude-code
claude login

Verify it works:

claude -p "say hello" --output-format json

2. Create a GitHub token (same as Option A above)

3. Create config.yaml

global:
  backend: cli                    # use claude CLI subprocess

projects:
  - name: "my-app"
    repo: "myorg/my-app"
    github_token_env: "GITHUB_TOKEN"
    workdir: "/workspace/my-app"

4. Create .env

GITHUB_TOKEN=ghp_...
# No ANTHROPIC_API_KEY needed

5. Start (mount the claude credentials from your home directory)

docker run -d \
  --name junior-swarm \
  --env-file .env \
  -v $(pwd)/config.yaml:/config/config.yaml:ro \
  -v junior-swarm-workspace:/workspace \
  -v $HOME/.claude:/root/.claude:ro \
  -p 8080:8080 \
  ghcr.io/programmism/junior-swarm:latest

Note: The -v $HOME/.claude:/root/.claude:ro mount passes your claude CLI credentials into the container. Run claude login once on the host before starting the container.


5. Trigger the agent

Add the label agent-ready to any issue in your repo. The agent picks it up on the next poll (default: every 5 minutes) and runs the full development cycle automatically.

Check status: http://localhost:8080/status


How the development cycle works

Each project runs its own loop. On every cycle the agent goes through these stages in order:

triage → planning → implementation → pull_request → release → validation
Stage What the agent does
triage Searches for open issues labeled agent-ready. If none found, checks the configured backlog. If still nothing, goes idle until next poll.
planning Reads the codebase, understands the issue, designs the approach. Breaks large issues into sub-issues if needed.
implementation Creates a feature branch, writes code, adds tests, runs go test / linter, commits. Fixes all failures before moving on.
pull_request Pushes the branch, opens a PR, polls CI checks every 60 s. If checks fail — reads logs, fixes, pushes again. When all checks pass, merges.
release Reads existing tags, determines next semantic version, pushes a tag, creates a GitHub Release.
validation Watches deployment pipelines. If something breaks, files a new issue and restarts the cycle to fix it. On success, marks the cycle complete.

Customizing the cycle

You can replace the default cycle with your own stages in config.yaml. Each stage has:

  • stage — name (used in logs)
  • hint_prompt — instructions injected into the conversation at the start of this stage
  • skip_if — condition to skip this stage (see below)
  • max_iter — max Claude API calls for this stage (overrides global max_cycle_iterations)

Minimal 2-stage example — just implement and open a PR, skip release and validation:

projects:
  - name: "my-app"
    repo: "myorg/my-app"
    github_token_env: "GITHUB_TOKEN"
    workdir: "/workspace/my-app"
    cycle:
      - stage: "triage"
        hint_prompt: |
          Find an open issue labeled 'agent-ready'.
          Call CycleSetIssue with the issue number, then CycleAdvance.
          If nothing to do, call CycleIdle.

      - stage: "implement-and-pr"
        hint_prompt: |
          Create a branch, implement the fix, run tests, push, open a PR.
          Wait for CI. When merged, call CycleComplete.

Skip stages conditionally:

cycle:
  - stage: "triage"
    hint_prompt: "..."

  - stage: "implementation"
    hint_prompt: "..."

  - stage: "release"
    hint_prompt: "..."
    skip_if: "no_pr"    # skip release if no PR was merged this cycle
    max_iter: 10        # release needs fewer iterations

skip_if supported values:

Value Skips when
no_issue No work item was selected (triage went idle)
no_branch No branch was created yet
no_pr No PR was opened yet

Configuration reference

Full config with all options:

global:
  # Claude backend — "api" (default) or "cli"
  backend: "api"

  # Path to the claude CLI binary (only used when backend is "cli").
  # Defaults to "claude" (looked up from PATH).
  claude_path: "claude"

  claude_model: "claude-opus-4-6"      # Claude model (api mode only)
  max_tokens: 8192                      # Max tokens per response (api mode only)
  max_cycle_iterations: 40             # Max tool calls per stage
  cycle_interval_seconds: 300          # Seconds between cycles when idle
  log_level: "info"                    # debug | info | warn | error
  log_format: "json"                   # json | text
  max_retries: 3                       # GitHub API retry attempts on transient errors
  retry_backoff_seconds: 2             # Base backoff (doubles: 2s, 4s, 8s…)
  requests_per_minute: 0              # Claude API rate limit (0 = unlimited, api mode only)

projects:
  - name: "my-app"
    repo: "owner/repo"                 # required
    github_token_env: "GITHUB_TOKEN"   # required — name of env var holding the PAT
    workdir: "/workspace/my-app"       # required — clone directory inside container

    # --- Optional ---

    # Per-project backend override (overrides global.backend for this project only)
    backend: "cli"
    claude_path: "/usr/local/bin/claude"

    # Static tasks when no GitHub issues exist.
    # Completed items are remembered across restarts.
    backlog:
      - title: "Add health check endpoint"
        body: "Implement GET /health returning 200 OK with JSON {status: ok}"

    # Extra tools installed once at startup (tracked by .toolchain-installed marker)
    toolchain:
      - name: "golangci-lint"
        install: |
          curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
            | sh -s -- -b /usr/local/bin v1.62.0

    # Env vars injected into every shell command for this project
    env:
      GOPROXY: "https://proxy.golang.org"

    # Per-project overrides of global defaults
    claude_model: "claude-opus-4-6"
    max_tokens: 16384
    max_cycle_iterations: 60
    cycle_interval_seconds: 600
    max_retries: 5
    requests_per_minute: 10

    # Custom cycle (omit to use built-in 6-stage default)
    cycle:
      - stage: "triage"
        hint_prompt: "..."
      - stage: "implementation"
        hint_prompt: "..."
        max_iter: 80
      - stage: "release"
        hint_prompt: "..."
        skip_if: "no_pr"

GitHub token — detailed setup

One token for all repos (same account/org)

projects:
  - name: "backend"
    repo: "myorg/backend"
    github_token_env: "GITHUB_TOKEN"   # same var name
    workdir: "/workspace/backend"

  - name: "frontend"
    repo: "myorg/frontend"
    github_token_env: "GITHUB_TOKEN"   # same var name
    workdir: "/workspace/frontend"
# .env
GITHUB_TOKEN=ghp_one_token_for_both

Separate token per project

projects:
  - name: "backend"
    repo: "myorg/backend"
    github_token_env: "GITHUB_TOKEN_BACKEND"

  - name: "client-project"
    repo: "client-org/their-repo"
    github_token_env: "GITHUB_TOKEN_CLIENT"
# .env
GITHUB_TOKEN_BACKEND=ghp_...
GITHUB_TOKEN_CLIENT=ghp_...

The value of github_token_env is just the name of an environment variable. You pick the name, then define that variable in .env (or your secrets manager). The agent reads the token at runtime using os.Getenv(github_token_env).


Run without Docker

Prerequisites: Go 1.23+, git, gh CLI, and either an Anthropic API key or the claude CLI.

git clone https://github.com/programmism/junior-swarm.git
cd junior-swarm
go build -o junior-swarm ./cmd/junior-swarm

# API mode:
export ANTHROPIC_API_KEY=sk-ant-...
export GITHUB_TOKEN=ghp_...
./junior-swarm --config config/config.yaml

# CLI mode (after `claude login`):
export GITHUB_TOKEN=ghp_...
./junior-swarm --config config/config.yaml  # config.yaml has backend: cli

Docker Compose

services:
  junior-swarm:
    image: ghcr.io/programmism/junior-swarm:latest
    env_file: .env
    volumes:
      - ./config.yaml:/config/config.yaml:ro
      - workspace:/workspace
      # For CLI mode: mount claude credentials from host
      # - $HOME/.claude:/root/.claude:ro
    ports:
      - "8080:8080"
    restart: unless-stopped

volumes:
  workspace:
docker compose up -d

Monitoring

Endpoint Description
GET /health {"status":"ok"} when running
GET /status JSON with each agent's current stage and last error

Token usage is logged at INFO level after each stage (API mode only):

{"level":"INFO","msg":"stage tokens","input":1240,"output":380,"cache_read":0,"cache_write":0}

Project structure

cmd/junior-swarm/   entry point
internal/
  agent/            cycle orchestration, rate limiter
  backend/          Claude execution backends (api, cli)
  config/           YAML config loading and validation
  cyclestate/       per-cycle mutable state, backlog persistence
  logging/          structured logging setup
  supervisor/       multi-agent management, health server
  tools/            20 agent tools (bash, git, GitHub API, file system, cycle control)
  workspace/        repo cloning, sandboxed command execution
  channels/         inbox channels (Telegram, Slack, MCP)
  inbox/            user↔agent messaging between stages
config/
  example.yaml      full configuration reference
docker/
  Dockerfile        multi-stage build
  entrypoint.sh     container startup
docker-compose.yml
install.sh          one-liner installer

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors