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

@davecodes/pi-routines

v0.3.1

Published

Pi extension for scheduled and event-driven routines

Downloads

213

Readme

pi-routines

release license

A pi extension that gives your agent scheduled and event-driven routines — check CI every 5 minutes, brief you at session start, summarize your day at shutdown, run a pomodoro timer, react to a GitHub PR, or fire from a webhook.

No daemon. No external scheduler. No cloud. Routines run inside your live pi session as normal LLM turns, with full tool access, on your model credentials, with state that never leaves your machine.


Features

  • 🕒 Six trigger kinds, freely mixable on the same routine (max 4 per routine):
    • pulse — fixed interval, minimum 30 s
    • cron — 5-field POSIX cron with timezone support
    • oneoff — fire once at an ISO-8601 timestamp, then auto-disable
    • hook — pi lifecycle events (session_start / agent_end / session_shutdown) with once: daily|per_session
    • apiPOST 127.0.0.1:7424/routines/<id>/trigger with a bearer token (off by default)
    • github — polled gh events: pull_request.opened|closed, issues.opened, push, with label / branch / merged filters
  • 📦 11 bundled templatesci-watch, pomodoro, morning-briefing, morning-cron, deploy-watch, session-wrap, pr-babysitter, test-guardian, api-webhook, oneoff-reminder, github-pr-review
  • Pause / resume/routine-pause and /routine-resume keep the routine in the store with full run history but silence every fire path (scheduler / hooks / api → 423 Locked). Manual /routine-run-now bypasses the pause.
  • 🛡 maxRunsPerDay soft cap — opt-in per-routine daily limit; capped fires are recorded as skipped runs and consume zero provider tokens (gated before any LLM turn opens)
  • 🔇 Silent mode — quiet routines whose response is exactly [~] are suppressed from chat
  • 💾 Persistent state — atomic writes (.tmp → rename) with .bak rollback; corrupt JSON or missing files degrade gracefully to an empty store
  • 🧠 LLM tools — the agent can create / list / delete / update routines and persist arbitrary state across ticks
  • 🎛 Slash commands — full user-facing UI: /routine, /routines, /routine-install, /routine-pause, …
  • 🔁 Hot-reload safeglobalThis.__piRoutinesCleanup stops old timers + servers before re-arming
  • 🖨 Print-mode aware — registers tools but skips timers/widget/hook fires in pi --print
  • 🪞 Prompt placeholders{cwd}, {date}, {time}, {state}, {tickCount}, {apiArgs} (api triggers), {githubEvent} (github triggers)

How does this compare to Claude Code Routines?

Anthropic's own Claude Code Routines (research preview, April 2026) live in their cloud. pi-routines is the local, in-session counterpart. They solve different shapes of the same problem.

| | Claude Code Routines (cloud) | pi-routines (this extension) | | ------------------------ | --------------------------------------------- | -------------------------------------------------- | | Where it runs | Anthropic-managed cloud session | Inside your live pi session, on your machine | | Always-on / laptop closed | Yes | No — only while pi is running | | Min cadence | 1 hour | 30 seconds | | Schedule kinds | recurring presets + cron (via /schedule update) + one-off | pulse, cron, oneoff — all three | | API trigger | POST .../v1/claude_code/routines/<id>/fire | POST 127.0.0.1:7424/routines/<id>/trigger, off by default | | GitHub trigger | Webhook from Claude GitHub App, PR + Release events | Polled via your local gh CLI; PR/issues/push | | Session-lifecycle hooks | None (cloud has no session boundary) | session_start / agent_end / session_shutdown with once: daily \| per_session | | State across ticks | None (each run is fresh) | RoutineSetState + userState (≤ 2 KB JSON) | | Silent / quiet mode | None | quiet: true + [~] token + suppressor | | Connectors / tools | Account MCP connectors | Whatever tools your pi session has | | Daily cap | 5 / 15 / 25 by plan | Opt-in per-routine maxRunsPerDay (default: unlimited) | | Cost model | Counts against subscription | Your own provider tokens via pi | | Privacy | Code + secrets uploaded to Anthropic | Stays local | | Discoverability | Web UI + /schedule | 11 slash commands + 4 LLM tools + bundled skill | | Manage from web GUI | Yes | No |

