pipeline-worker
Automate the last mile of your local changes: pipeline-worker takes the uncommitted diff in your repo and drives it — unattended to a green merge request.
- Captures your staged + unstaged changes (your working tree is only read, not modified, up through this point).
- Replays them in a disposable git worktree.
- Asks a coding agent (Claude Code or GitHub Copilot CLI) to infer the intent: change type, branch slug, commit message, summary.
- Runs your
build/lint/testcommands, fail-fast. - Commits, pushes, and opens a GitLab MR or GitHub PR — the branch name is composed from the configurable
branchPattern. - Polls the CI pipeline; on failure it hands the failing job logs to the agent, commits the fix, pushes, and re-polls — capped at
maxFixAttemptsbefore escalating to a human with an MR comment. - Once the MR/PR is ready to merge, resets your repo's current branch back to HEAD (see
PIPELINE_WORKER_CLEANUPbelow) — your changes now live safely on the feature branch instead of sitting uncommitted locally too.
Polling is plain REST and costs zero agent tokens; the agent is invoked only when a pipeline actually fails, with truncated logs and a token-efficient TOON-encoded MCP server for anything more it needs.
Requirements
- Node.js >= 20.12 and git
- One coding agent CLI on your PATH: Claude Code (
claude) or GitHub Copilot CLI (copilot) - A GitLab or GitHub token with API access to the repo
Install
npm install -g pipeline-worker
Quick start
Set these once in your shell profile (~/.zshrc / ~/.bashrc) and every
repo on the machine picks them up — no per-repo setup needed:
export PIPELINE_WORKER_AGENT=claude
export PIPELINE_WORKER_FORGE=gitlab
export PIPELINE_WORKER_GITLAB_HOST=https://gitlab.example.com
export PIPELINE_WORKER_GITLAB_TOKEN=glpat-xxxxx
export PIPELINE_WORKER_GITLAB_REPO_BASE=$HOME/REPO # local dir that mirrors the GitLab namespace root — enables auto-detected projectId in any repo underneath it
Then, in any repo:
cd your-repo
# hack, hack, hack — leave the changes uncommitted, then:
pipeline-worker
Configuration
pipeline-worker is configured entirely through real environment variables — set them in your shell profile once, and every repo picks them up.
| Env var | Default | Meaning |
|---|---|---|
PIPELINE_WORKER_AGENT |
claude |
claude or copilot |
PIPELINE_WORKER_FORGE |
gitlab |
gitlab or github |
PIPELINE_WORKER_GITLAB_HOST |
— | e.g. https://gitlab.example.com |
PIPELINE_WORKER_GITLAB_PROJECT_ID |
— | numeric project id |
PIPELINE_WORKER_GITLAB_REPO_BASE |
— | local dir mirroring the GitLab namespace root, for auto-detecting projectId |
PIPELINE_WORKER_GITLAB_TOKEN |
— | GitLab API token |
PIPELINE_WORKER_GITHUB_REPO |
auto-detected from origin |
owner/name slug — only needed when origin isn't a GitHub remote |
PIPELINE_WORKER_GITHUB_TOKEN |
falls back to GITHUB_TOKEN |
GitHub token |
PIPELINE_WORKER_POLL_INTERVAL_SECONDS |
15 |
pipeline poll cadence; use 60 for slow pipelines |
PIPELINE_WORKER_BRANCH_PATTERN |
pipeline-worker/{name} |
feature branch naming template — see below |
PIPELINE_WORKER_CLEANUP |
true |
reset repoRoot to HEAD once the MR/PR is opened and CI is green (false to keep your local uncommitted changes as-is) |
build / lint / test local check commands and maxFixAttempts (default 5) are not configurable via env var — see auto-detection below.
Branch naming
branchPattern (env var or .pipeline-worker.yml) controls the feature branch name, built from three placeholders:
| Placeholder | Filled by |
|---|---|
{type} |
feature, bugfix, or chore — inferred from the diff by the agent |
{ticket} |
the --ticket <id> flag passed to pipeline-worker run |
{name} |
a short kebab-case slug describing the change — inferred by the agent |
For example, a team using GitLab issue-linked branches would set:
export PIPELINE_WORKER_BRANCH_PATTERN='{type}/{ticket}/{name}'
pipeline-worker run --ticket PROJ-123
# -> bugfix/PROJ-123/fix-login-redirect
A pattern that includes {ticket} requires --ticket to be passed; the run fails fast at the naming step otherwise.
Check command auto-detection
build / lint / test are picked from the repo's toolchain (first marker found wins; mixed-language repos should set the commands explicitly):
| Toolchain | Marker | build | lint | test |
|---|---|---|---|---|
| Node / TypeScript | package.json |
npm run build |
npm run lint |
npm test — each only if the script is declared |
| .NET | *.sln / *.csproj / *.fsproj / *.vbproj at root |
dotnet build |
dotnet format --verify-no-changes |
dotnet test |
| Go | go.mod |
go build ./... |
go vet ./... |
go test ./... |
| Python | pyproject.toml / setup.py / requirements.txt |
— | — | pytest |
A stage with no command (—) is skipped. If no toolchain is detected and no commands are configured, all local checks are skipped with a warning.
Commands
| Command | What it does |
|---|---|
pipeline-worker (or pipeline-worker run) [--ticket <id>] |
Capture the current diff and drive it to a green MR/PR |
pipeline-worker serve |
Start the forge MCP server over stdio (used by the agent during fix runs) |
pipeline-worker resume --branch <name> |
Resume watching/fixing a run after a crash |
pipeline-worker status --branch <name> |
Print the persisted state of a run |
How the fix loop stays bounded
Every retry path has a cap: local checks abort the run before an MR is ever opened; if no CI pipeline shows up for the MR/PR within 60s, the run ends there instead of polling; otherwise pipeline polling gives up after a 2-hour safety window; fix attempts stop at maxFixAttempts; a fix attempt that changes no files, or a pipeline that ends canceled/skipped, escalates immediately instead of spending agent tokens. Escalation always leaves a comment on the MR/PR so a human knows to take over.