@0xrsydn/pstack-pi
v0.17.1
Published
Pi-native port of pstack with rigorous workflows, exact role routing, and isolated subagents.
Maintainers
Readme
pstack-pi
A Pi-native port of pstack, Lauren Tan's rigorous engineering workflow for Cursor.
The original skills, workflows, and prose are MIT-licensed work by Lauren Tan. This repository adapts harness-specific behavior to Pi.
Install
Use a pinned Git tag or commit for reproducible installs:
pi install git:github.com/0xrsydn/[email protected]During development, load the local checkout:
pi -e /path/to/pstack-piThe tag pins the published release. Use a full commit ID when you need an exact development revision.
Restart Pi after installation, or run /reload in an existing session. Run pi list to confirm the package declaration.
Set up models
Run the native command:
/setup-pstackThe command:
- Reads authenticated models from Pi's model registry.
- Asks for the pstack reasoning budget.
- Recommends the upstream Grok 4.7 and Claude Opus 5.5 pair when available.
- Shows every upstream routing role.
- Validates each model before saving.
- Writes
~/.pi/agent/pstack-models.jsonatomically.
Pi separates model identity from thinking level. The config follows the same design:
{
"version": 1,
"budget": "large",
"roles": {
"feature-refactoring": {
"model": "opencode/grok-4.7",
"thinking": "xhigh"
},
"judgment-prose": {
"model": "anthropic/claude-opus-5-5",
"thinking": "xhigh"
},
"arena-runners": [
{
"model": "anthropic/claude-opus-5-5",
"thinking": "xhigh"
},
{
"model": "opencode/grok-4.7",
"thinking": "xhigh"
}
]
}
}Provider names are examples. Grok and Claude are recommendations, not requirements. All 17 roles accept any chat model available through Pi, including models registered by custom provider extensions. pstack-pi does not store credentials or require a particular provider.
"model": "inherit-parent" uses the active parent model and, unless overridden, its thinking level. The bundled agents have no fixed model. A missing role therefore inherits the parent when you use a bundled agent. A custom agent can supply its own model; see routing precedence.
Without a config, the extension resolves the upstream Grok 4.7 and Claude Opus 5.5 pair from Pi's available models. It uses the large budget and falls back to the parent for an unavailable family. A partial config does not merge with that preset: omitted roles have no assignment.
The command migrates the old code / execution / judgment / panel file when possible.
Budget mapping:
| Budget | Pi thinking level |
| --- | --- |
| unlimited | max |
| large | xhigh |
| medium | high |
| small | medium |
The default budget is large. Budget names are not thinking-level names: a medium budget requests high thinking. Setup has no low budget option.
Pi limits the effective thinking level to what the selected model supports. Unlike Cursor, pstack-pi does not encode effort in a model slug or invent a new slug for an unsupported tier.
Customize models
Run /setup-pstack, choose a budget, then select Change one role to edit any assignment. Panel roles let you choose models in order, including repeated models and inherit-parent. The interactive panel editor accepts up to eight entries.
For per-role thinking levels, edit ~/.pi/agent/pstack-models.json directly. These levels are accepted:
off, minimal, low, medium, high, xhigh, maxFor example, this partial config uses GPT for code, Claude for judgment, and Grok at a lower effort for exploration:
{
"version": 1,
"budget": "medium",
"roles": {
"feature-refactoring": {
"model": "openai-codex/gpt-6.1-sol",
"thinking": "high"
},
"judgment-prose": {
"model": "anthropic/claude-opus-5-5",
"thinking": "high"
},
"how-explorer": {
"model": "opencode/grok-4.7",
"thinking": "low"
},
"arena-runners": [
{ "model": "openai-codex/gpt-6.1-sol", "thinking": "high" },
{ "model": "anthropic/claude-opus-5-5", "thinking": "high" },
{ "model": "inherit-parent" }
]
}
}Use only routes available in your Pi session. A custom provider route can replace any example above. Unavailable configured models stop delegation and require a config update; pstack-pi does not silently substitute a different paid model.
The budget field records the setup choice. Explicit role thinking values control delegation. Re-running setup reapplies the selected budget to all explicit assignments, including panels, and replaces manual thinking differences.
Configuration is read on each pstack_task call. Changing it affects subsequent calls without restarting Pi. It does not switch the parent chat model. Use Pi's /model and /thinking for the parent; save those defaults separately for new sessions.
Routing precedence
The agent selects instructions and tools. The role selects a model assignment. The bundled poteto-agent and general-purpose agents do not pin a provider or model.
The model is selected in this order:
- Explicit per-task
model. - Configured
roleassignment. - Custom agent frontmatter
model, if present. - Active parent Pi model.
At either of the first two steps, inherit-parent or auto selects the parent directly and bypasses an agent's fixed model.
An explicit per-task thinking overrides the role's thinking value. When the model is inherited without an explicit thinking value, the child inherits the parent thinking level. An explicit model without a thinking value uses the child Pi default.
For a one-off override, ask Pi to call pstack_task with arguments such as:
{
"agent": "poteto-agent",
"role": "feature-refactoring",
"model": "openai-codex/gpt-6.1-sol",
"thinking": "high",
"task": "Implement the agreed change and run the relevant tests."
}Supplying model bypasses the role assignment, including its thinking value. Supply thinking too when you need a specific effort. The same overrides work on individual tasks[] and chain[] entries.
Exact routing roles
pstack-pi preserves all 17 upstream role boundaries. The preset assigns models as follows; every assignment can be changed.
| Role | Preset model | Assignment shape |
| --- | --- | --- |
| feature-refactoring | Grok 4.7 | Single |
| bug-fix | Grok 4.7 | Single |
| perf-issue | Grok 4.7 | Single |
| hillclimb | Grok 4.7 | Single |
| judgment-prose | Claude Opus 5.5 | Single |
| hardest-tasks | Claude Opus 5.5 | Single |
| how-explorer | Grok 4.7 | Single |
| how-explainer | Claude Opus 5.5 | Single |
| why-investigators | Grok 4.7 | Single |
| why-synthesizer | Claude Opus 5.5 | Single |
| reflect-tooling | Grok 4.7 | Single |
| reflect-judgment-divergent-synthesizer | Claude Opus 5.5 | Single |
| arena-runners | Claude Opus 5.5, Grok 4.7 | Panel |
| arena-cross-judge-pool | Claude Opus 5.5, Grok 4.7 | Panel |
| swarm-workers | Grok 4.7 | Single |
| architect-runners | Claude Opus 5.5, Grok 4.7 | Panel |
| interrogate-reviewers | Claude Opus 5.5, Grok 4.7 | Panel |
A single role requires one assignment object. A panel requires a non-empty list. Skills use the runner or reviewer list length to decide how many tasks to submit. The extension does not create extra tasks merely because a panel has more entries.
Within each tool call, pstack_task cycles each panel in order. The arena cross-judge pool supplies one judge and prefers a model family different from the parent model.
Pi-native delegation
Extension loading versus skill invocation
Pi loads this package's declared extension at session startup or reload. The pi.extensions entry in package.json points to extensions/pstack/index.ts. Its factory registers /setup-pstack and pstack_task immediately.
Invoking a skill is not required to register or activate the tool. Disabling skills with --no-skills does not disable the extension. Package resource filters, --no-extensions, and tool-selection settings can disable extension loading or tool exposure independently.
A loaded extension does not mean a running subagent. Child processes start only when pstack_task is called. Skills provide workflow instructions; they do not act as an activation gate.
Do not copy the package entry point into ~/.pi/agent/extensions/ while also installing the package. Install it once to avoid duplicate tool and command registrations.
Delegation modes
The package registers pstack_task, a focused tool based on Pi's official subagent example. It starts isolated Pi subprocesses and supports:
- One task.
- Parallel
tasks[]with bounded concurrency. - Sequential
chain[]with{previous}substitution. - Per-task model and thinking overrides.
- Exact pstack role routing.
- Read-only tool restriction.
- Streaming progress, usage, errors, and cancellation.
- Package, user, and trusted project agent definitions.
The dedicated name avoids collisions with other Pi subagent extensions. pstack-pi does not require pi-subagents, tmux, or another orchestration package.
Package agents have the lowest precedence. User agents in ~/.pi/agent/agents/ can override them. With agentScope: "both", project agents in .pi/agents/ override both user and package agents. agentScope: "project" loads only project agents.
System design: Pi primitives
pstack-pi preserves pstack workflows, not Cursor's runtime. It uses Pi's extension, skill, model, and package interfaces instead of recreating Cursor services. Child tasks use Pi's command-line interface (CLI).
| Responsibility | Pi primitive | pstack-pi choice |
| --- | --- | --- |
| Workflow instructions | Package skills and /skill:<name> | Load detailed instructions only when needed |
| Interactive configuration | registerCommand() and ctx.ui.select() | Native /setup-pstack, separate from a model turn |
| Available model discovery | ctx.modelRegistry.getAvailable() | Use Pi authentication and custom providers; no second catalog |
| Delegation | registerTool() with a TypeBox schema | One named pstack_task tool with three execution modes |
| Child execution | Pi CLI JSON/print mode | Separate process and context per task |
| Agent instructions | Markdown frontmatter and appended system prompt | Reusable agent definitions, separate from model roles |
| Progress and results | Tool onUpdate, content, and details | Stream progress and retain structured child results |
| Usage accounting | Tool-result usage | Report child model usage through Pi's session totals |
| Terminal display | Pi tool renderers and pi-tui components | Use the host theme and transcript, not another terminal UI |
| Persistent routing | getAgentDir() and withFileMutationQueue() | Versioned JSON, queued atomic writes, file mode 0600 |
| Distribution | package.json Pi resource manifest | Install extension and skills together from a pinned source |
Why these choices
- Skills for policy, extension code for execution. Playbooks remain readable instructions. Validation and process management are executable operations.
- Separate agent, role, and model. Changing a provider does not require copying an agent prompt or rewriting a playbook.
- Use Pi's provider layer. Models and credentials stay under Pi control. Custom providers need no pstack-specific integration.
- Use isolated CLI children. This follows Pi's official subagent example and avoids shared conversation state. It adds process startup overhead.
- Keep orchestration inside one tool call. Single, parallel, and chain modes avoid a required background-task service or external orchestrator.
- Use a dedicated tool name.
pstack_taskavoids the genericsubagentname used by other extensions. - Store routing outside session history. Model choices apply across sessions. Child outputs and usage remain attached to the parent's tool results.
No pi-subagents, tmux, cloud-agent service, or custom Pi runtime is required. The implementation uses the Pi CLI rather than an in-process session from Pi's software development kit (SDK).
Execution path
/skill:poteto-mode or another pstack skill
-> parent Pi selects agent + role + task
-> pstack_task reads routing and discovers agents
-> resolve model, thinking, tools, and working directory
-> launch pi --mode json -p --no-session
-> collect child messages and stream progress
-> return content + details + usage to the parentThe child receives the task brief and agent instructions, not the parent conversation. The bundled poteto-agent also receives the packaged poteto-mode skill path.
Limits and trust
- A parallel call accepts at most eight tasks and runs at most four children concurrently.
- A chain launches a fresh child for each step.
{previous}passes the preceding text output, not a resumable child session. - Calls wait for their children. They do not provide durable background jobs or Cursor-style resume handles.
- Cancellation forwards the tool abort signal to the child process. It is not an operating-system process-tree sandbox.
- Children share the working directory unless a task supplies
cwd. The tool does not automatically create worktrees or prevent competing writes. readonly: trueselectsread,grep,find,lsas the child's declared tools. It is not a filesystem sandbox. It does not unload extensions or remove MCP integrations.- Children load their own Pi resources. Extensions loaded only through the parent's command line are not automatically forwarded.
- Project agents are opt-in. Untrusted project agents require confirmation when a UI is available, unless confirmation is explicitly disabled.
Use trusted extensions and separate worktrees or non-overlapping file scopes for parallel writers.
Implementation: extension, agent discovery, routing config, and setup.
Pi references: extensions, skills, packages, and the official subagent example.
Cursor-to-Pi mapping
| Cursor concept | Pi adaptation |
| --- | --- |
| /setup-pstack skill-driven setup | Native /setup-pstack extension command |
| Task | pstack_task |
| subagent_type | agent |
| Task model slug with effort suffix | provider/model-id plus separate thinking |
| generalPurpose | Bundled general-purpose agent |
| Background task fan-out | One bounded parallel tasks[] call |
| Cursor model rule | Versioned ~/.pi/agent/pstack-models.json |
| Cursor skill paths | Pi package skills and .pi/skills/ |
Skills
Invoke the main mode explicitly:
/skill:poteto-modeOther explicit skills use /skill:<name>, for example /skill:arena, /skill:why, or /skill:interrogate.
Skills added in 0.17.0:
/skill:correctfixes a repeated mistake class in the repository itself./skill:sharpenmakes a task deeper before rewriting the prompt./skill:in-your-own-wordsrestates the goal and the problem before work starts./skill:ketchupreports what changed since your last message and what waits on you./skill:benchmark-checklistvets a performance number before you report it./skill:create-formal-verification-skilland/skill:maintain-formal-verification-skillgenerate and maintain a project-local formal check.
The package retains Pi's progressive skill loading. Most pstack skills use disable-model-invocation: true and load only when explicitly invoked or referenced by another pstack workflow.
Cursor Cloud Automations are not included. Cursor-specific control plugins degrade to direct verification through the tools available in the Pi session.
Personal working preferences
Run /skill:automate-me to create or update a personal -mode skill.
The workflow retrieves user and assistant text from Pi sessions for the selected working directory.
It reports coverage and requires user confirmation before recording preferences.
It does not schedule jobs or infer preferences from assistant responses alone.
The bundled Node.js helper reads the default workspace session directory and honors
PI_CODING_AGENT_DIR and PI_CODING_AGENT_SESSION_DIR.
For Pi's --session-dir or sessionDir setting, pass the resolved directory explicitly:
node skills/automate-me/scripts/read-history.mjs --cwd /path/to/project --session-dir /path/to/sessions --days 28Each file's header must match the selected working directory before its messages are read.
The helper retains stored branches, deduplicates fork copies, and strips expanded skill instructions from user evidence.
It excludes tool output, generated summaries, context-edited messages, and sessions forked from outside the selected directory.
These conservative exclusions can omit valid history. Prompt-template authorship still needs user confirmation.
The CLI returns coverage only by default. Use --offset 0 --limit 50 for bounded message pages.
It reports missing, malformed, or skipped data. Legacy messages without IDs are skipped.
When history is unavailable, the workflow asks whether to correct retrieval or draft from direct answers only.
Private history and evidence paths do not belong in the published mode skill.
Upstream scope
This port includes the Pi-native setup, all 17 routing roles, isolated delegation, and adaptations of the existing bundled workflows. It is not a complete synchronization with the latest Cursor pstack release.
The port now includes the portable upstream skills: correct, sharpen, in-your-own-words, ketchup, benchmark-checklist, create-formal-verification-skill, maintain-formal-verification-skill, and the principle leaves principle-attack-the-premise, principle-explain-the-number, and principle-test-behavior-not-implementation.
These upstream items stay follow-up work:
statechartandpoteto-help, which need a Pi adaptation of Cursor-specific mechanics.ketchup-video, which renders a video that a terminal cannot show inline.make-bot-uiand thebennyautomations, which target Cursor and Grok Bot services.- The newest upstream rewrites of
poteto-mode, its playbooks,why,how, andinterrogate. This port edits those files for Pi, so each needs a three-way merge instead of a copy.
Cursor-specific execution mechanics must be adapted to Pi before inclusion. Personal provider choices and the parent Pi default model are user configuration, not package defaults.
Development
Requirements:
- Pi 1.1.0 or newer.
- Node.js 22.19 or newer.
Run the checks:
npm install --ignore-scripts
npm run checkThe tests cover config validation, budget application, legacy migration, atomic persistence, and workspace-scoped Pi history retrieval. History tests use temporary session files for branches, fork copies, date filtering, malformed data, and workspace isolation. TypeScript checks the extension against Pi's public extension types.
Release workflow
pstack-pi uses its own package version. It does not claim full parity with the same-numbered upstream Cursor release. The routing config's version is a separate schema version.
- Update
package.json, both root version fields inpackage-lock.json, and CHANGELOG.md in a pull request. - Keep versions aligned. Before 1.0, use a minor bump for breaking changes and a patch bump for compatible fixes.
- Run
npm ci --ignore-scripts,npm run check, andnpm pack --dry-run. Verify examples and release references. - Merge the reviewed pull request. Fetch
mainwith Jujutsu and re-run checks against the merged commit. - Create an annotated
v<version>tag on that exact commit. Never move or overwrite a published release tag. - Push only that tag, then publish a GitHub release with changes, migration notes, known limits, and validation results.
- Attach the
npm packarchive and a SHA-256 checksum file. The archive is a package artifact, not an npm registry publication. - Update consumers to the published tag or its exact commit. Install the new source and verify startup with skills disabled.
Jujutsu handles change management. Use Git only to create and push annotated tags, which this workflow cannot manage through Jujutsu. Publishing a GitHub release does not run npm publish; npm registry publication requires a separate decision and credentials.
