TUI loop harness that orchestrates OpenCode to execute tasks from a PLAN.md file iteratively
Project description
pyocloop
A Python TUI that orchestrates OpenCode to execute tasks from a PLAN.md file iteratively, one session at a time.
pyocloop is inspired by and compatible with ocloop by @d3vr. It reimplements the same concept in pure Python using Textual, fixing TUI display issues and a path resolution bug present in the original.
How it works
- pyocloop starts an
opencode servesubprocess - On each iteration it creates a session, sends your loop prompt (with the plan file path injected), and waits for the session to go idle
- OpenCode reads the plan, executes the next task, marks it
[x], and optionally appends<plan-complete>when done - The TUI shows live progress: task counter, progress bar, current task, token count, and elapsed/average time per iteration
- The loop stops when OpenCode writes
<plan-complete>to the plan file
Requirements
- Python 3.11+
- OpenCode installed and configured (
opencodeon your PATH)
Installation
git clone https://github.com/rbarzic/pyocloop
cd pyocloop
pip install .
For development (editable install):
pip install -e .
Usage
ocloop COMMAND [OPTIONS]
Commands:
run Run the OCLoop orchestration loop
bootstrap Create a starter PLAN.md and .loop-prompt.md in a directory
ocloop run
ocloop run [OPTIONS]
Options:
-m, --model TEXT Model to use (format: providerID/modelID).
List available models with: opencode models
-a, --agent TEXT Agent to use
--prompt PATH Path to loop prompt file [default: .loop-prompt.md]
--plan PATH Path to plan file [default: PLAN.md]
-p, --port INT OpenCode server port [default: 4096]
-r, --run Start iterations immediately (no keypress needed)
-d, --debug Debug mode (skip plan file validation)
--verbose Log every SSE event in the activity panel
--log PATH Write all log entries to a file
--help Show this message and exit
ocloop bootstrap
ocloop bootstrap [DIRECTORY] [OPTIONS]
Arguments:
DIRECTORY Directory to initialise [default: current directory]
Options:
-f, --force Overwrite existing files
--help Show this message and exit
Key bindings
| Key | Action |
|---|---|
S |
Start the loop |
Space |
Pause / Resume |
R |
Retry after an error |
Q |
Quit (aborts current session) |
Example
The examples/ directory contains a self-contained demo: answering EU capitals quiz questions.
File layout:
your-project/
├── PLAN.md # task list (or use --plan to point elsewhere)
├── .loop-prompt.md # instructions for OpenCode (or use --prompt)
└── ...
examples/PLAN.md — task list:
# EU Capitals Quiz Plan
## Backlog
### Phase 1: Western Europe
- [ ] **1** What is the capital of Austria?
- [ ] **2** What is the capital of Belgium?
- [ ] **3** What is the capital of France?
...
examples/loop-prompt.md — loop prompt (note the {{PLAN_FILE}} placeholder):
Execute the next task from {{PLAN_FILE}}.
Before starting:
1. Read {{PLAN_FILE}} fully
Task selection:
- Pick the FIRST uncompleted task
- Mark it [x] after completion
Completion check:
- If all tasks are [x] or [BLOCKED], append:
<plan-complete>SUMMARY</plan-complete>
Bootstrap and run:
# Create starter files in a new directory
ocloop bootstrap ./myproject
# Edit the generated files, then run from inside the directory
# (--plan defaults to PLAN.md and --prompt defaults to .loop-prompt.md)
cd myproject
ocloop run --model openai/gpt-4.5 --run
Or run from outside the directory by specifying paths explicitly:
ocloop run \
--model openai/gpt-4.5 \
--plan ./myproject/PLAN.md \
--prompt ./myproject/.loop-prompt.md \
--run
Plan file format
- [ ] Pending task
- [x] Completed task
- [MANUAL] Task requiring human intervention (skipped by the loop)
- [BLOCKED: reason] Task that could not be completed
The loop ends when OpenCode appends this tag to the plan file:
<plan-complete>Summary of what was done</plan-complete>
Loop prompt tips
- Use
{{PLAN_FILE}}as the placeholder — pyocloop replaces it with the absolute path at runtime - Instruct OpenCode to mark tasks
[x]after completion and append<plan-complete>when all done - Keep prompts focused: one task per session works best for reliable progress tracking
Available models
Run opencode models [provider] to list models for a specific provider. Below are the models available at time of writing (your installation may differ):
| Provider | --model value |
Notes |
|---|---|---|
| OpenAI | openai/gpt-5.5 |
Latest flagship |
| OpenAI | openai/gpt-5.5-pro |
Pro tier |
| OpenAI | openai/gpt-5.5-fast |
Faster/cheaper |
| OpenAI | openai/gpt-5.4-mini |
Lightweight |
| z.ai (coding) | zai-coding-plan/glm-5.1 |
Coding-optimised |
| z.ai (coding) | zai-coding-plan/glm-5-turbo |
Fast coding model |
| z.ai (coding) | zai-coding-plan/glm-4.7 |
Previous gen |
| z.ai (limited ctx) | zai-limited-context/glm-5.1 |
Smaller context window |
| DeepSeek | opencode/deepseek-v4-flash-free |
Free via opencode |
| GitLab Duo | gitlab/duo-chat-sonnet-4-6 |
Anthropic Sonnet via GitLab |
| GitLab Duo | gitlab/duo-chat-opus-4-7 |
Anthropic Opus via GitLab |
| GitLab Duo | gitlab/duo-chat-gpt-5-4 |
GPT-5.4 via GitLab |
Anthropic models can be used directly if you configure the
anthropicprovider in opencode.
Architecture
pyocloop does not call any LLM directly. It delegates all AI work to the opencode binary:
ocloop (Textual TUI)
└─ opencode serve (subprocess)
├─ POST /session create session
├─ POST /session/{id}/prompt_async send prompt
├─ GET /event?directory=… SSE stream (progress events)
└─ GET /config detect active model
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 pyocloop-0.2.0.tar.gz.
File metadata
- Download URL: pyocloop-0.2.0.tar.gz
- Upload date:
- Size: 82.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
335820cee54a1745f7abd64ed704325f09bd104afbfee90334c994b940558725
|
|
| MD5 |
27e8874348171b0a2879d36521095d9b
|
|
| BLAKE2b-256 |
2870b37b3215ce0b4c8a790dde12d3a80447e6bece25c8210dd31c3d086af8b9
|
Provenance
The following attestation bundles were made for pyocloop-0.2.0.tar.gz:
Publisher:
publish.yml on rbarzic/pyocloop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyocloop-0.2.0.tar.gz -
Subject digest:
335820cee54a1745f7abd64ed704325f09bd104afbfee90334c994b940558725 - Sigstore transparency entry: 1553219419
- Sigstore integration time:
-
Permalink:
rbarzic/pyocloop@eac07bcb20b20379362b13e07714038e2190484e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/rbarzic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eac07bcb20b20379362b13e07714038e2190484e -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyocloop-0.2.0-py3-none-any.whl.
File metadata
- Download URL: pyocloop-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.3 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 |
3c5a3dc80fc27c2aa7a71e9c0296b419782c53e948193a47cea53d890247600d
|
|
| MD5 |
ffe708748ee5c33b3e4004e85035495a
|
|
| BLAKE2b-256 |
5b49ce983872f4a75f10f69cd3c629b9e2570ff64681cac070ecdab134eeed86
|
Provenance
The following attestation bundles were made for pyocloop-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on rbarzic/pyocloop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyocloop-0.2.0-py3-none-any.whl -
Subject digest:
3c5a3dc80fc27c2aa7a71e9c0296b419782c53e948193a47cea53d890247600d - Sigstore transparency entry: 1553219421
- Sigstore integration time:
-
Permalink:
rbarzic/pyocloop@eac07bcb20b20379362b13e07714038e2190484e -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/rbarzic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eac07bcb20b20379362b13e07714038e2190484e -
Trigger Event:
push
-
Statement type: