pstack-claude-mirror 0.9.133 → 0.9.135

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.
Files changed (37) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGES.md +16 -0
  3. package/README.md +23 -7
  4. package/UPSTREAM-README.md +7 -1
  5. package/bin/pstack-claude.mjs +278 -130
  6. package/docs/reference.md +4 -4
  7. package/package.json +27 -9
  8. package/plugins/pstack/.antigravity-plugin/plugin.json +1 -1
  9. package/plugins/pstack/.claude-plugin/plugin.json +1 -1
  10. package/plugins/pstack/.codex-plugin/plugin.json +1 -1
  11. package/plugins/pstack/.github/plugin/plugin.json +1 -1
  12. package/plugins/pstack/.grok-plugin/plugin.json +1 -1
  13. package/plugins/pstack/.muse-plugin/plugin.json +1 -1
  14. package/plugins/pstack/LICENSE +22 -0
  15. package/plugins/pstack/hooks/copilot-hooks.json +4 -2
  16. package/plugins/pstack/hooks/session-start.ps1 +76 -12
  17. package/plugins/pstack/hooks/session-start.sh +6 -0
  18. package/plugins/pstack/models.json +1 -1
  19. package/plugins/pstack/package.json +1 -1
  20. package/plugins/pstack/pi/slash.ts +19 -5
  21. package/plugins/pstack/skills/poteto-mode/SKILL.md +1 -0
  22. package/plugins/pstack/skills/poteto-mode/playbooks/bug-fix.md +17 -0
  23. package/plugins/pstack/skills/poteto-mode/references/agy-tools.md +5 -4
  24. package/plugins/pstack/skills/poteto-mode/references/codex-tools.md +3 -2
  25. package/plugins/pstack/skills/poteto-mode/references/copilot-tools.md +2 -1
  26. package/plugins/pstack/skills/poteto-mode/references/pi-tools.md +1 -1
  27. package/plugins/pstack/skills/poteto-mode/scripts/.prettierignore +1 -1
  28. package/plugins/pstack/skills/poteto-mode/scripts/codex-session-effort.mjs +35 -7
  29. package/plugins/pstack/skills/poteto-mode/scripts/watch-pr/github.port.test.ts +427 -0
  30. package/plugins/pstack/skills/poteto-mode/scripts/watch-pr/github.test.ts +3 -399
  31. package/plugins/pstack/skills/poteto-mode/scripts/watch-pr/policy.port.test.ts +708 -0
  32. package/plugins/pstack/skills/poteto-mode/scripts/watch-pr/policy.test.ts +7 -666
  33. package/plugins/pstack/skills/poteto-mode/scripts/worktree-audit.mjs +42 -1
  34. package/plugins/pstack/skills/reflect/scripts/find-transcript.mjs +2 -2
  35. package/plugins/pstack/skills/setup-pstack/SKILL.md +6 -4
  36. package/plugins/pstack/skills/setup-pstack/references/runtimes.md +1 -1
  37. package/tools/forks.json +5 -5
@@ -12,6 +12,10 @@
12
12
  // and archived_sessions under $CODEX_HOME, and Pi's sessions and pstack subagent
13
13
  // sessions under $PI_CODING_AGENT_DIR (defaults: ~/.claude, ~/.codex, ~/.pi/agent).
14
14
  //
15
+ // Compressed rollouts use native Zstd when it reads complete streams. Other
16
+ // runtimes fall back to zstd on PATH. A decode failure makes the whole chat
17
+ // scan unknown, so no worktree can be declared safe from that scan.
18
+ //
15
19
  // Every probe yields a Fact, { known: true, value } or { known: false }. A hold
16
20
  // bucket needs only its own fact; `safe` needs every fact known.
17
21
  import { execFileSync } from "node:child_process";
@@ -20,6 +24,7 @@ import { homedir } from "node:os";
20
24
  import { basename, dirname, join } from "node:path";
21
25
  import process from "node:process";
22
26
  import { fileURLToPath } from "node:url";
27
+ import zlib from "node:zlib";
23
28
 
24
29
  import { candidates, rethrowUnlessRemoved } from "../../reflect/scripts/find-transcript.mjs";
25
30
 
