Skip to content
indrazmPublic

About

Bounded, externally verified feedback loops for coding-agent CLIs.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

Repository files navigation

Loops

Loops runs coding agents in a bounded, externally verified feedback loop.

For each iteration it:

  1. Creates or reuses an isolated Git worktree.
  2. Asks an agent to implement the task.
  3. Runs every verification gate.
  4. Feeds failures into the next attempt.

A run succeeds only when the agent exits successfully and every gate passes. Iteration, timeout, output, and no-progress limits keep runs bounded.

Supported agents: Codex, OpenCode, Claude Code, Pi, and Cursor Agent.

Loops does not commit or publish by default. Tasks may opt into a verified commit or GitHub pull request; Loops never merges or deploys.

Requirements

  • Node.js 20.17 or newer
  • Git
  • macOS or Linux
  • At least one installed and authenticated CLI: codex, opencode, claude, pi, or cursor-agent

Install

npm install
npm link
loops help

Quick start

Create a task file in the repository you want to change:

{
  "name": "add-health-endpoint",
  "goal": "Add GET /health returning HTTP 200 with {\"status\":\"ok\"} and add tests.",
  "agent": {
    "provider": "pi"
  },
  "verification": [
    {
      "type": "command",
      "name": "tests",
      "command": "npm test"
    }
  ]
}

Validate, preview, and run it:

loops validate tasks/add-health-endpoint.task.json
loops run tasks/add-health-endpoint.task.json --dry-run
loops run tasks/add-health-endpoint.task.json

A dry run validates the task and Git base without creating .loop, a run, or a worktree.

Inspect the result:

loops list
loops inspect <run-id>
cd .loop/worktrees/<run-id>
git status
git diff

Only status: "success" means all verification passed.

Configuration

Agents

Set agent.provider to codex, opencode, claude, pi, or cursor:

{
  "agent": {
    "provider": "pi",
    "sandbox": "workspace-write",
    "model": null,
    "extraArgs": []
  },
  "sessionStrategy": "resume"
}
  • Codex: codex exec --json; access maps to Codex sandbox modes.
  • OpenCode: opencode run --dir <worktree> --format json; write runs use --auto, reviews use its plan agent.
  • Claude Code: claude -p --output-format json; write runs use acceptEdits, reviews use plan mode.
  • Pi: pi --mode json; reviews allow only read, grep, find, and ls tools.
  • Cursor Agent: cursor-agent --print --output-format stream-json; write runs use Cursor's enabled sandbox with forced non-interactive approvals, while reviews use plan mode.

Model identifiers are provider-specific. OpenCode expects provider/model; Pi accepts model patterns or provider/model; Codex, Claude, and Cursor use native identifiers.

sessionStrategy is fresh by default. Set it to resume to continue the provider session across iterations and resumed Loops runs.

Loops pins every provider to its isolated worktree. Cursor's own worktree option is intentionally not used, avoiding nested worktrees. OpenCode and Pi do not provide a Codex-style filesystem sandbox; a Loops worktree is not a security boundary, and both CLIs retain the permissions of the Loops process.

Verification

At least one verification gate is required. Gates run sequentially.

Use command gates for tests, builds, linting, type checking, and schemas:

{
  "type": "command",
  "name": "typecheck",
  "command": "npm run typecheck"
}

Use agent gates for semantic, architecture, UX, or security review:

{
  "type": "agent",
  "name": "security-review",
  "provider": "pi",
  "prompt": "Check for authentication bypasses and sensitive-data exposure."
}

Agent gates run in read-only or plan mode. Loops supplies the run's base commit, changed paths, preceding command-gate results, and earlier verdicts from the same review gate. The reviewer is instructed to inspect the complete in-scope change without rerunning deterministic commands, and to separate acceptance failures from optional improvements.

The preferred structured verdict is:

{
  "passed": false,
  "summary": "One acceptance criterion remains unmet",
  "blockingFindings": [
    {
      "criterion": "Exact acceptance criterion or repository rule",
      "evidence": "Specific file and behavior"
    }
  ],
  "advisories": ["Useful non-blocking improvement"],
  "evidence": ["Passing evidence or relevant context"]
}

Set passed to false only when blockingFindings is non-empty. Advisories do not fail a run. The legacy passed, summary, and evidence shape remains accepted for existing agents and task files.

Agent judgments are probabilistic. Prefer at least one deterministic command gate and place semantic agent gates after it so the reviewer receives the gate result.

Limits and worktrees

{
  "limits": {
    "maxIterations": 5,
    "maxNoProgress": 2,
    "timeoutSeconds": 1800,
    "maxOutputChars": 12000
  },
  "worktree": {
    "enabled": true,
    "base": "HEAD",
    "keep": true
  }
}

These are the defaults. No-progress detection fingerprints the repository and verification results. Failed checks, agent diagnostics, and Git status become feedback for the next iteration.

Use --no-worktree only when you explicitly want the current checkout modified. It disables automatic delivery.

Delivery

Delivery runs only after all verification passes:

{ "delivery": { "mode": "none" } }

Supported modes:

  • none — default; leave changes uncommitted in the worktree.
  • commit — create a commit using delivery.commitMessage.
  • pr — commit and push a unique branch, use the configured agent to author the PR, then monitor it until merge-ready.

Pull-request configuration:

{
  "delivery": {
    "mode": "pr",
    "commitMessage": "Implement health endpoint",
    "remote": "origin",
    "base": "main",
    "branchPrefix": "loops",
    "title": "Add health endpoint"
  }
}

