Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ The prompt and `-y` update only when a bootstrap invocation selects the default

The installer does not update the nested pstack clone or installed package versions. Use `update-pstack` for that independent update. After a source update, the installer installs each newly required package that is absent.

A second run with the same inputs leaves all owned file bytes unchanged. It removes stale files only from the installed Jig and pstack updater resource directories. It never writes `auth.json`, `models-store.json`, `private/`, or `sessions/`.
A second run with the same inputs leaves all owned file bytes unchanged. It removes stale files from installed skills and the installed Jig and pstack updater resource directories. It never writes `auth.json`, `models-store.json`, `private/`, or `sessions/`.

To use existing source trees, run:

Expand Down Expand Up @@ -84,6 +84,16 @@ The command fast-forwards only the independent pstack Git checkout. It then reru

`/skill:update-pstack` runs the same procedure without the `/update-pstack` prompt alias.

## Pi-specific design skills

Every install applies `overlay/skills` while it conforms pstack into `$HOME/.pi/agent/skills-pstack`. `update-pstack` reapplies the same overlays through `install.sh`. The source pstack tree stays unchanged.

Architect uses one model to sketch, an optional parent pick, then implementation. Exhaust the Design Space permits sequential or parent-inline sketches without a candidate quota. Poteto-mode no longer routes design or parallel work to arena or swarm. Same-model parallel work remains available for disjoint workstreams. The Pi adapter's model policy takes precedence over model defaults in other imported skills and playbooks.

A replacement `SKILL.md` selects the overlay directory as the skill source. Conformance removes old supporting-file symlinks instead of retaining upstream runner prompts. A `patch.json` contains exact-match `old` and `new` text blocks and preserves the skill's other files. If an upstream edit removes or duplicates a patch target, installation fails with `overlay drift` rather than silently keeping the old routing. Review and update the patch before retrying.

The sticky prompt, `/poteto`, and poteto-agent read the installed poteto-mode copy. Existing sessions need to reload their instructions to use the new routes.

## Required packages

Fresh installs get these packages. Refresh removes retired npm package registrations and uninstalls their copies from Pi's managed npm directory. It preserves unrelated packages and backs up settings before removing registrations. `PI_STACK_SKIP_PACKAGES=1` skips physical package operations until the next normal install.
Expand Down Expand Up @@ -160,6 +170,7 @@ bash -n bin/jig.sh install.sh scripts/check-jig.sh
python3 -m unittest discover -s scripts/jig_tests -p 'test_*.py'
bash scripts/check-overlay.sh
bash scripts/check-conform-skills.sh
python3 scripts/test_skill_overlays.py
bash scripts/check-update-pstack.sh
bash scripts/check-subagents.sh
bash scripts/check-jig.sh
Expand Down
23 changes: 18 additions & 5 deletions bin/conform-skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import annotations

import argparse
import json
import os
import re
import sys
Expand Down Expand Up @@ -159,18 +160,29 @@ def link_or_replace(src: Path, dest: Path) -> None:
dest.symlink_to(target)


def conform_one(src: Path, out_root: Path, verbose: bool) -> Path:
def conform_one(src: Path, out_root: Path, verbose: bool, overlays: Path | None = None) -> Path:
src = src.resolve()
original = src
overlay = overlays / src.name if overlays else None
if overlay and (overlay / "SKILL.md").is_file():
src = overlay.resolve()
skill_md = src / "SKILL.md"
if not skill_md.is_file():
raise SystemExit(f"no SKILL.md in {src}")
dest_dir = (out_root / src.name).resolve()
if dest_dir == src:
raise SystemExit(f"refusing in-place rewrite of {src}. Set --out to a different directory")
dest_dir.mkdir(parents=True, exist_ok=True)
if dest_dir in (src, original):
raise SystemExit(f"refusing in-place rewrite of {dest_dir}. Set --out to a different directory")
text = skill_md.read_text(encoding="utf-8")
if overlay and (overlay / "patch.json").is_file():
for patch in json.loads((overlay / "patch.json").read_text(encoding="utf-8")):
if not patch["old"] or text.count(patch["old"]) != 1:
raise SystemExit(f"overlay drift in {skill_md}: expected one match for {patch['old']!r}")
text = text.replace(patch["old"], patch["new"], 1)
new_text, name, changed = rewrite_skill_text(text, src.name)
dest_dir.mkdir(parents=True, exist_ok=True)
dest_md = dest_dir / "SKILL.md"
if dest_md.is_symlink():
dest_md.unlink()
if not dest_md.exists() or dest_md.read_text(encoding="utf-8") != new_text:
dest_md.write_text(new_text, encoding="utf-8")
if verbose and changed:
Expand All @@ -195,6 +207,7 @@ def main(argv: list[str] | None = None) -> int:
)
parser.add_argument("--out", required=True, type=Path, help="directory that will hold one child per skill")
parser.add_argument("--tree", type=Path, help="walk this directory for SKILL.md (Pi discovery rules)")
parser.add_argument("--overlays", type=Path, help="skill directories with replacement SKILL.md or exact-match patch.json")
parser.add_argument("skills", nargs="*", type=Path, help="skill directories that contain SKILL.md")
parser.add_argument("-v", "--verbose", action="store_true")
args = parser.parse_args(argv)
Expand All @@ -212,7 +225,7 @@ def main(argv: list[str] | None = None) -> int:
if src in seen:
continue
seen.add(src)
conform_one(src, out, args.verbose)
conform_one(src, out, args.verbose, args.overlays)
return 0


