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-mesh-agents

v0.5.3

Published

A Pi extension that turns the **current pi session into an orchestrator** for a persistent team of subagents. The orchestrator LLM spawns a *mesh* — a long-lived team of **real pi processes** working in the shared codebase — manages it over time (join, re

Readme

🕸️ pi-mesh-agents

A Pi extension that turns the current pi session into an orchestrator for a persistent team of subagents. The orchestrator LLM spawns a mesh — a long-lived team of real pi processes working in the shared codebase — manages it over time (join, redirect, kill, close), and is woken by one-line digest pushes when agents finish tasks or need decisions. Children keep the full bridge (inbox/outbox JSONL files, bus provenance, per-agent session files, digests, accounting) and gain process isolation and durable post-mortem records; every bridge crossing is appended to a tamper-evident, hash-chained JSONL log. On top of that, four visibility modes per mesh control what gets looked at — not what runs.

Deliberately lean: no ports, no network listeners, no MCP, no compile step — one codebase, eight tools, one log per mesh.

⚙️ How it works

  • The orchestrator is your session. The extension registers eight tools. Its LLM decides team composition at any moment — spawn a mesh, add agents later, reuse standing ones, kill stuck ones, close when done, re-join after a process death. mesh_spawn is non-blocking: it returns the roster, not the results.
  • Children are real pi processes. Every child is its own headless pi --mode rpc coprocess session, hosted by the plugin (one real pi process per agent — the in-process createAgentSession path is gone, hosting is built in, not optional). The tui display mode adds a per-agent viewer window that renders the child's stream with pi's own transcript components (P04). Children are isolated processes — a child crash is one error envelope, not a dead orchestrator.
  • The inbox/outbox bridge. The orchestrator talks to children over inbox/<agentId>.jsonl (TUI mode — headless delivery rides the coprocess's stdin) and hears them through outbox/<agentId>.jsonl (all modes; the plugin's per-file outbox tailer is the only intake). The orchestrator is the bus's single writer: child-originated envelopes (msg/response/status/claim/done) land on the chain at outbox-tail time — ms-scale lag vs. tool-call time, same envelope shapes.
  • Done means standing by. A child's done is not terminal: the agent keeps its full context and accepts new instructions via mesh_instruct (reuse), until it is killed or the mesh closes. An optional per-agent task budget (maxTasks, 1–32) auto-releases context-heavy agents at the budget so a fresh replacement takes over.
  • Digests wake the orchestrator. On task-done, blocked/waiting, kill, task-limit release, error, and close, a one-line digest is pushed into the orchestrator's session and triggers a turn if it is idle. The orchestrator is event-driven, not polling. Peer chatter never enters the orchestrator's context — only lifecycle digests do.
  • Provenance. Every event crossing a bridge (orchestrator→child, child→child, done, kill, close) is one line in .mesh/agents/<meshId>/bus.jsonl, linked by a last-event hash chain (canonical sorted-key sha256, genesis-bound). result.md is written at close.
  • Durable post-mortem, always. Each child has its own pi session file (sessions/<agentId>.jsonl, pi --resume-openable) in all modes — the canonical post-mortem record; headless modes additionally leave a replayable token-level stream/<agentId>.jsonl. See Visibility modes.
  • Persistence across restarts. Every mesh close writes state.json (roster, usage, last summaries, bus head, mode, session paths). A mesh killed by process death (outcome shutdown) can be re-joined in a later session with mesh_rejoin — a fresh mesh whose brief carries the previous brief + every agent's last summary + pointers to the old evidence (bus, result, session files).
  • Resource accounting. Every agent's lifetime tokens/turns/context-percent flow from the child's outbox status lines (cumulative, per turn_end) and are shown in mesh_status, done digests, the close result, and result.md — objective replace/kill triggers ("agent at 70% of window, replace soon", "6 min without bus activity, kill it").
  • Opt-in claims. Setting claims: true enables the file-claim protocol (claim child tool + claim bus envelopes); conflict adjudication is asynchronous — conflicts arrive at the claiming child as CONFLICT messages (record-and-advise, no OS locks).
  • Children are isolated by design. They do not inherit the orchestrator's conversation; the shared brief.md is their only shared mesh context. Children load the operator's global skills and the workspace AGENTS.md like any pi session (resource loading is not extension loading — pi -ne strips extensions only) and have no MCP tools (-ne strips builtin:mcp, matching the v1 posture). The bus log is the only durable evidentiary mesh artifact — stream/ and outbox/ are operational, not evidentiary.
  • Clean shutdown. Exiting pi mid-mesh runs the session_shutdown hook, which finalizes every open mesh (leave + close envelopes, outcome shutdown) so every chain terminates cleanly. Close policy: mesh_close / shutdown does NOT touch the dash panes — the mesh's panes persist in the shared dash (live tail while the tui children keep running, frozen tail once a child dies — the post-mortem room; pane removal is operator-driven only, and the operator-owned dash server is never killed by the plugin); headless children are released (SIGTERM → clean session_shutdown → leave line, suppressed by the gone-set invariant). With the P04 per-agent window fallback, the viewer windows are not closed — they keep rendering the final stream.

👁️ Display modes

Two modes, one per mesh, fixed at mesh_spawn by the mode setting (default tui). Every child is the same thing in both modes: a headless pi --mode rpc coprocess (one real pi session per agent, byte-faithful stream/<agentId>.jsonl tee, sessions/<agentId>.jsonl record). The mode controls what is displayed — never what runs:

| mode | child | live view | post-mortem | |---|---|---|---| | blind | headless --mode rpc coprocess | none (lowest CPU) | sessions/ + stream/ | | tui | the same headless coprocess | a pane in the machine-global agent dash — the child's stream rendered by pi's own transcript components (thinking, tool executions + partial output, response text, footer) — or a per-agent viewer window as fallback (below) | sessions/ + stream/; dash panes are removed at mesh_close, the stream files remain replayable |

The viewer (scripts/meshview-agent.mjs) is display-only: it imports pi's TUI runtime + the exported transcript components from the same pi install that hosts the agents (no second agent loop, no stdin, no session machinery) and tails the child's stream/<agentId>.jsonl. A blind run is replayable afterwards even though it was never visible live:

node scripts/meshview-agent.mjs --mesh <meshId> --agent <agentId> --once

The agent dash (default tui display)

One dash per machine, shared by every mesh on the machine: a tmux server (socket mesh-dash, session dash, one grid window) hosted in a brand-new iTerm2 window created by osascript before tmux runs (the window attaches to the server; the server outlives the window — close it and the next join reopens/reattaches). Each tui agent joins the grid at join time: one pane per agent, row-major filling with gridMax columns (default 4 — a new row starts when a row fills, and the first cell of a new row takes the full row width). The layout is applied as an exact tmux custom-layout string (deterministic + idempotent; ±1 px verified); a pane's title carries MESH-<meshId>-<agentId>.

  • Pop out / back in (mesh_dash tool, actions pop / pop-in, target <mesh>/<agent>): the agent's pane leaves the grid into its own full window (pop-<mesh>-<agent>) of the dash session and re-joins the grid tail on pop-in (slots are never reused). The grid reflows around the gap; the viewer and the child are untouched.
  • Grid shape (mesh_dash action grid, 1–16 columns): re-lays the current panes; persisted in the dash state.
  • mesh_dash action status: the live slot table (slot, mesh/agent, mode, pane, pane pid); repair: reconcile state against the live panes (normally runs itself — a server-global pane-exited hook fires the CLI repair whenever a viewer pane dies).
  • CLI (outside pi): node scripts/mesh-dash.mjs [<socket>] <status|pop|pop-in|grid|repair|attach> — socket-first positional form.
  • Inside the dash it is plain tmux: C-b q pane indices, C-b z zoom a pane to the full window, C-b {/} move a pane, C-b x kill a pane (⚠ kills exactly that agent's viewer — the child keeps working, and the hook prunes + re-lays the grid within seconds).
  • Dash state: ~/.mesh-dash/state.json (socket → per-agent slot/mode/paneId map, gridMax) + dash.log (op log); lock-protected, stale-steal after 5 s.
  • Environment (all optional): MESH_DASH_SOCKET (default mesh-dash), MESH_DASH_STATE_DIR (default ~/.mesh-dash / ~/.mesh-dash-<socket>), MESH_DASH_GRID_MAX (default 4), MESH_DASH_DRY_RUN=1 (skip the osascript window ops — logged as "would attach"), MESH_DASH_DISABLED=1 (no dash at all — every tui mesh falls back to the P04 per-agent viewer windows).

The P03 jsonl/raw watcher-pane modes are retired (P04): the rendered viewer covers both granularities (per-token deltas AND completed blocks — pi's own renderer).

👀 Viewing subagents

  1. The agent dash (default on desktop): every tui agent across all meshes renders in the shared dash grid — one window no matter how many agents or meshes are running. Opened automatically at the first join (interactive desktop sessions only; headless/CI/test runs never pop windows unless the test harness forces them); blind meshes display nothing.
  2. Per-agent viewer windows — the fallback: when the dash is disabled (MESH_DASH_DISABLED=1), a dash join fails, or the osascript attach fails, the agent gets a dedicated terminal window instead, labeled MESH-<meshId>-<agentId> and running the viewer for that agent (windows setting, default true).
  3. meshview <meshId> (dotfiles repo — companion plan F03) — on-demand grouped view, including post-mortem.
  4. Replay: node scripts/meshview-agent.mjs --mesh <meshId> --agent <agentId> --once (or pi --resume .mesh/agents/<meshId>/sessions/<agentId>.jsonl for the full interactive session).

Window/pane death ≠ child death — and the plugin NEVER removes a pane (the post-mortem is the first class citizen). Closing a viewer window or killing a dash pane kills exactly the viewer process; the child coprocess — a separate process with its own lifecycle — keeps working, and the post-mortem (sessions/ + stream/) is untouched. Re-open a viewer for a live agent any time with the replay command. The plugin never removes a dash pane — not on release (task limit, mesh_kill), not on crash, not on mesh_close, not on session shutdown. A mesh's panes persist in the shared dash after close: live tail while the tui children keep running (close policy), frozen tail once a child dies — the post-mortem room, with the grid keeping the slots. Pane removal is an operator-driven event only: you close the pane (the pane-exited hook prunes + re-lays the grid within seconds) or you explicitly tell the agent to remove it — the agent then runs the mesh_dash tool's leave <mesh>/<agent> action (or the CLI's leave command), which removes exactly that pane (kill pane, re-layout, clear the state entry; the child coprocess is NOT touched and keeps working). The agent never calls leave on its own — not as cleanup, not because a pane looks stale or post-mortem. With the P04 fallback, the surviving viewer windows keep rendering and teardown is explicit: close the windows. The dash tmux server is operator-owned — the plugin never kills it. The attach window opens only when no client is attached to the dash session (a second window would just mirror the first — concurrent joins within the in-flight grace never stack a second window; and the osascript itself scans for an existing MESH-DASH tab before creating, so a second window can never be created; a manual attach likewise never stacks on a live client).

🔧 Tools

| Tool | Purpose | |---|---| | mesh_spawn | Create a persistent mesh (brief + optional initial roster, optional per-mesh cap). Non-blocking — results arrive as digest notifications. The mesh's mode (blind/tui) comes from the mode setting. | | mesh_task | Join a new agent (fresh context) into a running mesh. Fails at the cap — kill first or reuse a standing one. | | mesh_instruct | Give a live agent new instructions. Delivered after its current turn if working (steer), immediately if standing by or waiting (followUp). | | mesh_kill | Immediately abort and remove an agent (frees its slot, discards its context — kill-pane / SIGTERM, clean leave). | | mesh_status | Current formation: roster (name, status, task counts vs budget, latest summary), the mode-appropriate view:/postmortem: line, messages left for you since last check, provenance head hash. Omit meshId to list active, closed, and past meshes. | | mesh_close | End the mesh: per-mode close policy, write result.md with the final per-agent table, return the result. Idempotent. Surviving server is explicit-teardown-only. | | mesh_events | Read recent bus events (the mesh's memory) — post-mortem and re-orientation after compaction; works on closed meshes. | | mesh_rejoin | Re-join an interrupted mesh (outcome shutdown) as a fresh continuation mesh with a continuation brief (previous brief + last summaries + old evidence pointers — bus, result, session files, streams). | | mesh_dash | The shared agent dash: status (live slot table), pop/pop-in (an agent's pane out of / back into the grid, target <mesh>/<agent>), grid <n> (set gridMax columns), repair (reconcile state vs live panes). The dash is the default tui display (one machine-global grid for all meshes; P04 per-agent windows are the fallback). |

Every agent gets an automatic stable display name (SubAgent-<NN>, per pi process) that rides the join envelope, every digest, the roster, result.md, the child's own send/done/claim receipts, and the styled pane title (📁 <WORKSPACE> 🖥 MESH-SUBAGENT-<NN> 🆔 <PID> — the agent's own pid).

🧠 Skill

The package ships a mesh-agents skill (skills/mesh-agents/SKILL.md) — a distilled orchestrator playbook: the digest protocol (the six wake lines and how to respond to each), the seed policy (kill/replace, reuse vs replace, verdict-gated loops, cap discipline, human escalation), CLAIM CONFLICT adjudication, the rejoin/post-mortem paths, and the tested-topology map (T01 fan-out → T13 headless modes) so the orchestrating model knows which graph shapes it can rely on. Pi advertises the skill to the model and loads it when mesh work is relevant; force it with /skill:mesh-agents. The tool schemas carry the contracts; the skill carries the how.

🚀 Quickstart

No install step: the extension is loaded from TypeScript via pi's jiti loader (erasable TS only, no build). From the repo root:

pi -e ./src/index.ts

or add the extension path to the extensions array in ~/.pi/agent/settings.json (the pi.extensions field in package.json declares it the same way). Then ask your agent to use the mesh — e.g. "Use mesh_spawn with a brief and two agents to review and fix the parser." A mesh-agents widget line appears while meshes are active; digest notifications arrive as Mesh notification (automatic event — not a user request): … messages.

Requirements: tmux on PATH for tui meshes — the agent dash is a tmux server (desktop display; blind meshes need no tmux; a missing/broken tmux degrades a tui join to the P04 per-agent window fallback). unichar/tmux-window (dotfiles) are not required at runtime — the styled-glyph mapping is inlined in the plugin and the auto-window degrades to no-window; meshview is the operator-facing attach command once the dotfiles companion lands.

Artifacts land under the session's working directory (.mesh/agents/ is the mesh runtime home — post-mortems and provenance verification against a running or previously run mesh start there):

.mesh/agents/<meshId>/
  brief.md     # shared team context (verbatim from mesh_spawn.brief)
  bus.jsonl    # the hash-chained provenance log
  result.md    # final per-agent table + messages to orchestrator (written at close)
  state.json   # roster + usage + summaries + bus head + mode/session paths (written at close; mesh_rejoin's input)
  sessions/    # per-agent pi session files — the post-mortem record, ALL modes (supersedes transcripts/)
  inbox/       # orchestrator → child (TUI mode; headless delivery rides stdin)
  outbox/      # child → orchestrator (all modes; the tailer's intake)
  stream/      # teed token stream (ALL modes — the viewer/dash pane's source, replayable)
  tmux.json    # runtime agent map (legacy filename — the P04/P05 schema has no panes; the dash's pane map lives in ~/.mesh-dash/state.json)

The legacy transcripts/ dir is gone (the always-on sessions/ supersedes it); old settings files with "transcripts": true remain valid — the key is accepted and ignored. Mesh dirs are never deleted by the plugin (operator rule); mesh ids are m-<6 chars>. Meshes from before the .mesh/ move live under tmp/mesh-agents/<meshId>/ and remain readable: mesh_status (listing), mesh_events, and mesh_rejoin all fall back to the legacy location.

🎛️ Settings

Built-in defaults ← global ~/.pi/agent/mesh-agents.json ← project <cwd>/.mesh-agents.json (later wins; missing file = absent level; unknown keys ignored; malformed files fail mesh_spawn/mesh_task with invalid mesh settings <path>: <detail> and create nothing):

{
  "maxAgents": 4,
  "defaultModel": "provider/id",
  "pushEvents": true,
  "claims": false,
  "windows": true,
  "mode": "tui"
}
  • maxAgents — max alive agents per mesh (working + standing), integer 1–16; default 4. mesh_spawn accepts a per-mesh maxAgents override (1–16), fixed for that mesh's lifetime.
  • defaultModel — children without a model spec use this; re-read fresh on every join (spawn roster and each mesh_task), so later joiners pick up changes.
  • pushEvents — false disables digest pushes entirely (the mesh still works; the orchestrator checks mesh_status). Read at mesh_spawn, fixed per mesh.
  • mode — hosting + visibility mode per mesh: blind | tui (default tui). Read at mesh_spawn, fixed for that mesh's lifetime — one mode per mesh, fixed at spawn (no mixed modes). See Display modes. (The P03 jsonl/raw modes are retired — the rendered viewer covers both granularities.)
  • windows — the fallback display: when the dash is disabled (MESH_DASH_DISABLED=1) or a dash join/attach fails, auto-open a per-agent terminal window at the agent's join (desktop + interactive only; blind meshes: no-op — nothing to display). With the dash enabled (the default), tui agents join the shared agent dash instead and no per-agent windows open. Default true; read at mesh_spawn, fixed per mesh. meshview/replay work regardless.
  • claims — true enables the file-claim protocol: children call the claim tool before editing paths another agent claimed; conflicts arrive at the claiming child as CONFLICT messages (asynchronous adjudication) + digests the orchestrator can act on (record-and-advise — no OS locks). Default false; read at mesh_spawn, fixed per mesh.
  • transcripts — deprecated, ignored (the always-on sessions/ supersedes it; the key is accepted for compatibility with old settings files).
  • Resource accounting (tokens/turns/context-percent/elapsed per agent, mesh totals) is always on — no setting; with local models the currency is tokens/turns/elapsed, and dollar totals fall out of the same data for remote models.

Per-agent overrides: the agent spec's model ("provider/id", resolved via resolveCliModel) beats the settings default, which beats the orchestrator's active model. The effective spec string is recorded on every join envelope and in result.md.

🔐 Provenance

  • Chain: hash(envelope) = sha256(canonical(envelope minus its own hash)), prevHash = the stored hash of the previous envelope, genesis = sha256("pi-mesh-agents|genesis|" + meshId). Appends are synchronous; the log order is the true event order.

  • Single writer: the orchestrator appends every envelope — join/instruct at tool-call time; child-originated msg/response/status/claim/done at outbox-tail time (ms-scale lag vs. the child's tool call; the envelope content shapes and the event set are unchanged). stream/ and outbox/ files are operational, not evidentiary.

  • Event types: spawn, join, instruct, msg, response, status, claim, done, error, leave, close.

  • Claim adjudication is async (P03): the child's claim receipt is "submitted"; conflicts arrive as CONFLICT messages (the child never sees adjudication synchronously).

  • Verify any bus (active or closed):

    node scripts/verify-bus.mjs .mesh/agents/<meshId>/bus.jsonl
    # → OK <n> events, head <hash>

The verifier recomputes every hash and link, the genesis, seq monotonicity, first-event-is-spawn, last-event-is-close, and mesh-id consistency.

🛠️ Development

Runtime is npm-install-free; the dev tooling (typescript, eslint, typebox, the pi package for types) is in devDependencies:

npx tsc --noEmit        # typecheck (src/ + scripts/)
npx eslint .           # lint (src, scripts, tests, configs)
node scripts/bus-smoke.ts       # bus unit smoke — synthetic chain with every event type + tamper detection
node scripts/settings-smoke.ts  # settings unit smoke — precedence + validation (mode/windows/deprecated transcripts)
node scripts/bridge-smoke.ts    # bridge unit smoke — line types, renderSendText byte-identity, unichar vectors, line IO
node scripts/rpc-smoke.ts       # headless pi --mode rpc child boot (real spawn; get_state/prompt/stream/leave)
node scripts/tmux-smoke.ts      # tmux hosting + TUI child boot (real servers; pane round-trip, ready line, watcher gone-lines)

The last two smokes spawn real pi processes (pinned local model, thinking low — P03_SMOKE_MODEL overrides) and real tmux servers (isolated -L sockets); they leave no server or orphan process behind.

Conventions: erasable TypeScript only (interfaces/annotations — no enums, parameter properties, or namespaces), explicit .ts extensions on relative imports (works under both pi's jiti loader and node's type stripping), import type for type-only imports. The child extension (src/child.ts) is self-contained on purpose — it runs in the child's own process and imports no mesh-agents src/ module.

🧪 Tests

tests/ is a permanent, re-runnable modality suite that drives a real live mesh with a real orchestrator LLM per topology and asserts the plan's criteria against the durable evidence:

node tests/runner.mjs            # all tests, T01 → T13, ~60+ min
node tests/runner.mjs T02 T06    # selected tests
node tests/runner.mjs --list

| Test | Topology / modality | |---|---| | T01 | Fan-out (hub-and-spoke, no peer traffic) | | T02 | Pipeline (sequential stages, artifact flow, verdict gate) | | T03 | Review loop (verdict-gated, fresh critic per round, loop cap) | | T04 | Pool (reuse via mesh_instruct, budget-forced handoff, steer/followUp delivery evidence) | | T05 | Blocked handoff (waiting → instruct answer → same-context resume) | | T06 | Peer-to-peer direct + broadcast + reply thread + orchestrator isolation | | T07 | Concurrent meshes (two live at once) | | T08 | Model heterogeneity (spec > settings > orchestrator, per-join resolution) | | T09 | Tool allowlist (per-agent built-in allowlist, read-only agent) | | T10 | Mesh rejoin (shutdown → continuation brief → fresh mesh) | | T11 | Usage / claims / session files (always-on sessions/ supersedes the old opt-in transcripts) | | T12 | Tmux hosting — tui mode (real tmux server: pane titles, session files, instruct evidence, kill/pane-loss, post-mortem survival) | | T13 | Headless modes — blind/jsonl/raw (no/with tmux, exact session paths, stream files, watcher panes, liveness via exit event, release at close) |

Regression discipline: after a T11–T13 change, run node tests/runner.mjs T11 T12 T13 then node tests/runner.mjs T01 T02 (failure triage per T00 §5).

Design notes (details in tests/README.md): each test uses a fresh headless pi --mode rpc harness session with the extension loaded; the verbatim orchestrator prompt is extracted from the current plan file (.prompts/T<NN>-R<NN>_*.md, highest revision) at run time, so plan revisions are picked up automatically; agentic runs are pinned to the local 256k model with thinking low (--no-pin overrides); results append to tmp/mesh-tests/REPORT.md (T00 §4 format) with raw event logs under tmp/mesh-tests/raw/; a failed test is auto re-run once; soft criteria are recorded but do not fail a test.

📁 Layout

mesh-agents/
  src/
    index.ts        # extension entry: eight tools, digest push, widget, session_shutdown
    mesh.ts         # Mesh runtime (join/instruct/kill/status/close, outbox tailer, per-mode dispatch)
    childproc.ts    # child process hosts (TUI panes + headless RPC coprocesses, stream tee, tmux.json)
    child.ts        # self-contained child extension (send/done/claim tools, inbox watch, outbox reporting)
    children.ts     # child spec + task prompt (byte-identical default)
    bridge.ts       # inbox/outbox line types, renderSendText, unichar glyph mapper, line IO helpers
    bus.ts          # hash-chained JSONL bus (pure Node)
    settings.ts     # settings load/merge/validate (pure Node; mode/windows)
    errors.ts       # MeshError (machine-readable codes)
  skills/
    mesh-agents/
      SKILL.md      # packaged orchestrator skill (shipped in the npm tarball; manifest pi.skills)
  scripts/
    verify-bus.mjs  # chain verifier
    mesh-tmux-watch.mjs  # 1 s pane-liveness watcher process (tui meshes only)
    meshwatch.mjs   # plain line-printing watcher (jsonl/raw panes; also the stream replay tool)
    bus-smoke.ts    # bus unit smoke
    settings-smoke.ts
    bridge-smoke.ts
    tmux-smoke.ts   # tmux hosting + TUI child boot (real servers)
    rpc-smoke.ts    # headless RPC child boot (real spawn)
  tests/
    runner.mjs      # the test runner
    lib/            # harness + assertion helpers
    T0<NN>-<NAME>/  # one dir per test (T01–T13)
  .prompts/         # plan files (P01 build, P02 upgrades, P03 hosting, T00–T13 tests) — gitignored
  .mesh/            # mesh runtime artifacts: .mesh/agents/<meshId>/ (brief, bus, result, state, sessions, inbox, outbox, stream, tmux.json) — gitignored
  tmp/              # test-runner state (tmp/mesh-tests/) + task-output scenario dirs — gitignored

📌 Scope (v1)

In: persistent orchestrator-managed meshes, process-hosted children (real pi TUI panes / headless --mode rpc coprocesses — built in, no in-process fallback), four visibility modes (blind/jsonl/raw/tui, per mesh at spawn), the inbox/outbox bridge with always-on per-agent session files, dynamic membership, task budgets, structured done results (status/verdict/waiting_on), blocked handoffs, per-agent model + tool-allowlist modalities, concurrent meshes, hash-chained provenance (single orchestrator writer), settings layer (mode, windows), mesh persistence across pi restarts (state.json + mesh_rejoin with session-file evidence pointers), per-agent resource accounting, per-agent file-claim protocol (async conflict messages), and the tmux session lifecycle (survives window close; explicit teardown only).

Not in v1 (by design): network listeners/ports/MCP, git worktrees (already handled at the session level by the AGENTS.md rule + git-worktree skill — the plugin is cwd-agnostic) and OS-level file locks (the opt-in claim protocol is the escalation step; flock only if same-file parallel editing becomes a real pattern), mesh-to-mesh communication, TTS/human notifications from the plugin (the digest → orchestrator → agent-notify chain is the design — a plugin-direct path would only help when the orchestrator isn't listening, and the extension API offers no human channel anyway), live-session resume (re-join starts fresh children; the session files are the continuity), dollar cost as a primary currency (tokens/turns/elapsed are the currency with local models; dollar totals fall out of the same data for remote models). Children load the operator's skills/AGENTS.md like any pi session and carry no MCP tools.

Plans and specs: .prompts/P01-R09_MESH-AGENTS.md (build spec), .prompts/P02-R01_… (persistence/accounting/transcripts/claims), .prompts/P03-R01_TMUX-CHILD-HOSTING.md (process hosting + visibility modes — the binding revision), and .prompts/T00-… (test series index). Companion (separate repo): dotfiles F03 — the meshview attach/kill/list command.