Git and GitHub authentication must already be configured. Loops always uses the deterministic delivery.title; the configured implementation provider writes a free-form Markdown body from the verified diff and is never asked to encode PR metadata as JSON. The provider then monitors checks and review feedback with gh and may prepare local fixes. Loops re-runs every verification gate before committing and pushing those fixes. It does not rerun the gates for an unchanged commit that was already verified. Monitoring is bounded by limits.maxIterations and limits.timeoutSeconds.

A PR is complete when checks pass, it has no conflicts or requested changes, required reviews are satisfied, and the delivery agent reports no actionable feedback. If a human merges it while Loops is monitoring, delivery also succeeds when GitHub reports that the merged PR head is the exact commit Loops pushed and its checks and review requirements are satisfied. Closed, unmerged PRs and merged PRs at another head fail delivery. Loops never merges the PR itself.

Delivery steps, attempts, pull-request URL, and a heartbeat are persisted while monitoring, so loops list, loops inspect, and loops watch expose current progress during long provider or CI waits. Delivery failures preserve the worktree, commit, branch, PR, and evidence for recovery. Automatic delivery requires an isolated worktree.

If PR delivery is interrupted after a verified commit was pushed, reconcile it without rerunning implementation:

loops reconcile <run-id>
loops reconcile <run-id> --pr https://github.com/owner/repository/pull/123
loops reconcile <run-id> --pr https://github.com/owner/repository/pull/123 --verify-head

Reconciliation succeeds only when the worktree is clean, its HEAD and the remote branch both equal the persisted verified delivery commit, and the selected PR has that exact head with successful checks and satisfied review requirements. Use --pr when a follow-up PR was opened for a commit pushed after the original PR merged. If a manual recovery added a focused follow-up commit, --verify-head reruns every configured gate on the clean current head before replacing the persisted commit evidence.

Compatibility

Omitted agent, limit, worktree, session, and delivery fields use the defaults above. The default agent is Codex with workspace-write access.

Legacy task files using a top-level codex block or command gates without "type": "command" remain valid.

Commands

loops init [task-file] [--interactive | --defaults]
loops validate <task-file>
loops run <task-file> [--dry-run] [--no-worktree] [--quiet]
loops resume <run-id> [--task <task-file>] [--repo <path>] [--quiet]
loops reconcile <run-id> [--pr <url>] [--verify-head] [--repo <path>]
loops watch <run-id> [--repo <path>] [--quiet]
loops list [--repo <path>]
loops inspect <run-id> [--repo <path>]
loops cleanup <run-id> [--repo <path>]
loops wizard
loops help

Bare loops opens the interactive wizard in a terminal. Existing task files are never overwritten.

Pressing Ctrl-C cancels the active process group, records cancelled, and preserves the worktree. A per-run lock prevents concurrent execution of the same run.

Terminal statuses are:

success  no_progress  max_iterations  failed_to_start
cancelled  provider_error  delivery_failed  timeout

Headless and agent usage

Use explicit commands:

loops validate tasks/change.task.json
loops run tasks/change.task.json --dry-run
loops run tasks/change.task.json --quiet
loops inspect <run-id>

With non-TTY stdout, run and resume print the final state as JSON. Progress and errors use stderr; --quiet suppresses progress.

When another agent operates Loops:

  • Do not use bare loops, loops wizard, or interactive loops init.
  • Use a task file; there is no --json flag or task-from-stdin interface.
  • Treat only success as completion.
  • Do not enable commit or PR delivery without authorization.
  • Preserve successful changes before cleanup.

See skills/operate-loops/SKILL.md for the reusable workflow.

Storage and cleanup

.loop/
├── runs/<run-id>/
│   ├── state.json
│   ├── events.jsonl
│   ├── feedback.md
│   └── final-report.json
└── worktrees/<run-id>/

State writes are atomic. Resume interrupted non-terminal runs with loops resume <run-id>.

loops list, loops inspect, and loops watch inspect the execution lock and heartbeat as well as persisted state. A dead or missing executor is reported as interrupted with a resume instruction; a live executor whose heartbeat has not advanced for 15 seconds is reported as stalled. These are observations rather than terminal task statuses, so an interrupted run remains resumable.

loops cleanup <run-id> force-removes only that worktree and keeps its logs. Inspect and preserve useful changes first. With delivery mode none, commit in the worktree and cherry-pick the commit, or copy the changes manually.

Security

Agents and verification commands run with your local permissions. Command gates use the platform shell. PR delivery pushes with your Git remote and existing gh credentials.

Treat task files, repositories, scripts, agent configuration, extensions, PR comments, and CI output as untrusted input. Agent-monitored PR delivery reads remote feedback through gh; review who can comment on the repository. Use a disposable container or VM for untrusted work and expose only required files, network access, and credentials.

Loops does not manage provider authentication or include authentication files and the full environment in prompts and reports.

Override executable paths when needed:

LOOPS_CODEX_PATH
LOOPS_OPENCODE_PATH
LOOPS_CLAUDE_PATH
LOOPS_PI_PATH
LOOPS_CURSOR_PATH
LOOPS_GH_PATH

Development

npm install
npm run check
npm test

Tests use temporary Git repositories and fake agents. They require no model calls or network access.

About

Bounded, externally verified feedback loops for coding-agent CLIs.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages