Skip to content
Merged
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
2 changes: 0 additions & 2 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ jobs:
- name: Script tests
working-directory: plugins/pstack/skills/poteto-mode/scripts
run: bun run test
- name: Setup script tests
run: bun test plugins/pstack/skills/setup-pstack/scripts
- name: Script typecheck
working-directory: plugins/pstack/skills/poteto-mode/scripts
run: bun run typecheck
Expand Down
2 changes: 1 addition & 1 deletion .omp-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"plugins": [
{
"name": "pstack",
"description": "60 skills, 23 playbooks, 24 principles, the poteto-mode pin, and two agents. Upstream cursor/plugins pstack at c742dd6.",
"description": "59 skills, 23 playbooks, 24 principles, the poteto-mode pin, and two agents. Upstream cursor/plugins pstack at c742dd6.",
"version": "0.15.36",
"category": "development",
"license": "MIT",
Expand Down
66 changes: 38 additions & 28 deletions PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ the head a second time and getting a different answer.
### How to re-sync

The port is a build, not a maintained tree. `plugins/pstack/skills` and `plugins/pstack/agents` are
upstream text at the pinned sha with `omp-port/rules.sed` applied, then `omp-port/patches/*.patch`,
then the paths in `omp-port/owned.txt` carried across. A hand edit under either directory fails the
`reproducible` check, which rebuilds the pin and diffs it against the tree, and the next build
deletes it. Put the change in `rules.sed`, in a patch, or in an owned path instead.
upstream text at the pinned sha minus the paths in `omp-port/excluded.txt`, with
`omp-port/rules.sed` applied, then `omp-port/patches/*.patch`, then the paths in
`omp-port/owned.txt` carried across. A hand edit under either directory fails the `reproducible`
check, which rebuilds the pin and diffs it against the tree, and the next build deletes it. Put the
change in `rules.sed`, in a patch, or in an owned path instead.

The `upstream-sync` workflow does this daily. It runs `bash omp-port/check-port.sh` on `main` first,
so a red trunk stops the run rather than being merged over, then builds at the newest upstream
Expand All @@ -48,8 +49,9 @@ while any open PR still carries a `sync/upstream-` head.

A maintainer runs `bash omp-port/sync-upstream.sh [<target-sha>]` for the same build locally, target
defaulting to that newest commit, or `bash omp-port/sync-upstream.sh rebuild` to rebuild at the
current pin after editing `rules.sed`, `patches/`, or `owned.txt`. A target that never touched
`pstack/` is refused, because the next run has to find that sha again in the path-filtered history.
current pin after editing `rules.sed`, `patches/`, `owned.txt`, or `excluded.txt`. A target that
never touched `pstack/` is refused, because the next run has to find that sha again in the
path-filtered history.
Then run `bash omp-port/check-port.sh` and commit.

There is nothing to resolve. The build never edits an upstream file in place, so upstream text and
Expand Down Expand Up @@ -196,8 +198,9 @@ The marketplace install is the user shape.

That caches the plugin at `~/.omp/plugins/cache/plugins/pstack-omp___pstack___<version>` and links
it at the same `~/.omp/plugins/node_modules/pstack` path. Verified on omp 18.1.13, 2026-09-07. The
skills load, `setup-pstack` is among them and the rest hide, and both extensions register from
`package.json` `omp.extensions`, so `--poteto` injects the reminder in `-p` mode.
skills load, the ones without `disable-model-invocation` visible and the rest hidden, and both
extensions register from `package.json` `omp.extensions`, so `--poteto` injects the reminder in
`-p` mode.

The agents do not load from a marketplace root. omp scans a marketplace plugin's `agents/` directory
only through the `claude-plugins` discovery provider, which is off by default
Expand Down Expand Up @@ -235,9 +238,22 @@ installed.
the model slugs upstream names first and a catch-all under them whose hits the sync and the gate
report as `untiered` so they earn a tiered rule. `omp-port/patches/` carries the port's own code as
unified diffs applied after the rules, which is where a change no substitution can express belongs.
`omp-port/owned.txt` names three port-owned skills. `pstack-omp` is the sole live task, vibe, eval,
and runtime contract. `omp-mechanics` keeps only pstack-specific OMP deltas, and `setup-pstack`
owns model selection and config writes. Upstream syncs preserve these paths without rewriting them.
`omp-port/owned.txt` names two port-owned skills. `pstack-omp` is the sole live task, vibe, eval,
and runtime contract. `omp-mechanics` keeps only pstack-specific OMP deltas. Upstream syncs preserve
these paths without rewriting them.

`omp-port/excluded.txt` names upstream paths the port does not ship, one per line. `extract_upstream`
deletes each one right after the export, so no rule, patch, or probe sees that text, and a rule that
only an excluded file matched reads as stale. The `excluded paths` check fails when an excluded path
is in the tree or a tree file names it. Without that check, a reworded upstream line would ship a
`/skill:<name>` for a skill that is not installed.

The one entry is `skills/setup-pstack`. Upstream's version writes a Cursor rules file. pstack on omp
needs no model configuration, because an agent with no override runs on its frontmatter model and
then on the parent session's model. The Choose models section of `skills/omp-mechanics/SKILL.md`
says how to set `task.agentModelOverrides` and `modelRoles` by hand, and `rules.sed` points
poteto-help there. An upstream commit that touches only an excluded path still appears in the drift
list. Syncing it moves the pin and changes nothing in the tree.

### A patch rewrites code, never prose

Expand Down Expand Up @@ -282,25 +298,19 @@ checks are gone. What remains is residue detection: Cursor wire fields and tool
transcript layout, model slugs, retired role keys, install paths, a claimed `hub` tool, an unscoped
`omp cleanse --all` recommended as a diagnostic, and build integrity.

`skills/setup-pstack` is port-owned, so the build restores it from `HEAD` and never rebuilds it from
upstream. Upstream's version writes a Cursor rules file with no omp meaning, so the fork is
deliberate, but the drift list alone does not mark it: an upstream edit to that skill shows up as an
ordinary pstack commit with no patch conflict and no reminder to re-derive it.
The build never rebuilds an owned path from upstream. It restores the path from the worktree or
from `HEAD`. When upstream also carries that path, the fork is deliberate, but the drift list alone
does not mark it. An upstream edit shows up as an ordinary pstack commit with no patch conflict and
no reminder to re-derive the owned copy.
`bash omp-port/sync-upstream.sh check` therefore writes a `shadowed <path>:` line per owned path to
stderr, counting the upstream commits sitting ahead of the pin on that path. That count resets the
moment the pin moves past the commit, which is how ab1f702's edit to setup-pstack step 3(c) went
unread through the c742dd6 sync. So `omp-port/owned.txt` also records, per owned path, the upstream
object a maintainer last read against the owned copy, and `owned drift` fails when upstream's
object at the target differs. The failure prints both objects and the `git diff` that shows the
change. Carry what the owned copy needs, then record the new object. The two other owned paths have
no upstream counterpart and record `-`, so upstream adding one fails the same way.

`skills/setup-pstack/scripts/apply-models.mjs` makes the config write. `omp config set` stores a
selector the catalog does not have and an alias no role defines without complaint, so a bad value
used to surface only at spawn. The script checks every alias, effort, and agent value against
`omp models --json` and the merged `modelRoles` before its first write, preserves every entry pstack
does not own, and converges on a rerun. Its tests run in the gate against a stub `omp` on `PATH`,
and the `setup config contract` check asserts the skill still routes through it.
moment the pin moves past the commit. That is how ab1f702's edit to setup-pstack step 3(c) went
unread through the c742dd6 sync, while the port still owned setup-pstack. So `omp-port/owned.txt`
also records, per owned path, the upstream object a maintainer last read against the owned copy, and
`owned drift` fails when upstream's object at the target differs. The failure prints both objects
and the `git diff` that shows the change. Carry what the owned copy needs, then record the new
object. Neither owned path has an upstream counterpart, so both record `-`, and upstream adding one
fails the same way.

Known deltas the port mirrors faithfully and will not diverge on. The guide says the verification feature map lives at `references/features` while both trees write `features/`. The guide recommends a daily `/maintain-verification-skill` run while both trees state no cadence.

Expand Down
69 changes: 56 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,67 @@
# pstack-omp

An omp marketplace carrying one plugin.
pstack-omp ports Lauren Tan's [pstack](https://github.com/cursor/plugins/tree/main/pstack) from
Cursor to [oh-my-pi](https://github.com/can1357/oh-my-pi) (`omp`). It is for omp users who want
pstack's `poteto-mode` router, its playbooks for bug fixes, features, reviews, and shipping, and its
review panels. The plugin carries 59 skills, with 23 playbooks and 24 principle leaves, and two
extensions.

`pstack` is Lauren Tan's engineering methodology for Cursor, ported to oh-my-pi and made
model and provider agnostic, 60 skills with 23 playbooks and 24 principle leaves.
Wide parallel runs are a `swarm` dispatch, not a separate plugin.
## Install

```
/marketplace add negoro26/pstack-omp
/marketplace install pstack@pstack-omp
```

The marketplace install loads the skills and both extensions, `potetomode` and `pstackpolicy`. It
does not load the two agents; see the agent step in `plugins/pstack/README.md`.
Then start a task:

`PORTING.md` is the full porting record. `omp-port/check-port.sh` is the gate the port
passes before any release. Upstream is `cursor/plugins` at the commit named in the catalog.
Licensed MIT, same as upstream, with Lauren Tan's copyright retained.
```
/poteto-mode fix the flaky login test and prove it with a passing run
```

`/poteto-mode` pins the router for the session and sends the rest of the line as your first message.
It needs no configuration. Each agent runs on its frontmatter model, then on your chat model.
`/poteto-mode off` unpins the router.

The install loads the skills and both extensions, `potetomode` and `pstackpolicy`. It does not load
the two optional agents. The agent step is in [`plugins/pstack/README.md`](plugins/pstack/README.md).

## Choose models

To give an agent its own model, add an entry under `task.agentModelOverrides` in
`~/.omp/agent/config.yml`. [Choose models](plugins/pstack/skills/omp-mechanics/SKILL.md#choose-models)
in `omp-mechanics` gives the key, the value, and the optional `modelRoles` aliases. A review panel in
`arena` or `interrogate` gives a second opinion only when its agents resolve to different model
families. With no overrides, a panel agent without a frontmatter model runs on your chat model, so
check the model each panel agent resolves to before you trust a panel verdict.

## Why this port

- The plugin tree is a build, not a hand-kept copy. `omp-port/sync-upstream.sh` exports upstream
`cursor/plugins` at the commit in `omp-port/UPSTREAM`, then applies `omp-port/rules.sed` and
`omp-port/patches/`. `omp-port/check-port.sh` rebuilds that commit and fails when the tree differs.
It also fails on a rule that drops upstream text nobody reviewed and on a rule that splices its
match back into the line.
- `.github/workflows/upstream-sync.yml` syncs once a day. It runs `check-port.sh` on `main` and
against the new upstream commit, builds the sync, runs the gate again on the result, and merges the
sync pull request only when the build and the gate pass.
- The `pstackpolicy` extension refuses the forge mutations the playbooks otherwise only ask for in
prose, such as `gh pr merge`, closing or retargeting a pull request, posting a review, and a push to
`main`. It allows them only when omp starts with `PSTACK_LANDING_GRANT=1`. The full list is in
[`plugins/pstack/README.md`](plugins/pstack/README.md).
- The `potetomode` extension pins `poteto-mode` per session. `/poteto-mode on|off|status`,
`alt+shift+t`, and `omp --poteto` set the pin, and a resumed session keeps its saved state.

## Other OMP ports

[dsebban/skills](https://github.com/dsebban/skills) is Daniel Sebban's OMP port of pstack. You clone
it and list its `skills/` directory under `skills.customDirectories`. It ships skills this port does
not, such as `setup-pstack` for guided model configuration and `orchestrate-omp`. The `pstack-omp`
runtime adapter here incorporates his OMP routing work, which resolves the live task or vibe surface
without a fixed agent roster. [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) carries his license
notice.

## More

The `pstack-omp` runtime adapter incorporates Daniel Sebban's OMP routing work from
[dsebban/skills](https://github.com/dsebban/skills). It resolves the live task or vibe
surface without requiring a fixed agent roster. The original scripts remain in place.
The pin respects saved on/off state before the `--poteto` default on session resume.
`PORTING.md` records every substitution and the re-sync procedure. Licensed MIT, same as upstream,
with Lauren Tan's copyright retained.
70 changes: 21 additions & 49 deletions omp-port/check-port.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,11 @@ scan() {

scan "$PAT_SLUG" "model-agnostic" slug

model_levers=""
for f in skills/setup-pstack/SKILL.md skills/poteto-mode/SKILL.md; do
grep -Fq 'task.agentModelOverrides' "$f" || model_levers="$model_levers $f"
done
if [ -n "$model_levers" ]; then
report "omp model levers" "FAIL"
for f in $model_levers; do violate "$f must name task.agentModelOverrides"; done
else
report "omp model levers" "PASS setup-pstack and poteto-mode name task.agentModelOverrides"
fi
if grep -rIlq '/model' skills/setup-pstack/SKILL.md 2>/dev/null; then
report "chat-model lever" "PASS /model named in setup-pstack"
if grep -Fq 'task.agentModelOverrides' skills/poteto-mode/SKILL.md; then
report "omp model levers" "PASS poteto-mode names task.agentModelOverrides"
else
report "chat-model lever" "FAIL"
violate "setup-pstack must tell the user to pick the chat model with /model"
report "omp model levers" "FAIL"
violate "skills/poteto-mode/SKILL.md must name task.agentModelOverrides"
fi

# Review diversity is no longer asserted here. These three checks pinned exact sentences inside
Expand Down Expand Up @@ -289,41 +279,6 @@ runtime_contract() {
else
report "one-item role routing" "PASS every explicit single-spawn line has one-item context"
fi

bad=""
setup_skill=skills/setup-pstack/SKILL.md
setup_ref=skills/omp-mechanics/references/setup-pstack-config.md
expected_aliases=$'pstack_fast_code\npstack_instruction\npstack_judgment'
actual_aliases=$(grep -hoE '^[[:space:]]{2,}pstack_[a-z0-9_]+:' "$setup_ref" 2>/dev/null | sed 's/^[[:space:]]*//; s/:$//' | sort -u || true)
[ "$actual_aliases" = "$expected_aliases" ] || bad="unexpected pstack-owned modelRoles alias set"$'\n'
expected_alias_shapes=$' pstack_fast_code: "<detected-selector>:<effort>"\n pstack_instruction: "<detected-selector>:<effort>"\n pstack_judgment: "<detected-selector>:<effort>"'
actual_alias_shapes=$(awk '/^modelRoles:/{roles=1; next} roles && /^task:/{exit} roles && /^ pstack_/{print}' "$setup_ref" | sort || true)
[ "$actual_alias_shapes" = "$expected_alias_shapes" ] || bad="$bad"'unexpected pstack alias shape'$'\n'
grep -q '^task:$' "$setup_ref" && grep -q '^ agentModelOverrides:$' "$setup_ref" && grep -qF ' <exact discovered agent name>: "@pstack_fast_code"' "$setup_ref" || bad="$bad"'task.agentModelOverrides YAML shape is missing'$'\n'
for alias in pstack_fast_code pstack_judgment pstack_instruction; do grep -qF "$alias" "$setup_skill" || bad="$bad$alias is missing from setup-pstack"$'\n'; done
setup_keys=$(grep -nE '^[[:space:]]{4,}pstack_[a-z0-9_]+:' "$setup_skill" "$setup_ref" 2>/dev/null || true)
[ -z "$setup_keys" ] || bad="$bad$setup_keys"$'\n'
old_setup=$(grep -nE '^([[:space:]]{4,})?(feature, refactoring|arena runners|arena cross-judge pool|architect runners|swarm workers|interrogate reviewers|reflect judgment, divergent, synthesizer):|For panel roles .* value is a list' "$setup_skill" 2>/dev/null || true)
[ -z "$old_setup" ] || bad="$bad$old_setup"$'\n'
grep -Fxq 'A `task.agentModelOverrides` entry is pstack-owned only when its exact key is a discovered agent and its value is one of `@pstack_fast_code`, `@pstack_judgment`, or `@pstack_instruction`.' "$setup_skill" || bad="$bad"'per-agent setup ownership is missing'$'\n'
grep -Fxq 'Preserve every unrelated entry in both maps.' "$setup_skill" || bad="$bad"'unrelated setup ownership preservation is missing'$'\n'
grep -Fq 'capability, exact discovered agent name' "$setup_skill" || bad="$bad"'setup capability mapping is missing'$'\n'
grep -Fq 'The effort ladder is `max > xhigh > high > medium > low > minimal`.' "$setup_skill" || bad="$bad"'setup effort ladder is missing minimal or ordering'$'\n'
grep -Fq 'APPLY=$(realpath skill://setup-pstack)/scripts/apply-models.mjs' "$setup_skill" && grep -Fq -- '--dry-run' "$setup_skill" && [ -f skills/setup-pstack/scripts/apply-models.mjs ] || bad="$bad"'setup does not plan and write through scripts/apply-models.mjs'$'\n'
grep -Fq 'selected model' "$setup_skill" && grep -Fq '`thinking.efforts`' "$setup_skill" || bad="$bad"'setup does not validate supported model efforts'$'\n'
grep -Fq 'An omitted override uses the discovered agent' "$setup_skill" && grep -Fq "frontmatter model first, then the parent session's active or default model" "$setup_skill" || bad="$bad"'setup omission precedence is missing'$'\n'
grep -Fq 'Family diversity is optional' "$setup_skill" || bad="$bad"'setup family fallback is missing'$'\n'
bad_alias_choices=$(grep -nE 'offer(s|ing).*`(inherit-parent|auto)`| `(inherit-parent|auto)` always pass|omission for parent inheritance|Omit an override to inherit the parent model' "$setup_skill" 2>/dev/null || true)
[ -z "$bad_alias_choices" ] || bad="$bad$bad_alias_choices"$'\n'
grep -Fq 'exactly one supported effort suffix from `max`, `xhigh`, `high`, `medium`, `low`, and `minimal`' "$setup_ref" || bad="$bad"'setup reference suffix list is missing minimal'$'\n'
grep -Fq 'remaining base to equal a selector from `omp models --json`' "$setup_ref" || bad="$bad"'setup reference base-selector validation is missing'$'\n'
grep -Fq 'selected model' "$setup_ref" && grep -Fq '`thinking.efforts`' "$setup_ref" || bad="$bad"'setup reference does not validate model efforts'$'\n'
if [ -n "$bad" ]; then
report "setup config contract" "FAIL"
while read -r line; do [ -n "$line" ] && violate "$line"; done <<<"$bad"
else
report "setup config contract" "PASS ownership, aliases, model efforts, suffix, base selector, and the apply-models lever"
fi
}
runtime_contract

Expand Down Expand Up @@ -352,6 +307,23 @@ else
report "guide claims" "PASS ${#guide_skills[@]} skills, prototype and multi-phase-plan playbooks, check-plan.mjs, feature map, /poteto-mode command"
fi

# The build deletes every path in omp-port/excluded.txt, so one in the tree came back by hand or
# through owned.txt. A line that names one sends an agent to a skill that is not installed.
excl=""
while IFS= read -r p; do
[ ! -e "$p" ] || excl="$excl$p is in the tree"$'\n'
hits=$(grep -rInF -- "$(basename "$p")" "${SCOPE[@]}" 2>/dev/null |
awk -v p="$p" 'index($0, p "/") != 1 && index($0, p ":") != 1' || true)
[ -z "$hits" ] || excl="$excl$hits"$'\n'
done < <(excluded_paths)
if [ -n "$excl" ]; then
report "excluded paths" "FAIL"
while read -r e; do [ -n "$e" ] && violate "$e"; done <<<"$excl"
violate "omp-port/excluded.txt keeps these out of the build; delete the path, or add a rules.sed rule for the line that names it"
else
report "excluded paths" "PASS $(excluded_paths | grep -c .) path(s) absent from the tree and named nowhere in it"
fi

# rules.sed read as data rather than run, so these need no clone and run before the contracts-only
# exit, where the fixture mutations reach them. Each is a shape that once shipped with every other
# check green.
Expand Down
Loading
Loading