Pick the cloud one when you need the routine to run while your laptop is closed. Pick pi-routines when you need sub-hourly cadences, session-lifecycle hooks, state across ticks, or on-device privacy.


Requirements

  • pi (@earendil-works/pi-coding-agent)
  • Node.js ≥ 22
  • pnpm (or npm / yarn — the lockfile is pnpm but any will work)
  • Optional: gh CLI for GitHub-triggered routines

Install

Quick install (recommended — via the pi CLI)

If you don't have pi yet:

curl -fsSL https://pi.dev/install.sh | sh
# or
npm install -g @earendil-works/pi-coding-agent

Then install this package:

# From npm (recommended)
pi install npm:@davecodes/pi-routines

# Pinned version
pi install npm:@davecodes/[email protected]

# Or directly from GitHub
pi install git:github.com/Davidcreador/pi-routines
pi install git:github.com/Davidcreador/[email protected]

Project-local install (writes to .pi/npm/ or .pi/git/ instead of ~/.pi/agent/):

pi install -l npm:@davecodes/pi-routines

Restart pi (or run /reload) — the extension auto-loads. You'll see new slash commands (/routine, /routines, /routine-install, …) and a footer widget showing active routine count.

⚠️ Security: pi packages run with full system access. Review the source before installing third-party packages.

Manage the package

pi list                                    # show installed packages
pi update npm:@davecodes/pi-routines       # update this package
pi remove npm:@davecodes/pi-routines       # uninstall
pi config                                  # enable/disable extensions, skills

Manual install (alternative — for development or pinned local paths)

git clone https://github.com/Davidcreador/pi-routines.git
cd pi-routines
pnpm install

Then register the absolute path in ~/.pi/agent/settings.json:

{
  "extensions": ["/absolute/path/to/pi-routines"]
}

💡 Back up first: cp ~/.pi/agent/settings.json{,.bak}. Restart pi after editing.

Uninstall (manual install)

Remove the path from ~/.pi/agent/settings.json and restart pi. Optionally delete persisted state: rm ~/.pi/agent/extensions/routines/state.json.


30-Second Tour

# Install a bundled "ci-watch" routine (polls CI every 3 minutes)
/routine-install ci-watch

# Install a "morning-briefing" routine (fires once per day on session_start)
/routine-install morning-briefing

# Install a pomodoro check-in (every 25m, up to 8 ticks)
/routine-install pomodoro

# Ad-hoc pulse: every 30 seconds, do a thing
/routine 30s remind me to drink water

# Ad-hoc hook: on session shutdown, save my notes
/routine-on session_shutdown summarize what I did this session

# Natural language: pi figures out the right trigger + writes a prompt
/schedule every weekday at 9am summarize my open PRs
/schedule when a PR opens on Davidcreador/pi-routines, leave review notes
/schedule in 2 weeks open a cleanup PR for the feature flag

# Pause + resume without losing run history
/routine-pause ci-watch
/routine-resume ci-watch

# List active routines (paused / quiet / day-cap flags shown in FLAGS column)
/routines

# Stop one by name
/routine-stop ci-watch

Slash Commands

Create & manage

| Command | Purpose | | --- | --- | | /routine <interval> <prompt> | Create a pulse routine. e.g. /routine 10m check the build | | /routine-on <event> <prompt> | Create a hook routine. Events: session_start, agent_end, session_shutdown | | /schedule <natural language> | LLM-powered routine creation — supports every trigger kind, including cron, oneoff, api, and github. e.g. /schedule weekdays at 9am summarize my open PRs | | /routines | List active routines (id, name, triggers, last status) | | /routine-install <template> | Install a bundled template by name | | /routine-pause <id\|name> | Pause a routine without deleting it (keeps tickState + run history) | | /routine-resume <id\|name> | Resume a paused routine | | /routine-stop <id\|name> | Stop and delete a routine | | /routine-export-cron | Export pulse routines as standalone prompt files + optional macOS launchd plists |

