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-lego

v0.1.1

Published

Turn repeated agent corrections into reusable blocks.

Readme

pi-lego

Turn repeated agent corrections into reusable blocks.

pi-lego is a framework for executable agent conventions. A block detects a command pattern, explains why it is unhelpful, gives an actionable alternative, and can offer a reason-bearing local exception. The default extension includes the head and tail blocks.

This is corrective feedback, not a sandbox or permissions engine. It does not verify authorization, parse every shell construct, show confirmation dialogs, or grant permissions through slash commands.

Install

Install the published package:

pi install npm:pi-lego

For local development, install a checkout instead:

pi install /path/to/pi-lego

Restart pi or run /reload. The package's default extension registers the included blocks for bash and cmux_open_terminal tool calls.

Included blocks

Ordinary command output should stream because pi already bounds model-visible output and preserves the full result when it truncates. Using head or tail to hide ordinary output loses evidence; let the full output stream and rely on the harness bounds instead.

When a block matches, the command is not executed and the agent sees feedback like:

Command blocked (not executed): `tail` not allowed.
Pi already bounds model-visible output and preserves the full result when it truncates, so hiding ordinary output with `tail` loses useful evidence.
Instead: Re-run the same command without the `tail` segment and let the output stream. Use a producer's own filters when the query itself is narrow.
Exception: put `# allow tail: <specific reason>` in the leading comment block.

head

head waits for N lines or EOF. A finite producer that closes after fewer lines returns normally, but a live producer that keeps stdout open can wait indefinitely. Prefer producer-native bounds or a timeout for live streams. An initial-lines query can declare its narrow intent in the leading comment block:

# allow head: the initial lines are the query
head -n 50 app.log

tail

A last-lines query can declare its narrow intent in the leading comment block:

# allow tail: the final lines are the query
journalctl --unit app | tail -n 50

The leading comment block may contain blank lines and multiple standalone comments, so each matching block can have its own exception. Parsing stops at the first executable line. Reasons must be nonempty, but may contain ordinary punctuation because the comment is inert. Quoted strings, inline comments, malformed comments, and comments after an executable line do not bypass a block.

Write a block

Blocks are small TypeScript objects. There is no JSON DSL.

Import from the public pi-lego and @earendil-works/pi-coding-agent package names, not checkout paths or package internals. Your extension must be able to resolve its dependencies from its own location. For a packaged extension, declare pi-lego in dependencies; installing pi-lego as a Pi extension does not by itself put it on every standalone extension's module search path.

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { registerBlocks, type ConventionBlock } from "pi-lego";

const archive: ConventionBlock = {
  id: "archive",
  pattern: { command: "archive" },
  rationale: "Archiving during an edit loop hides the files under review.",
  alternative: "Inspect the working files directly.",
  exception: {
    description: "specific reason",
  },
};

export default function (pi: ExtensionAPI): void {
  registerBlocks(pi, [archive]);
}

A command pattern matches executable positions through pipelines, control flow, substitutions, nested shell commands, and the built-in wrappers command, nohup, sudo, env, timeout, xargs, find -exec, bash/sh/zsh/fish -c, op run --, op plugin run --, and mise exec/mise x ... --. Use detect(command) instead when a convention needs custom matching; a block cannot define both. If exception.comment is omitted, it defaults to allow <id>. Omit exception entirely for a block that cannot be overridden.

If several blocks match, pi-lego reports all of them. Each exception only bypasses the block that owns its exact comment marker; all other matching blocks are still reported.

Add command wrappers

Custom wrapper definitions compose with the built-ins. A declarative prefix can be a shell string or an exact token array:

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { defineWrapper, registerBlocks } from "pi-lego";

const wrappers = [
  defineWrapper({ prefix: "launcher start --" }),
  defineWrapper({ prefix: ["runner", "exec", "--"] }),
];

export default function (pi: ExtensionAPI): void {
  registerBlocks(pi, [archive], { wrappers });
}

String prefixes are parsed once by defineWrapper, so quoting is honored: launcher 'special mode' -- contains three tokens. A string prefix must be one static simple command; assignments, redirects, pipelines, control operators, parameter expansion, and command substitution are rejected. Token arrays are already-tokenized exact prefixes and are not shell-expanded.

Use a resolver for wrappers whose options do not have one fixed prefix:

const unusual = defineWrapper({
  command: "launcher",
  resolve(args) {
    const marker = args.indexOf("execute:");
    return marker < 0 ? undefined : { words: args.slice(marker + 1) };
  },
});

A resolver can return { words: [...] } for an already-tokenized command or { script: "..." } for nested Bash source. It receives dequoted argument values; it never executes or expands them.

Scope and limitations

  • Matchers inspect command text before tool execution. They do not constrain other tools or commands launched outside these two pi tool calls.
  • Shell structure is parsed without execution by unbash. It targets Bash (with much POSIX sh syntax), not PowerShell, cmd.exe, or every construct of other shells. Malformed input is inspected through unbash's best-effort partial AST; parser recovery can still omit an invocation.
  • Wrapper definitions model command-specific argument semantics. The built-ins cover only the forms listed above; unsupported flags or an unusual form may require a custom resolver. Wrapper expansion is capped at 64 commands to stop cyclic custom definitions.
  • Exceptions are local declarations of intent. They are not capabilities, signed approvals, or an audit system.
  • A block author owns false-positive and false-negative behavior in its matcher.

Positioning

Custom blocking hooks are already part of pi's extension API. pi-lego focuses on composing corrections for safe-but-wrong approaches: explain the convention, offer a useful alternative, and allow a narrow local exception when warranted.

The wrapper registry and flag-boundary approach were adapted from pi-guard's src/wrappers.ts, by Jason Diamond, under the MIT license. See THIRD_PARTY_NOTICES.md. Shell parsing uses unbash 4.0.11 under the ISC license.

Related projects cover adjacent needs:

  • pi-guardrails focuses on dangerous operations, secrets, and protected files.
  • pi-permission-system provides centralized allow, deny, and ask decisions.
  • pi-guard provides extensible matchers and shell parsing.

Development

Requires Node.js 22.19 or newer.

npm install
npm run check
npm pack --dry-run

License

MIT