pi-pstack-adapter 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +9 -5
- package/VERSION +1 -1
- package/adapter-skills/setup.md +12 -8
- package/docs/compatibility.md +79 -16
- package/package.json +2 -1
- package/src/certified.ts +3 -3
- package/src/generate.ts +24 -7
- package/src/registry.ts +10 -1
- package/src/status.ts +7 -1
- package/src/upstream.ts +31 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here. release-please writes new entries from Conventional Commit PR titles.
|
|
4
4
|
|
|
5
|
+
## [0.7.0](https://github.com/elderfo/pi-pstack-adapter/compare/v0.6.0...v0.7.0) (2026-10-19)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **pstack:** certify pstack 0.15.26 with create-formal-verification-skill ([#17](https://github.com/elderfo/pi-pstack-adapter/issues/17)) ([82367d9](https://github.com/elderfo/pi-pstack-adapter/commit/82367d93e372fc49c603f56e79469197a77814a2))
|
|
11
|
+
* **pstack:** certify pstack 0.15.28 with maintain-formal-verification-skill ([#20](https://github.com/elderfo/pi-pstack-adapter/issues/20)) ([0dee303](https://github.com/elderfo/pi-pstack-adapter/commit/0dee303dbd349dfd9726ff6e3c17a8d96ecf95a2))
|
|
12
|
+
* **roles:** add a general role and cap delegate thinking at the configured level ([#19](https://github.com/elderfo/pi-pstack-adapter/issues/19)) ([df1db04](https://github.com/elderfo/pi-pstack-adapter/commit/df1db045c49907665b7a2ef504b3ab52da287502))
|
|
13
|
+
|
|
5
14
|
## [0.6.0](https://github.com/elderfo/pi-pstack-adapter/compare/v0.5.0...v0.6.0) (2026-10-17)
|
|
6
15
|
|
|
7
16
|
|
package/README.md
CHANGED
|
@@ -135,8 +135,10 @@ skill.
|
|
|
135
135
|
|
|
136
136
|
## Choose models for pstack roles
|
|
137
137
|
|
|
138
|
-
pstack assigns work to named model roles such as `feature`, `bug-fix`, and `judgment`.
|
|
139
|
-
|
|
138
|
+
pstack assigns work to named model roles such as `feature`, `bug-fix`, and `judgment`. The
|
|
139
|
+
`general` role covers every other delegate, such as explorers, swarm workers, and ad-hoc helpers.
|
|
140
|
+
A role you leave unset uses `general`, and uses the model of the agent that starts it only when
|
|
141
|
+
`general` is unset too.
|
|
140
142
|
|
|
141
143
|
To list the models in your Pi installation and assign them to roles, run the setup skill:
|
|
142
144
|
|
|
@@ -159,9 +161,11 @@ PISTACK_MODELS='feature=provider/model:medium,judgment=provider/other-model:high
|
|
|
159
161
|
|
|
160
162
|
Role names are lowercase letters, digits, and hyphens. A model value is printable ASCII with no spaces or markup characters, at most 128 characters. A config entry that breaks these rules stops the adapter at startup with an error that names the file, so fix the key there and restart Pi.
|
|
161
163
|
|
|
162
|
-
A role without a level passes none, so the delegation backend applies its own default. A role
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
A role without a level passes none, so the delegation backend applies its own default. A role's
|
|
165
|
+
level is a ceiling: a delegate may lower it for routine work but never raises it. A role you leave
|
|
166
|
+
unset, with `general` unset too, inherits the model and thinking level of the agent that starts
|
|
167
|
+
it. A `strongest-judgment` delegate at `xhigh` would then start every helper at `xhigh`, so set
|
|
168
|
+
`general` to keep helpers at a cheaper level. The `pstack_adapter` tool's `set_model` action takes the same
|
|
165
169
|
level in its `thinking` parameter. A level the model does not offer is clamped by Pi at run time.
|
|
166
170
|
|
|
167
171
|
## Run Pi noninteractively
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.7.0
|
package/adapter-skills/setup.md
CHANGED
|
@@ -9,8 +9,9 @@ Three things are configurable. Model roles, the upstream source, and the delegat
|
|
|
9
9
|
## Model roles
|
|
10
10
|
|
|
11
11
|
pstack skills name a model per role. In Pi those names mean nothing, so each role maps to a
|
|
12
|
-
model in your own catalogue. A role you never configure
|
|
13
|
-
|
|
12
|
+
model in your own catalogue. A role you never configure uses the `general` role. When `general` is
|
|
13
|
+
unconfigured too, the role inherits the model of whichever agent starts it, which is a working
|
|
14
|
+
default, not a failure.
|
|
14
15
|
|
|
15
16
|
Roles used by pstack workflows.
|
|
16
17
|
|
|
@@ -23,6 +24,7 @@ Roles used by pstack workflows.
|
|
|
23
24
|
| `hillclimb` | sustained metric improvement |
|
|
24
25
|
| `judgment` | prose, review, and judgment delegates |
|
|
25
26
|
| `strongest-judgment` | the hardest cross-cutting changes |
|
|
27
|
+
| `general` | every other delegate: explorers, investigators, swarm and verification workers, ad-hoc helpers, and any unconfigured role |
|
|
26
28
|
|
|
27
29
|
The status report lists the same roles, so read it back rather than trusting this table if the
|
|
28
30
|
adapter has been upgraded.
|
|
@@ -30,9 +32,11 @@ adapter has been upgraded.
|
|
|
30
32
|
Each role also has a thinking level: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or
|
|
31
33
|
`max`. Upstream pstack calls this a reasoning budget and encodes it in the model slug. A role with
|
|
32
34
|
no thinking level passes none, so the delegation backend applies its own default. A role left on
|
|
33
|
-
`inherit` uses the
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
`inherit` with `general` also on `inherit` uses the model and thinking level of the agent that
|
|
36
|
+
starts it. A `strongest-judgment` delegate at `xhigh` would then start every helper at `xhigh`, so
|
|
37
|
+
configure `general` to stop that. A role's level is a ceiling: delegates may lower it for routine
|
|
38
|
+
work, but never raise it. Model choice often affects cost more than thinking level does, so
|
|
39
|
+
consider a cheaper model for the bulk roles as well.
|
|
36
40
|
|
|
37
41
|
Steps.
|
|
38
42
|
|
|
@@ -45,10 +49,10 @@ Steps.
|
|
|
45
49
|
role is on `inherit`. It applies to every role that got a model in step 2. A role on
|
|
46
50
|
`inherit` cannot have a level, so tell the user that and say that those roles follow the
|
|
47
51
|
parent session's level. Ask one single-select question first. Put the whole choice in each option
|
|
48
|
-
title so the user can pick without opening anything else, for example "Balanced: code roles
|
|
52
|
+
title so the user can pick without opening anything else, for example "Balanced: code and general roles
|
|
49
53
|
medium, judgment high, strongest-judgment xhigh (suggested)".
|
|
50
54
|
|
|
51
|
-
| choice | code roles (`feature`, `refactoring`, `bug-fix`, `perf-issue`, `hillclimb`) | `judgment` | `strongest-judgment` |
|
|
55
|
+
| choice | code and general roles (`feature`, `refactoring`, `bug-fix`, `perf-issue`, `hillclimb`, `general`) | `judgment` | `strongest-judgment` |
|
|
52
56
|
| --- | --- | --- | --- |
|
|
53
57
|
| Balanced (suggest this first) | `medium` | `high` | `xhigh` |
|
|
54
58
|
| Low cost | `low` | `medium` | `high` |
|
|
@@ -58,7 +62,7 @@ Steps.
|
|
|
58
62
|
|
|
59
63
|
These are starting points, not measurements. They keep the heaviest reasoning on the roles
|
|
60
64
|
that make the hardest calls. Offer Custom for a user who wants different levels. Ask once for
|
|
61
|
-
the code roles, once for `judgment`, and once for `strongest-judgment`. Ask a role on its own
|
|
65
|
+
the code and general roles, once for `judgment`, and once for `strongest-judgment`. Ask a role on its own
|
|
62
66
|
only when the user says it should differ from its group. Backend default passes no level and
|
|
63
67
|
leaves the choice to the delegation backend. A Custom user who wants thinking disabled picks
|
|
64
68
|
`off`.
|
package/docs/compatibility.md
CHANGED
|
@@ -4,7 +4,7 @@ The compatibility registry in `src/registry.ts` assigns one support tier to ever
|
|
|
4
4
|
certified pstack revision. Generation reads it, the wrapper frontmatter publishes it, and
|
|
5
5
|
`/pistack-status` reports it.
|
|
6
6
|
|
|
7
|
-
Certified revision: pstack `0.15.
|
|
7
|
+
Certified revision: pstack `0.15.28` at `d457567332aa6989e753ea289f30ea2bfbc80bc0` in
|
|
8
8
|
`https://github.com/cursor/plugins`.
|
|
9
9
|
|
|
10
10
|
## Platforms
|
|
@@ -21,17 +21,76 @@ executables in the shell the skills run in, with Pi's `shellCommandPrefix` appli
|
|
|
21
21
|
that matters most, because Git Bash adds `/usr/bin` and `/mingw64/bin` to the `PATH` Pi
|
|
22
22
|
started with. A shell that is missing, inactive, or cannot run a command blocks every workflow.
|
|
23
23
|
|
|
24
|
-
Windows evidence was last gathered at pstack `0.15.
|
|
25
|
-
Node 22, Bun 1.3, and Pi 1.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
byte.
|
|
34
|
-
- `
|
|
24
|
+
Windows evidence was last gathered at pstack `0.15.28`, on Windows 11 Pro with Git for Windows
|
|
25
|
+
2.53.0, Node 22.23.2, Bun 1.3.14, and Pi 1.3.0:
|
|
26
|
+
|
|
27
|
+
- The type check and unit suite pass: 183 tests, 179 passed, none failed, and the four symlink
|
|
28
|
+
tests skipped. All eight integration tests pass with no skips, including the Windows 8.3
|
|
29
|
+
short-path case and both formal-verification wrapper regressions.
|
|
30
|
+
- The isolated install passes from Git Bash with the same CLIProxyAPI scratch copy as below: 57
|
|
31
|
+
skills, no `subagent` or `ask_user` with the provider extension loaded, and cache reuse without
|
|
32
|
+
bootstrap permission. The installed checkout's 172 files match an LF export of the pinned tree
|
|
33
|
+
byte for byte. The probe reports 57 skills, two agents, no diagnostics, 55 bodies
|
|
34
|
+
byte-identical, `setup-pstack` replaced, and none mismatched.
|
|
35
|
+
- The installed status report shows `tested` trust at the certified commit, and
|
|
36
|
+
`/pistack-check pistack-maintain-formal-verification-skill` clears the workflow. `git` is
|
|
37
|
+
its only executable, and Git for Windows bundles it.
|
|
38
|
+
- `maintain-formal-verification-skill` ran in the isolated install against a fixture whose
|
|
39
|
+
`.pi/skills/` verify skill recorded a sync commit, followed by a rename of a covered function
|
|
40
|
+
and a new batch function that wrote the guarded state without its duplicate-ID guard. It
|
|
41
|
+
found the skill, ran the stale check (it crashed on the rename), read all three commits since
|
|
42
|
+
the sync commit, renamed the call, extended the harness to the new function, and reported the
|
|
43
|
+
bug with the commit that introduced it and a two-step counterexample. It proved the updated
|
|
44
|
+
check could pass and fail by temporarily fixing and then breaking the product file, restored
|
|
45
|
+
it, moved the sync commit, and committed only the verification and verify-skill changes.
|
|
46
|
+
This supports `dependency-gated` on `git`.
|
|
47
|
+
- The changed `create-formal-verification-skill` reran in the isolated install. It generated
|
|
48
|
+
`.pi/skills/verify-inventory-ledger/SKILL.md` with the coverage record and sync commit new in
|
|
49
|
+
`0.15.28`, plus a dependency-free Node verifier over the real functions (28 states, 168 calls).
|
|
50
|
+
The check caught six deliberate breaks, and the product file's content was unchanged after
|
|
51
|
+
each restore. `adapted` still holds.
|
|
52
|
+
|
|
53
|
+
Earlier Windows evidence, gathered at pstack `0.15.26` with the same toolchain:
|
|
54
|
+
|
|
55
|
+
- The type check and unit suite pass: 173 tests passed, none failed, and four symlink tests
|
|
56
|
+
skipped because Windows requires Developer Mode or administrator rights. The seven scheduled
|
|
57
|
+
integration tests pass with no skips: six cache-containment cases, including a Windows 8.3
|
|
58
|
+
short-path case, and a formal-verification wrapper regression.
|
|
59
|
+
Run these separately with `npm run test:integration`; their CI lane is daily and manually
|
|
60
|
+
dispatchable, not per PR. The existing unit-suite CI matrix still covers all three platforms.
|
|
61
|
+
- The isolated install passes from Git Bash: the cold run registers no skills without bootstrap
|
|
62
|
+
permission, the permitted run registers all 56 skills, and a later run reuses the cache
|
|
63
|
+
without bootstrap permission. The local CLIProxyAPI provider requires its extension, config,
|
|
64
|
+
and model catalog in the sandbox; a scratch copy of the install script supplies them. Its
|
|
65
|
+
tool probe still reports no `subagent` or `ask_user`. Sandbox credentials are removed on exit.
|
|
66
|
+
- The certification probe passes on an LF export of the pinned tree: 56 skills, two agents,
|
|
67
|
+
no diagnostics, 54 bodies byte-identical, `setup-pstack` replaced, and none mismatched.
|
|
68
|
+
All 171 files in the isolated installed cache match the LF export byte for byte.
|
|
69
|
+
- `create-formal-verification-skill` completed in the isolated install without `subagent` or
|
|
70
|
+
`ask_user`. It generated `.pi/skills/verify-inventory-reservations/SKILL.md` and a dependency-free
|
|
71
|
+
Node 22 explicit-state verifier that imports the fixture's actual reserve/release functions.
|
|
72
|
+
It checked 49 reachable states and 490 transitions for stock 0–3 and five request IDs, caught
|
|
73
|
+
six mutant copies, then caught a removed retry guard in the actual product file. Restoring
|
|
74
|
+
that file restored the passing result and its original hash. This supports `adapted`: the
|
|
75
|
+
generated skill path is mapped to Pi. It does not certify TLA+, proof assistants, installation
|
|
76
|
+
of other verifiers, unbounded inputs, or real database/async concurrency.
|
|
77
|
+
- The revised `ketchup` ran read-only in a separate fixture. It listed both supplied product
|
|
78
|
+
choices (CSV versus JSON and seven versus thirty days of history), drew a Mermaid sequence
|
|
79
|
+
diagram for a late-retry edge case it reproduced, and left the fixture unchanged. It did not
|
|
80
|
+
contact GitHub for this fixture-only task.
|
|
81
|
+
- Installed status reports show native Windows plugin and generated-skill paths and `tested`
|
|
82
|
+
trust at the certified commit. `/pistack-check` clears the formal-verification workflow and
|
|
83
|
+
`ketchup`, but still names `uptime` as missing for `benchmark-checklist`. Delegation and
|
|
84
|
+
structured questions remain unavailable in the isolated install; no delegating tier changed.
|
|
85
|
+
- A cache inside a Git worktree must own its worktree, Git directory, and common directory before
|
|
86
|
+
bootstrap or reuse can change Git state. The Windows run exposed ancestor discovery from an
|
|
87
|
+
empty nested cache; this is fixed. Regressions also cover incomplete metadata, a foreign Git
|
|
88
|
+
file, foreign worktree/common directory settings, and short paths, while checking that the
|
|
89
|
+
enclosing repository's HEAD, config, index, and tracked bytes stay unchanged.
|
|
90
|
+
|
|
91
|
+
Historical coverage below has not been rerun at `0.15.26` unless stated otherwise:
|
|
92
|
+
|
|
93
|
+
- At `0.15.23`, `in-your-own-words`, `ketchup`, and `poteto-help` ran end to end in the isolated install, with
|
|
35
94
|
no `subagent` or `ask_user` tool. `ketchup` reported a commit and a pending decision with
|
|
36
95
|
Mermaid diagrams and changed nothing. `poteto-help` answered with `/pistack-mode on` and read
|
|
37
96
|
the guide from the `plugin directory` row, whose Windows path it resolved.
|
|
@@ -123,14 +182,15 @@ Lowering a tier needs one reproduction of the failure and a note saying what bro
|
|
|
123
182
|
| `typescript-best-practices` | `/skill:pistack-typescript-best-practices` | - | - | - |
|
|
124
183
|
| `unslop` | `/skill:pistack-unslop` | - | - | - |
|
|
125
184
|
|
|
126
|
-
### adapted (
|
|
185
|
+
### adapted (3)
|
|
127
186
|
|
|
128
187
|
| upstream skill | Pi skill | capabilities | executables | why |
|
|
129
188
|
| --- | --- | --- | --- | --- |
|
|
189
|
+
| `create-formal-verification-skill` | `/skill:pistack-create-formal-verification-skill` | - | - | Manual invocation only; no delegation. It installs or runs the verification tool it chooses and temporarily breaks product code to prove the check. Generated skills belong under `.pi/skills/` in Pi. Verified on a Windows Node finite-state fixture; other toolchains need their own prerequisites. |
|
|
130
190
|
| `create-verification-skill` | `/skill:pistack-create-verification-skill` | - | - | Only the generated skill's output path needs remapping to Pi's skill directory. |
|
|
131
191
|
| `ketchup` | `/skill:pistack-ketchup` | - | - | Read-only. Pi has no `/visualize` skill or image generation, so its visuals are Mermaid diagrams and screenshots that already exist. It checks PR and CI state only for work the chat touched. |
|
|
132
192
|
|
|
133
|
-
### dependency-gated (
|
|
193
|
+
### dependency-gated (14)
|
|
134
194
|
|
|
135
195
|
| upstream skill | Pi skill | capabilities | executables | why |
|
|
136
196
|
| --- | --- | --- | --- | --- |
|
|
@@ -142,6 +202,7 @@ Lowering a tier needs one reproduction of the failure and a note saying what bro
|
|
|
142
202
|
| `figure-it-out` | `/skill:pistack-figure-it-out` | `delegation` | - | Phase B fan-out and the architect/arena routing require parallel subagents. |
|
|
143
203
|
| `how` | `/skill:pistack-how` | `delegation` | - | Every path spawns explorer or explainer subagents; nothing runs inline. |
|
|
144
204
|
| `interrogate` | `/skill:pistack-interrogate` | `delegation` | `git` | Multi-model adversarial review is entirely one subagent per configured model. |
|
|
205
|
+
| `maintain-formal-verification-skill` | `/skill:pistack-maintain-formal-verification-skill` | - | `git` | Verified end to end on a Windows Node finite-state fixture at pstack 0.15.28. Manual invocation only; no delegation. It diffs the covered code with git from the verify skill's sync commit, edits only the verification and the verify skill, and temporarily breaks product code to prove an updated check can still fail. In Pi, look for the verify skill under `.pi/skills/`; the verification tool needs its own prerequisites. |
|
|
145
206
|
| `maintain-verification-skill` | `/skill:pistack-maintain-verification-skill` | `delegation` | - | Source wave launches one read-only subagent per feature file. |
|
|
146
207
|
| `no-comments` | `/skill:pistack-no-comments` | `delegation` | `git` | Step 1 must spawn the comment-sicko agent; the skill only triages its report. |
|
|
147
208
|
| `show-me-your-work` | `/skill:pistack-show-me-your-work` | `delegation` | - | The mandatory cross-model review of the trail requires spawning a different-family subagent. |
|
|
@@ -206,8 +267,10 @@ through `delegate_task`. That run is the evidence the tier rules above require.
|
|
|
206
267
|
|
|
207
268
|
## Skills the adapter replaces
|
|
208
269
|
|
|
209
|
-
`setup-pstack` writes a Cursor `.mdc` rule
|
|
210
|
-
|
|
270
|
+
`setup-pstack` writes a Cursor `.mdc` rule, so the adapter substitutes its own body and
|
|
271
|
+
publishes it as `/skill:pistack-setup`. Upstream's optional closing offers to run
|
|
272
|
+
`create-verification-skill` and `create-formal-verification-skill` are left out of the Pi body;
|
|
273
|
+
both skills stay available by name. The upstream skill stays represented, and the
|
|
211
274
|
wrapper frontmatter records `pistack-upstream-skill: setup-pstack`.
|
|
212
275
|
|
|
213
276
|
## Unknown skills
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-pstack-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Run Cursor's official pstack plugin inside Pi from an unmodified upstream checkout",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"check": "tsc --noEmit",
|
|
49
49
|
"test": "node --experimental-strip-types --test test/*.test.ts",
|
|
50
|
+
"test:integration": "node --experimental-strip-types --test test/integration/*.test.ts",
|
|
50
51
|
"test:isolated": "bash test/isolated-install.sh"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
package/src/certified.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
export const CERTIFIED = {
|
|
6
6
|
repo: "https://github.com/cursor/plugins",
|
|
7
7
|
ref: "main",
|
|
8
|
-
commit: "
|
|
8
|
+
commit: "d457567332aa6989e753ea289f30ea2bfbc80bc0",
|
|
9
9
|
pluginPath: "pstack",
|
|
10
|
-
pstackVersion: "0.15.
|
|
11
|
-
skillCount:
|
|
10
|
+
pstackVersion: "0.15.28",
|
|
11
|
+
skillCount: 56,
|
|
12
12
|
agentCount: 2,
|
|
13
13
|
} as const;
|
package/src/generate.ts
CHANGED
|
@@ -459,6 +459,17 @@ ${agentSystemPrompt(notes, upstreamPrompt)}
|
|
|
459
459
|
`;
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
/**
|
|
463
|
+
* A child that starts its own agents would otherwise hand them its own model and thinking, so a
|
|
464
|
+
* strongest-judgment delegate's helpers would all run at its level. The rule travels in every brief.
|
|
465
|
+
*/
|
|
466
|
+
const NESTED_DELEGATION_RULE = `Nested delegation: every agent that delegates, a child included, gives each new child an
|
|
467
|
+
explicit role, using \`general\` when no other role fits, and never lets it inherit the delegating
|
|
468
|
+
agent's model or thinking. Put this line in each brief, verbatim, just before any read-only or
|
|
469
|
+
no-commit rule: "If you start agents, give each the model and thinking of its own pstack role from the
|
|
470
|
+
\`pstack_adapter\` status report, using \`general\` when no role fits. Never let one inherit your
|
|
471
|
+
model or thinking, and never raise a role above its configured level."`;
|
|
472
|
+
|
|
462
473
|
function delegationSection(namespace: string, preferred: DelegationProvider): string {
|
|
463
474
|
const sections = delegationOrder(preferred).map((backend) => (backend.id === "t3" ? t3Section(namespace) : subagentSection(namespace)));
|
|
464
475
|
const order = delegationOrder(preferred).map((backend) => `\`${backend.tool}\``).join(", then ");
|
|
@@ -488,8 +499,9 @@ function t3Section(namespace: string): string {
|
|
|
488
499
|
\`target.options\` under the option id that \`${T3_CAPABILITIES_TOOL}\` lists for that model's
|
|
489
500
|
thinking setting. For Pi models the id is \`thinking\`, as in \`options: { thinking: "<level>" }\`.
|
|
490
501
|
A role whose thinking reads "backend default" omits \`options\`, so the backend applies its own
|
|
491
|
-
default. An unconfigured role
|
|
492
|
-
thinking.
|
|
502
|
+
default. An unconfigured role uses the \`general\` role. Only when \`general\` is unconfigured
|
|
503
|
+
too does the child omit \`target\` and inherit the parent model and thinking.
|
|
504
|
+
- ${NESTED_DELEGATION_RULE}
|
|
493
505
|
- If the model does not list the level, use the highest level it lists that is below the requested
|
|
494
506
|
one, and tell the user. If it lists none below the requested level, or lists no thinking option,
|
|
495
507
|
omit \`options\` and tell the user. If the level is \`off\` and the model does not list \`off\`, omit
|
|
@@ -520,7 +532,9 @@ function subagentSection(namespace: string): string {
|
|
|
520
532
|
- Model: pass a role's \`provider/model\` as \`model\`. When the thinking column of the \`model roles\`
|
|
521
533
|
table in the \`pstack_adapter\` \`status\` report names a level for the role, append it as
|
|
522
534
|
\`provider/model:<level>\`. A role whose thinking reads "backend default" gets no suffix, so
|
|
523
|
-
pi-subagents applies its own default. An unconfigured role
|
|
535
|
+
pi-subagents applies its own default. An unconfigured role uses the \`general\` role, and omits
|
|
536
|
+
\`model\` only when \`general\` is unconfigured too.
|
|
537
|
+
- ${NESTED_DELEGATION_RULE}
|
|
524
538
|
- Background: \`async: true\`.`;
|
|
525
539
|
}
|
|
526
540
|
|
|
@@ -536,12 +550,15 @@ that names a Cursor tool, path, or model.
|
|
|
536
550
|
| \`run_in_background: true\` | \`mode: "async"\` on \`delegate_task\`, or \`async: true\` on \`subagent\`. |
|
|
537
551
|
| \`AskUserQuestion\`, \`AskQuestion\` | the \`ask_user\` tool. |
|
|
538
552
|
| \`TodoWrite\`, \`TaskCreate\`, \`TaskUpdate\` | no Pi equivalent. Keep an uncommitted \`todo.md\` checklist instead. |
|
|
539
|
-
| \`claude-opus-5-5-xhigh\`, \`grok-4.7-xhigh-fast\`, other Cursor model slugs | the role models configured by \`/skill:${namespace}-setup\`. Call the \`pstack_adapter\` tool with \`action: "status"\` to read the current role table. An effort word in a slug, such as \`-xhigh\` or \`-max\`, is not a model id. The role table's thinking column carries that level, and \`-fast\` has no equivalent. An unconfigured role
|
|
540
|
-
| "pick each subagent's reasoning level and fast or standard speed", a role's starting level from the \`/setup-pstack\` budget | the thinking column of the \`model roles\` table is a role's
|
|
541
|
-
| a role line valued \`auto\` or \`inherit-parent\` | an unconfigured role.
|
|
553
|
+
| \`claude-opus-5-5-xhigh\`, \`grok-4.7-xhigh-fast\`, other Cursor model slugs | the role models configured by \`/skill:${namespace}-setup\`. Call the \`pstack_adapter\` tool with \`action: "status"\` to read the current role table. An effort word in a slug, such as \`-xhigh\` or \`-max\`, is not a model id. The role table's thinking column carries that level, and \`-fast\` has no equivalent. An unconfigured role uses the \`general\` role. When \`general\` is unconfigured too, omit \`target\` or \`model\` so the child inherits the parent model and thinking. |
|
|
554
|
+
| "pick each subagent's reasoning level and fast or standard speed", a role's starting level from the \`/setup-pstack\` budget | the thinking column of the \`model roles\` table is a role's ceiling. When it names a level, you may lower it along \`max\`, \`xhigh\`, \`high\`, \`medium\`, \`low\`, \`minimal\`, \`off\` as the body asks, then pass it by the backend rules in [Delegation](#delegation). Never raise a role above its configured level, even where the body says to raise it for the hardest work. Send that work to \`strongest-judgment\` at its own level instead. A role whose thinking reads "backend default" keeps that setting, because it has no level to move. An unconfigured role takes the \`general\` role's level as its ceiling, and keeps the parent's setting only when \`general\` is unconfigured too. Fast or standard speed has no Pi equivalent, so leave it out. If the backend rejects the moved level, use the role's configured level and say so. |
|
|
555
|
+
| a role line valued \`auto\` or \`inherit-parent\` | an unconfigured role. Use the \`general\` role, or omit \`target\` or \`model\` only when \`general\` is unconfigured too. |
|
|
542
556
|
| \`~/.cursor/rules/pstack-models.mdc\` | adapter configuration written by \`/skill:${namespace}-setup\`. |
|
|
543
557
|
| role lines \`feature, refactoring\`, \`bug-fix\`, \`perf-issue\`, \`hillclimb\`, \`judgment and prose\`, \`hardest tasks\` | adapter roles \`feature\` or \`refactoring\`, \`bug-fix\`, \`perf-issue\`, \`hillclimb\`, \`judgment\`, and \`strongest-judgment\`. |
|
|
544
|
-
|
|
|
558
|
+
| role lines \`how explainer\`, \`why synthesizer\`, \`reflect judgment, divergent, synthesizer\` | adapter role \`judgment\`. |
|
|
559
|
+
| role lines \`how explorer\`, \`why investigators\`, \`reflect tooling\`, \`swarm workers\`, and verification | adapter role \`general\`. |
|
|
560
|
+
| panel role lines \`arena runners\`, \`arena cross-judge pool\`, \`architect runners\`, \`interrogate reviewers\` | the list \`judgment\`, \`general\`, one entry each, so the panel keeps two models when those roles differ. |
|
|
561
|
+
| a delegate no role line names, such as an ad-hoc helper | adapter role \`general\`. |
|
|
545
562
|
| a Custom Mode, "Use as Mode", or Option+Enter / Alt+Enter on \`/poteto-mode\` | \`/${namespace}-mode on\`, which keeps Poteto Mode active for the Pi session until \`/${namespace}-mode off\`. |
|
|
546
563
|
| searching \`~/.cursor/plugins/\` for an installed pstack skill, or comparing \`.cursor-plugin/plugin.json\` versions | the generated skill directory. A pstack skill is \`../<pi-name>/SKILL.md\` beside the current wrapper, where \`<pi-name>\` is its Pi name in [skill names](skill-names.md). Most names only gain the \`${namespace}-\` prefix, but some are renamed. The \`generated skills\` row of the \`pstack_adapter\` tool's \`action: "status"\` report gives that directory's absolute path. A parent passes the absolute path to \`${namespace}-poteto-mode/SKILL.md\` to any \`${namespace}-poteto-agent\` it spawns. |
|
|
547
564
|
| a link that leaves the skill directory, such as \`../../README.md\` or \`../../docs/guide/\` | the same path under the \`plugin directory\` row of the \`pstack_adapter\` status report. That is the upstream checkout at the resolved commit, so read it there, not from a public \`main\` copy. |
|
package/src/registry.ts
CHANGED
|
@@ -13,12 +13,14 @@ export const SKILL_REGISTRY: Readonly<Record<string, RegistryEntry>> = {
|
|
|
13
13
|
"blast-radius": { tier: "dependency-gated", capabilities: [], executables: ["git", "gh"], note: "Needs git and gh to read the diff, commits, and PR before any analysis." },
|
|
14
14
|
"bro": { tier: "native", capabilities: [], executables: [] },
|
|
15
15
|
"correct": { tier: "dependency-gated", capabilities: [], executables: ["git"], note: "Mines commit history, then commits fixes and edits lint, CI, and agent instruction files. Run it on a branch you will review." },
|
|
16
|
+
"create-formal-verification-skill": { tier: "adapted", capabilities: [], executables: [], note: "Manual invocation only; no delegation. It installs or runs the verification tool it chooses and temporarily breaks product code to prove the check. Generated skills belong under `.pi/skills/` in Pi. Verified on a Windows Node finite-state fixture; other toolchains need their own prerequisites." },
|
|
16
17
|
"create-verification-skill": { tier: "adapted", capabilities: [], executables: [], note: "Only the generated skill's output path needs remapping to Pi's skill directory." },
|
|
17
18
|
"figure-it-out": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Phase B fan-out and the architect/arena routing require parallel subagents." },
|
|
18
19
|
"how": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Every path spawns explorer or explainer subagents; nothing runs inline." },
|
|
19
20
|
"in-your-own-words": { tier: "native", capabilities: [], executables: [] },
|
|
20
21
|
"interrogate": { tier: "dependency-gated", capabilities: ["delegation"], executables: ["git"], note: "Multi-model adversarial review is entirely one subagent per configured model." },
|
|
21
22
|
"ketchup": { tier: "adapted", capabilities: [], executables: [], note: "Read-only. Pi has no `/visualize` skill or image generation, so its visuals are Mermaid diagrams and screenshots that already exist. It checks PR and CI state only for work the chat touched." },
|
|
23
|
+
"maintain-formal-verification-skill": { tier: "dependency-gated", capabilities: [], executables: ["git"], note: "Verified end to end on a Windows Node finite-state fixture at pstack 0.15.28. Manual invocation only; no delegation. It diffs the covered code with git from the verify skill's sync commit, edits only the verification and the verify skill, and temporarily breaks product code to prove an updated check can still fail. In Pi, look for the verify skill under `.pi/skills/`; the verification tool needs its own prerequisites." },
|
|
22
24
|
"maintain-verification-skill": { tier: "dependency-gated", capabilities: ["delegation"], executables: [], note: "Source wave launches one read-only subagent per feature file." },
|
|
23
25
|
"make-bot-ui": { tier: "unsupported", capabilities: [], executables: ["curl", "tailscale", "sudo"], note: "Built entirely on Cursor Automations webhook routines and the secret-request card." },
|
|
24
26
|
"no-comments": { tier: "dependency-gated", capabilities: ["delegation"], executables: ["git"], note: "Step 1 must spawn the comment-sicko agent; the skill only triages its report." },
|
|
@@ -89,7 +91,13 @@ export const CAPABILITY_LABELS: Readonly<Record<CapabilityId, string>> = {
|
|
|
89
91
|
|
|
90
92
|
export const ADAPTER_OWNED = "(adapter-owned)";
|
|
91
93
|
|
|
92
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Model roles pstack workflows name. `general` covers every delegate no other role names. A role
|
|
96
|
+
* with no configured model falls back to `general`, and inherits the parent only when `general`
|
|
97
|
+
* is unconfigured too.
|
|
98
|
+
*/
|
|
99
|
+
export const GENERAL_ROLE = "general";
|
|
100
|
+
|
|
93
101
|
export const MODEL_ROLES: readonly string[] = [
|
|
94
102
|
"feature",
|
|
95
103
|
"refactoring",
|
|
@@ -98,6 +106,7 @@ export const MODEL_ROLES: readonly string[] = [
|
|
|
98
106
|
"hillclimb",
|
|
99
107
|
"judgment",
|
|
100
108
|
"strongest-judgment",
|
|
109
|
+
GENERAL_ROLE,
|
|
101
110
|
];
|
|
102
111
|
|
|
103
112
|
export function entryFor(upstreamName: string, generatedBareName?: string): RegistryEntry {
|
package/src/status.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CERTIFIED } from "./certified.ts";
|
|
2
|
-
import { CAPABILITY_LABELS, MODEL_ROLES, TIER_ORDER } from "./registry.ts";
|
|
2
|
+
import { CAPABILITY_LABELS, GENERAL_ROLE, MODEL_ROLES, TIER_ORDER } from "./registry.ts";
|
|
3
3
|
import type { AdapterConfig } from "./config.ts";
|
|
4
4
|
import { splitModelSpec, trustWarning } from "./config.ts";
|
|
5
5
|
import { DELEGATION_BACKENDS, delegationOrder } from "./delegation.ts";
|
|
@@ -91,8 +91,14 @@ export function renderStatus(input: StatusInput): string {
|
|
|
91
91
|
lines.push(`| role | model | thinking |`);
|
|
92
92
|
lines.push(`| --- | --- | --- |`);
|
|
93
93
|
const extra = Object.keys(config.models).filter((role) => !MODEL_ROLES.includes(role)).sort();
|
|
94
|
+
const general = config.models[GENERAL_ROLE];
|
|
94
95
|
for (const role of [...MODEL_ROLES, ...extra]) {
|
|
95
96
|
const configured = config.models[role];
|
|
97
|
+
if (configured === undefined && general !== undefined) {
|
|
98
|
+
const spec = splitModelSpec(general);
|
|
99
|
+
lines.push(`| ${role} | ${spec.model} (from ${GENERAL_ROLE}) | ${spec.thinking ?? "backend default"} (from ${GENERAL_ROLE}) |`);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
96
102
|
if (configured === undefined) {
|
|
97
103
|
lines.push(`| ${role} | inherit (parent session model) | inherit (parent session thinking) |`);
|
|
98
104
|
continue;
|
package/src/upstream.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { MANIFEST_RELATIVE_PATH } from "./manifest.ts";
|
|
@@ -202,7 +202,7 @@ async function reuseCheckout(
|
|
|
202
202
|
wanted: string,
|
|
203
203
|
pluginPath: string,
|
|
204
204
|
): Promise<{ commit: string; pluginDir: string } | undefined> {
|
|
205
|
-
if (!
|
|
205
|
+
if (!(await isCacheCheckout(git, dir))) return undefined;
|
|
206
206
|
if ((await git(["cat-file", "-e", `${wanted}^{commit}`], dir)).code !== 0) return undefined;
|
|
207
207
|
|
|
208
208
|
// A read-only cache cannot take the setting. That is fine while its files are already LF, and
|
|
@@ -307,15 +307,43 @@ async function headCommit(git: GitRunner, dir: string): Promise<string> {
|
|
|
307
307
|
return result.code === 0 ? result.stdout.trim() : "";
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Git discovers ancestor repositories from an empty cache inside a project. A successful
|
|
312
|
+
* rev-parse alone therefore does not authorize config, fetch, or checkout. Adapter caches
|
|
313
|
+
* must own both their worktree and standalone metadata, including the common directory.
|
|
314
|
+
*/
|
|
315
|
+
async function isCacheCheckout(git: GitRunner, dir: string): Promise<boolean> {
|
|
316
|
+
const metadata = join(dir, ".git");
|
|
317
|
+
try {
|
|
318
|
+
const entry = lstatSync(metadata);
|
|
319
|
+
if (!entry.isDirectory() || entry.isSymbolicLink()) return false;
|
|
320
|
+
const identity = await git([
|
|
321
|
+
"rev-parse", "--show-toplevel", "--absolute-git-dir", "--git-common-dir",
|
|
322
|
+
], dir);
|
|
323
|
+
if (identity.code !== 0) return false;
|
|
324
|
+
const paths = identity.stdout.trim().split(/\r?\n/);
|
|
325
|
+
if (paths.length !== 3) return false;
|
|
326
|
+
return [dir, metadata, metadata].every((expected, index) => {
|
|
327
|
+
const actual = paths[index];
|
|
328
|
+
return actual !== undefined && relative(realpathSync.native(expected), realpathSync.native(resolve(dir, actual))) === "";
|
|
329
|
+
});
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
310
335
|
async function bootstrap(git: GitRunner, dir: string, repo: string, ref: string, pluginPath: string): Promise<void> {
|
|
311
336
|
mkdirSync(dir, { recursive: true });
|
|
312
|
-
if (
|
|
337
|
+
if (await isCacheCheckout(git, dir)) {
|
|
313
338
|
await git(["remote", "set-url", "origin", repo], dir);
|
|
314
339
|
} else {
|
|
315
340
|
// A half-created or damaged cache is disposable. Start it over rather than repairing it.
|
|
316
341
|
rmSync(dir, { recursive: true, force: true });
|
|
317
342
|
mkdirSync(dir, { recursive: true });
|
|
318
343
|
await expect(git(["init", "--quiet", "."], dir), "initialize the adapter cache checkout");
|
|
344
|
+
if (!(await isCacheCheckout(git, dir))) {
|
|
345
|
+
throw new Error(`The pstack adapter cache at ${dir} could not verify ownership of its Git worktree and metadata after initialization. Check directory permissions and inherited Git directory overrides, then start Pi again.`);
|
|
346
|
+
}
|
|
319
347
|
await expect(git(["remote", "add", "origin", repo], dir), `add ${repo} as the cache remote`);
|
|
320
348
|
}
|
|
321
349
|
await pinLineEndings(git, dir);
|