pi-schedule

内容来源:README.md(说明文档) · 原始地址 · 查看安装指南

原始内容

pi-schedule

Recurring scheduled tasks for pi agents.

Give the agent a way to schedule work like:

  • security reviews
  • package / artifact version checks
  • status polls
  • any other recurring prompt-driven task

Published as a pi package (pi install).

Install

pi install npm:pi-schedule
# or local
pi install ./pi-schedule
# or one-shot
pi -e ./src/extension.ts

How it works

Piece Behavior
Tool schedule — create / list / cancel / enable / disable / run_now / history
Kinds prompt (default) · shell · notify · message — what fires when due
Storage Hybrid: global ~/.pi-schedule/schedules.json + project .pi/schedule.json
Syntax Intervals (30m, 2h, 1d) and daily wall-clock (09:00)
Fire On session start when due; also while the session stays open (30s ticker)
Skip If pi was launched with an initial prompt (pi "do X"), due jobs are not checked or triggered for that start
Reliability Run ledger, single-flight locks, missed-window policy, privilege tiers, fire caps — see docs/RELIABILITY.md

Storage is daemon-ready: each job tracks nextRunAt / lastRunAt so a future headless runner can share the same files.

Agent skill

The package ships a schedule skill (skills/schedule/SKILL.md) that loads on-demand and teaches the agent how to schedule well: when to use kind (prompt / shell / notify / message), every vs dailyAt, privilege tier, shell wakeOn, the missed-window tradeoff, and self-contained prompts. The tool is self-describing for mechanics; the skill owns the patterns.

Agent tool

schedule
  action: create | list | cancel | enable | disable | run_now | history
  name?:          short label (create)
  kind?:          prompt | shell | notify | message   (default prompt)
  prompt?:        task / reminder text; shell follow-up instruction
  command?:       shell only — run via bash -lc
  wakeOn?:        shell only — always | failure | success | never
  successPrompt?: shell only — agent text on exit 0
  failurePrompt?: shell only — agent text on non-zero / killed
  timeoutMs?:     shell only — default 60000, max 600000
  once?:          one-shot relative delay ("10m" / "30s"), then terminate (xor every/dailyAt)
  maxRuns?:       max deliveries (ok+error) before auto-disable
  every?:         "30m" | "2h" | "1d"   (xor dailyAt/once)
  dailyAt?:       "09:00"               (xor every/once)
  scope?:         "global" | "project"  (default: project if .pi exists)
  missedWindow?:  "catch_up_one" | "skip"   (default catch_up_one)
  tier?:          "read_only" | "suggest" | "mutate"  (default read_only; shell→mutate)
  id?:            job id
  limit?:         history row count

Job kinds

kind What happens Agent turn?
prompt (default) Inject isolated task contract yes
shell pi.exec("bash", ["-lc", command]); optional wake via wakeOn only if wake fires
notify UI/console reminder no
message Session custom message (display only) no

Shell jobs always store tier=mutate (command runs outside the agent tool path). Prefer wakeOn=failure for CI polls so success is silent.

