Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,37 @@ All notable changes to Looper are documented here. Versions follow
[Semantic Versioning](https://semver.org/); the loop spec format is versioned
separately via `version:` in `loop.yaml` (currently `1`).

## 0.3.0 — 2026-07-05

### Added — loop pattern library
- `templates/loops/` — five named, pre-designed loops the wizard customizes
instead of starting blank: `security-scan` (promoted from the real run
that produced hermes-ecosystem's security fixes), `code-review`,
`bug-hunt`, `docs-sync`, and `research-synthesis`. Each is a complete,
compiler-validated `loop.yaml` with `{{PLACEHOLDER}}` slots, a README
(use-when, placeholder table, customization notes), and helper check
scripts where the pattern needs them.
- `/looper [target-dir] --template <name>` — Template Mode in the wizard:
a compressed interview that asks only for the placeholder slots, model
selection, and paths, while keeping the full critique, structural-rule,
privacy, and preview flow.
- `looper.py compile` warns when unresolved `{{PLACEHOLDER}}` tokens remain
in the resolved spec; the wizard treats the warning as an emit blocker.
- `scan-secrets.py` (security-scan template): deterministic secret/PII
candidate sweep over working tree + full git history — streaming reads,
directory-pruned walk, masked excerpts only, placeholder-value
suppression. Detects underscore-joined credential names (`SECRET_KEY`,
`DB_PASSWORD`, `client_secret`), does not suppress real secrets whose
value merely contains `test`, and dedupes on a hash of the raw value so
distinct secrets that mask alike are never dropped.
- Template checker scripts reject `???` placeholders, only waive
required-field validation for a genuinely empty report (standalone
no-findings line with no finding signal), and validate citations resolve
to a file under the sources directory.
- 2 new tests (18 total): every template must compile (with the expected
placeholder warning) and be listed in the catalog; the warning must
disappear after substitution.

## 0.2.1 — 2026-07-05

### Fixed
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,28 @@ then offers to run the loop right there in the same Claude Code session.
If you want a different folder name, pass it after `/looper`, for example
`/looper client-onboarding-loop`.

### Start from a pattern template

Instead of a blank interview, start from a named, pre-designed loop:

```text
/looper my-review --template code-review
```

| Template | Use when |
|----------|----------|
| `security-scan` | Read-only sweep of a repo for secrets, PII, and vulnerabilities → triaged `SECURITY-FINDINGS.md`. |
| `code-review` | Review a branch's diff against its base → typed, severity-rated `REVIEW.md` grounded in the diff. |
| `bug-hunt` | Reproduce a reported bug, fix the root cause, prove it with before/after repro evidence. |
| `docs-sync` | Find and fix doc/code drift → per-item `DRIFT-REPORT.md`; docs follow code, code untouched. |
| `research-synthesis` | Synthesize collected sources into a cited `REPORT.md`; every claim traceable to a file. |

Each template is a complete, compiler-validated `loop.yaml` with a handful of
`{{PLACEHOLDER}}` slots; the wizard asks only for those, picks models from
what's installed, and still runs its full critique, privacy, and preview flow
before emitting. See [`templates/loops/`](templates/loops/) for the catalog
and per-template docs — including how to add your own.

### Easy: run in the same session

The default path is to let Looper continue in the same conversation. It follows
Expand Down
55 changes: 47 additions & 8 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ description: >
cross-model review council. Use when the user wants to design, build, or set
up an agent loop, iterative agent workflow, self-review loop, LLM-as-judge
loop, multi-model council, reviewer/judge gate, or /goal-style looping
process. Guide goal refinement, typed verification criteria, reviewer and
judge selection, privacy boundaries, termination guards, no-progress stops,
and lightweight observability, then emit a RUN_IN_SESSION.md handoff prompt
plus portable loop.yaml, loop.resolved.json, LOOP.md, and run-loop.py.
process. Start from a named pattern template (security-scan, code-review,
bug-hunt, docs-sync, research-synthesis) or from a blank interview. Guide
goal refinement, typed verification criteria, reviewer and judge selection,
privacy boundaries, termination guards, no-progress stops, and lightweight
observability, then emit a RUN_IN_SESSION.md handoff prompt plus portable
loop.yaml, loop.resolved.json, LOOP.md, and run-loop.py.
disable-model-invocation: true
argument-hint: "[target-dir]"
argument-hint: "[target-dir] [--template <name>]"
allowed-tools: Read, Write, Bash
---

Expand All @@ -23,9 +25,11 @@ advanced external runner.

## Workflow

1. Resolve the target path from the `/looper` argument. If no target is given,
use `./looper-output`. If the target contains an existing `loop.yaml`, treat
the task as an edit/resume instead of a fresh scaffold.
1. Resolve the target path and optional `--template <name>` from the
`/looper` arguments. If no target is given, use `./looper-output`. If the
target contains an existing `loop.yaml`, treat the task as an edit/resume
instead of a fresh scaffold. If a template was requested, follow Template
Mode below instead of the blank-slate interview in step 3.
2. Load the relevant rubric only when entering that stage:
- Goal stage: `references/goal-rubric.md`.
- Verification stage: `references/verification-rubric.md`.
Expand Down Expand Up @@ -67,6 +71,41 @@ advanced external runner.
the same file is the easy restart path and `run-loop.py` is available for
advanced external execution.

## Template Mode

The pattern library lives at `${CLAUDE_SKILL_DIR}/templates/loops/` — one
directory per template containing a complete, compilable `loop.yaml` (with
`{{PLACEHOLDER}}` tokens marking project-specific slots), a `README.md`
(use-when, placeholder table, customization notes), and optionally
`scripts/` with helper checkers. The catalog index is
`templates/loops/README.md`.

A template is a pre-answered interview, not a bypass of design review:

0. If the target directory already contains a `loop.yaml`, the edit/resume
rule in step 1 wins: do **not** overwrite it with a template. Say the
directory already has a loop and ask the user to pick an empty target or
confirm they want it replaced before continuing.
1. If `--template` has no name, an unknown name, or the user asks what is
available, show the catalog table (template + use-when) and let them pick.
2. Read the template's `loop.yaml` and `README.md`. Use the template as the
seed instead of a blank spec.
3. Run a compressed interview in place of the seven blank-slate stages:
ask for each `{{PLACEHOLDER}}` slot named in the template README, run
the host-model stage against detected CLIs (`detect-models`) and swap
`host` / `council` invocations to what is actually installed and authed,
then confirm target and workspace paths.
4. Everything after the interview still applies unchanged: critique each
pre-filled stage against its rubric (step 4), the structural rules
(steps 5–8) including the cross-vendor egress statement, the ASCII flow
preview, confirmation, emission, and compile.
5. Never emit while any `{{` token remains in `loop.yaml`. The compiler
prints `looper: warning: unresolved template placeholders remain ...`
for this case — treat that warning as a blocker, not advice.
6. At emission, copy the template's `scripts/` directory (when present)
into `<target>/scripts/` alongside the standard emitted files, before
running compile.

## File Rules

- Write argv arrays, never shell command strings, for all model and check
Expand Down
16 changes: 12 additions & 4 deletions commands/looper.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Design and scaffold a Looper agent loop.
argument-hint: [target-dir]
argument-hint: [target-dir] [--template <name>]
allowed-tools: Read, Write, Bash
---

Expand All @@ -23,9 +23,17 @@ Find the Looper skill root before doing any loop-design work:
exactly. Treat the located directory as `CLAUDE_SKILL_DIR` when running
helper scripts.

## Target Directory
## Arguments

- If `$ARGUMENTS` is empty, use `./looper-output`.
- Otherwise, treat `$ARGUMENTS` as the target directory argument.
Parse `$ARGUMENTS` as `[target-dir] [--template <name>]`, in any order:

- If `--template` is present, take the next token as the template name and
follow the skill's Template Mode (the catalog lives at
`templates/loops/` inside the skill directory). `--template` with no
name means: show the catalog and ask.
- Remove `--template <name>` from the arguments first; everything left is the
target directory — treat it as a single path even if it contains spaces
(e.g. `C:\My Loops\out`), and do not split it into multiple tokens. If
nothing is left, use `./looper-output`.

Then continue with the Looper skill workflow.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "looper-skill"
version = "0.2.1"
version = "0.3.0"
description = "A Claude Code skill that scaffolds well-designed agent loops."
readme = "README.md"
requires-python = ">=3.9"
Expand Down
27 changes: 27 additions & 0 deletions scripts/looper.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@
]


# Loop templates under templates/loops/ mark project-specific slots with
# {{TOKEN}} placeholders. They compile as ordinary strings; the warning below
# keeps a half-customized template from being run by accident.
PLACEHOLDER_PATTERN = re.compile(r"\{\{[A-Za-z0-9_]+\}\}")


def find_placeholders(value: Any) -> set[str]:
found: set[str] = set()
if isinstance(value, str):
found.update(PLACEHOLDER_PATTERN.findall(value))
elif isinstance(value, list):
for item in value:
found.update(find_placeholders(item))
elif isinstance(value, dict):
for key, item in value.items():
found.update(find_placeholders(key))
found.update(find_placeholders(item))
return found


def reject_secret_material(values: list[str], context: str) -> None:
for value in values:
for pattern in SECRET_PATTERNS:
Expand Down Expand Up @@ -831,6 +851,13 @@ def cmd_compile(args: argparse.Namespace) -> int:
source = args.loop_yaml.resolve()
spec = load_yaml(source)
resolved = normalize_spec(spec, source)
placeholders = sorted(find_placeholders(resolved))
if placeholders:
print(
"looper: warning: unresolved template placeholders remain "
f"({', '.join(placeholders)}); fill them in before running this loop",
file=sys.stderr,
)
out = args.out or source.with_name("loop.resolved.json")
write_json(out, resolved)
if args.render:
Expand Down
44 changes: 44 additions & 0 deletions templates/loops/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Loop pattern library

