Metadata-Version: 2.4
Name: loopuccino
Version: 0.10.0
Summary: A tiny, local checker for resumable AI coding work.
Author: Noah TaeHwan
License-Expression: MIT
Project-URL: Homepage, https://github.com/Noah-TaeHwan/loopuccino
Project-URL: Source, https://github.com/Noah-TaeHwan/loopuccino
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest<10,>=8; extra == "test"
Dynamic: license-file

<p align="center">
  <img src="https://raw.githubusercontent.com/Noah-TaeHwan/loopuccino/main/assets/brand/loopuccino-logo.svg" width="420" alt="loopuccino">
</p>

<p align="center"><em>Brew a better handoff.</em></p>

<p align="center">
  <a href="https://github.com/Noah-TaeHwan/loopuccino/actions/workflows/ci.yml"><img src="https://github.com/Noah-TaeHwan/loopuccino/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://github.com/Noah-TaeHwan/loopuccino/releases"><img src="https://img.shields.io/github/v/release/Noah-TaeHwan/loopuccino" alt="Release"></a>
  <a href="https://pypi.org/project/loopuccino/"><img src="https://img.shields.io/pypi/v/loopuccino" alt="PyPI"></a>
  <a href="LICENSE"><img src="https://img.shields.io/github/license/Noah-TaeHwan/loopuccino" alt="License: MIT"></a>
  <img src="https://img.shields.io/badge/python-3.9%20%E2%80%93%203.13-blue" alt="Python 3.9 through 3.13">
</p>

## What is loopuccino

`loopuccino` is a pair: an [Agent Skill](skills/loopuccino/SKILL.md) that designs
the loop, and a CLI that checks the paperwork the loop leaves behind. The skill
turns iterative, long-running, or multi-agent AI work into an explicit contract —
what the outcome is, which evidence decides it, and when to stop without success.
The CLI is the deterministic gate on the way out: it reads one `handoff.md` and
reports whether the next session can resume from it without guessing.

## Quick start

```bash
git clone https://github.com/Noah-TaeHwan/loopuccino.git && cd loopuccino
pipx install loopuccino                          # or: uv tool install loopuccino
loopuccino install --platform claude --apply      # -> ~/.claude/skills/loopuccino
```

Start a new session in your agent and `/loopuccino` is available. The CLI is
published to PyPI (`pipx install loopuccino` / `uv tool install loopuccino`);
the Agent Skill wires in from the checkout via `loopuccino install`, which
symlinks the skill directory from the repository source.

## The CLI

`loopuccino init` writes a starter handoff. It refuses to replace an existing
file unless you pass `--force`, and exits `2` when it refuses.

```console
$ loopuccino init
loopuccino init: wrote handoff.md
```

`loopuccino check` reads one Markdown file, writes nothing, and exits `0` when
no errors were found.

```console
$ loopuccino check handoff.md
loopuccino check: handoff.md

PASS  CHECK-OK     Required handoff contract checks passed

Result: PASS (0 errors, 0 warnings)
```

A document that is missing a required part fails with a diagnostic and exit `1`:

```console
$ loopuccino check bad.md
loopuccino check: bad.md

ERROR STRUCT-001            Missing required section: goal

Result: FAIL (1 errors, 0 warnings)
```

