davidorex-pi-context

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

原始内容

In short

This monorepo is my attempt to direct and produce a controllable environment for working with llm's to build things in code.

I'm considering renaming it "pi-prometheus-bound." Or maybe just "pi-bound." I currently run it with a script that wholly constrains the main Pi agent to tools given by the extensions, plus read and grep and ls.

You should (is the intention) be able to create exactly the kind of context substrates you need -- and switch between them, or inter-related them (think git branches, but for substrate shapes in your projects) -- and out of those substrates, construct the on-the-fly agents you need, for the particular purposes you might have.

Philosophy

削斧柯,其则不远

When cutting wood to make an axe handle, the model is right there in your hand.

The use of the tool shows you the shape of how to make the version of the tool that you want. Reshape it in-air based on your experience using it.

Packages

Package npm Description
@davidorex/pi-context npm:@davidorex/pi-context Pi extension. Schema-driven project state: typed JSON blocks with write-time validation, generic block CRUD tools, content-addressed item identity, closure-table relations, and state derived from the substrate. Block types are added by declaring JSON Schemas in the substrate; no code change.
@davidorex/pi-context-cli npm i -g @davidorex/pi-context-cli Standalone CLI (not a Pi extension). Exposes pi-context's op-registry as a pi-context <op> [flags] binary; subcommands and flags are derived by reflection over the registry, so they track the op set without per-command code. Calls the same library functions the extension calls; no running Pi instance required. Depends on @davidorex/pi-context.
@davidorex/pi-jit-agents npm:@davidorex/pi-jit-agents Library (not a Pi extension). Loads, compiles, and executes .agent.yaml specs in-process, with phantom-tool structured-output enforcement. Consumed by pi-workflows and pi-behavior-monitors.
@davidorex/pi-workflows npm:@davidorex/pi-workflows Pi extension. Workflow orchestration from .workflow.yaml specs: DAG execution, typed step types, typed data flow between steps, an expression engine, and checkpoint/resume. Output schemas validate the boundary between steps.
@davidorex/pi-behavior-monitors npm:@davidorex/pi-behavior-monitors Pi extension. Monitors that classify agent activity against JSON pattern libraries via side-channel LLM calls, steer corrections, and write structured findings.
@davidorex/pi-agent-dispatch npm:@davidorex/pi-agent-dispatch Pi extension. In-pi orchestrator surface: agent-as-tool dispatch (call-agent), capability-grant authoring (author-tool-grant), real-check gate (run-real-checks), attested commit (commit-attested), bounded loop (run-work-order-loop), and dynamic composite tools per config.tool_operations[]. A per-tool authorization gate at the pi-dispatch layer intercepts write-class tools, prompts via ctx.ui.confirm, and stamps the verified operator identity on the write.
@davidorex/pi-project-workflows npm:@davidorex/pi-project-workflows Meta-package. Bundles the four extensions (pi-context, pi-workflows, pi-behavior-monitors, pi-agent-dispatch) as a single pi install; depends on all four and registers each.

Quick Start

# Install all four extensions in any Pi project (one command, via the meta-package)
pi install npm:@davidorex/pi-project-workflows

# Or install the extensions individually
pi install npm:@davidorex/pi-context
pi install npm:@davidorex/pi-workflows
pi install npm:@davidorex/pi-behavior-monitors
pi install npm:@davidorex/pi-agent-dispatch

# Optional: the standalone pi-context CLI (not a Pi extension; no running Pi required)
npm i -g @davidorex/pi-context-cli

# In this monorepo, install the operator pi-context binary as a publish-free
# packed copy of the working tree (no npm link; a repo rebuild can't touch it):
npm run promote:cli