Named, pre-designed loops the Looper wizard customizes instead of starting
from a blank interview:

```
/looper my-loop-dir --template <name>
```

Each template is a complete, compilable `loop.yaml` plus a README describing
when to use it and which `{{PLACEHOLDER}}` slots to fill. Templates with a
`scripts/` directory ship helper checkers that the wizard copies next to the
emitted loop. A template is a pre-answered interview, not a bypass: the
wizard still critiques every stage, enforces the structural rules, and shows
the flow preview before emitting.

| Template | Use when |
|----------|----------|
| [security-scan](security-scan/) | Read-only sweep of a repo for secrets, PII, and vulnerabilities → triaged `SECURITY-FINDINGS.md`. |
| [code-review](code-review/) | Review a branch's diff against its base → typed, severity-rated `REVIEW.md` grounded in the diff. |
| [bug-hunt](bug-hunt/) | Reproduce a reported bug, fix the root cause, prove it with before/after repro evidence. |
| [docs-sync](docs-sync/) | Find and fix doc/code drift → per-item `DRIFT-REPORT.md`; docs follow code, code untouched. |
| [research-synthesis](research-synthesis/) | Synthesize collected sources into a cited `REPORT.md`; every claim traceable to a file. |

## Conventions

- `{{PLACEHOLDER}}` tokens mark the project-specific slots; the README table
in each template says what goes where. `looper.py compile` warns if any
remain, and the wizard refuses to emit while one survives.
- Every template compiles as-is (CI enforces this), so the catalog can't
drift from the compiler's contract.
- Templates that edit files (`bug-hunt`, `docs-sync`) do so under
side-effect approval and never commit; read-only templates say so in
their `execution.side_effects.notes`.
- Programmatic checks invoke the interpreter as `python` (matching the
repo's example loop). On a machine where only `python3` is on PATH, the
wizard should swap the interpreter token when it customizes the template.

## Adding a template

Add `templates/loops/<name>/` with `loop.yaml` (compilable, placeholders
only inside string values), `README.md` (use-when, emits, placeholder
table), and optional `scripts/`. Add a row to the table above. The test
suite compiles every template automatically.
39 changes: 39 additions & 0 deletions templates/loops/bug-hunt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# bug-hunt

Reproduce a reported bug, fix the root cause, and prove it: the repro command
is observed failing before the change (plan gate) and passing after it
(delivery gate), with the full test suite still green.

## Use when

You have a bug report and a way to trigger the bug from the command line, and
you want the fix held to a before/after evidence standard instead of "the
error went away".

## What it emits

- `loop-workspace/fix-plan.md` — includes the verbatim failing repro output.
- `loop-workspace/FIX-REPORT.md` — root cause, fix at file:line,
before/after evidence.
- The fix itself, applied to the target repo's working tree (never
committed — committing stays your call).

## Placeholders

| Token | Replace with |
|-------|--------------|
| `{{REPO_DIR}}` | Path to the repository containing the bug. |
| `{{REPRO_CMD}}` | Command that triggers the bug, written as a plain string, e.g. `python -m pytest tests/test_x.py::test_bug -x`. The compiler splits it into an argv for you. |
| `{{TEST_CMD}}` | Command for the full test suite, e.g. `python -m pytest`. Plain string, split for you. |

Also: put the bug report (the issue text, stack trace, expected vs actual)
in `inputs/bug-report.md` inside the emitted loop directory.

## Customization notes

- **No command-line repro yet?** Write the repro as a small script first and
point `{{REPRO_CMD}}` at it — a bug you can't trigger deterministically
can't be gated programmatically, and this template's value collapses to
judge-only.
- The loop edits the target repo's working tree under side-effect approval;
run it on a clean branch so the diff is easy to review and revert.
Loading
Loading