Run control & history

| Command | Purpose | | --- | --- | | /routine-run-now <id\|name> | Fire a routine immediately, bypassing schedule and pause | | /routine-runs <id\|name> [--limit N] | Show recent runs: time, trigger, status (incl. skipped w/ reason), duration, snippet |

HTTP API server (for api triggers)

| Command | Purpose | | --- | --- | | /routine-server start [port] | Start local API server (default 7424, 127.0.0.1 only, off by default) | | /routine-server stop | Stop the local API server | | /routine-server status | Show port, uptime, request count | | /routine-token generate <id\|name> | Generate a bearer token (shown once) | | /routine-token rotate <id\|name> | Rotate the bearer token | | /routine-token show <id\|name> | Show token preview (first 8 chars only) |


Bundled Templates

| Template | Trigger | What it does | | ------------------- | -------------------------------- | ------------------------------------------------------------------------ | | ci-watch | pulse 3m | Polls CI for the current branch; alerts on status change. Requires gh. | | pomodoro | pulse 25m, 8× | Focus check-in: progress, rabbit holes, next 25-min suggestion. | | morning-briefing | session_start (once daily) | Git log summary + todo file scan + 3-bullet day-plan. | | morning-cron | cron 0 9 * * 1-5 | Same shape as morning-briefing but fires on a real cron at 9am weekdays, capped at 1 run/day. | | deploy-watch | pulse 5m | Monitors a deploy URL or process; alerts on failure or success. | | session-wrap | session_shutdown | End-of-session summary: what shipped, open threads, next session. | | pr-babysitter | pulse 10m | Watches your open PR for new reviews/comments. | | test-guardian | pulse 2m | Re-runs the failing test you're working on; alerts when it passes. | | api-webhook | api with allowArgs: true | Receives a webhook payload and acts on it. Demonstrates {apiArgs}. | | oneoff-reminder | oneoff | Fires once at the timestamp you edit into the template, then disables. | | github-pr-review | github pull_request.opened | First-pass review notes on every new PR. Requires gh. Demonstrates {githubEvent}. |


Prompt placeholders

Inside a routine's prompt string, the following placeholders are substituted on every fire:

| Placeholder | Value | | --- | --- | | {cwd} | Current working directory of the pi session | | {date} | Today's date (locale string) | | {time} | Current time (locale string) | | {state} | JSON of userState (the LLM-writable per-routine memory) | | {tickCount} | The fire number (1-indexed) | | {apiArgs} | JSON of the API payload — set only when an api trigger fired with allowArgs: true. Otherwise {}. | | {githubEvent} | JSON of the most recent GitHub event payload that triggered the fire. Otherwise {}. |

Oversized userState (> 2 KB serialized) is replaced with {} and the prompt gets a [state truncated] note appended.


LLM Tools

The agent itself can manage routines via these tools (use them from a routine prompt or any conversational turn):

| Tool | Purpose | | ----------------- | ------------------------------------------------------------------------------ | | RoutineCreate | Create or update a routine. Accepts pulse / cron / oneoff / hook / api / github triggers (singular or multi-trigger array). | | RoutineList | List active routines | | RoutineDelete | Stop and remove a routine (often used from within a routine to self-terminate) | | RoutinePause | Pause a routine (e.g. self-suspend after a terminal condition) | | RoutineResume | Resume a previously paused routine | | RoutineSetState | Persist arbitrary state between ticks (e.g. "last CI status I reported") |

See skills/routine/SKILL.md for the full LLM-facing guide that pi auto-injects when relevant.


State & Files

