Configuration reference (loop.yaml)
Paths are relative to the folder containing loop.yaml. Durations use Go
syntax (45m, 1h30m, 90s).
Top level
| Key | Default | Description |
|---|---|---|
name |
folder name | Project name, available as {{ .Project }} in templates. |
repo
| Key | Default | Description |
|---|---|---|
url |
required | Clone URL of the target repository. |
base |
main |
Base branch for worktrees and pull requests. |
workdir |
worktree |
worktree keeps one base clone in .loop/repo and adds a worktree per run; clone makes a full clone per run. |
branch_prefix |
loop/ |
Prefix for run branches. |
github |
derived from url |
owner/name used for the GitHub API. Set it when the URL is not a github.com URL. |
fork |
none | owner/name of a fork to push branches to when you have no push access to the repository. Pull requests open from the fork against github. |
push_url |
derived from url and fork |
Git URL of the fork; set it when url is not a github.com URL. |
Set GITHUB_API_URL for GitHub Enterprise.
sources
An ordered list. Order matters: it is the pick-up order.
Common keys:
| Key | Default | Description |
|---|---|---|
name |
the type | Unique name; item ids are <name>:<native id>. |
type |
required | markdown, github or jira. |
labels |
none | Only items carrying all of these labels are listed. Recommended: [ready-for-agent]. |
claim |
false (true when claim_label is set) |
Mark items in progress so two loops never pick the same one. |
claim_label |
loop:in-progress |
Label used by GitHub and Jira for the claim. |
comments |
see description | Load ticket comments into the template data (.Item.Comments). Unset means true for markdown and Jira; for GitHub, true on a private repository and false on a public one, where anyone can comment. See security. |
type: markdown
| Key | Default | Description |
|---|---|---|
path |
backlog |
Folder of *.md files. |
Frontmatter keys: id, title, status (open, in-progress, closed),
created, labels, depends_on, model, loop_run. Without a title the
first # heading or the file name is used. Claiming writes
status: in-progress and loop_run; closing writes status: closed and
closed_note. These edits touch only the keys loop owns: every other key
keeps its position, quoting and comments, and a file without frontmatter
gets a minimal one. Loop notes are appended under a ## Loop log heading.
type: github
| Key | Default | Description |
|---|---|---|
repo |
repo.github |
owner/name whose issues form the backlog. |
Claiming adds the label and a comment with the run id. Closing happens
through Closes #n in the PR body or explicitly after the merge.
type: jira
| Key | Default | Description |
|---|---|---|
url |
required | Site URL, e.g. https://acme.atlassian.net. |
jql |
required | Query for the backlog. Labels from labels are appended. |
transitions.in_progress |
none | Transition (or target status) to run on claim. |
transitions.done |
Done |
Transition to run when the PR merged. |
Credentials: JIRA_EMAIL + JIRA_API_TOKEN (Cloud, basic auth) or
JIRA_TOKEN (bearer, Server/Data Center).
prompts
Paths to template files; missing keys use the built-in defaults
(loop init writes copies to prompts/).
| Key | Used for |
|---|---|
session |
The initial implementation session. |
review |
Fix rounds triggered by reviews and PR comments. |
ci |
Fix rounds triggered by failing checks. |
conflict |
Resolving merge conflicts git could not resolve. |
verify |
Fixing a failing verify step. |
See prompts.md.
agent
| Key | Default | Description |
|---|---|---|
runner |
claude |
claude (Claude Code CLI) or cursor (Cursor agent CLI). |
command |
runner name | Executable override. |
model |
runner default | Model for every session. Items override it with a model: line or frontmatter. |
permission_mode |
acceptEdits |
Claude only: default, acceptEdits, bypassPermissions or plan. Written to the workdir settings and passed to claude --permission-mode. |
allow |
git defaults | Claude only: permission rules the headless session may use without prompting, for example Bash(npm test:*). See below. |
deny |
git push, gh pr, gh api, git reset --hard |
Claude only: rules the session may never use. |
timeout |
45m |
Wall clock limit per session. |
max_turns |
200 |
Passed to claude --max-turns. |
attempts |
2 |
Attempts for the initial session before the run fails. |
skills |
none | Folders symlinked into <workdir>/.claude/skills/. The links are kept out of git through the repository’s info/exclude. |
env |
none | Extra environment variables for sessions and steps. |
env_passthrough |
none | Variable names or globs (DATABASE_URL, MY_APP_*) sessions inherit from loop’s environment on top of the built-in allowlist. See below. |
extra_args |
none | Extra CLI arguments. |
Permissions in headless sessions
A headless claude -p session cannot ask for permission: a tool call the
rules do not allow is denied, and an agent that cannot run the tests or
commit stalls or gives up. Before every session loop writes
<workdir>/.claude/settings.local.json (kept out of git) with
permission_mode as the default mode and the allow and deny rules, so
the headless session and anyone who later joins it with loop join get the
same rules.
The defaults allow the git commands needed to inspect and commit work and
deny git push, gh pr, gh api and git reset --hard, because pushing
and opening the PR is loop’s job. The defaults contain nothing project
specific, so add the commands your verify steps and your agent need:
agent:
permission_mode: acceptEdits
allow:
- "Bash(npm test:*)"
- "Bash(npm run lint:*)"
- "Bash(go test:*)"
Setting allow replaces the defaults; include the git rules you still
want. permission_mode: bypassPermissions skips all checks and is the
quickest way to get a first run going in a sandbox, at the cost of the
agent being able to run anything. loop doctor warns when only the default
rules are configured. The Cursor runner always runs with --force, which
is Cursor’s equivalent of bypassing permissions.
Environment of a session
Agent sessions do not inherit loop’s whole environment. They get an
allowlist: shell and locale basics, proxy settings, git identity variables,
the toolchain variables of Go, Node, Rust, Java and Python, the agent CLIs’
own configuration and credentials (ANTHROPIC_*, CLAUDE_*, CURSOR_*),
the LOOP_* variables and agent.env. GITHUB_TOKEN, GH_TOKEN,
JIRA_* and anything else are withheld, so an agent cannot push, merge or
comment with loop’s credentials. Name what else your project needs in
env_passthrough:
agent:
env_passthrough: [DATABASE_URL, "MY_APP_*"]
run: and script: steps are your own scripts and keep the full
environment; agent: steps are sessions and get the allowlist.
How the prompt reaches the agent: claude receives it on standard input
with a pre-assigned session id. agent (Cursor) receives short prompts
as the positional argument; long ones (over 16 KiB, typical for tickets
with comment threads) are piped on standard input while the positional
argument points at the prompt file in the run folder, so the command line
never exceeds the operating system’s argument limit.
steps
Everything loop runs around a session is a step list in loop.yaml. There
are no hidden hook directories and no global configuration. Each step is
exactly one of:
| Kind | Meaning |
|---|---|
run: <command> |
Shell command executed with sh -c inside the workdir. |
script: <path> |
Executable file, path relative to loop.yaml, executed inside the workdir. |
agent: <path> |
Prompt template rendered with the run data and executed as an agent session. |
Optional keys: name, model and timeout (agent steps).
| Phase | When |
|---|---|
steps.setup |
After checkout, before the session. |
steps.verify |
After the initial session and after every review, CI or conflict fix round, before anything is pushed. A failing run or script step starts a session with the verify template and the list runs again; a failing agent step fails the run. |
steps.before_pr |
After verify, before the push. A failure fails the run. |
steps.merged |
After the PR merged. Failures are logged. |
steps.cleanup |
Before the workdir is removed. Failures are logged. |
steps.blocked |
When a run parks because it needs a human: fix rounds used up, branch protection, PR closed, gate declined. Failures are logged. |
steps.failed |
When a run fails: no usable session result, setup or verify error, push or PR creation error. Failures are logged. |
steps:
setup:
- script: hooks/setup.sh
- run: npm ci
verify:
- name: tests
run: npm test
- name: self-review
agent: prompts/self-review.md
merged:
- run: echo "merged $LOOP_PR_URL" >> "$LOOP_PROJECT_DIR/merged.log"
blocked:
- script: hooks/notify-slack.sh # reads LOOP_RUN_ERROR and LOOP_PR_URL
failed:
- script: hooks/notify-slack.sh
Steps see this environment: LOOP_PROJECT, LOOP_PROJECT_DIR,
LOOP_WORKDIR, LOOP_BRANCH, LOOP_BASE, LOOP_ITEM_ID,
LOOP_ITEM_TITLE, LOOP_ITEM_URL, LOOP_RUN_ID, LOOP_RUN_DIR,
LOOP_SUMMARY_FILE, LOOP_PR_URL, LOOP_PR_NUMBER, LOOP_RUN_PHASE,
LOOP_RUN_ERROR (the reason a run parked), plus agent.env.
pr
| Key | Default | Description |
|---|---|---|
draft |
true |
Open as draft; marked ready for review once CI is green. |
title |
{{ .Item.Title }} |
Title template. |
body |
built-in | Body template, inline or a file path. {{ .Summary }} is what the agent wrote to the summary file. |
reviewers |
none | Reviewers to request. |
labels |
none | Labels to add. |
link_issue |
true |
Add Closes #n for GitHub issues. |
commit_uncommitted |
true |
Commit whatever the agent left uncommitted. |
workflow
| Key | Default | Description |
|---|---|---|
poll_interval |
60s |
How often open PRs are polled. |
fix_rounds |
3 |
Review, CI and verify fix sessions per run. |
conflict_attempts |
1 |
Conflict resolution sessions per run. |
merge |
manual |
manual, when-green, when-green-and-approved, github-auto-merge. |
merge_method |
squash |
squash, merge or rebase. |
delete_branch |
true |
Delete the remote branch after the merge. |
close_issue_on_merge |
true |
Close the ticket after the merge. When false, loop waits until someone closes it. |
gates |
[] ([before-merge] for auto merge policies) |
Pause points: before-pr, before-fix, before-merge. |
concurrency |
1 |
Runs in an agent, verify or fix phase at the same time. |
required_checks |
all | Only these check names decide green or red. |
ci_log_lines |
200 |
Lines from the end of a failed GitHub Actions job log passed to the CI fix prompt. 0 disables log fetching. |
cleanup |
true |
Remove the workdir when the item is closed. |
pr_commands |
true |
Let collaborators with push access drive a run from the PR: /loop approve releases a gate, /loop resume restarts a blocked run. |
Run state
Each run lives in .loop/runs/<run id>/:
| File | Content |
|---|---|
run.yaml |
Phase, branch, workdir, PR, fix rounds, handled comment ids, sessions, event history. |
run.log |
Human-readable log of the run. |
session-NN-<kind>.prompt.md |
The exact prompt of each agent session. |
session-NN-<kind>.log |
Raw agent output of each session. |
summary.md |
What the agent wrote for the PR description. |
lock |
Advisory lock held by the process driving the run. |