# Initialize project structure
/context init <substrate-dir>  # bootstrap pointer + substrate/schemas dirs only (no config, no schemas, no blocks)
/context accept-all  # adopt the packaged conception (samples/conception.json) as config.json
/context install     # reconciles the substrate against installed_schemas + installed_blocks + installed_agents in config.json
/context check-status  # read-only: report which installed schemas are behind the catalog + the version gap (writes nothing beyond the idempotent ceremony seeds: config migration decls + catalog-implied block-schema chains)
/workflow init       # creates .workflows/ for run state

Block kinds reach the substrate only by declaring their names in config.json's installed_* arrays (via /context accept-all or by hand) and running /context install, which copies them from the package-shipped samples catalog (samples/). The substrate (config + lenses + closure-table relations) is degree-zero state that defines where the rest lives and how items group into views.

Substrate-management primitive (/context switch family)

A substrate-dir is selected by the .pi-context.json pointer (contextDir field). /context switch is the substrate-management primitive parallel to git switch:

/context switch <existing-dir>    # flip pointer to an existing substrate dir
/context switch -c <new-dir>      # bootstrap a fresh substrate dir + flip pointer in one operation
/context switch -                 # round-trip to previous_contextDir
/context list                     # enumerate substrate dirs with config.json (active marked)
/context archive <dir>            # move a (non-active) substrate dir to archive/

Pointer-flip mutations (context-switch / context-archive / context-init / context-accept-all and other write-class tools) route through the pi-agent-dispatch auth-gate, which prompts via ctx.ui.confirm and stamps the verified operator identity on the substrate write — agent-issued tool calls become human-authorized at the pi-dispatch boundary regardless of caller-supplied writer fields.

This enables the per-arc substrate engagement pattern: significant design/spec arcs (multi-step feature work, spec drafting, dependency migrations) get their own .context-<arc-name>/ substrate dir with bespoke vocabulary (custom block kinds + relation_types + lenses) decomposing the design space. The substrate IS the spec; the markdown form at analysis/*.md is source-of-content; the substrate is source-of-structure + cross-references.

Constrained pi session (pi-context pi-bound)

The standalone pi-context CLI also provides a process mode that launches a pi coding-agent session restricted to the composed pi-extension tool surface:

pi-context pi-bound [--grant <id>]... [...pi-args]

On every launch it registers the extensions into the target dir's .pi/ (pi install -l against the @davidorex/pi-project-workflows root), derives the static tool allowlist from the installed packages' generated skills/*/SKILL.md (@davidorex/pi-context + @davidorex/pi-project-workflows), always adds the built-in read-only tools read,ls,grep,find, appends the bounded composites declared in the active substrate's config.tool_operations[], then launches pi --tools <union> ...pi-args. It runs from the process cwd and reads that dir's .pi-context.json for composites (warns, non-fatally, if absent).

  • --grant <id> (repeatable) — scope the bounded composites to only the named ids (default: all declared).
  • Any other token passes through verbatim to pi (e.g. --continue / -c to resume).

This process mode replaces the former scripts/launch-constrained-pi.sh launch script. When /context update finds a locally-modified schema with irreconcilable conflicts against the catalog, it surfaces the conflict set to the calling agent (it does not spawn a subordinate mergetool); the agent reconciles each conflict and commits it with /context resolve-conflict, which writes the resolved body, advances the merge base to the catalog, and registers a known migration chain when the reconciliation advances the schema's version.

Directory Ownership

After initialization, three directories coexist in a project:

.pi/            — Pi platform (agents, skills, settings). Managed by Pi itself.
.pi-context.json          — bootstrap pointer naming the single ACTIVE substrate dir (contextDir)
.pi-context-registry.json — project-root registry enumerating ALL substrates by substrate_id (git-tracked)
<substrate-dir>/ — pi-context. Created by /context init <substrate-dir> (skeleton-only).
  config.json   — substrate bootstrap: root, substrate_id, naming, hierarchy, lenses, installed_*
  relations.json — closure-table edges (created on first authored edge)
  migrations.json — per-substrate schema-version migration registry
  schemas/      — JSON Schema files (empty until /context install reifies declared names)
  objects/      — content-addressed object store, one objects/<content_hash>.json per content version (git-tracked)
  <name>.json   — block data files (each a /context install target or user-authored)
