npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pi-anti-repeat

v0.2.1

Published

Detects and stops repeated work in the Pi coding agent, including loops inside streamed thinking

Downloads

242

Readme

pi-anti-repeat

pi-anti-repeat is an extension for the Pi coding agent that stops an agent that is stuck repeating itself. It watches the model's streamed thinking and its finished runs, and when the same work keeps coming back, it cuts the response off and tells the model to change its approach.

It catches loops inside a single thought as it streams, including the two ways streaming models collapse in practice: a phrase or single token repeated forever (such as = the = the = the), and reasoning that falls apart into runs of punctuation (such as = = = =). In one recorded case a model spent 74 seconds writing the same five sentences about 375 times. Replayed through the detector, that response is stopped after 866 words, about 5 seconds in. A response that collapsed into 72 KB of = the is stopped within the first 500 words. It also catches runs that end with the same outcome or the same error again and again.

Install

pi-anti-repeat is an npm package:

pi install npm:pi-anti-repeat

Or install from Git:

pi install git:github.com/osolmaz/pi-anti-repeat

Run /reload in an open Pi session to load it. Add @<version>, @<tag>, or @<commit> to pin a version. Anti-Repeat needs Pi 0.87.0 or later.

Anti-Repeat is on from the start of every session. It shows nothing in the footer while it is only watching; the status line appears after it corrects or stops a run. Use /anti-repeat off to turn it off for the current session. Nothing is saved between sessions.

Detection and correction

On the first detection in an epoch, Anti-Repeat sends one visible anti-repeat message. The message tells the model to stop the current approach and restate what it has verified before it chooses a different next action. If the repetition was in streamed thinking, the response is cut off first and the message starts a new turn. On a second detection in the same epoch, it stops the run and sends nothing else.

An epoch starts with each new instruction from you. Short prompts such as "continue" or "go ahead" keep the current epoch, so a loop cannot hide behind them.

These checks trigger a detection:

  • Three separate 96-word passages each appear three times in one streamed thought, or one sequence of up to 96 words repeats across a span of at least 288 words. Case, punctuation, whitespace, and Unicode composition do not matter, apart from separator runs: each 16 separator characters in a row count as one reserved word, and an emoji counts as two.
  • The same cycle of one to four run outcomes repeats three times.
  • The same error ends three runs in a row.
  • Four runs started by "continue" prompts repeat at least 85% of the same actions.

A long run on its own never triggers anything. Work that keeps making different progress continues.

Commands

/anti-repeat status
/anti-repeat off
/anti-repeat on
/anti-repeat reset

reset starts a new epoch, which also clears a stop.

Cost

The thinking check runs on every streamed chunk, so it is built to be cheap. Streaming 1 MB of thinking through the detector takes about 50 ms, and the cost per word does not grow with the windowWords option. The listener is removed while Anti-Repeat is off, paused, or stopped. Memory stays bounded at 2,048 tracked windows and 256 recent window keys per response, and twelve run summaries.

Anti-Repeat never stores raw model output, thinking text, or tool content. The corrective message is the only session entry it adds.

Use it from code

Pi distributions can create the extension with their own options instead of installing the default:

npm install pi-anti-repeat
import { createAntiRepeat } from "pi-anti-repeat";

export default createAntiRepeat({
  enabled: true,
  command: "anti-repeat", // or false for no command
  status: "anti-repeat", // footer status key, or false
  notify: true,
  detectors: {
    reasoning: { windowWords: 96, repeats: 3, matchedWindows: 3 },
    repeatedError: false, // turn one detector off
  },
  policy: ({ detection, corrections, activeResponse }) => (corrections === 0 ? "correct" : "stop"),
  message: (detection) => "You are repeating yourself. Try a different approach.",
  isContinuation: (text) => /^(continue|go on)$/i.test(text.trim()),
});

The policy returns ignore, notify, correct, or stop for each detection. The default corrects once and then stops. Invalid options throw when createAntiRepeat is called.

The detectors are also available without Pi from pi-anti-repeat/core:

import { RepeatDetector } from "pi-anti-repeat/core";

const detector = new RepeatDetector();
detector.startReasoning();
for (const delta of stream) {
  const detection = detector.observeReasoning(delta);
  if (detection !== null) break;
}

Events for other extensions

Anti-Repeat reports on the anti-repeat channel of pi.events. Each event has version: 1, a type (detected, corrected, stopped, reset, enabled, or disabled), the current epoch, and the detection when there is one. An extension that continues runs on its own should pause when it sees stopped.

Other extensions can control it on the anti-repeat:control channel:

import { ANTI_REPEAT_CONTROL_CHANNEL } from "pi-anti-repeat/protocol";

pi.events.emit(ANTI_REPEAT_CONTROL_CHANNEL, {
  version: 1,
  action: "ignore-next-run", // or "pause", "resume", "reset"
  source: "my-extension",
});

Send ignore-next-run just before you restart a run on purpose, for example after cutting off a response, so the regenerated thinking is not counted as a repeat.