Run LLM coding agents from reusable prompt files
Project description
Loopflow
Loopflow helps you create and run Waves. Waves are chains of coding agents working together in pre-defined ways.
Waves are first built manually through more interactive exploration. Eventually waves become autonomous through looping, scheduled cron runs, and watching for changes.
Waves
A wave is area × direction × flow.
| Field | Usage | Form |
|---|---|---|
| Area | Scope and context | pathset |
| Flow | Process followed / steps taken | sequence of prompts |
| Direction | Defines success, quality, and aesthetics | prompt |
# wave/designer/designer.yaml
flow: build
mode: loop
direction:
- ux
area:
- designs/
triggers:
- signal: wave
source_wave_id: infra
flow: build
Modes
The wave's mode controls its execution pattern.
| Mode | Behavior | Example |
|---|---|---|
| manual | Single run | Ship one feature, run one audit |
| loop | Continuous until stopped | Work through a backlog, grind PRs |
Crons
Crons schedule supplementary flows on a wave without changing its primary mode. They run independently of the worker pool, and workers: 0 is valid for cron-driven waves.
# member wave — workers handle the primary flow, crons sweep maintenance
flow: build
workers: 2
mode: loop
crons:
- flow: sync
schedule: "0 0 1 * *"
# root wave — no workers, all work comes from crons
flow: garden
workers: 0
mode: manual
crons:
- flow: govern-identity
schedule: "0 0 * * 0"
- flow: govern-coordination
schedule: "0 0 * * *"
Triggers
A trigger pairs a signal (what changed) with a flow (what to run). Triggers are a list — multiple triggers of the same signal are fine.
| Signal | What changed | Default flow |
|---|---|---|
| repo | Paths changed on main | integrate |
| wave | Another wave completed | build |
| ci_failure | CI failed on a wave PR | ci-fix |
Every new wave ships with two default triggers: repo (whole repo → integrate) and ci_failure → ci-fix. These don't need to be declared in the YAML.
Steps
lf debug -c # paste an error, watch it fix
lf op ingest --item 2-daemon-integrity.md # move one roadmap item to scratch/
lf design # interactive design session
lf gstack:office-hours # run an imported gstack workstyle step
lf npx:explain-code # fetch from npx skills ecosystem and run
Steps are prompts that run coding agents. Add your own in .lf/steps/.
Steps and flows are organized into three categories by agency: build (manual work you drive), govern (autonomous coordination the system drives), ops (side-channel utilities).
Build steps (build/)
Manual work — you invoke these, often interactively.
| Step | What it does |
|---|---|
kickoff |
Elaborate design — alternatives, research, imagine success/failure |
research |
Map the territory — architecture, complexity, quality, potential |
iterate |
Read research, write design to address it |
refresh-plan |
Reconcile scratch/ with the branch after rebasing |
reduce |
Find simplification opportunities |
polish |
Find polish priorities |
expand |
Find expansion opportunities |
5whys |
Root cause analysis on a bug fix |
implement |
Build from a design doc |
compress |
Simplify touched code |
gate |
Ship-ready code and reviewer-friendly docs |
debug |
Fix an error |
ci-fix |
Fix failing CI checks for the current PR |
integrate-upstream |
Adapt wave code after rebasing onto main |
qa |
Thorough quality assessment of the current branch |
triage |
Assess QA findings, separate blocking from polish |
design |
Interactive design session |
explore |
Investigate the codebase |
demo |
Experience-first walkthrough of observable changes |
code-review |
Walk through structural and architectural decisions |
review-design |
Reshape AI-elaborated design into user intent |
refine |
Refine existing work |
review-open-work |
Survey branches, PRs, worktrees, and waves for inbox-zero triage |
Govern steps (govern/)
Autonomous coordination — crons, triggers, and waves-watching-waves drive these.
| Step | What it does |
|---|---|
scan |
Read member wave state — PRs, blocks, progress, git activity |
assess |
Judge wave health and identify pressure points |
wave-report |
Read health signals across all waves |
mutate |
Compose and apply coordinated mutations across member waves |
review |
Review mutations, amend or revert if needed |
ingest |
Refresh PM-backed waves, then pick a wave item into scratch/ |
s5-scan |
Scan chord identity, roster, policy, and recent structural change |
s5-assess |
Assess identity, boundary, roster, and autonomy drift |
s4-scan |
Scan dependencies, advisories, upstream APIs, and other external signals |
s4-assess |
Assess which environmental changes matter and what they imply |
s3-scan |
Scan live health, velocity, CI, retries, and usage signals |
s3-assess |
Assess control health, mechanical blocks, and worker-pool size |
s2-scan |
Scan backlogs, PR overlap, area overlap, and conflict history |
s2-assess |
Assess coordination risk, conflict map, and safe ordering |
Ops steps (ops/)
Side-channel utilities — wrappers around git, PR, release, and wave state.
| Step | What it does |
|---|---|
init |
Set up loopflow in this repo |
commit |
Commit with generated message |
rebase |
Rebase onto main |
pr |
Generate PR title/body and call lf op pr --title --body |
land |
Land PR, rotate worktree |
lint |
Run linter, fix issues |
update-wave |
Create, update, or delete wave state |
split-wave |
Split a wave into smaller independent waves |
release |
Run the full release workflow (notes, PR, tag, status) |
release-notes |
Write narrative RELEASE_NOTES.md from release context, preferring release decisions when present |
synthesize |
Combine multiple perspectives into one |
validate |
Validate flows, steps, and directions |
Flows
lf design && lf implement && lf gate # chain steps manually
lf build # or use a named flow
Steps chain into flows. Flows feed into waves.
Flows can include mechanical ops items directly:
- implement
- gate
- op: land --create-pr
Build flows (build/)
| Flow | Steps |
|---|---|
build |
kickoff → review-design → loop(code → xor(demo, code-review), exit: gate) → deploy |
build-or-silent |
op: pm pull → ingest → xor(build, silence) |
design-and-ship |
design → implement → reduce → polish → deploy |
queue |
gate → update-wave → deploy |
code |
implement → compress → lint → gate |
pair |
design → code |
deploy |
gate → op: land --create-pr → op: pm push-diff |
ship |
refresh-plan → implement → gate → op: pr → op: land |
incident |
debug → 5whys → code → deploy |
Govern flows (govern/)
| Flow | Steps |
|---|---|
garden |
scan → assess → xor(garden-act, silence) |
garden-act |
mutate → review |
govern-operations |
ingest → xor(s1-build, silence) |
govern-coordination |
s2-scan → s2-assess → mutate |
govern-control |
s3-scan → s3-assess → mutate |
govern-intelligence |
s4-scan → s4-assess → mutate |
govern-identity |
s5-scan → s5-assess → mutate |
s1-build |
kickoff → code → deploy |
Ops flows (ops/)
| Flow | Steps |
|---|---|
release |
op: release run patch |
sync |
rebase → integrate-upstream → op: pm pull |
deploy lands the branch, then syncs PM state when there is PM work to do. sync rebases and pulls PM state for the current branch's wave. On branches with no PM-enabled wave, or no wave/<name>/ changes, the PM step in either flow exits cleanly.
Release artifacts
cat release/unreleased/DECISIONS.md
lf op release run patch
find release -maxdepth 2 -type f | sort
| Path | What it does |
|---|---|
release/unreleased/DECISIONS.md |
Append-only ledger of release-worthy intent and policy decisions during the current cycle |
release/vX.Y.Z/DECISIONS.md |
Archived decision ledger for a shipped version |
release/vX.Y.Z/NOTES.md |
Snapshot of the release notes generated for that shipped version |
RELEASE_NOTES.md |
Always-latest release notes at the repo root |
Interactive runs append to release/unreleased/DECISIONS.md when they make a durable product or process decision. Headless runs do not. If the ledger exists, lf op release run promotes release/unreleased/ to release/v<version>/, uses DECISIONS.md to shape the narrative release notes, and archives the generated root notes to release/v<version>/NOTES.md. If the ledger is absent, release notes fall back to merged PR history.
Browse the catalog
lfd serve
curl -s "http://127.0.0.1:2486/v0/catalog?repo=$(pwd)" | jq '.result.flows[] | {name, category, source}'
Open Flows in Concerto to browse the same catalog visually. The left pane groups flows and steps by build, govern, and ops; the right pane shows every parent flow that uses the selected flow or step.
Branches (xor)
Branches route a flow based on an agent's assessment of the current state. Exactly one path runs.
# flow: garden
- scan
- assess
- xor:
router: assess
paths:
act:
flow: garden-act
description: "Adjustments needed — mutate waves, then review"
silence:
description: "Everything is healthy"
The xor construct runs a router step that reads scratch/ and chooses a path. The router's prompt gets routing instructions appended automatically — the step author focuses on what to think about, not how to express the choice. A path with no flow: or step: (like silence) is a clean no-op exit.
If no router: is specified, a generic routing agent picks a path based on scratch/ contents.
Playing in the Waves
Once you have played with chaining steps into flows, you're ready to ride some waves.
lfq create engbot . # create a wave
lfq run engbot # ride a wave
Configure flow/area/direction with loopflow.update_wave(...), then ride it with loopflow.run_wave(...).
python - <<'PY'
import loopflow.api as loopflow
loopflow.update_wave("engbot", flow="build", direction=["ux"], area=["designs/"])
loopflow.run_wave("engbot")
PY
You can compose multiple directions to add additional nuance or perspectives.
lf research -d ux,clarity
lf research -d ceo
Install
curl -fsSL https://github.com/loopflowstudio/loopflow/releases/latest/download/install.sh | sh
Default install location is ~/.local/bin. Override with LF_INSTALL_DIR=/path.
First install guides you through connecting Claude, GitHub, and optional providers. Use --no-interactive to skip (CI, Docker, scripted installs).
Built-in steps and flows included. lf init sets up your coding agent and preferences.
cargo install --git https://github.com/loopflowstudio/loopflow --bin lf --bin lfd
Install the Rust binaries directly with cargo.
Query lfd (lfq)
uv tool install loopflow
lfq # status overview
lfq list # list waves
lfq show engbot # show wave details
lfq logs engbot # tail agent output
lfq stop engbot # stop a running wave
lfq delete engbot # remove wave and history
lfq usage # token usage summary (group by wave)
lfq usage --wave engbot # usage for one wave (group by step)
lfq providers # list providers with auth status and models
lf op auth status # local provider auth for lf steps and ops
lf op auth asana # connect Asana locally for `lf op` / step integrations
lf op auth notion # connect Notion locally for `lf op` / step integrations
lf op auth linear # connect Linear locally for `lf op` / step integrations
lfq auth status # provider auth status (GitHub / Claude / Codex / OpenCode Zen / Asana / Linear)
lfq auth github # connect GitHub in your browser
lfq auth claude # connect Claude in your browser
lfq auth codex # connect Codex in your browser
lfq auth zen # connect OpenCode Zen in your browser
lfq auth asana # connect Asana with OAuth
lfq auth linear # connect Linear with OAuth
lfq auth notion # connect Notion with OAuth
lfq auth disconnect github
lfq token revoke abc123 # revoke connection tokens by hash prefix
lfq token revoke --all # revoke all connection tokens
PM provider config:
# .lf/config.yaml
pm:
provider: notion
notion:
parent_page: 32af8f99-... # optional: reuse an existing parent page/teamspace
title_property: Name # optional schema overrides
status_property: Status
done_value: Done
priority_property: Priority
lf op branches list --user @me --stale 60d # preview stale remote branches
lf op branches prune --user @me --stale 60d # delete after confirmation
lf op pm init pm # connect/create one wave project, link items, write IDs
lf op pm init --all # bootstrap every wave/ project on the shared PM team
lf op pm pull pm # rewrite one wave from PM; remote changes win
lf op pm pull --all # rewrite every wave from PM; remote changes win
lf op pm export pm # push one wave to PM; local changes win
lf op pm export --all # push every PM-enabled wave
lf op pm push-diff pm # push only branch-changed items to PM; no-op if wave/<name>/ is unchanged
lf op pm push-diff --all # push-diff every PM-enabled wave
lf op pm status # show linked waves and local/remote counts
Asana task descriptions preserve basic markdown formatting on sync. Loopflow writes rich text through html_notes and falls back to plaintext notes when older tasks don't have rich text yet.
PM-backed lf op ingest refreshes the wave from the provider before it picks an item. If the pull fails, ingest warns and falls back to the local wave/<name>/ mirror.
Flow-driven pm pull and pm push-diff also skip cleanly when the current branch doesn't resolve to a PM-enabled wave. CI-only and non-PM branches can reuse the same flows without extra flags.
Explicit lf op pm pull <wave> and lf op pm push-diff <wave> still target the named wave. Only the flow-driven variants auto-skip.
uv tool install loopflow installs the Python CLI (lfq) and Python API only.
Use the install script or cargo to install lf and lfd.
Python API
uv pip install loopflow
import loopflow.api as loopflow
loopflow.waves()
loopflow.create_wave("engbot", repo=".", flow="build", direction=["clarity"])
loopflow.create_wave("ux", repo=".", flow="build", direction=["ux"], area=["docs/"])
loopflow.create_wave("infra", repo=".", flow="govern-control", direction=["infra"], area=["rust/"])
loopflow.add_trigger("ux", signal="wave", source_wave_id="infra")
loopflow.run_wave("ux")
import loopflow.api as loopflow
loopflow.create_wave("redesign", repo=".")
redesign = loopflow.wave("redesign")
print(redesign.primary_flow)
print(redesign.area)
tmux Plugin
# Add to .tmux.conf
set -g @plugin 'loopflowstudio/loopflow.tmux'
run '~/.tmux/plugins/tpm/tpm'
Status bar shows wave state: [lf: main] or [lf: 3 waves | engbot]. Customize the format:
# .tmux.conf
set -g @loopflow_status_format '⚡#{status}' # change wrapper
set -g @loopflow_status_format '[#{branch}]' # branch only
set -g @loopflow_status_format '[lf: #{status}]' # default
Variables: #{status} (computed text), #{branch}, #{step}, #{waves}, #{wave}.
Keybindings start with prefix+l:
| Key | Action |
|---|---|
r |
Run step/wave |
s |
Stop |
o |
Open logs |
p |
Open PR |
n |
Next iteration |
d |
Land PR |
u |
Start/bootstrap |
w |
Pick wave/worktree |
L |
Pick layout |
? |
Help |
Two built-in layouts: lf-dev (editor + agent + shell), lf-swarm (monitor + 3 worktree workers).
Works without lf or lfq installed — status shows placeholder, keybindings display clear messages.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file loopflow-0.9.10.tar.gz.
File metadata
- Download URL: loopflow-0.9.10.tar.gz
- Upload date:
- Size: 9.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d17d8584d2272a1b37290d975f13a854bd8a471dd7bc936090d0709e36a20a0e
|
|
| MD5 |
685f00ca68507e8912d86b3ed9860f57
|
|
| BLAKE2b-256 |
b8dd204aa2d86c3c40602d4ae13d7699eaa9e013f347350e702b987783a22c8b
|
Provenance
The following attestation bundles were made for loopflow-0.9.10.tar.gz:
Publisher:
release.yml on loopflowstudio/loopflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
loopflow-0.9.10.tar.gz -
Subject digest:
d17d8584d2272a1b37290d975f13a854bd8a471dd7bc936090d0709e36a20a0e - Sigstore transparency entry: 1372707263
- Sigstore integration time:
-
Permalink:
loopflowstudio/loopflow@646cb18830de0f72a090a67c6a2d65b91ea12a81 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/loopflowstudio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@646cb18830de0f72a090a67c6a2d65b91ea12a81 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file loopflow-0.9.10-py3-none-any.whl.
File metadata
- Download URL: loopflow-0.9.10-py3-none-any.whl
- Upload date:
- Size: 26.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4205ad853fe887bf1a7c7213d1c2193d30da87361dce9c98980fa8f1a21b747
|
|
| MD5 |
b9cb072166940f32c1a785e0e8d494b5
|
|
| BLAKE2b-256 |
80b58d9bc591921a2160442c113109ab45cdbb6a2f745e103da58d7782fb7e0b
|
Provenance
The following attestation bundles were made for loopflow-0.9.10-py3-none-any.whl:
Publisher:
release.yml on loopflowstudio/loopflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
loopflow-0.9.10-py3-none-any.whl -
Subject digest:
a4205ad853fe887bf1a7c7213d1c2193d30da87361dce9c98980fa8f1a21b747 - Sigstore transparency entry: 1372707354
- Sigstore integration time:
-
Permalink:
loopflowstudio/loopflow@646cb18830de0f72a090a67c6a2d65b91ea12a81 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/loopflowstudio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@646cb18830de0f72a090a67c6a2d65b91ea12a81 -
Trigger Event:
workflow_dispatch
-
Statement type: