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

@timmyjl12/pi-advisor

v0.1.0

Published

Fork of pi-omp-advisor: a live advisor for pi and Oh My Pi that watches your session as it works and sends advice mid-run, with an operator-configurable status row instead of transcript/widget noise.

Readme

@timmyjl12/pi-advisor

A live advisor that watches pi or Oh My Pi (OMP) as it works and can send it advice mid-run — with an operator-configurable status row and notice cards instead of transcript noise.

Fork of Scott-Meyer/pi-omp-advisor v0.6.0, which is in turn a port of oh-my-pi's advisor/watchdog system. Not affiliated with, endorsed by, or supported by oh-my-pi / Stencil Labs, Inc., or by the pi project / Earendil Works. The name states what the code is a port of; neither upstream project has any involvement in it. "oh-my-pi", "omp", and "pi" are used nominatively to identify those projects, not as marks of this one. Also distinct from the unrelated @hazrid1993/pi-advisor npm package.

This is a port of the advisor/watchdog system in oh-my-pi onto pi's own Agent SDK — retaining its compact observer context, advise description, delivery channels, and emission guard. Small prompt clarifications and editable pending advice are specific to this port; PROVENANCE.md documents those differences and the fork's own deviations.

What it does

On session start, pi-advisor builds one live in-process AgentSession per configured advisor, each with its own model and its own throwaway context, and feeds it a compact digest of the primary agent's transcript — normally one batch per primary turn, one line per tool call. Its recent model context is bounded to 32,000 estimated input tokens by default, and primary-agent reasoning is excluded unless explicitly enabled.

An advisor reaches the primary agent and operator through advise(note, severity?, ShortTitle?) and update_advice(targetId, note, ShortTitle?, severity?). Alongside them, an advisor gets whatever investigative tools its config grants — by default the read-only set read, grep, find (configured as glob too) — so it can check a claim before raising it. Every tool response automatically includes a live snapshot of its pending queue and review allowance, eliminating multi-turn state-query churn. How a note reaches the primary depends on severity and on what the primary is doing:

| Situation | Channel | |---|---| | nit during active work | aside — batched, delivered at the next step boundary, no interruption | | concern / blocker | held for one reconfirm cycle, then steered if it still applies — see below (off in pi-advisor.json restores upstream's immediate steer) | | Primary already gave its final answer, nothing queued | preserve — visible, cancellable inbox entry; released above the next normal user prompt (blocker still steers) | | Within immuneTurns (default 3) of a previous interrupt | concerns downgraded to aside (blocker exempt) | | Print mode while idle, or a stopped/aborting primary run | preserve — including blockers after a stop |

Escape aborting a primary run leaves auto-resume suppressed after the live abort signal disappears. Restarting an advisor does not clear that stop; submitting a new normal user prompt does. A blocker can still restart a naturally completed run that you did not stop.

Hold-and-reconfirm (fork default; delivery.holdHighSeverity). An advisor reviews asynchronously — seconds behind the primary — so a concern or blocker steered on first emission usually lands on work the primary has already moved past. Ported from pasky/pi-omplike-advisor: mid-run interrupting notes are held in a machine queue instead of steering. On the advisor's next review they ride a reconfirm preamble: the advisor re-raises each survivor via advise (same severity, or higher — never lower); silence drops the rest. Survivors then deliver through the normal channel logic (steer, preserve, immuneTurns and all). Held notes appear in the advisor's own pending-queue snapshots, so it can revise or withdraw them too.

Catch-up block (delivery.catchUpBlock, off by default). At the end of a primary turn, while notes are held — or on a terminal turn — the primary's next step waits for the advisor's reconfirm review: 15s → 30s → 60s backoff, capped at 120s, Escape-abortable with held notes retained. Terminal turns deliver unconfirmed concerns/blockers best-effort (the last chance before control returns to you); mid-run timeouts keep them held for the next cycle. With the block off (the default), hold-and-reconfirm still works — held notes reconfirm at the advisor's natural cadence instead of gating the turn end. request_stop remains immediate — holding affects advice delivery only.

Non-blocker notes raised during work stay deferred. A completing update gives the advisor a chance to reconsider them before they enter these delivery channels; a failed or aborted review does not release them. Blockers retain immediate routing. This is review between completed model/tool cycles, not a pre-execution check of each tool call.

Two gates keep the primary's transcript clean even when an advisor model misbehaves: a noise filter (stop, done, lgtm, no issues, …) and an allowance of up to 3 accepted notes per review update (with allowance metadata [Review allowance: N of 3 used] returned on every tool completion), both applied at the tool-call boundary before a note can enter the delivery state machine.

How the fork surfaces state: one status row, notice cards, no clutter

Upstream shows advisor state in two places you cannot miss: an inbox widget above the editor, and Warning:/Error: lines appended straight into the chat transcript. This fork moves both:

  • One aggregated status row (via pi's setStatus("advisor", …)) answers "is it on, what model, what thinking level, is anything waiting for me" — 🧭 advisor: ON · deepseek-v4.1-flash · max · 2 queued · concern×1 nit×1 · ▲ "race in persistInbox". pi's native footer renders it as a status entry; with @narumitw/pi-statusline installed it renders below the powerline in the extension-status block. The leading icon tracks what needs you: 🛑 a queued blocker, ⚠️ a concern, ⏸️ paused, 💤 off, ⏳ starting, 🧭 running. Every segment is optional and orderable — see statusRow.fields below.
  • Operator notices become advisor-style cards in the transcript — the same bordered cards advisories use, but labelled ERROR / WARNING / NOTE (never an advice severity like CONCERN): ─ Advisor · ERROR ─. Config-save failures, cancellation requests, and thinking-level clamps land there, keeping the footer clean. The card's model-facing body is a self-describing <advisor-notice …> wrapper, so the primary model is never told the AI advisor authored an operator message.
  • The above-editor inbox widget is off by default. Set inboxWidget to aboveEditor/belowEditor in pi-advisor.json to bring it back (up to three queued notes, exactly as upstream rendered them).

Subagent telemetry. Marked subagent instances also publish de-duplicated pi-advisor:status snapshots through their own pi.events bus, before any UI or footer-visibility checks. Payload: { state, models, reviews, lastReviewAt? }. States are off, starting, idle, reviewing, paused, error, no_model, and stopped; models retain full routes, or the configured selector when unavailable. reviews counts successful completions in the current runtime; lastReviewAt is the latest successful completion in epoch milliseconds, not the last advice or failed attempt. Main instances never publish this channel. Updated pi-subagents uses it for live cards and /agents details, including when main advisor is OFF or the footer is hidden. Shutdown retains a final stopped snapshot before disposal. No extra model calls or settings are involved; /reload both updated extensions to enable the display.

Advisor turn failures use the configured autonomous notice route, not raw terminal output, and carry the sanitized provider cause inline (terminal controls, line breaks and Bearer credentials stripped — bodies/headers are never included). Identical consecutive failures report once per advisor/runtime generation; a different error or a successful review allows a new notice. The status row keeps the error visible until recovery, rendered as advisor: ERROR — <cause…> (first ~60 chars). Every distinct failure is also appended as one JSONL record to <agentDir>/pi-advisor/advisor-errors.jsonl (timestamp, advisor, model, generation, cause) so a transient failure remains auditable after the session; the file is dedupe-limited, trim it by hand if needed. /advisor status shows the last failure (timestamp, advisor, model, full sanitized message) and the log path. Print/JSON warnings and errors use a safe stderr fallback because those modes do not render UI notices.

The row's thinking segment reports the advisor's effective level — the one pi's clamp against the model entry's thinkingLevelMap leaves in place. When a requested level (for example max) is silently clamped to high because the model entry declares no thinkingLevelMap support for it, a warning notice card names the model, the requested level, and the models.json fix — the clamp never happens invisibly.

The row's activity segment is proof the advisor is doing work in a session where it legitimately stays quiet: reviewing while a review is in flight, then a completed-review tick and how many primary turns are still waiting (for example reviewing · 12 reviews or 12 reviews · 2 pending). Every value is read from state the orchestrator already keeps, so liveness costs no extra model calls — a healthy advisor that finds nothing to say now looks reviewed rather than broken. It is separate from queue, which is only the count of notes waiting for you.

Preserved notes remain in an extension-owned Advisor inbox instead of pi's invisible nextTurn queue. The status row shows the queue at a glance (and the optional widget can render up to three queued notes — see inboxWidget). Open the inbox with Ctrl+Shift+A or /advisor inbox to deliver or dismiss one note, or deliver/dismiss the whole queue. Ctrl+Shift+R pauses or resumes observation without releasing the queue; Ctrl+Shift+X clears the queue immediately. Notes you keep are rendered as advisor cards above the next accepted normal user message and included in that turn's model context. Queue and pause state are persisted as session metadata, so they survive extension reloads. Late ordinary notes, including default-severity nits, stay here for you to read and discard before submitting your next prompt. Typing alone does not release them. Asides queued during work are checked again at handoff in case the primary has since finished or been stopped. On pi, one more release gesture exists for the empty-chat case: with the session idle, the editor empty, and something queued, a bare Enter delivers the whole queue and starts the turn (pi drops empty submits before extensions' input event, so this hooks the editor's own input path). An Enter claimed by a dialog, picker, or autocomplete never reaches it, and it never fires while the advisor is paused or the agent is mid-run.

OMP 18.2.4 does not expose pi's composable editor API, so it cannot support that optional empty-Enter gesture. A preserved OMP note remains queued until the next accepted nonempty user prompt, or until you explicitly deliver it from /pi-advisor inbox. The inbox widget/card is not a receipt that the note already reached the primary agent; there is no automatic standalone delivery in this case.

The advisor can inspect its own unsent notes with pending_advice, replace their text with revise_advice, or remove them with withdraw_advice. A small pending summary accompanies its updates. These tools add no repository access and cannot change another advisor's notes or recall a message already handed to Pi. Revisions keep the same ID and severity and update the visible inbox when applicable. User dismissal wins: a withdrawn or cleared note cannot be resurrected by revision. Successful revisions also update duplicate tracking without spending a new-note slot, so their replacement text is not accepted again as fresh advice.

Pending IDs survive within-session model-context rebuilds; preserved inbox notes also survive reloads. Deferred notes still being reviewed are in-memory only. Normal user prompts still release the preserved inbox immediately, so the advisor does not get a fresh review of that new prompt before those notes are handed off.

A limited memory, not a second full transcript

Each advisor has a bounded context budget. Its prefix grows unchanged for prompt-cache reuse until a new review would cross the ceiling; then all history before that review expires at once. Nothing summarizes or replays the reasoning path that filled the old context. Pending advice is stored separately, so a reset does not remove its IDs or prevent revision/withdrawal.

Self-compaction (fork addition, context.compactAtPercent, default 80). The character-based estimate is not the provider's accounting. Two guards ported from pi-omplike-advisor complement it: before each review, if the advisor's actual input has crossed 80% of its model's true context window, retained history expires early — the same atomic whole-history reset, just triggered by observed usage; and a review that resolves stopReason "length" (overflow) clears its history and replays the batch once into a fresh context — a replay that still overflows fails the review without releasing deferred or held notes. 0 disables the percent trigger; the estimated budget stays on regardless.

The limit applies before every model request, including follow-ups after the advisor's own tool calls. It counts estimated system/tool overhead as well as conversation text, and is reduced on small models to leave reply headroom. Estimates use Pi's character-based heuristic, not an exact provider tokenizer. A budget is a ceiling, not a target amount of history to fill. It limits future model input, not host session records or the provider's data-retention policy.

The latest observation and current tool exchange are kept. Large observation text or tool output can be shortened with an explicit omission marker; actual assistant tool calls and their result identities are not rewritten. If a required exchange cannot fit safely, that review fails without releasing deferred advice. An incomplete exchange from a known interrupted review is discarded as a whole, without inventing missing results or treating the review as successful. Standing system/project instructions are not silently cut to make room.

Use /advisor config to change each advisor's budget and primary-reasoning setting. /advisor status shows each advisor's effective provider/model, estimated budget, retained message count, whole-history reset count, advisor wakes/model requests/tool calls, and pending turn batch. The live route also appears in the compact footer, stream selector/header, inbox labels, and delivered Advisor cards. Cards record the model that generated each advise tool call; an in-place revision updates that provenance to the model that generated the replacement text. Changing this extension still requires /reload before the new policy is active. On OMP, use the namespaced /pi-advisor command because /advisor is reserved by OMP's native advisor.

Optional emergency stop

Add request_stop explicitly to an advisor's tools to grant cancellation:

advisors:
  - name: advisor
    tools: [read, grep, glob, request_stop]

That advisor also gets current_tool, which returns the exact execution targetId and a compact call summary. Tool lifecycle events update this host-side state but never wake the advisor model. A normally batched review samples the live state immediately before prompting. Separately, the oldest-message timeout can review a finalized assistant tool-call message while its tool is still running; the default is intentionally four minutes, including when maxBehind is 1. This makes cancellation a rare fallback rather than one model request per tool start.

request_stop(targetId, reason) uses Pi's supported active-turn abort, not arbitrary process control. It accepts only a sole in-flight foreground call and rejects stale or ambiguous targets, multiple calls, paused/off advisors, and repeated requests until the run settles. The target ID is unique even when a provider reuses its own tool-call ID. A blocker advisory alone does not invoke cancellation.

The reason is shown immediately and recorded in session metadata. After the primary settles, a visible receipt reports what was observed without restarting it. Acceptance means cancellation requested, not guaranteed termination or rollback: tools must cooperate with abort, and detached jobs or already-completed external side effects are not undone. Resume explicitly after inspecting the reason. After changing the grant or extension code, run /reload before testing.

Install

pi install npm:@timmyjl12/pi-advisor

Try it for one run without installing: pi -e npm:@timmyjl12/pi-advisor. Restart pi (or run /reload) after installing.

Requires pi 0.84.2 or newer (it uses createAgentSession, DefaultResourceLoader, and loadProjectContextFiles; on an older pi a missing export throws during extension load, which takes down the whole session) and Node 22.19+, matching pi itself.

To hack on it, install an editable checkout as a local-path package instead (pi install /absolute/path/to/pi-advisor, then npm install inside the checkout, because pi does not install dependencies for local paths). Keep only one entry for this extension: a local path plus an npm: spec loads it twice and both copies register /advisor.

That is enough: normal sessions immediately run one implicit default advisor on the active chat model, including a model selected with --model. No WATCHDOG.yml or command is required. This means installation adds a second model call after ordinary primary turns; use /advisor off for the current session or persist main: false when that is not wanted.

WATCHDOG.yml is optional customization for model choice, cadence, tools, and named advisor rosters. A WATCHDOG.md by itself adds standing instructions to the implicit default advisor.

Do not symlink src/ into ~/.pi/agent/extensions/. That directory is for single-file/self-contained extensions and gets no dependency installation. pi-advisor needs the yaml package, which pi does not provide to extensions (pi's host-provided set is @earendil-works/pi-ai, pi-agent-core, pi-coding-agent, pi-tui, and typebox). A symlinked src/ resolves yaml only by accident — either because the symlink target happens to sit next to a node_modules/ containing it, or because some unrelated package hoisted it into ~/.pi/agent/npm/node_modules. Move the code to a machine where neither holds and every pi session fails to start. Installing as a package makes pi run npm install for it instead.

A missing yaml no longer throws at load: WATCHDOG.yml/.yaml are skipped with an actionable error and WATCHDOG.md still works, so a packaging mistake degrades pi-advisor rather than bricking pi.

Configuration

WATCHDOG.yml (or .yaml), discovered at user scope (~/.pi/agent/WATCHDOG.yml), at the project root, and walking up from cwd — including inside a .omp/ directory. A more specific file replaces an advisor with the same name.

main: true         # run in normal pi sessions (default true)
subagents: false   # run inside subagent processes too (default false; see note below)
syncBacklog: off   # backpressure: off, queued turns, or { pauseAt: 4, resumeAt: 1 } hysteresis
maxBehind: 3       # completed primary turns accumulated per advisor wake (default 3, min 1)
flushTimeoutMs: 240000 # maximum age of the oldest unseen message (default 4 minutes, min 100ms)
flushOnSettled: true # deliver pending observations as soon as the primary finishes (default true; set false for strict turn batching)

advisors:
  - name: advisor
    model: openai/gpt-5.1-codex-mini   # or provider/id:high for a thinking level
    tools: [read, grep, glob]     # default; `glob` maps to pi's `find`
    contextTokens: 32000         # default estimated input ceiling; configurable, minimum 2048
    includePrimaryThinking: false  # default; independent of the advisor's own thinking level
    maxBehind: 3          # per-advisor turn-batch override
    flushTimeoutMs: 240000 # per-advisor maximum wait for a partial batch
    flushOnSettled: true  # per-advisor settlement-flush override
    instructions: Pay extra attention to auth and data-loss risk.
    enabled: true

WATCHDOG.md files on the same search path are loaded as freeform standing instructions shared by every advisor.

pi-advisor.json — the fork's own UI settings

Everything the fork changes about display lives in one JSON file at ~/.pi/agent/pi-advisor.json (the same <agentDir>/<package>.json convention pi-statusline and pi-fallback-chains use — deliberately not ~/.pi/agent/extensions/pi-advisor.json, which belongs to the unrelated @hazrid1993/pi-advisor package). It never overrides WATCHDOG.yml, which stays byte-compatible with upstream. A missing file means built-in defaults; a malformed file is reported in a notice card and left untouched; unknown keys are preserved on save.

{
  "statusRow": {
    "enabled": true,
    "fields": ["state", "model", "thinking", "queue", "severity", "latest"],
    "leadingIcon": "auto",
    "notePreviewChars": 60
  },
  "inboxWidget": "off",
  "notices": {
    "autonomous": "card",
    "command": "card",
    "statusTtlMs": 8000,
    "maxChars": 160
  },
  "severities": {
    "nit": "…when a note is non-urgent polish…",
    "concern": "…when the agent might be heading the wrong way…",
    "blocker": "…only when continuing is clearly unsound…"
  },
  "delivery": {
    "holdHighSeverity": "all",
    "catchUpBlock": { "enabled": false, "baseMs": 15000, "capMs": 120000 }
  },
  "context": {
    "compactAtPercent": 80
  }
}

| Option | Values | Meaning | |---|---|---| | statusRow.enabled | true (default) | Show the aggregated advisor status row; false clears it entirely | | statusRow.fields | ordered list from state, model, thinking, activity, queue, severity, latest | Which segments render and in what order; a segment with nothing to say is omitted | | statusRow.leadingIcon | auto (default), none, or a literal icon | auto derives 🛑/⚠️/⏸️/💤/⏳/🧭 from queued severity and run state | | statusRow.notePreviewChars | integer 8–500 (default 60) | Truncation for the latest segment | | inboxWidget | off (default), aboveEditor, belowEditor | Where queued notes render besides the row; upstream showed aboveEditor | | notices.autonomous | card (default), status, notify | Advisor-initiated events: cancellation requests, thinking-level clamps | | notices.command | card (default), status, notify | Feedback for /advisor … commands and menus | | notices.statusTtlMs | integer 0–600000 (default 8000) | How long a status-routed notice stays; 0 until replaced | | notices.maxChars | integer 8–2000 (default 160) | Truncation for a status-routed notice | | severities.nit / .concern / .blocker | free text (defaults below) | When to use each severity, injected as a <severity-calibration> block into every advisor's system prompt — so nit/concern/blocker mean the same thing to the advisor model every session. Edit via /advisor ui → Severity definitions…; a blank string removes that level's line, all three blank removes the block. A changed definition rebuilds the advisors on save | | delivery.holdHighSeverity | all (default), concerns, off | Mid-run interrupting notes held for one reconfirm cycle instead of steering stale (all = pasky's model; concerns keeps blockers immediate; off restores upstream OMP semantics). Idle/final-answer routing unchanged | | delivery.catchUpBlock.enabled / .baseMs / .capMs | false (default) / 15000 / 120000 | At turn end, stall the primary's next step while held notes await reconfirmation (backoff base→cap, Escape-abortable). Toggle via /advisor ui → Catch-up block | | context.compactAtPercent | integer 0–95 (default 80) | Proactive self-compaction trigger as % of the model's true context window; 0 disables. The reactive overflow replay is always on. Edit via /advisor ui → Self-compaction |

Notice routes: card renders an advisor-style transcript card labelled ERROR/WARNING/NOTE — display-only: the context hook strips notice cards from the model-bound copy, so the primary's context window carries only real advisor advice (<advisory> notes and stop receipts), never operator feedback; status shows a transient advisor:notice row in the footer/statusline; notify restores upstream's raw Warning:/Error: transcript lines.

Severity calibration. The default severities definitions synthesize the severity guidance of current oh-my-pi (18.2.8 src/prompts/advisor/system.md — twelve concrete concern triggers, the blocker conditions around sampled scope, stubs, and yielding before a convergence condition) with the one-definition-per-level framing of pasky/pi-omplike-advisor. Override all three from pi-advisor.json or /advisor ui.

Text-only advisor models get the bundled no-vision.md note appended to their system prompt (adopted from pi-omplike-advisor): a cheap fast advisor model that cannot see images is told the main agent can, so it never "advises" about missing visual evidence.

Set model explicitly to choose a different advisor model. If omitted, the advisor inherits the active chat model and thinking level, including its live provider/authentication route. Pick something fast when overriding it — an advisor's judgment always lags the primary by its own round-trip time.

Commands

Run /advisor (or /pi-advisor on OMP) to open the interactive settings overlay. Its Session, Models, Project, User defaults, Display, and Advanced pages show current values and provide searchable controls; type to filter, use Tab/Shift+Tab to change pages, arrows to move, Enter to open, and Esc to back out or close. It follows your Pi theme and shrinks to a single column on narrow terminals. Deep config editors still open their existing prompts after the overlay closes, and direct subcommands remain available.

Session model changes only the chosen advisor for this session (including resuming its branch); it never changes Pi's chat model or writes a config file. Select Use saved advisor default to remove the temporary choice, or Follow Pi chat model to override a saved advisor pin just for this session. Saving a project or user default writes that advisor's model to the corresponding WATCHDOG.yml; project settings take precedence over user defaults. Choosing a new model rebuilds the advisor's own live context. No Pi global model setting is changed. Pickers show models available with configured provider credentials.

Thinking effort is set directly inside the model picker (oh-my-pi habits apply): with a model highlighted, press t to cycle its thinking-effort levels — off, minimal, low, medium, high, plus xhigh and max where the model entry declares them — and T to cycle back. The row's badge shows the chosen level (reasoning · high); a cycle back past the first step returns to unpinned. Enter applies whatever the badge shows. The picker fills the terminal, so long model lists stay visible. Press / first when you want to search for something whose name starts with t: / opens the search box, where every letter types, and Esc leaves it (the query stays). Clients without the overlay are asked the level as a follow-up question. Both paths save the level as the same provider/id:level selector suffix WATCHDOG.yml already accepts (model: x-ai/grok-code-fast:high), for this session or as a saved default — session overrides and saved defaults carry it alike — and a saved pin seeds the cycle, including when re-opening the picker. A model that no longer advertises the pinned level keeps the pin (cycling round-trips through it) and pi's runtime clamp — with this fork's clamp warning — still applies. An unpinned model keeps the advisor's inherited/default level.

  • /advisor settings | models — open the overlay or its model page
  • /advisor model session — select an advisor-only model for this session
  • /advisor model [project|user] — pick and save an advisor default; without a scope, the interactive picker targets this project. These are separate from Pi's /model and Pi's default chat model
  • /advisor on | off — enable/disable for this process (on restarts the implicit default advisor when no roster is configured)
  • /advisor main on|off — default for normal sessions
  • /advisor subagents on|off [persist] — default inside subagent processes. Without persist this sets PI_ADVISOR_SUBAGENTS for this process tree only (session-tree only, nothing written to disk); with persist it additionally writes subagents: to the resolved project WATCHDOG.yml and reports the path, so future process trees default the same way. See Subagent sessions below

How subagent detection works. pi core does not mark subagent processes. Both subagents: and PI_ADVISOR_SUBAGENTS apply only when the spawner sets PI_SUBAGENT_CHILD=1 on the child — which the pi-subagents package does. A custom spawner must therefore set PI_SUBAGENT_CHILD=1 to be recognized at all, and may additionally set PI_ADVISOR_SUBAGENTS=1|0 to override subagents: for that child. Without PI_SUBAGENT_CHILD=1 a child is treated as an ordinary main session and follows main: — setting PI_ADVISOR_SUBAGENTS alone does nothing.

  • /advisor — open the settings overlay in interactive Pi (legacy selection dialogs remain available in clients without custom terminal overlays). ON/OFF now is temporary; to save ON/OFF, open Project settings or User defaults and choose Advisor ON/OFF. Main-session and subagent defaults are separate; project settings override user defaults. Subcommands also offer descriptions and Tab completion
  • /advisor status — which advisors are running, and their state
  • /advisor inbox — inspect, deliver, or dismiss preserved advisories waiting for the next normal user prompt (Ctrl+Shift+A opens the same inbox)
  • /advisor stream [name] — open a read-only popup over one advisor's own chat stream: its observations, reviews, and tool use as it works, updating live. Deliberately rare-use — it exists for the cases where you really want to see what the advisor is seeing, not as part of the normal loop
  • /advisor pause / /advisor resume — suspend or resume observation while retaining the queue (Ctrl+Shift+R toggles)
  • /advisor clear — immediately discard every queued advisory (Ctrl+Shift+X; no confirmation)
  • /advisor config — choose the project or user-default WATCHDOG.yml editor; each also edits its own WATCHDOG.md attention instructions
  • /advisor ui — pick status-row segments and icon, inbox-widget placement, notice routing, severity definitions, high-severity hold-and-reconfirm, the catch-up block, and self-compaction; saves ~/.pi/agent/pi-advisor.json (statusline works as an alias)
  • /advisor help — command, shortcut, queue, and pause behavior reference

Subagent sessions

Running the advisor inside subagent sessions is a first-class, persisted setting that works with ANY subagent tool — ours (pi-subagents) or another spawner — not a pi-subagents special case.

  • Default off. pi-advisor stays off inside any child process/session its spawner marked with PI_SUBAGENT_CHILD=1, whatever the main session's state. pi core never sets that marker; spawners like pi-subagents must set it themselves (pi-subagents does).
  • Precedence, lowest to highest:
    1. subagents: in a discovered WATCHDOG.yml — persisted, at user scope (~/.pi/agent/WATCHDOG.yml) and project scope; a project file's true wins over a less specific file's false, same rule as instructions.
    2. PI_ADVISOR_SUBAGENTS=1|0 in the environment — session-tree: set in one process, inherited by the children it goes on to spawn, never written to disk.
    3. Per-agent frontmatter when the spawner supports it — e.g. a spawner-owned key such as advisor: true|false, which the spawner materializes by raising PI_ADVISOR_SUBAGENTS=1|0 on that particular child.
  • The command: /advisor subagents on|off sets tier 2 for this process tree only; /advisor subagents on|off persist also writes tier 1 to the resolved project file. /advisor config edits the same persisted field at project or user scope. /advisor on|off typed inside a child overrides that child alone, whatever any default says.
  • Other spawner tools: set PI_SUBAGENT_CHILD=1 in the child before extensions load — that is the whole contract. Without the marker the child follows main: like any ordinary main session, and setting PI_ADVISOR_SUBAGENTS alone is deliberately ignored (the env is inherited by the main process too, where honoring it would silently flip the main session's own gate).

Security and privacy

Read this before installing the extension. An advisor starts enabled and is a second agent that watches your session, so it has real data-flow, cost, and trust implications.

Your session content is sent to the advisor's model provider, by two separate routes. If model: names a different provider than your main session, all of this goes to a second vendor.

  1. The per-turn digest (src/advisor/session-history-format.ts), which is deliberately compact rather than a transcript dump:

    | Included | Form | |---|---| | your messages | verbatim within the recent window; oversized text may be shortened | | assistant replies | verbatim within the recent window; oversized text may be shortened | | assistant reasoning | excluded by default; available text included only with includePrimaryThinking: true | | tool calls | name + one primary argument, truncated to 120 chars (so file paths, commands, grep patterns, URLs) | | successful tool results | status and size only — ⇒ ok · 31 lines, no body | | failed tool results | status, size, and the first line of the error | | edit/write results | fenced unified diff (8 lines of context, max 200 lines); subject to the context window and shortening | | your ! bash runs | command preview + exit status + line count, no output |

    So ordinary file reads and command output do not leave as content — but your own prompts and applied diffs can. A context budget is not a secret-redaction policy.

  2. The advisor's own tool calls. It holds read/grep/glob by default and uses them to check claims, so it can read project files directly. Those results enter the advisor's bounded context and can go to its provider, independent of what the digest summarizes. Large results may be shortened before the next model request; older results expire with the rest of the conversation.

An advisor without an explicit model: follows the active chat provider. If you choose a different advisor model, session content also goes to that provider. To stop route 2 entirely, set tools: [], which grants no investigative tools (the advisor keeps advise).

Advisors can be granted write access, and are not sandboxed. tools: accepts edit, write, and bash in addition to the read-only default set. An advisor is a full pi agent, so granting those gives a model that runs automatically, with no turn-by-turn confirmation from you, the ability to modify files and execute commands in your working directory. The default (read, grep, glob) is read-only and is what you want unless you have a specific reason otherwise.

The advisor reads untrusted content, and there is no output quarantine. Error text, edit diffs, and anything it reads with its own tools may contain text crafted to manipulate a model. A manipulated advisor can put arbitrary text into your primary agent's context via advise().

Pi stores each note as a distinct customType: "advisor" message and the TUI shows it in a full-width Advisor card. At the provider boundary, however, pi currently converts all extension custom messages to the model's user role and does not forward customType. The extension therefore adds primary-system context saying that messages wrapped in <advisory> come from a separate AI advisor watching the session and are not authored by the user. It also explains one transcript behavior: if a late advisory causes a second completion after a completed response, that newer response may scroll the preceding one out of view and should stand on its own without assuming the preceding response was read. Neither sentence tells the primary whether or how to act on the advisor's technical claim.

Notes also carry guidance="weigh, don't blindly obey". That wording can reduce blind compliance, but neither it nor the visible card is a security boundary or output quarantine. Combined with the point above, a prompt-injection payload reaching an advisor that holds bash is a genuine risk. Keep advisors read-only.

Cost and rate limits. Each advisor is a live model session prompted roughly once per primary turn, so it consumes tokens continuously against whatever credentials that provider uses — a second, ongoing bill alongside your main session, per advisor.

Scope of what's watched. Advisors run in main sessions by default (main:) and not in subagent processes (subagents:). Both are configurable; see Subagent sessions above for precedence across spawner tools and the caveat about how child processes are identified.

Releasing

Published to npm as @timmyjl12/pi-advisor from a local checkout, after npm run typecheck, npm test, and npm run verify:package pass. See DEVELOPMENT.md for the release steps.

Layout

  • src/index.ts — extension wiring: config discovery, lifecycle events, the pi.sendMessage host bridge
  • src/advisor/orchestrator.ts — one ActiveAdvisor per config, per-advisor delta cursor, batching/WIP inference, channel routing, immune-turn window
  • src/advisor/advise-logic.ts — AdviseState, severity ranking, resolveAdvisorDeliveryChannel
  • src/advisor/advise-tool.ts — the advise tool, with the emission guard gating at its boundary
  • src/advisor/context-window.ts — per-request rolling memory budget, safe exchange eviction, and explicit shortening
  • src/advisor/emission-guard.ts — noise filter + one-note-per-update budget
  • src/advisor/session-history-format.ts — compact primary-transcript render
  • src/advisor/delta-render.ts — per-source-message chunking for prompt-cache locality
  • src/advisor/watchdog-config.ts — WATCHDOG.yml/.md discovery and merge
  • src/prompts/ — advisor system prompt and tool description

License

MIT. Includes MIT-licensed material from oh-my-pi (© Mario Zechner, Can Bölük, Stencil Labs, Inc.) — see LICENSE and PROVENANCE.md for the file-level breakdown of what is copied, ported, and original.