.workflows/     — pi-workflows (run state). Created by /workflow init.
  runs/         — workflow execution state, session logs, outputs

.pi/ is Pi's territory — neither extension writes to it. The substrate directory is tracked in git (substrate, schemas, and blocks are source). .workflows/ is gitignored (runtime state). config.json and relations.json always live at the substrate-dir root (the bootstrap-chosen dir, pointer-resolved; they define root); everything else lives under <config.root>/... and a relocated root reaches every read/write because all path construction routes through resolveContextDir(cwd).

What Each Extension Provides

pi-context

Tool families: block CRUD (read/write/append/update/remove-block-item, top-level + nested), item-level read/query (read-block-item, read-block-page, filter-block-items, resolve-item(s)-by-id, join-blocks, find-references, walk-ancestors, context-walk-descendants, context-edges-for-lens, context-lens-view, gather-execution-context), substrate writes (append-relation, amend-config, write-schema, write-schema-migration, rename-canonical-id), content-addressing lifecycle (promote-item), discovery/introspection (read-config, read-schema, read-samples-catalog, read-catalog-schema, list-tools, context-current-state, context-bootstrap-state), lifecycle/state (context-status, context-validate, context-validate-relations, context-reconcile, complete-task — the validator ops, context-roadmap-validate included, accept optional narrowing filters + offset/limit pagination bounding the returned issues[]: severity/code on all three, block additionally on context-validate only (its issues carry a block field; the relation/roadmap validators reject block as an unknown parameter); status always reflects the FULL evaluation, and each op's over-cap boundary refusal names its own parameters), substrate management (context-init, context-accept-all, context-install, context-switch, context-list, context-archive), and the derived roadmap (context-roadmap-load/render/validate — a view over authored milestone_precedes_milestone edges with per-milestone phase/task rollups) — all writes carry automatic schema validation. Read packages/pi-context/skills/pi-context/SKILL.md or call list-tools for the current set.

Item identity + cross-substrate. Identity-bearing items carry a three-layer identity — a mutable id refname, a content-independent oid minted once and immutable, a content_hash over the item's content projection (persisted to a git-tracked objects/<content_hash>.json store), and a content_parent version chain. Each substrate's config.json carries a substrate_id; a project-root .pi-context-registry.json enumerates all substrates by substrate_id so closure-table edges can point across substrates (a {kind:"item", oid, substrate_id} endpoint), resolved/classified by resolveRef as active/foreign/dangling/unregistered.

Commands:

  • /context init <substrate-dir> — write the substrate skeleton (bootstrap pointer + dirs; no config, no schemas, no blocks); refuses with loud error when the existing pointer's contextDir differs from the caller's argument (points to /context switch -c <new-dir> as the correct command for that operation)
  • /context accept-all — adopt the packaged conception (samples/conception.json) as config.json
  • /context install — reconcile <substrate-dir>/ against installed_schemas / installed_blocks / installed_agents declared in config.json from the package registry. Populated block data is never overwritten (reported as preserved), and empty or absent blocks get the catalog starter. Declared agent specs materialize under agents/ (output schemas under agents/schemas/) as editable project-tier specs, never overwritten even under --update and never baselined. Install also base-stamps each as-installed schema body into the object store and records an install baseline (config.installed_from: catalog source + per-schema fingerprint of the installed schemas) used for installed-vs-catalog drift detection; the baseline covers schemas only and a re-install on an unchanged substrate is idempotent. (Bringing the installed schema model current is /context update, below.)
  • /context check-status (CLI: pi-context context-check-status) — read-only: reports drift between the installed schemas and the catalog (in-sync / catalog-ahead / locally-modified / both-diverged / no-baseline / missing-*), and for each schema behind the catalog (catalog-ahead / both-diverged) which schema is behind and by what version gap — the baseline → catalog version pair, or a content-only basis when the version string is unchanged; writes nothing beyond the idempotent ceremony seeds of the catalog's config migration declarations and the block-schema migration chains implied by each installed schema's catalog starter+schema version pair into migrations.json (every substrate-lifecycle ceremony seeds before its first config read, so a substrate missing a catalog-implied chain self-heals on the ceremony)
  • read-catalog-schema (CLI: pi-context read-catalog-schema --kind <canonical_id>) — read-only: fetches and prints the verbatim bundled catalog *.schema.json body for a named block kind (the raw JSON Schema — properties / definitions / $id, not the read-samples-catalog projection), diffable locally against the installed <substrate>/schemas/<name>.schema.json without hunting through node_modules. Package-intrinsic; mutates nothing
  • /context update [--dryRun] (CLI: pi-context update [--dryRun]) — bring the installed schema model current with the catalog, routing each schema by drift state: in-sync no-op; catalog-ahead resync (migration-aware); locally-modified / both-diverged reconciled by a deterministic 3-way merge of base (the as-installed body in the object store, keyed by the baseline content_hash) × ours (installed schema) × theirs (catalog schema) — disjoint edits auto-merge (required / enum / array-type nodes merge as sets), and a schema with irreconcilable per-path conflicts is left unmodified — the conflict set is returned in the op output (under conflicts) alongside a readable report, and the calling agent reconciles it then commits via /context resolve-conflict (below; no subordinate resolver is spawned). Update also additively propagates catalog-new config-registry entries (relation_types / invariants / block_kinds / lenses) absent from the config, preserving user-authored entries and any locally-diverged body of an existing entry (reported under registryAdditions). A version-bump catalog-ahead resync, or a 3-way merge/resolve-conflict that advances a schema's version, registers the shipped catalog migration chain's declarations into migrations.json, reported under migrationsRegistered (each { schema, from, to }). A catalog-ahead schema whose resync is refused (blocked) carries its diagnostic under blockedDetail (one entry per blocked schema): the refusal reason — no-migration-chain (no shipped chain reaches the catalog version) vs validation-failed (the forward-migrated items fail the catalog schema) vs write-failed (a non-validation throw at the write boundary, e.g. the block writer's duplicate-item-id guard; the failures entry carries the thrown message, the items were NOT flagged invalid, and no markers or pending-blocked record are produced) — the installed→catalog version pair, and for a validation failure the per-item failures naming the failing item id, field, and constraint; on the CLI's text surface this renders as a readable report below the op output. A live validation-failed block also persists a pending-blocked record (pending-blocked.json, pinning the target catalog schema + the chain reaching it) consumable by /context resolve-blocked (below). A validation-failed block additionally gets git-style failure markers written INTO the block file at the offending items (full-line <<<<<<< BLOCKED … / >>>>>>> target: … sentinels), pinning the pre-marker bytes; the schema and migrations.json stay byte-unchanged, and /context resolve-blocked strips the markers and re-validates. --dryRun predicts the precise per-schema outcome (resync / migrate / block / merge / conflict) by running the forward-migration and re-validation in memory, alongside the per-blocked-schema diagnostic detail, the config-registry entries that would be added, and the migration declarations that would be registered, and writes nothing beyond the idempotent ceremony seeds (the catalog's config migration declarations + the catalog-implied block-schema chains; no markers). A run that refuses any schema while applying registry additions or other-schema resyncs/migrations/merges additionally reports the partiality under partialApplication (applied/notApplied mirrors of the result channels + a one-line summary naming what was applied alongside what was refused and why), so a blocked run never reads as a no-op; --dryRun reports the predicted partiality in the same shape. On a substrate whose config carries no substrate_id, a LIVE update establishes the identity at entry (mint + persist + register, before the first identity-stamping write) so a pre-identity substrate heals on the ceremony instead of refusing, reported under substrateIdEstablished; the same entry establishment runs in install and resolve-blocked, an established identity is never re-minted, and --dryRun establishes nothing
  • /context resolve-conflict --schemaName <name> [--schema <reconciled>] (CLI: pi-context resolve-conflict --schemaName <name> [--schema <reconciled>]) — commit the reconciliation of a merge conflict update surfaced: writes the reconciled schema body (meta-validated, atomic), advances the merge base for that schema to the catalog, and registers a known catalog migration chain when the reconciliation advances the schema's version, so the next update sees the schema as locally-modified and its deterministic merge takes the reconciled body (base === theirs → ours), converging with zero conflicts and preserving the resolution (a bare write-schema does not advance the base, so update would re-report the same conflict). Omit --schema to treat the current on-disk body as already reconciled and only advance the base
  • /context resolve-blocked --schemaName <name> (CLI: pi-context resolve-blocked --schemaName <name>) — commit the resolution of a schema update blocked. Run after fixing the block's failing items (or widening the local schema): when the block file carries git-style failure markers (written by update), it strips the full-line marker sentinels first, then re-validates the corrected block against the pinned target schema from the pending-blocked record, and on pass registers the chain declarations, writes the target schema, advances the block envelope and merge base to the target (so a subsequent update converges in-sync instead of re-blocking), and clears the pending entry; on fail it returns the remaining per-item failures and writes nothing (the marker file is left untouched). The commit itself is all-or-nothing: a throw partway through it restores every touched file byte-exact and reports the failure, never a partial commit
  • /context reconcile [--dryRun] (CLI: pi-context context-reconcile [--dryRun], auth-gated) — converge stored rollup-kind statuses with their derivation (the repair half of the derived-status invariant class): --dryRun previews the exact delta set; a live run applies it through the validated write path (identity-stamped, envelope-stamped, writer-attested). Never touches authored statuses or prose — authored statuses surface for review through context-validate's signals (among them declared invariant violations, the status-vocabulary warning, the staleness sweep for stale_conditions-bearing items); prose is not validated, its review is human
  • /context switch <dir> | -c <new-dir> | - — substrate-management primitive: flip pointer to existing dir, bootstrap-and-flip in one op, or round-trip to previous_contextDir
  • /context list — enumerate substrate dirs with config.json (active marked)
  • /context archive <dir> — move a (non-active) substrate dir to archive/
  • /context view <lensId> — render a configured lens (groupByLens projection) into the conversation
  • /context lens-curate <lensId> — surface bin-assignment suggestions for uncategorized items as a follow-up turn
  • /context status — derived project state (source metrics, test counts, block summaries, git state)
  • /context add-work — extract structured items from conversation into typed blocks
  • /context validate — cross-block referential integrity checks
  • /context help — show available subcommands

Key concept: Users define block types by adding JSON Schemas to <substrate-dir>/schemas/. Any <substrate-dir>/*.json file with a matching schema gets automatic write-time validation. No code changes needed to add new block types.

pi-workflows

Tools: workflow, workflow-list, workflow-agents, workflow-validate, workflow-status, workflow-init

Commands:

  • /workflow init — scaffold .workflows/ directory
  • /workflow list — discover and select a workflow to run
  • /workflow run <name> — execute a workflow (tab-completes with discovered workflow names)
  • /workflow resume <name> — resume from checkpoint
  • /workflow validate [name] — validate workflow specs
  • /workflow status — show workflow vocabulary and discovery
  • /workflow help — show available subcommands

Keybindings: Ctrl+H pause, Ctrl+J resume

Key concept: Workflows are .workflow.yaml specs with typed data flow between steps. Each step runs as a subprocess with its own context window. The DAG planner infers parallelism from ${{ steps.X }} expression references and context declarations. Agent steps support context: [stepName] to inline prior step narrative text into the dispatch prompt, complementing expression-based structured data flow. The monitor step type integrates behavior classification as a verification gate. Bundled agent specs (with their output schemas) ship in the pi-context samples catalog and materialize into a substrate's agents/ via /context install; agent templates ship with pi-jit-agents. A substrate's own agents/ specs win over the bundled tier, and project .pi/templates/ override the bundled templates.

pi-behavior-monitors

Tools: monitors-status, monitors-inspect, monitors-control, monitors-rules, monitors-patterns

Commands:

  • /monitors on|off — enable/disable all monitoring
  • /monitors <name> — inspect a monitor
  • /monitors <name> rules|patterns|dismiss|reset — manage monitor state
  • /monitors help — show available commands

Programmatic API: invokeMonitor(name, context?) — exported function for synchronous classification without event-handler side effects. Returns ClassifyResult directly.

Key concept: Monitors are .monitor.json specs with Nunjucks classify templates. They observe agent activity via Pi event handlers (message_end, turn_end, agent_end), classify against JSON pattern libraries using side-channel LLM calls, and steer corrections or write structured findings. Verdicts: CLEAN (no issue), FLAG (known pattern), NEW (unknown pattern, optionally learned).

pi-agent-dispatch

Tools (static): call-agent, author-agent-spec, author-tool-grant, run-real-checks, commit-attested, run-work-order-loop, write-schema-migration

Tools (dynamic): composite Pi tools registered per config.tool_operations[] entries (kind-typed: read-files / git-log / grep-paths / command-allowlist). Each closure-binds its instance_params and is granted via --tools <canonical_id> like any built-in.

Dispatch-layer event handlers:

  • auth-gate (tool_call handler) — intercepts canonical write-class tools (author-agent-spec / author-tool-grant / commit-attested / write-schema / write-schema-migration / amend-config / write-block / rename-canonical-id / context-init / context-accept-all / context-install / context-switch / context-archive / workflow-execute / workflow-resume / workflow-init / monitors-control / monitors-rules); refuses non-interactive contexts unconditionally; calls ctx.ui.confirm interactively; on confirm, stamps verified operator identity (git config user.email → process.env.USER cascade) onto event.input.writer. Substrate attestation reflects actually-confirming-user, not agent-supplied claim.
  • read-truncation-gate (tool_result handler) — hard-refuses pi built-in read truncation by replacing event.content with single-text-item carrying canonical directive (paginate / grep / sed byte-range as appropriate per TruncationResult shape).

Key concept: an in-pi orchestrator authors agent specs + composes operation-granular capability grants, dispatches a privileged sub-agent under a bounded grant clamped to the parent's grant at dispatch, runs deterministic real-checks (build / typecheck / test / runtime demo / adversarial probe) as the terminal verdict (no LLM in the gate loop), and commits the agent's per-file changes with an Attested-by: agent/<id> footer. run-work-order-loop provides bounded iteration with a human-OK gate at iteration boundaries; on a passing real-check its commit routes through the same commit-attested auth-gate every other caller of that tool goes through (interactive confirm, or completed-pending-commit with no commit attempted in a non-interactive context); a work-order may declare on_fail: "retry" so a non-interactive real-check failure retries the bounded loop instead of aborting.

Development

# Install dependencies
npm install

# Build all packages (tsc compiles to dist/)
npm run build

# Run all tests
npm test

# Run per-package
npm test -w packages/pi-context
npm test -w packages/pi-workflows
npm test -w packages/pi-behavior-monitors

# Run integration tests (requires pi on PATH, spawns LLM subprocesses)
RUN_INTEGRATION=1 npm test -w packages/pi-workflows

# Lint and format (Biome v2.4.9, scoped to packages/ + scripts/)
npm run lint       # check for lint issues
npm run format     # auto-fix formatting
npm run check      # lint + typecheck

# Clean build artifacts
npm run clean

# Derive project state
npx tsx -e "
  import { contextState } from './packages/pi-context/src/context-sdk.js';
  console.log(JSON.stringify(contextState('.'), null, 2));
"

Architecture

  • Main conversation is the control plane; workflows are subordinate. Each workflow step runs as a subprocess (pi --mode json) with its own context window. The main LLM orchestrates; step agents execute.
  • Agent specs are .agent.yaml only (no .md fallback). Compiled to prompts via Nunjucks at dispatch time. Agents declare inputSchema for typed input validation at dispatch, contextBlocks to inject project block data into templates, and output.format/output.schema for output validation.
  • contextBlocks — an agent YAML field. Each entry is either a bare block-name string (whole-block injection) or an object { name, item?, focus?, depth? } (per-item or scoped injection). At dispatch time, string entries inject the whole block under _<name> (hyphens become underscores). Object entries with item resolve the ID via the cross-block resolver and inject under _<name>_item (single-entry case) or _<name>_items array (multi-entry case for the same name — e.g., three decisions in one contextBlocks array). depth controls cross-reference recursion; focus carries kind-specific scope hints. Templates access block data via {{ _conventions.rules }} or render via the per-item macros under templates/items/ (e.g., {% from "items/conventions.md" import render_convention %}{{ render_convention(rule) }}). Whole-block delegators in templates/shared/macros.md map over items. Missing blocks are null; templates guard with {% if _conventions %}. No substrate directory means injection is skipped entirely. This is how project state flows into agent prompts declaratively.
  • inputSchema — an agent YAML field defining a JSON Schema for the agent's input. Validated at dispatch time before the agent subprocess is spawned. If validation fails, the step fails immediately — no LLM call is made.
  • Per-item macros (templates/items/<kind>.md) — one per block kind, each rendering a single item. Macro names follow canonical singular convention (e.g. render_decision, render_feature, render_framework_gap, render_convention); the renderer registry maps each kind to its canonical macro via CANONICAL_MACRO_NAMES. Macros are depth-aware: cross-block ID references inline via the resolve and render_recursive Nunjucks globals when depth > 0, fall back to bare-ID emission at depth = 0, and produce the named cycleMarker / unrenderedMarker / notFoundMarker sentinels on cycles, missing macros, or unresolved IDs. Budget-annotated fields render through the enforceBudget Nunjucks global; warnings surface on CompiledAgent.budgetWarnings.
  • Whole-block delegators (templates/shared/macros.md) — thin for x in data.<key> { render_<kind>_item(x) } wrappers over the per-item macros, for callers that want to dump a whole block.
  • Shared render-helpers (templates/shared/render-helpers.md) — helper macros for the recursion / optional-array / optional-scalar patterns. Per-item macros import what they need; new block kinds added later get the same recursion behavior without copy-pasting the pattern.
  • All resolved via three-tier template search — users override by placing alternate macro files in .pi/templates/items/<kind>.md or .pi/templates/shared/....
  • DAG planner infers parallelism from ${{ steps.X }} expression references and context: [stepName] declarations. Steps without explicit dependencies run sequentially by declaration order.
  • Step context injection — agent steps with context: [step1, step2] get prior step textOutput inlined into their dispatch prompt as labeled markdown sections. Complements expression-based structured data flow with narrative text inlining.
  • Monitor step type — workflows can invoke monitors as verification gates via monitor: <name>. CLEAN → completed, FLAG/NEW → failed.
  • Atomic writes — all block and state persistence uses tmp file + rename for crash safety. State write failure is fatal.
  • Checkpoint/resume — incomplete runs can be resumed from last completed step. completion field controls post-workflow message to main LLM.
  • Three-tier resource search — project .pi/ > user ~/.pi/agent/ > package builtin (agents, templates, workflows)
  • Workflow SDK (packages/pi-workflows/src/workflow-sdk.ts) — single queryable surface for the extension's capabilities. All functions derive dynamically from code registries and filesystem. Vocabulary: stepTypes(), filterNames(), validationChecks(). Discovery: availableAgents(), availableWorkflows(). Contracts: agentContracts(cwd) projects each agent's inputSchema, contextBlocks, and output format; agentsByBlock(cwd, blockName) finds agents consuming a given block. Validation: validateWorkflow() checks 11 dimensions including inputSchema required-key matching, contextBlocks existence, StepType metadata enforcement, and template-input alignment with contextBlocks-injected variables.
  • ESM, TypeScript compiled via tsc to dist/. Pi loads compiled JS from each package's dist/index.js. Cross-package imports use .js extensions for Node16 module resolution.
  • Skill self-install — each extension copies its skills/ directory to ~/.pi/agent/skills/ on activation, ensuring skills are discoverable regardless of install method.
  • Pre-commit hook (husky, .husky/pre-commit) runs, in order: npm run check, npm test, scripts/check-changelog.ts (published-surface commits must grow [Unreleased]), scripts/parity-check.ts (op↔CLI parity), scripts/check-config-schema.ts (expand-contract discipline on the bundled config schema), and scripts/check-comment-citations.ts (delta-scoped: flags only a canonical_id tracker citation newly introduced in a .ts/.tsx comment under a package's src/ tree, never a pre-existing one). CI (GitHub Actions) runs check + build + test on Node 22/23.
  • All packages use direct dependencies. pi-jit-agents depends on pi-context for block-api reads during contextBlocks injection. pi-workflows and pi-behavior-monitors depend on pi-context for block state; consumer migration to adopt pi-jit-agents as their agent runtime is tracked in the jit-agents v2 spec at analysis/2026-05-30-jit-agents-spec-v2.md (markdown source-of-content) + .context-jit-spec-v2/ (substrate source-of-structure: 3 axioms + 10 decisions + 5 concepts + 2 dispatch-modes + 6 v1-supersessions + open-question FGAPs + inter-entity edges + full-spec-render composition lens). pi-agent-dispatch depends on pi-context + pi-jit-agents. pi-context has no knowledge of workflows, monitors, jit-agents, or agent-dispatch.

For LLMs

When working in this repository:

  • Read package READMEs for detailed API docs: pi-context, pi-context-cli, pi-jit-agents, pi-workflows, pi-behavior-monitors, pi-agent-dispatch, pi-project-workflows
  • packages/pi-context/src/context-sdk.ts — derived state, block discovery, the contextState() function
  • packages/pi-context/src/block-api.ts — block CRUD with schema validation
  • packages/pi-workflows/src/workflow-sdk.ts — vocabulary, discovery, introspection for workflows
  • packages/pi-workflows/src/workflow-spec.ts — YAML parsing and STEP_TYPES registry
  • packages/pi-workflows/src/expression.ts — expression evaluator and filter registry
  • packages/pi-behavior-monitors/index.ts — single-file extension: monitors, classification, steering, invokeMonitor() export
  • .project/ contains this project's own block data (issues, decisions, architecture, inventory) — useful for understanding the extension's development state
  • Use /context status to see derived metrics. Use /workflow list to see available workflows.

Release

All packages use lockstep versioning — every release bumps all packages to the same version. Run from the repo root:

npm run release:patch    # bump all packages patch, update CHANGELOGs, commit, tag
npm run release:minor    # bump all packages minor
npm run release:major    # bump all packages major

This invokes scripts/release.mjs, which: checks for uncommitted changes, bumps versions across all workspaces (via scripts/bump-versions.js), stamps [Unreleased] CHANGELOG sections with the new version and date, commits, and tags vX.Y.Z. The script does not publish or push — after it completes, the human must run npm publish --workspaces --access public (requires npm login + OTP) and git push origin main && git push origin v<version>.

License

MIT.