Metadata-Version: 2.4
Name: pyocloop
Version: 0.2.0
Summary: TUI loop harness that orchestrates OpenCode to execute tasks from a PLAN.md file iteratively
Project-URL: Homepage, https://github.com/rbarzic/pyocloop
Project-URL: Repository, https://github.com/rbarzic/pyocloop
Project-URL: Issues, https://github.com/rbarzic/pyocloop/issues
Author-email: Ronan Barzic <rbarzic@gmail.com>
License: MIT
License-File: LICENSE
Keywords: ai,automation,opencode,textual,tui
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: httpx-sse>=0.4
Requires-Dist: httpx>=0.27
Requires-Dist: textual>=0.80
Requires-Dist: typer>=0.12
Description-Content-Type: text/markdown

# pyocloop

![pyocloop screenshot](pyocloop.png)

A Python TUI that orchestrates [OpenCode](https://opencode.ai) to execute tasks from a `PLAN.md` file iteratively, one session at a time.

pyocloop is inspired by and compatible with [ocloop](https://github.com/d3vr/ocloop) by [@d3vr](https://github.com/d3vr). It reimplements the same concept in pure Python using [Textual](https://textual.textualize.io/), fixing TUI display issues and a path resolution bug present in the original.

## How it works

1. pyocloop starts an `opencode serve` subprocess
2. 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
3. OpenCode reads the plan, executes the next task, marks it `[x]`, and optionally appends `<plan-complete>` when done
4. The TUI shows live progress: task counter, progress bar, current task, token count, and elapsed/average time per iteration
5. The loop stops when OpenCode writes `<plan-complete>` to the plan file

## Requirements

- Python 3.11+
- [OpenCode](https://opencode.ai) installed and configured (`opencode` on your PATH)

## Installation

```bash
git clone https://github.com/rbarzic/pyocloop
cd pyocloop
pip install .
```

For development (editable install):

```bash
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:

```markdown
# 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):

```markdown
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:**

```bash
# 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:

```bash
ocloop run \
  --model openai/gpt-4.5 \
  --plan ./myproject/PLAN.md \
  --prompt ./myproject/.loop-prompt.md \
  --run
```

## Plan file format

```markdown
- [ ] 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 `anthropic` provider 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
```
