GitHub - jaksa76/ai-coding-factory

5 min read Original article ↗

Autonomous AI agents that pull issues from a task manager, implement them, and push code — continuously. Inspired by AI Coding Factories.

How it works

Workers poll a task manager for unassigned issues, claim one, clone the repo, invoke an AI agent to implement it, commit and push the result, then loop back for the next issue. The task manager is the single source of truth — no local task store.

The task management backend is pluggable via the TASK_MANAGER environment variable (default: jira).

[Jira] ← issues queue
   ↓  workers poll for unassigned open issues
[Worker pool: claude | copilot | codex | ...]
   ↓  push to trunk, post comments, transition status
[Jira + Git]

Installation

Clone the repository and run the setup script:

git clone https://github.com/jaksa76/ai-coding-factory.git
cd ai-coding-factory
./setup.sh

setup.sh will:

  1. Check prerequisites (docker, git)
  2. Ask which AI agent you want to use (claude, copilot, ...)
  3. Create symlinks in bin/ for all tools (loop, implement, plan, factory, task-manager, worker-builder, agent)
  4. Add bin/ to your PATH in your shell config
  5. Collect your credentials and write them to an env file (e.g. .env.factory)

Re-run setup.sh at any time to update credentials or change the agent.

Configuration

All configuration is passed via environment variables. setup.sh writes these to an env file for you, but you can also set them manually.

Task manager

The default backend is jira. Provide your Jira credentials:

export TASK_MANAGER=jira   # optional, jira is the default
export PROJECT=MYPROJ
export JIRA_SITE=mycompany.atlassian.net
export JIRA_EMAIL=worker@mycompany.com
export JIRA_TOKEN=<jira-api-token>
export JIRA_ASSIGNEE_ACCOUNT_ID=<jira-account-id>

For GitHub Issues, use:

export TASK_MANAGER=github
export PROJECT=owner/repo
export GH_ASSIGNEE=myuser
export GH_TOKEN=<github-pat>

For the TODO backend, use:

export TASK_MANAGER=todo
export PROJECT=TODO.md
export TODO_ASSIGNEE=myuser

PROJECT is the canonical target passed to loop --project:

  • Jira: project key, for example MYPROJ
  • GitHub: repository in owner/repo form
  • TODO: path to the todo file

Git

export GIT_REPO_URL=https://github.com/myorg/myrepo.git
export GIT_USERNAME=myuser
export GIT_TOKEN=<github-pat>

AI agent

Claude (API key):

export ANTHROPIC_API_KEY=<your-api-key>

Claude (subscription): log in with claude login first, then import your credentials (see below). Or set them manually from ~/.claude/.credentials.json:

export CLAUDE_ACCESS_TOKEN=<access-token>
export CLAUDE_REFRESH_TOKEN=<refresh-token>
export CLAUDE_TOKEN_EXPIRES_AT=<timestamp>
export CLAUDE_SUBSCRIPTION_TYPE=pro

GitHub Copilot:

export GH_TOKEN=<your-github-token>
export GH_USERNAME=<your-github-username>

Optional

export FEATURE_BRANCHES=true           # create a feature branch + PR per issue
export PLAN_BY_DEFAULT=true            # require a planning step for all issues
export NO_ISSUES_WAIT=60               # seconds to wait when no issues are available
export INTER_ISSUE_WAIT=1200           # seconds to wait between issues
export IMPLEMENTATION_PROMPT="..."     # override the default implementation prompt
export PLANNING_PROMPT="..."           # override the default planning prompt

Running

There are three ways to run workers: directly, via Docker, or via the factory.

Directly

Run the loop script directly on your machine (no Docker required):

This uses whatever agent script is on your PATH (set by setup.sh) and the environment variables in your current shell. Useful for development and testing.

Via Docker

Build a worker image and run it with your env file:

docker build -f workers/claude/Dockerfile -t worker-claude .
docker run --env-file .env.factory worker-claude

Via the factory (multiple workers)

Use factory to manage multiple Docker workers at once:

factory workers                          # start 1 implementation worker
factory workers 3                        # start 3 implementation workers
factory workers --env-file .env.factory  # pass credentials to workers

This uses the worker-claude image by default. Override with FACTORY_WORKER_IMAGE=<image>. Images are automatically built or rebuilt when missing or outdated.

You can also set the env file globally:

export FACTORY_ENV_FILE=.env.factory
factory workers 3

Monitor and control workers:

factory status              # list running workers
factory logs <worker-id>    # stream a worker's output
factory stop <worker-id>    # stop a specific worker
factory stop --all          # stop all workers

For lower-level control (e.g. a specific image):

factory add --image worker-copilot 2

Via AWS ECS (Fargate)

To run workers on AWS ECS Fargate instead of local Docker, create a runtime symlink in the factory directory pointing to runtime-aws:

ln -sf runtime-aws factory/runtime

Then set the required AWS variables:

export FACTORY_AWS_REGION=us-east-1
export FACTORY_AWS_CLUSTER=ai-coding-factory
export FACTORY_AWS_SUBNET_ID=subnet-...
export FACTORY_AWS_SECURITY_GROUP_ID=sg-...

All other factory commands (factory workers, factory status, factory logs, etc.) work the same way.

Feature branches

To open a PR per issue instead of pushing directly to the default branch:

export FEATURE_BRANCHES=true

Or add a needs-branch label to individual Jira issues. The worker creates feature/<ISSUE-KEY>, pushes it, opens a PR, posts a comment with the PR URL, and attempts to transition the issue to In Review.

Individual issues can opt out with the skip-branch label (takes precedence over FEATURE_BRANCHES=true).

Importing Claude credentials

If you use a Claude subscription, OAuth tokens expire periodically. After running claude login to refresh them, import the new credentials into your env file with:

factory import-claude-credentials --env-file .env.factory

This reads ~/.claude/.credentials.json and updates the CLAUDE_* variables in your env file in place.


Planning

If your workflow requires a planning step before implementation, run planner workers alongside your regular workers. Planners claim issues, generate a written plan, commit it to plans/<ISSUE-KEY>.md in the repo, and move on. A human reviews the plan; once approved, a regular implementation worker picks the issue up.

Build the planner image

docker build -f planner/Dockerfile -t planner-claude .

Start planner workers

factory planners                          # start 1 planning worker
factory planners 2                        # start 2 planning workers
factory planners --env-file .env.factory  # pass environment variables to planners

This uses the planner-claude image by default. Override with FACTORY_PLANNER_IMAGE=<image>.

Workflow

  1. Planner claims an eligible issue and generates plans/<ISSUE-KEY>.md
  2. Issue is transitioned to Awaiting Plan Review
  3. Human reviews and approves the plan (transitions issue to Plan Approved)
  4. Regular implementation worker picks it up from the Plan Approved queue

Opt-in / opt-out

By default, planning is opt-in. Add a needs-plan label to an issue to require a planning step for that specific issue.

To require planning for all issues by default, set:

export PLAN_BY_DEFAULT=true

Individual issues can bypass planning with the skip-plan label (takes precedence over PLAN_BY_DEFAULT=true).