Expand Down
8 changes: 5 additions & 3 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pstack_skill_names=(
how
why
architect
principle-exhaust-the-design-space
interrogate
tdd
unslop
Expand All @@ -27,6 +28,7 @@ Disables builtin agents, the subagent intercom bridge, and intercom notification
Existing children keep their prompts until respawn.
Dated backups go to $HOME/.pi/agent/backups/subagents/.
Rewrites Cursor skill names into $HOME/.pi/agent/skills-pstack. Does not edit pstack.
Applies pi-stack's single-model skill overlays on every install and refresh.
Copies the Jig launcher, controller, skill, and references into $HOME/.pi/agent/jig/.
Copies the pstack updater command and controller into $HOME/.pi/agent/update-pstack/.
Merges defaultTools, skills, and packages into settings.json without changing project trust.
Expand Down Expand Up @@ -303,14 +305,14 @@ PY

install_md() {
local src="$1" dest="$2"
PSTACK="$pstack" python3 - "$src" "$dest" <<'PY'
PSTACK="$pstack" SKILLS_PSTACK="$agent/skills-pstack" python3 - "$src" "$dest" <<'PY'
import os
import sys
from pathlib import Path

src = Path(sys.argv[1])
dest = Path(sys.argv[2])
text = src.read_text().replace("__PSTACK__", os.environ["PSTACK"])
text = src.read_text().replace("__PSTACK__", os.environ["PSTACK"]).replace("__SKILLS_PSTACK__", os.environ["SKILLS_PSTACK"])
if dest.exists() and dest.read_text() == text:
sys.exit(0)
dest.parent.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -363,7 +365,7 @@ for name in cross-repo update-pstack; do
done
conform_src+=("$installed_jig/skills/jig")
if [[ ${#conform_src[@]} -gt 0 ]]; then
python3 "$here/bin/conform-skills.py" --out "$conform_out" "${conform_src[@]}"
python3 "$here/bin/conform-skills.py" --out "$conform_out" --overlays "$overlay/skills" "${conform_src[@]}"
fi

export PI_AGENT_DIR="$agent"
Expand Down
4 changes: 3 additions & 1 deletion overlay/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ The child reads poteto-mode in full and decides reversible details without super

Leave `async` on. That is the default. `async:false` only when this turn cannot continue without the child. Do not sleep-poll. Use blocking `subagent_wait` only when this turn must consume the result.

Do not pin child models to `cursor/*` unless that provider is authenticated. A missing pattern warns and the child waits on a model that never comes. Use `inherit` or a listed `provider/id`. Call `{ action: "models" }` before an explicit model.
Use the parent's model for every child. Do not fan out across model types or select models by role. This policy overrides model defaults in imported skills and playbooks. Same-model parallel work is allowed for disjoint workstreams with separate ownership. Use parent-inline or sequential sketches for design alternatives, then let the parent pick if needed. No runner competition or judge is required.

Read architect, poteto-mode, and principle-exhaust-the-design-space from `__SKILLS_PSTACK__`, not raw pstack. Installation and refresh reapply these Pi-specific overlays. Other imported skills remain upstream copies subject to this adapter's model policy.

`TodoWrite` is `TODO.md` in the working tree. Do not register a todo tool.

Expand Down
2 changes: 1 addition & 1 deletion overlay/APPEND_SYSTEM.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Sticky process

Non-trivial work. Read `__PSTACK__/skills/poteto-mode/SKILL.md` in full, including the Principles index, before you act. Trivial one-liners skip that read.
Non-trivial work. Read `__SKILLS_PSTACK__/poteto-mode/SKILL.md` in full, including the Principles index, before you act. Trivial one-liners skip that read.
After pstack's built-in Principles, read every trusted project `.cursor/skills/principle-*/SKILL.md` in full when present.

Also read `~/.pi/agent/AGENTS.md`. Read `TODO.md` and `PLAN.md` in the working tree when they exist.
Expand Down
49 changes: 49 additions & 0 deletions overlay/skills/architect/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
name: architect
description: Sketch types, signatures, and module boundaries with one model before implementation. Use for /architect, design requests, or changes that need a new code shape.
disable-model-invocation: true
---

# Architect

Use one model to sketch, then implement. Work parent-inline by default. Do not launch competing runners or delegate the choice to a judge.

Track these steps in `TODO.md`:

1. Ground the problem.
2. Sketch the design.
3. Let the parent pick if needed.
4. Implement and verify.
5. Revisit the sketch if evidence contradicts it.

## Ground the problem

Trace the affected callers, data, ownership, and constraints in the existing code. Identify the behavior that must stay unchanged. Skip this step only for greenfield work with no surrounding system.

## Sketch the design

Write the caller's usage first. Derive the types, signatures, module boundaries, and ownership from that usage. Use pseudocode or `not implemented` bodies where logic would obscure the shape.

Start with one sketch. If a concrete uncertainty remains, explore another sketch sequentially or parent-inline with the same model. There is no candidate quota or required second design.

Check the sketch for information leakage, shallow wrappers, order-dependent APIs, and unnecessary shared state. Prefer the smallest interface that hides the required complexity.

## Let the parent pick if needed

Proceed with the sketch when it satisfies the constraints. If alternatives remain, the parent compares their evidence and picks one. Do not add a judge or cross-judging phase.

Pause for human sign-off only when explicitly requested or when authorization or a genuine product decision is missing. A requested checkpoint shows the sketch before implementation.

## Implement and verify

Fill in the chosen sketch. Run checks against the requested behavior. Report deviations and the evidence that required them.

Same-model parallel work is allowed for disjoint implementation workstreams. Give each writer separate ownership. Follow the Pi adapter's model policy.

## Revisit the sketch

If repeated workarounds contradict the ownership or types, trace the new evidence and replace the wrong sketch. Return to a single-model sketch, not a competition.

## Output

For a small change, keep the usage, types, and signatures in one sketch. For a larger change, include a module map. Record the constraints, chosen design, unresolved questions, and verification plan. Explain rejected alternatives only when you actually explored them.
30 changes: 30 additions & 0 deletions overlay/skills/poteto-mode/patch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"old": "- Code crossing a function boundary → the **architect** skill, parallel design exploration before implementing.",
"new": "- Code crossing a function boundary → the installed **architect** skill. Sketch with one model, let the parent pick if needed, then implement."
},
{
"old": "- Parallel fan-out → the **swarm** skill for coverage matrices, races, gauntlets, and exploration partitions. Use **arena** for design or code bakeoffs with base selection and grafting.",
"new": "- Parallel work is optional. Use same-model children only for disjoint workstreams with separate ownership. Do not fan out across model types."
},
{
"old": "- Contested design → the **interrogate** skill (multi-model adversarial) before shipping.",
"new": "- Contested design → examine the disputed constraint and evidence parent-inline with the same model. The parent decides whether another sequential sketch is useful."
},
{
"old": "- **Exhaust the Design Space** (**principle-exhaust-the-design-space**). A novel interaction or architectural decision with no precedent. Build 2-3 competing prototypes and compare before committing.",
"new": "- **Exhaust the Design Space** (**principle-exhaust-the-design-space**). A novel interaction or architectural decision with unresolved tradeoffs. Read the installed leaf skill. Sequential or parent-inline sketches with one model are sufficient. No candidate quota or cross-model fanout."
},
{
"old": "**Use `subagent_type: \"poteto-agent\"` for any subagent you spawn inside a playbook step** (code-writing delegates, ad-hoc helpers). `/poteto-mode` and `poteto-agent` route through the same wrapper. Routed workflow skills (`how`, `why`, `interrogate`, `reflect`, `swarm`) set their own `subagent_type` for diverse-model review. Respect what the skill prescribes, don't override to `poteto-agent`.",
"new": "Use the Pi `subagent` tool with `agent: \"poteto-agent\"` only when delegation earns its cost. Work parent-inline otherwise. The Pi adapter's single-model policy overrides runner and role defaults in every routed skill and playbook. Do not use a routed skill to introduce cross-model fanout."
},
{
"old": "**Defaults for every `Task` call.** `run_in_background: true`, agent mode (readonly strips MCP), file pointers not inlined context, explicit model per role (configurable via `/setup-pstack`. Defaults `grok-4.6-fast-xhigh` for code, `claude-fable-5-1-thinking-max` for prose and judgment). Code delegates tier by difficulty. The hardest changes (cross-cutting design, gnarly concurrency, subtle algorithms) go to your strongest judgment model (`claude-fable-5-1-thinking-max`), whether the task needs judgment on vague intent or is a precisely specified sequence of steps to execute to the letter. Trivial mechanical edits go to your fast code model. Per-role lines in the `/setup-pstack` rule override these defaults and the model choices in the routed skills (`how`, `why`, `arena`, `swarm`, `architect`, `interrogate`, `reflect`). A role with no line keeps its default, and a role line of `inherit-parent` or `auto` runs that role on the parent chat model (omit Task `model`).",
"new": "Keep delegation asynchronous and pass file pointers instead of bulk context. Use the parent's model for every child. Do not select models by role, difficulty, or a model-diversity requirement. Same-model parallel children may handle disjoint workstreams. Use one workflow with `await runs.all` for that work, not for competing design candidates."
},
{
"old": "A second opinion is the same prompt against a different model. Agreement is high-signal.",
"new": "A second opinion is optional and uses the same model. The parent owns the final choice."
}
]
15 changes: 15 additions & 0 deletions overlay/skills/principle-exhaust-the-design-space/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
name: principle-exhaust-the-design-space
description: Explore concrete alternatives when a novel interaction or architectural choice has unresolved tradeoffs. Sequential or parent-inline sketches are sufficient.
disable-model-invocation: true
---

# Exhaust the Design Space

Start with the simplest sketch that meets the constraints. Explore another concrete alternative only when it can resolve a named uncertainty. Compare the evidence before implementing.

Sequential or parent-inline sketches with one model are sufficient. There is no prototype quota, mandatory second design, or cross-model fanout. The parent can pick among alternatives without a judge.

Use this principle for novel interactions or architectural choices with unresolved tradeoffs. Skip extra sketches when an established pattern or the constraints already determine the shape.

Same-model parallel work is allowed for disjoint workstreams. Parallelism is not a requirement for design exploration.
2 changes: 1 addition & 1 deletion prompts/poteto.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
description: Load poteto-mode process for this turn
---
Read `__PSTACK__/skills/poteto-mode/SKILL.md` in full, including the Principles index. Then do the user request in that style. Trivial one-liners skip the full read. Also read `~/.pi/agent/AGENTS.md`.
Read `__SKILLS_PSTACK__/poteto-mode/SKILL.md` in full, including the Principles index. Then do the user request in that style. Trivial one-liners skip the full read. Also read `~/.pi/agent/AGENTS.md`.
4 changes: 2 additions & 2 deletions scripts/check-overlay.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ description: stub for $name install test
# stub
EOF
done < <(bash "$root/install.sh" --print-pstack-skills)
sed -i 's/^name: poteto-mode$/name: Poteto Mode/' "$tmp/pstack/skills/poteto-mode/SKILL.md"
cp "$root/scripts/fixtures/poteto-mode/SKILL.md" "$tmp/pstack/skills/poteto-mode/SKILL.md"
mkdir -p "$tmp/pstack/skills/poteto-mode/playbooks"
printf 'playbook\n' >"$tmp/pstack/skills/poteto-mode/playbooks/investigation.md"
stub="$tmp/pstack"
Expand Down Expand Up @@ -371,7 +371,7 @@ description: stub for $name clone test
# stub
EOF
done < <(bash "$root/install.sh" --print-pstack-skills)
sed -i 's/^name: poteto-mode$/name: Poteto Mode/' "$fake/pstack/skills/poteto-mode/SKILL.md"
cp "$root/scripts/fixtures/poteto-mode/SKILL.md" "$fake/pstack/skills/poteto-mode/SKILL.md"
git init -q "$fake"
git -C "$fake" add pstack
git -C "$fake" -c user.email=t@t -c user.name=t commit -qm stub
Expand Down
Loading