@@ -199,11 +204,38 @@ function transcriptNeedles(spellings) {
199
204
  return [...new Set(forms)].map((form) => Buffer.from(JSON.stringify(form).slice(1, -1)));
200
205
  }
201
206
 
207
+ // Bun 1.4.4 returns the partial output of a truncated frame without an error,
208
+ // and nodejs/node#64592 reports the same for Node. The check also turns down a
209
+ // decoder that stops after the first frame. It runs once, not once per transcript.
210
+ function nativeZstdDecoder() {
211
+ const decode = zlib.zstdDecompressSync;
212
+ if (typeof decode !== "function") return undefined;
213
+ // A complete Zstd frame containing "a", independent of compressor availability.
214
+ const frame = Buffer.from("28b52ffd200109000061", "hex");
215
+ const joined = Buffer.concat([frame, frame]);
216
+ try {
217
+ if (decode(joined).toString() !== "aa") return undefined;
218
+ } catch {
219
+ return undefined;
220
+ }
221
+ for (const partial of [frame.subarray(0, -1), joined.subarray(0, -1)]) {
222
+ try {
223
+ decode(partial);
224
+ return undefined;
225
+ } catch { /* A complete-stream decoder must reject an unfinished frame. */ }
226
+ }
227
+ return decode;
228
+ }
229
+
230
+ const decodeZstd = nativeZstdDecoder() ?? ((input) => execFileSync("zstd", ["-d", "-c"], {
231
+ input, stdio: ["pipe", "pipe", "pipe"], maxBuffer: Infinity, timeout: 30_000,
232
+ }));
233
+
202
234
  // `spellings` maps each worktree path to every spelling a session could use.