| Path | Purpose | | --------------------------------------------- | -------------------------------------------------------------------- | | ~/.pi/agent/extensions/routines/state.json | Persistent routines + per-routine tick state (atomic write + .bak) | | ~/.pi/agent/extensions/routines/tokens.json | API bearer tokens, 0600 mode enforced | | ~/.pi/routines/prompts/<name>.md | routine-export-cron writes per-routine prompt files here | | ~/.pi/routines/launchd/<name>.plist | routine-export-cron optionally writes macOS launchd plists |

State is fault-tolerant: corrupt JSON or missing file → empty store, log warning, continue. Disk-full / permission errors on save → log to stderr, keep running with in-memory state.


Design Notes

  • One in-flight routine turn at a time (isRoutineTurnActive flag). Routines queue if they overlap.
  • Three-level recursion guard — flag + input-source tag + depth check prevents a routine from triggering another routine.
  • session_shutdown hooks fire on reason: "quit" only, never on "reload".
  • At most one agent_end hook fires per user-driven turn — protects against loops when a routine's response triggers agent_end itself. Enforced both globally (across routines) and within a single routine's trigger list.
  • maxRunsPerDay is gated before any LLM turn opens. Capped fires consume no provider tokens. Counter resets at local midnight. Manual fires bypass.
  • Pause keeps timers armed so /reload is cheap. The pause gate lives in scheduler.enqueueTriggerFire, hooks.pickHookRoutines, and server.handleRequest (returns 423 Locked).
  • Print mode (pi --print) registers tools but skips timers, widget, and hook fires — safe for one-shot CLI use.
  • Hot-reload safeglobalThis.__piRoutinesCleanup stops old timers + the HTTP server before the new instance arms its own on /reload.
  • Silent mode — routines marked quiet: true whose response is exactly [~] are suppressed from chat output (still counted as a tick; status recorded as silent).
  • HTTP server defense-in-depth — 127.0.0.1 bind, per-request loopback re-check, Host header allowlist, 4 KiB body cap, per-token leaky bucket, timingSafeEqual.

Development

pnpm typecheck   # tsc --strict --noEmit
pnpm lint        # biome check
pnpm lint:fix    # biome check --write
pnpm format      # biome format --write
pnpm test        # node:test runner
pnpm check       # typecheck + lint + test (all gates)

Run inline parser self-tests: pnpm test:smoke.


Project Layout

pi-routines/
├── extensions/index.ts        # entry point — wires everything
├── src/
│   ├── types.ts               # source of truth for all types + constants
│   ├── store.ts               # atomic state.json read/write + v1→v2 migration
│   ├── parser.ts              # interval / cron / oneoff parsing
│   ├── scheduler.ts           # timer + queue management
│   ├── executor.ts            # prompt assembly, fire, run recording
│   ├── suppressor.ts          # [~] silent-mode detection
│   ├── widget.ts              # footer status widget
│   ├── guard.ts               # recursion guard primitives
│   ├── hooks.ts               # session lifecycle handlers
│   ├── github-poller.ts       # `gh` polling for github triggers
│   ├── server.ts              # 127.0.0.1 HTTP server for api triggers
│   ├── tokens.ts              # bearer tokens (0600, timingSafeEqual)
│   ├── schedule-nl.ts         # /schedule NL → RoutineCreate meta-prompt
│   ├── format.ts              # describeTrigger, relativeTime — shared formatters
│   ├── path-probe.ts          # cross-platform `which` replacement
│   ├── tools/                 # 6 LLM tools + _mutate.ts + _resolve.ts
│   └── commands/              # 13 slash commands
├── templates/                 # 11 bundled routine templates
├── skills/routine/            # LLM-facing skill doc
└── tests/                     # node:test suites (~170 tests)

Contributing

PRs welcome. Before submitting:

  1. pnpm check must pass (typecheck + lint + tests)
  2. New routines/tools/commands need a test in tests/
  3. Follow the existing JSDoc style on exported types

License

MIT