`loopuccino check --contract` adds the opt-in loop contract layer described
[below](#the-loop-contract).

`loopuccino status` reads a loop contract card and the `loop-log.jsonl` beside
it, and prints where the loop stands — so progress can be checked without
interrupting whatever is running. `--all` prints the three most recent events
instead of only the latest.

```console
$ loopuccino status loop-contract.md
state  in_progress (mode: durable)
last   [round 3] verifier: pass — 18/18 re-verified
next   Re-run the acceptance oracle, then record the result here.
```

`loopuccino install` wires the Agent Skill into an agent's skill directory. It
reports the target state and changes nothing until you pass `--apply`:

```console
$ loopuccino install --platform claude
loopuccino install: platform=claude
source  ~/src/loopuccino/skills/loopuccino
target  ~/.claude/skills/loopuccino
status  MISSING  not installed
run again with --apply to perform the change

$ loopuccino install --platform claude --apply
loopuccino install: platform=claude
source  ~/src/loopuccino/skills/loopuccino
target  ~/.claude/skills/loopuccino
status  OK       symlink -> ~/src/loopuccino/skills/loopuccino
```

Both paths print in full; they are shortened to `~` here.

`loopuccino update` reports whether the installed CLI, the checkout, and
the skill wiring are in sync, and checks GitHub for a newer release
(offline is fine — the check degrades to local-only). Nothing changes
without `--apply`, which pulls the checkout and reinstalls the CLI:

```console
$ loopuccino update
loopuccino update: checkout ~/src/loopuccino
cli      0.8.0 (installed)
checkout 0.9.0 (pyproject)
latest   0.9.0 (github)
skill    claude  OK       symlink -> ~/src/loopuccino/skills/loopuccino
verdict  cli 0.8.0 < checkout 0.9.0
action   reinstall the CLI from ~/src/loopuccino
run again with --apply to perform the update
```

## The handoff contract

The file must contain these H2 sections:

| Section | Purpose |
|---|---|
| `State` | `state` and `previous_state` values |
| `Goal` | The intended outcome |
| `Current state` | What is true now |
| `Done` | Completed work or acceptance items |
| `Blocked` | `reason`, `owner`, and `decision_needed`, or `none` for all three |
| `Changed files` | Files changed in the work session |
| `Verification` | `command` and `result` entries |
| `Next action` | The first useful action for the next session |
| `Resume command` | A concrete command, path, or document reference |

Allowed states are `planned`, `in_progress`, `blocked`, `ready_for_review`,
`verified`, `handed_off`, and `closed`. A `verified` or `closed` handoff needs a
recorded passing result such as `passed`, `success`, or `green`; `loopuccino`
does not rerun the command.

Start from the synthetic template in [`template/handoff.md`](template/handoff.md).
`loopuccino init` writes that same file for you.

## The loop contract

`loopuccino check --contract handoff.md` adds one optional layer on top of the
handoff contract. It checks whether the document **declares** the five parts of
an execution contract before anyone claims the work is done:

| Slot | Heading the checker looks for | What it adds over the handoff contract |
|---|---|---|
| Outcome and non-goals | `Outcome and non-goals` | `Goal` states the intent; this states the observable result **and what must not change** |
| Authority and action boundary | `Authority and action boundary` | Which inputs win, which acts are allowed, and which need approval |
| Current state and next action | `Current state` plus `Next action`, or a combined `Current state and next action` | Already covered by the handoff contract; accepted in either form |
| Acceptance evidence | `Acceptance evidence` | `Verification` records what was run and what came back; this declares **up front** which check decides which claim, and what it cannot prove |
| Bounds, stopping, and handoff | `Bounds, stopping, and handoff` | Attempt, cost, and time bounds, what counts as no progress, and when to stop without success |

The flag is opt-in. Without it no `CONTRACT-*` diagnostic is ever emitted, so a
handoff that never adopted this contract is never failed for omitting it.

### Why these checks read structure only

These checks decide one question: *is each slot declared, non-empty, and
formatted as a declaration?* They do not decide whether a declared outcome is
the right outcome, whether an action boundary is safe, whether the named
evidence actually proves the claim, or whether the work is genuinely finished.
Those are judgement calls, and a regular expression that imitated them would
produce confident output with nothing behind it. That would break the
[safety boundary](#safety-boundary) below: `loopuccino` reports structure and
does not vouch for truth.

## The Agent Skill

The repository ships loopuccino as an [Agent Skill](skills/loopuccino/SKILL.md).
It is plain Markdown, so any harness that reads skill-style instructions can use
it. It is meant to trigger when AI work is iterative, long-running, resumable,
unattended, cross-session, high-risk, or multi-agent — the cases that need
evidence-closed completion, explicit stopping, recovery, challenge, or handoff.
Triggers are written in English and Korean.

The method is one contract and three modes. The contract names five things
before work starts: the **outcome and non-goals**, the **authority and action
boundary**, the **current state and next action**, the **acceptance evidence**
that decides which claim, and the **bounds, stopping, and handoff** rules. The
mode is the smallest mechanism that fits — `micro` when iteration fits in one
response, `durable` when the work must survive a context boundary, and
`orchestrated` only when independent evidence or disjoint parallel work actually
justifies coordination. The loop itself is `ACT -> CHECK -> DECIDE`, with a rare
fresh-eyes challenge inside `DECIDE` when machinery is growing but evidence is
not.

For unattended or judged runs in Claude Code, read
[Claude Code goal/loop](skills/loopuccino/references/claude-code-goal-loop.md)
and instantiate the thin [goal](skills/loopuccino/references/templates/goal.md)
and [loop](skills/loopuccino/references/templates/loop.md) templates per project.
Other harnesses reuse the same templates; only the command surface differs.

## Cross-agent install matrix

| Platform | Target | Method | Notes |
|---|---|---|---|
| `claude` | `~/.claude/skills/loopuccino` | symlink | Tracks the checkout; pulling the repo updates the skill |
| `codex` | `~/.codex/skills/loopuccino` | symlink | Same as above |
| `agents` | `~/.agents/skills/loopuccino` | symlink | Same as above, for harnesses that read `~/.agents/skills` |
| `hermes` | `<--dest>/loopuccino` | copy | `--dest` is required; commit the copy in that repository and redeploy so its runtime picks it up |

Without `--apply` the command only reports each target's state:

| Status | Meaning |
|---|---|
| `OK` | Installed and pointing at this checkout |
| `MISSING` | Nothing at the target path yet |
| `STALE` | A symlink pointing somewhere else; `--force` replaces it |
| `CONFLICT` | A real directory is in the way; `loopuccino` refuses to touch it |

Real directories at a target path are never removed. For `hermes`, `--force`
refreshes the copy.

## Design principles

- **Smallest mode.** The skill starts at the least machinery that can work, and
  does not promote to durable or orchestrated modes just because tools and
  agents happen to be available.
- **Evidence-closed completion.** `COMPLETED` requires the declared success
  condition to be evaluated true with the evidence it named up front. A budget
  limit, a tool that returned, or a partial check is never completion by itself.
- **Truthful stopping.** When a bound is reached, the honest answers are
  `PARTIAL`, `BLOCKED`, or `STOPPED`, with the best-known artifact preserved —
  not a claim of success and not a promise to report later.
- **Privacy lint.** This repository holds itself to the same standard: a test in
  the suite scans every tracked text file for private tokens and fails CI if one
  appears, so nothing from a working session leaks into a public artifact.

## Diagnostics and exit codes

- `STRUCT-001`: required section is missing.
- `STRUCT-002`: required section is empty.
- `STATE-001`: state is missing or not allowed.
- `STATE-002`: previous-to-current state transition is not allowed.
- `STATE-003`: `verified` or `closed` has no passing verification result.
- `RESUME-001`: blocked handoff is missing a reason, owner, or decision.
- `RESUME-002`: next action has no explicit target or command.
- `EVIDENCE-001`: non-final state has no passing verification result; this is a warning.

With `--contract`:

- `CONTRACT-001`: a loop contract slot is not declared.
- `CONTRACT-002`: a declared slot has no content.
- `CONTRACT-003`: a declared slot still holds only unfilled `<placeholder>` text.
  A line counts as a placeholder when it is exactly one `<...>` token, optionally
  preceded by a list marker and a `label:` and optionally wrapped in backticks;
  the slot is only reported when every one of its non-blank lines is such a line,
  so this check under-reports rather than guesses.
- `CONTRACT-004`: `Acceptance evidence` labels no `Structural`, `Semantic`,
  `Operational`, or `Success` line; this is a warning, because a label is only
  weak evidence that the planes were actually separated.

| Exit code | Meaning |
|---:|---|
| `0` | No errors; warnings may be present. |
| `1` | Handoff contract errors were found. |
| `2` | Usage or input-file error. |
| `3` | Unexpected checker error. |

## Safety boundary

`loopuccino` is deliberately local and deterministic:

- It uses only the Python standard library at runtime.
- `check` reads one Markdown file and writes nothing.
- `init` writes one file at the path you name, and will not replace an existing
  file unless you pass `--force`.
- `install` only touches the filesystem: no network, Git, or shell calls.
- `update` is the one command that reaches further: it may query the
  GitHub releases API (a single read-only request; nothing is uploaded),
  and with `--apply` it runs `git pull --ff-only` and reinstalls the
  CLI. `check`, `init`, `status`, and `install` never touch the network,
  Git, or a shell.
- It does not execute commands found in the handoff.
- Outside of `update`, it does not inspect Git or the filesystem beyond
  reading the input path.
- It does not scan secrets, call a model, or upload content. Outside of
  `update`'s single read-only release check, it makes no network requests.
- It emits human-readable output only.
- It judges the structure of what a document declares. It never judges whether
  those declarations are true, adequate, or well chosen.

## Development

```bash
uv sync --extra test
uv run pytest -q
uv build
```

The project targets Python 3.9 through 3.13. Pull requests run the same test
suite across that matrix. Released changes are recorded in
[`CHANGELOG.md`](CHANGELOG.md).

## Name

`loopuccino` is pronounced **loop-oo-CHEE-no** — loop plus cappuccino, which is
also what the mark draws: steam off the crema tracing a loop. The canonical
spelling is lowercase `loopuccino` for the brand, package, repository, and
command. Brand files live in
[`assets/brand/`](https://github.com/Noah-TaeHwan/loopuccino/tree/main/assets/brand).

## License

MIT. See [`LICENSE`](LICENSE).