Lifecycle: once and maxRuns

  • once — fire one time after a relative delay (once="10m", once="30s"), then auto-disable. Ideal for reminders and delayed follow-ups. run_now won't re-fire a terminated one-shot — recreate it.
  • maxRuns — cap a recurring job to N deliveries (counts ok + error; skips/locks don't count). After the cap, the job auto-disables with terminated: maxRuns. Re-enabling clears the flag and resumes counting.

A terminated job is disabled and excluded from due scans. list shows [off/terminated:once] or […:maxRuns].

Examples

# Daily STATIC security review at 09:00, project-scoped, read-only.
# (Static = read/search only. A git-driven "recent changes" review would need
#  bash, which read_only blocks — use tier="mutate" for that.)
schedule action=create name="security-review"
  prompt="Review the code under src/ for security issues (injection, auth bypass, exposed secrets). Summarize findings with file:line. If none, say 'No findings'."
  dailyAt="09:00" scope="project" tier="read_only"

# Direct shell poll — no agent turn on green; wake only on failure.
schedule action=create name="ci-poll" kind="shell"
  command="gh run list --limit 1 --json conclusion -q '.[0].conclusion'"
  wakeOn="failure"
  failurePrompt="Latest CI run failed. Inspect logs and propose or apply fixes."
  every="5m" missedWindow="skip"

# Human reminder (no model tokens).
schedule action=create name="stretch" kind="notify"
  prompt="Stand up and stretch." every="1h"

# One-shot reminder in 5 minutes, then done.
schedule action=create name="break" kind="notify"
  prompt="Eye break — look 20ft away for 20s." once="5m"

# Bounded CI poll — stop after 10 checks even if still failing.
schedule action=create name="deploy-watch" kind="shell"
  command="gh run list --limit 1 --json conclusion -q '.[0].conclusion'"
  wakeOn="failure" failurePrompt="Deploy failed — investigate."
  every="5m" maxRuns=10 missedWindow="skip"

# Check package versions every day via an agent prompt that must use the shell.
schedule action=create name="pkg-versions"
  prompt="Run `npm outdated` for prod dependencies. Report only meaningful updates as current→latest with a one-line rationale. If nothing meaningful, reply 'No findings'."
  every="1d" tier="mutate" missedWindow="skip"

# List / history / force / cancel
schedule action=list
schedule action=history id=abc123def456
schedule action=run_now id=abc123def456
schedule action=cancel id=abc123def456

Delivery rules

  1. Session start (startup / new / resume): load hybrid store → process due jobs.
  2. CLI initial prompt: only on process startup, if launched with a user message (pi "check this"), skip due checks. /new and /resume still process due jobs.
  3. Missed window: catch_up_one fires once when overdue; skip only fires within grace (max(2×tick, 25% period)), otherwise advances without firing.
  4. In-session ticker: every 30s, if the agent is idle, process newly due jobs (capped).
  5. run_now: attempts force delivery; tool reports actual status (ok / locked / error), never invents success.
  6. Locks + ledger: O_EXCL file lock + idempotency key; forensic trail in ~/.pi-schedule/runs.jsonl.

Fired jobs use an isolated prompt contract:

[scheduled-task]
runId: …
jobId: …
…

## Task
…

## Contract
- isolated run; do not invent findings; say "No findings" if empty
- PRIVILEGE: read_only | suggest | mutate

File layout

~/.pi-schedule/
  schedules.json
  runs.jsonl
  locks/

<project>/.pi/schedule.json

Reliability

Deep dive: docs/RELIABILITY.md

Summary of MVP mitigations:

  • External clock (nextRunAt), not LLM timing
  • Missed-window policy + fire caps
  • Single-flight locks + idempotency keys
  • Append-only run ledger (history)
  • Privilege tiers in the fire prompt
  • Create rate limit + max jobs per scope

MVP scope

  • tool + skill (a schedule skill ships in skills/, teaching kind/tier/scope/missed-window choices and self-contained prompt writing); no dedicated /schedule slash command yet
  • action kinds: prompt / shell / notify / message (shell via bash -lc, optional wakeOn)
  • lifecycle: once one-shots + maxRuns bounded polling
  • no cron expressions yet
  • no background OS daemon (in-session only; storage is ready)
  • delivered = action executed (prompt injected / shell finished / notify shown), not “agent finished correctly”

Dev

npm install
npm test
npm run typecheck

Publishing

Releases are CI-driven: push a v*.*.* tag and GitHub Actions publishes to npm with provenance. One-time setup (npm token + NPM_TOKEN secret) and the per-release flow are documented in docs/PUBLISHING.md.

npm version patch -m "release: %s"   # bumps package.json + tags
git push origin master --follow-tags   # triggers release.yml

License

MIT