203
235
  export function lastChats(roots, spellings) {
204
236
  const needles = [...spellings].map(([path, forms]) => [path, transcriptNeedles(forms)]);
205
237
  const latest = new Map();
206
- for (const { path: file } of roots.flatMap((root) => candidates(root, Infinity))) {
238
+ for (const { path: file } of roots.flatMap((root) => candidates(root, Infinity, { includeCompressed: true }))) {
207
239
  let text, mtime;
208
240
  try {
209
241
  text = readFileSync(file);
@@ -214,6 +246,15 @@ export function lastChats(roots, spellings) {
214
246
  rethrowUnlessRemoved(error);
215
247
  continue;
216
248
  }
249
+ if (file.endsWith(".jsonl.zst")) {
250
+ try {
251
+ text = decodeZstd(text);
252
+ } catch (error) {
253
+ // A missing fallback executable is not a transcript removed during enumeration.
254
+ const hint = error.code === "ENOENT" ? " Use a runtime with complete native Zstd support or install zstd." : "";
255
+ throw new Error(`cannot decode ${file}: ${String(error.stderr ?? "").trim() || error.message}${hint}`);
256
+ }
257
+ }
217
258
  for (const [path, forms] of needles) {
218
259
  if (mtime > (latest.get(path) ?? 0) && forms.some((needle) => mentions(text, needle))) latest.set(path, mtime);
219
260
  }
@@ -38,12 +38,12 @@ export function rethrowUnlessRemoved(error) {
38
38
  if (error.code !== "ENOENT") throw error;
39
39
  }
40
40
 
41
- export function candidates(projectsDir, maxDepth = 2) {
41
+ export function candidates(projectsDir, maxDepth = 2, { includeCompressed = false } = {}) {
42
42
  const files = [];
43
43
  const walk = (dir, depth) => {
44
44
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
45
45
  const full = join(dir, entry.name);
46
- if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(full);
46
+ if (entry.isFile() && (entry.name.endsWith(".jsonl") || (includeCompressed && entry.name.endsWith(".jsonl.zst")))) files.push(full);
47
47
  else if (entry.isDirectory() && depth < maxDepth) {
48
48
  try {
49
49
  walk(full, depth + 1);
@@ -17,13 +17,13 @@ Write the current runtime's per-role model override sheet. Each pstack skill nam
17
17
 
18
18
  On any runtime other than Claude Code, read [Other runtimes](references/runtimes.md) first. Its table gives the sheet path, how the sheet loads, and how to list models, and your runtime's section says which steps change. The rest of this file describes Claude Code.
19
19
 
20
- On Claude Code the config directory is `$CLAUDE_CONFIG_DIR` when that variable is set and `~/.claude` otherwise. This skill calls it `<config>`, and the sheet is `<config>/pstack-models.md`. Claude Code has no auto-applied "rules" mechanism like Cursor's `.mdc`, so inclusion is explicit: the user adds a line to `<config>/CLAUDE.md` (or their project `CLAUDE.md`) such as:
20
+ On Claude Code the config directory is `$CLAUDE_CONFIG_DIR` when that variable is set and `~/.claude` otherwise. This skill calls it `<config>`, and the user sheet is `<config>/pstack-models.md`. A project sheet, `.claude/pstack-models.md` at the repository root (`git rev-parse --show-toplevel`; outside a repository, the directory Claude Code starts in), is committed with the repository and replaces the user sheet in that project. The session hook reads it whenever it exists. The model rows defer to it through the line step 6 writes under its heading, so a hand-written project sheet needs that line too. Claude Code has no auto-applied "rules" mechanism like Cursor's `.mdc`, so inclusion is explicit: the user adds a line to `<config>/CLAUDE.md` such as:
21
21
 
22
22
  ```text
23
23
  @<config>/pstack-models.md
24
24
  ```
25
25
 
26
- with `<config>` written as the resolved path, so the file is loaded as context for every session.
26
+ with `<config>` written as the resolved path, so the file is loaded as context for every session. A project sheet loads from `@.claude/pstack-models.md` in the `CLAUDE.md` at the repository root.
27
27
 
28
28
  ## Steps
29
29
 
@@ -33,6 +33,8 @@ Enumerate the model names the `Agent` tool's `model` parameter accepts in this s
33
33
 
34
34
  ### 2. Load current state
35
35
 
36
+ On Claude Code, first ask whether to configure the user sheet, for every project, or the project sheet, for this repository on any machine. The rest of the steps use the chosen sheet.
37
+
36
38
  The default role-to-model mapping is the rule shape shown in the Write the override sheet step below. If the current runtime's sheet already exists, read it and treat its values as the current choices. Otherwise start from those defaults. A line whose role is not in that shape, such as `how critics`, is from a retired role. Drop it. An older sheet may name full model IDs that start with `claude-`, which the `Agent` tool rejects. Replace each with its family name, the word after `claude-`.
37
39
 
38
40
  ### 3. Map and confirm
@@ -51,7 +53,7 @@ Every real slug written must be in the detected set. `inherit-parent` and `auto`
51
53
 
52
54
  ### 6. Write the override sheet
53
55
 
54
- Write the current runtime's sheet with the shape below. Overwrite the whole file so re-runs stay idempotent.
56
+ Write the current runtime's sheet with the shape below. Overwrite the whole file so re-runs stay idempotent. A project sheet also gets this line under its heading, so a session that loads both sheets knows which one counts: `This project's sheet. In this repository it replaces the user's pstack-models.md; ignore that sheet's lines.`
55
57
 
56
58
  ```markdown
57
59
  # pstack model configuration
@@ -84,7 +86,7 @@ session hook: on
84
86
 
85
87
  ### 7. Wire it in
86
88
 
87
- On Claude Code, if `<config>/CLAUDE.md` does not already include `<config>/pstack-models.md`, append an `@` line naming the sheet's resolved path, such as `@~/.claude/pstack-models.md`, so the model rows load on every session. If the user prefers project scope, add the include to the project's `CLAUDE.md` instead.
89
+ On Claude Code, if `<config>/CLAUDE.md` does not already include `<config>/pstack-models.md`, append an `@` line naming the sheet's resolved path, such as `@~/.claude/pstack-models.md`, so the model rows load on every session. For a project sheet, append `@.claude/pstack-models.md` to the `CLAUDE.md` at the repository root instead, if it is not there, and tell the user to commit both files.
88
90
 
89
91
  ### 8. Confirm
90
92
 
@@ -6,7 +6,7 @@ The role lines are the same everywhere. What differs is the sheet path, how the
6
6
 
7
7
  | Runtime | Sheet | Load | List models | Status |
8
8
  | --- | --- | --- | --- | --- |
9
- | Claude Code | `<config>/pstack-models.md` | `@<config>/pstack-models.md` in `<config>/CLAUDE.md` | the `Agent` tool's model parameter | verified live |
9
+ | Claude Code | `<config>/pstack-models.md`, or `.claude/pstack-models.md` at the repository root, which replaces it in that project | `@<config>/pstack-models.md` in `<config>/CLAUDE.md`, or `@.claude/pstack-models.md` in the repository root's `CLAUDE.md` | the `Agent` tool's model parameter | verified live |
10
10
  | Codex | `<codex-home>/pstack-models.md` | model rows: paste into `<codex-home>/AGENTS.md`; hook setting: read by the plugin | your configured Codex models, see [codex-tools.md](../../poteto-mode/references/codex-tools.md#model-names) | hook contract tested; discovery verified |
11
11
  | Pi | `pstack-models.md` in the Pi agent directory, `$PI_CODING_AGENT_DIR` or `~/.pi/agent` | read by the pstack Pi extension, model rows and hook setting both; no include line | `pi --list-models`, see [pi-tools.md](../../poteto-mode/references/pi-tools.md#model-names) and its `setup-pstack` note | extension contract tested offline; live results in the repository's `docs/pi-equivalence.md` |
12
12
  | GitHub Copilot (CLI and app) | `${COPILOT_HOME:-~/.copilot}/pstack-models.md` | the plugin hook checks it and injects its role lines at session start; skills-only installs read it with `view` | the `task` tool's `model` enum, see [copilot-tools.md](../../poteto-mode/references/copilot-tools.md#model-names) | hook contract tested; CLI install smoke-tested |
package/tools/forks.json CHANGED
@@ -56,7 +56,7 @@
56
56
  },
57
57
  "plugins/pstack/skills/poteto-mode/SKILL.md": {
58
58
  "kind": "policy",
59
- "why": "Adds the Platform Adaptation section, whose runtime sentence the generator stamps from RUNTIMES, with its task-tool and todo.md fallback (per task slug when sessions share a checkout, and beside the desktop app's short update_plan list), the run-or-verify driver line, the deploy-platform and defect-severity triggers, the port's principle summaries, the project playbooks paragraph, and the closed-review-gate rule that the Codex and Pi mappings point to, and it points the Agent-call defaults at the Models section with full tool access and the namespaced pstack:poteto-agent name, requires stopping an abandoned agent before replacing it, renders every reply item as prose, layers technical-writing on top of unslop, and describes Autopilot-full as stopping at merge-ready for the operator's click and Autopilot-stack as Autopilot-full for PRs that build on each other.",
59
+ "why": "Adds the Platform Adaptation section, whose runtime sentence the generator stamps from RUNTIMES, with its task-tool and todo.md fallback (per task slug when sessions share a checkout, and beside the desktop app's short update_plan list), the run-or-verify driver line, the deploy-platform and defect-severity triggers, the focused-fix exception to the how and architect triggers, the port's principle summaries, the project playbooks paragraph, and the closed-review-gate rule that the Codex and Pi mappings point to, and it points the Agent-call defaults at the Models section with full tool access and the namespaced pstack:poteto-agent name, requires stopping an abandoned agent before replacing it, renders every reply item as prose, layers technical-writing on top of unslop, and describes Autopilot-full as stopping at merge-ready for the operator's click and Autopilot-stack as Autopilot-full for PRs that build on each other.",
60
60
  "since": "0.9.2",
61
61
  "upstream": "not-proposed"
62
62
  },
@@ -80,7 +80,7 @@
80
80
  },
81
81
  "plugins/pstack/skills/poteto-mode/playbooks/bug-fix.md": {
82
82
  "kind": "policy",
83
- "why": "The reply quotes the decisive failing and passing output trimmed to the assertion and counts, not the whole repro verbatim.",
83
+ "why": "Quotes decisive failing and passing output trimmed to the assertion and counts, separates verified code, publication, observed runtime behavior, and blockers, selects non-sensitive diagnostic fields before printing, and adds a focused-fix exception with explicit architecture and independent correctness review triggers for higher-risk fixes.",
84
84
  "since": "0.9.10",
85
85
  "upstream": "not-proposed"
86
86
  },
@@ -170,7 +170,7 @@
170
170
  },
171
171
  "plugins/pstack/skills/poteto-mode/scripts/watch-pr/github.test.ts": {
172
172
  "kind": "policy",
173
- "why": "Covers the port's GitHub reader: completed conclusions that gh buckets as pending, a checkout PR paired with a different explicit repository, a full open-PR page refused as a possibly cut stack, a stack that never takes a PR headed at the default branch as parent, child, or seed under the default branch the repository reports, while a fork branch of the same name still stacks, a check-rollup cursor that repeats or returns to an earlier page, a review-thread cursor that repeats, a review-thread page that promises more pages without a cursor, a binary that cannot be spawned, the base revision field, and a no-checks reading that needs both check reads to agree and fails closed otherwise. Expects the `missing-field` query failure.",
173
+ "why": "Adapts upstream's cases to the port's GitHub reader: a `reported` helper that narrows each check read to reported checks, rollup pages tagged `contexts`, the base revision read from `baseRef`, an empty `reviewDecision` rejected like any other unknown enum value, the `missing-field` query failure, `orderStack` taking the default branch and each head's repository in place of `isCrossRepository`, and a reader built with the shared deadline. The port's own cases are in `github.port.test.ts`.",
174
174
  "since": "0.9.24",
175
175
  "upstream": "not-proposed"
176
176
  },
@@ -182,7 +182,7 @@
182
182
  },
183
183
  "plugins/pstack/skills/poteto-mode/scripts/watch-pr/policy.test.ts": {
184
184
  "kind": "policy",
185
- "why": "Covers the review-required and branch-protection merge gates and their wait for pending checks, the behind-base gate that stops without waiting for pending or unreported checks and outranks a required review but not requested changes, the wait while GitHub has not computed mergeability (never ready and never a query error, re-polled at the interval, timing out at the caller's deadline, one status row on a one-shot pass, and blockers that do not depend on it still stopping at once), a PR with no checks configured (waiting in single, stack, and queued mode until the reading has persisted for 60 seconds however short the interval, each PR timed from its own first sighting and unconfirmed when the caller supplies no confirmer, timing out there under a shorter deadline, ready once it has, still stopped by conflicts, threads, and gates, honouring checks that register on the next poll, and failing closed while the head may not have reported), the second facts read in the snapshot plan and the retry when the PR's state, merge time, head branch, gate, review, draft, or conflict state changes between the two reads, except mergeability that the first read had not computed, and the shared deadline in the poll loop.",
185
+ "why": "Adapts upstream's cases to the port's policy: the second facts read in the snapshot plan and the read it adds to each sweep, a stack wait that carries its reason, `evaluateQueue` without a clock reading, and the shared deadline in the poll loop. The port's own cases are in `policy.port.test.ts`.",
186
186
  "since": "0.9.24",
187
187
  "upstream": "not-proposed"
188
188
  },
@@ -242,7 +242,7 @@
242
242
  },
243
243
  "plugins/pstack/skills/setup-pstack/SKILL.md": {
244
244
  "kind": "port-feature",
245
- "why": "Writes the override sheet at each runtime's own path, loaded by @-include on Claude Code, pasted into AGENTS.md on Codex, and read directly by the Muse plugin hook since none has Cursor's always-applied rules, replaces Cursor's budget step with a default effort and per-role effort levels and explains Codex's active-turn metadata requirement for session effort, adds the SessionStart hook toggle and names the Pi extension and OpenCode plugin as readers of it, keeps every runtime's sheet path table and per-runtime step changes in references/runtimes.md, routing GitHub Copilot to copilot.md, Kimi and Grok to runtime-specific setup, and OMP to native model configuration and its global routing toggle, and numbers its verification and formal verification offers as steps 9 and 10.",
245
+ "why": "Writes the override sheet at each runtime's own path, loaded by @-include on Claude Code with a committed project sheet that replaces the user's, pasted into AGENTS.md on Codex, and read directly by the Muse plugin hook since none has Cursor's always-applied rules, replaces Cursor's budget step with a default effort and per-role effort levels and explains Codex's active-turn metadata requirement for session effort, adds the SessionStart hook toggle and names the Pi extension and OpenCode plugin as readers of it, keeps every runtime's sheet path table and per-runtime step changes in references/runtimes.md, routing GitHub Copilot to copilot.md, Kimi and Grok to runtime-specific setup, and OMP to native model configuration and its global routing toggle, and numbers its verification and formal verification offers as steps 9 and 10.",
246
246
  "since": "0.9.2",
247
247
  "upstream": "not-proposed"
248
248
  },