From e28fb124ea7f1ce5d6ff1bc317b4bb15c009c2e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 01:44:43 +0000 Subject: [PATCH 1/5] chore: rename the distribution to pstack-flex Rename the marketplace from open-pstack to pstack-flex in both the Claude Code and Codex marketplace files, and point the plugin manifests, package name, install commands, and docs at thisguymartin/pstack-flex. The plugin keeps the name pstack, so skill names (pstack:poteto-mode), the opt-in hook, and monitor detection are unchanged. The monitor's Codex detection also matches the new plugin cache path (pstack-flex/pstack/). Attribution to open-pstack, pstack-claude, and Cursor pstack stays in README, NOTICE, and the upstream docs. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LSWNeBzAMt5FQZVziZCGLs --- .agents/plugins/marketplace.json | 2 +- .claude-plugin/marketplace.json | 8 ++++---- AGENTS.md | 2 +- README.md | 10 +++++----- UPSTREAM.md | 2 +- docs/USAGE.md | 8 ++++---- docs/reference.md | 20 +++++++++---------- plugins/pstack/.claude-plugin/plugin.json | 4 ++-- plugins/pstack/.codex-plugin/plugin.json | 8 ++++---- .../skills/poteto-mode/scripts/bun.lock | 2 +- .../scripts/monitor/adapters/codex.test.ts | 1 + .../scripts/monitor/adapters/codex.ts | 2 +- .../skills/poteto-mode/scripts/package.json | 2 +- 13 files changed, 36 insertions(+), 35 deletions(-) diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 1f5a1a5b..2b0d2473 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -1,5 +1,5 @@ { - "name": "open-pstack", + "name": "pstack-flex", "interface": { "displayName": "pstack" }, diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c0125930..afd3c0e6 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,9 +1,9 @@ { - "name": "open-pstack", + "name": "pstack-flex", "owner": { - "name": "Eric Litman" + "name": "Martin Patino" }, - "description": "Pstack for Claude Code and Codex, tracking Cursor's upstream pstack.", + "description": "pstack for Claude Code and Codex on the models you choose: subscription CLIs or API-key gateway lanes.", "plugins": [ { "name": "pstack", @@ -13,7 +13,7 @@ "author": { "name": "Lauren Tan (original)" }, - "homepage": "https://github.com/ericlitman/open-pstack", + "homepage": "https://github.com/thisguymartin/pstack-flex", "license": "MIT", "keywords": [ "pstack", diff --git a/AGENTS.md b/AGENTS.md index 6dc9c069..b1852655 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# open-pstack +# pstack-flex Track all durable work in this repository's GitHub Issues. Do not create a parallel Linear queue. Read `UPSTREAM.md` before changing upstream-derived content. diff --git a/README.md b/README.md index 61a9ac08..df6f2eed 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # pstack-flex [![CI](https://github.com/thisguymartin/pstack-flex/actions/workflows/ci.yml/badge.svg)](https://github.com/thisguymartin/pstack-flex/actions/workflows/ci.yml) -[![Fork of open-pstack v1.5.0](https://img.shields.io/badge/fork%20of-open--pstack%20v1.5.0-blue)](https://github.com/ericlitman/open-pstack/releases/tag/v1.5.0) +[![Based on open-pstack v1.5.0](https://img.shields.io/badge/based%20on-open--pstack%20v1.5.0-blue)](https://github.com/ericlitman/open-pstack/releases/tag/v1.5.0) [![MIT license](https://img.shields.io/github/license/thisguymartin/pstack-flex)](LICENSE) -**pstack-flex runs [Lauren Tan (@poteto)](https://x.com/poteto)'s [pstack](https://github.com/cursor/plugins/tree/main/pstack) in Claude Code and Codex on the models you actually have.** It is a fork of [ericlitman/open-pstack](https://github.com/ericlitman/open-pstack), which translates pstack's Cursor-specific parts for Claude Code and Codex. open-pstack assumes four frontier subscriptions. This fork keeps its skills and workflows and changes one thing: which models setup accepts and how they are reached. +**pstack-flex runs [Lauren Tan (@poteto)](https://x.com/poteto)'s [pstack](https://github.com/cursor/plugins/tree/main/pstack) in Claude Code and Codex on the models you actually have.** It is built on [ericlitman/open-pstack](https://github.com/ericlitman/open-pstack), which translates pstack's Cursor-specific parts for Claude Code and Codex. open-pstack assumes four frontier subscriptions. pstack-flex keeps its skills and workflows, changes which models setup accepts and how they are reached, and adds its own skills and an optional agent monitor. Lauren built pstack from the skills she uses to ship code at Cursor. In a [55-minute interview with Denis Labelle](https://x.com/DenisLabelle/status/2091337807939706928), she says that she shipped 1,000 pull requests in one month after steadily improving how her agents work and verify their results. @@ -80,7 +80,7 @@ Run these commands inside Claude Code: ```text /plugin marketplace add thisguymartin/pstack-flex -/plugin install pstack@open-pstack +/plugin install pstack@pstack-flex /reload-plugins ``` @@ -90,7 +90,7 @@ Run these commands in your shell: ```shell codex plugin marketplace add thisguymartin/pstack-flex --ref main -codex plugin add pstack@open-pstack +codex plugin add pstack@pstack-flex ``` Turn on Codex subagents in `~/.codex/config.toml` so pstack can compare work in parallel: @@ -205,4 +205,4 @@ Read [UPSTREAM.md](UPSTREAM.md) and [UPSTREAM-FLEX.md](UPSTREAM-FLEX.md) before ## License -MIT. pstack was created by Lauren Tan. open-pstack builds on Michael Denyer's [pstack-claude](https://github.com/michael-denyer/pstack-claude) port and includes attributed MIT-licensed work from Cursor Team Kit and Superpowers. pstack-flex is a fork of open-pstack. See [NOTICE.md](NOTICE.md) and the preserved license files for details. +MIT. pstack was created by Lauren Tan. open-pstack builds on Michael Denyer's [pstack-claude](https://github.com/michael-denyer/pstack-claude) port and includes attributed MIT-licensed work from Cursor Team Kit and Superpowers. pstack-flex started as a fork of open-pstack and is maintained as its own distribution. See [NOTICE.md](NOTICE.md) and the preserved license files for details. diff --git a/UPSTREAM.md b/UPSTREAM.md index 87968c73..c9c5704f 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -45,7 +45,7 @@ No output means the tracked pstack tree has not changed. This comparison does no ## Incorporate a change -1. Create or update a GitHub issue in `ericlitman/open-pstack` and branch from current `main`. +1. Create or update a GitHub issue in `thisguymartin/pstack-flex` and branch from current `main`. 2. Read each upstream pstack commit in order. Bring over its intent and content, then apply only the Claude Code and Codex substitutions documented in `CHANGES.md`. 3. Keep one shared `plugins/pstack/skills/` tree. Put harness translation in the existing `codex-tools.md` and provider routing in `provider-dispatch.md`; do not fork a skill per harness. 4. Update the commit and version in this file, the affected provenance rows in `NOTICE.md`, and `README-UPSTREAM.md` when upstream changes it. diff --git a/docs/USAGE.md b/docs/USAGE.md index 913acd12..0d77bcdd 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -34,15 +34,15 @@ flowchart TD Every lane is a real agent process with tools and file access. The parent harness (your Claude Code or Codex session) resolves the route once; children never pick their own models. -## Install this fork +## Install -The marketplace keeps upstream's name (`open-pstack`), so only the source changes. +The marketplace is named `pstack-flex` and carries the `pstack` plugin. If you have Eric Litman's open-pstack installed, remove it first: both ship a plugin named `pstack`, so their skills would share the `pstack:` names. Claude Code: ```text /plugin marketplace add thisguymartin/pstack-flex -/plugin install pstack@open-pstack +/plugin install pstack@pstack-flex /reload-plugins ``` @@ -50,7 +50,7 @@ Codex: ```shell codex plugin marketplace add thisguymartin/pstack-flex --ref main -codex plugin add pstack@open-pstack +codex plugin add pstack@pstack-flex ``` Plus [Bun](https://bun.sh) for the lane runner, and `multi_agent = true` under `[features]` in `~/.codex/config.toml` if Codex is your parent. Sign in only to the CLIs whose subscriptions you actually have — missing families are fine now. diff --git a/docs/reference.md b/docs/reference.md index 7cdf5a2f..c1034c75 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -1,4 +1,4 @@ -# open-pstack technical reference +# pstack-flex technical reference This page contains the full skill, dependency, runtime, and porting reference. For the plain-English introduction and quick start, see the [main README](../README.md). @@ -17,8 +17,8 @@ This is not a verbatim copy. Skill bodies have been edited so every Cursor-speci This repo ships as a Claude Code marketplace containing one plugin (`pstack`). ```text -/plugin marketplace add ericlitman/open-pstack -/plugin install pstack@open-pstack +/plugin marketplace add thisguymartin/pstack-flex +/plugin install pstack@pstack-flex /reload-plugins ``` @@ -29,8 +29,8 @@ pstack runs only when you ask for it. A `SessionStart` hook on startup, `/clear` The same plugin carries a `.codex-plugin/plugin.json` manifest and a root `.agents/plugins/marketplace.json`. Install it through the Codex marketplace: ```shell -codex plugin marketplace add ericlitman/open-pstack --ref main -codex plugin add pstack@open-pstack +codex plugin marketplace add thisguymartin/pstack-flex --ref main +codex plugin add pstack@pstack-flex ``` Codex discovers the plugin skills under the `pstack` namespace, so they list as `pstack:poteto-mode`, `pstack:tdd`, and so on. The namespace comes from `plugins/pstack/.codex-plugin/plugin.json`. To enable the multi-model and parallel-subagent skills (`interrogate`, `arena`, `how`, `why`, `reflect`, `architect`), turn on subagents in `~/.codex/config.toml`: @@ -43,8 +43,8 @@ multi_agent = true For local plugin development, you can clone the repository and link its skills directly: ```shell -git clone https://github.com/ericlitman/open-pstack -cd open-pstack +git clone https://github.com/thisguymartin/pstack-flex +cd pstack-flex for s in plugins/pstack/skills/*/; do ln -s "$PWD/$s" ~/.agents/skills/"$(basename "$s")"; done ``` @@ -82,13 +82,13 @@ Plugin-internal `skills//` path references in the docs below are relative The Codex build shares one `skills/` tree with the Claude Code build. Nothing is forked or generated. Two narrow references keep runtime translation separate: `codex-tools.md` maps harness primitives and `provider-dispatch.md` maps model providers. pstack otherwise keeps the upstream Claude-native prose and adds a one-line Platform note to each skill that names a Claude primitive, so the port stays in lockstep with upstream sync. - **Skill invocation.** Codex loads `SKILL.md` natively. There is no `Skill` tool. You invoke a skill by name (ask for it, or pick `pstack:poteto-mode` from the list). -- **Package surface.** The native `skills/` tree is the only workflow source. The plugin ships no `commands/` layer and does not link prompts into `~/.codex/prompts/`. Codex would migrate such files into duplicate source-command skills while loading the native skill tree. The 23 `principle-*` leaves declare `user-invocable: false`. Claude keeps them out of its user picker; Codex 0.149.0 currently shows them despite that metadata ([#8](https://github.com/ericlitman/open-pstack/issues/8)). +- **Package surface.** The native `skills/` tree is the only workflow source. The plugin ships no `commands/` layer and does not link prompts into `~/.codex/prompts/`. Codex would migrate such files into duplicate source-command skills while loading the native skill tree. The 23 `principle-*` leaves declare `user-invocable: false`. Claude keeps them out of its user picker; Codex 0.149.0 currently shows them despite that metadata ([open-pstack #8](https://github.com/ericlitman/open-pstack/issues/8)). - **Tool and built-in mapping.** Claude tool names and built-in skills resolve through [`codex-tools.md`](../plugins/pstack/skills/poteto-mode/references/codex-tools.md). Model execution resolves separately through [`provider-dispatch.md`](../plugins/pstack/skills/poteto-mode/references/provider-dispatch.md), so Codex can keep Sol native while invoking Claude and Grok externally. - **Subagents.** The `Agent` tool maps to Codex `spawn_agent` / `wait_agent`, enabled by `multi_agent = true`. Parallel fan-out is multiple `spawn_agent` calls in one turn. If the native Codex lane is unavailable, record that lane as a dropout; external Claude and Grok lanes still run, and no provider is silently substituted. There is no `poteto-agent` subagent type on Codex; route ad-hoc subagents by dispatching a `spawn_agent` told to read `poteto-mode` first. -- **Opt-in.** Codex runs the plugin's `hooks/` SessionStart hook and adds the opt-in gate to each session as a developer message (observed on Codex 0.157.1). Codex records trust for the hook in `~/.codex/config.toml` under `hooks.state`. Enter `pstack:poteto-mode` by name, or add a standing instruction to `~/.codex/AGENTS.md` if you want every non-trivial task routed into it. After a plugin update, run `codex plugin marketplace upgrade open-pstack` so the installed copy carries the current gate. +- **Opt-in.** Codex runs the plugin's `hooks/` SessionStart hook and adds the opt-in gate to each session as a developer message (observed on Codex 0.157.1). Codex records trust for the hook in `~/.codex/config.toml` under `hooks.state`. Enter `pstack:poteto-mode` by name, or add a standing instruction to `~/.codex/AGENTS.md` if you want every non-trivial task routed into it. After a plugin update, run `codex plugin marketplace upgrade pstack-flex` so the installed copy carries the current gate. - **Models.** `/setup-pstack` writes provider-qualified descriptors and asks one requested effort per assigned family (`low`, `medium`, `high`, `xhigh`, `max`). The first-run panel is Fable max, GPT-6 Astra high, Grok 4.7 xhigh, and Opus max. Fable and Opus use Claude's rolling aliases. Runtime dispatch normalizes older versioned descriptors in memory, so an installed sheet stops pinning immediately. A setup rerun persists that migration while keeping each role's family and effort. The GPT-6 Astra, Sol, and Luna Codex families are stock: GPT-6 Sol high carries `feature, refactoring`, `bug-fix`, `perf-issue`, and `hillclimb`; Luna high carries `how explorer` and `swarm workers`; Astra high sits on every panel. GPT-5.6 Sol remains a selectable family. In Codex, every Codex family uses native `spawn_agent`; Claude and Grok use the deterministic external runner. In Claude Code, Fable and Opus use native agents; the Codex families and Grok use the runner. Children never detect the parent or reroute themselves. The solo code roles stay on a Codex model instead of upstream's Fable default because it costs less for these frequent delegated code roles. -Verified in fresh installed Claude Code and Codex sessions: the user-facing skills are discovered and namespaced under `pstack`; both parents fan out the configured panel through the documented native/external route table, retain long-running handles without a default timeout, and cross-judge only after every candidate is terminal. The `principle-*` leaves remain available for `poteto-mode` to read by path. Claude honors their `user-invocable: false` metadata; Codex 0.149.0 does not ([#8](https://github.com/ericlitman/open-pstack/issues/8)). +Verified upstream in fresh installed open-pstack Claude Code and Codex sessions (pstack-flex's own live-test record is in [LIVE-GATE.md](LIVE-GATE.md)): the user-facing skills are discovered and namespaced under `pstack`; both parents fan out the configured panel through the documented native/external route table, retain long-running handles without a default timeout, and cross-judge only after every candidate is terminal. The `principle-*` leaves remain available for `poteto-mode` to read by path. Claude honors their `user-invocable: false` metadata; Codex 0.149.0 does not ([open-pstack #8](https://github.com/ericlitman/open-pstack/issues/8)). ## Dependencies diff --git a/plugins/pstack/.claude-plugin/plugin.json b/plugins/pstack/.claude-plugin/plugin.json index 59bbf654..61c3b97c 100644 --- a/plugins/pstack/.claude-plugin/plugin.json +++ b/plugins/pstack/.claude-plugin/plugin.json @@ -6,8 +6,8 @@ "author": { "name": "Lauren Tan" }, - "homepage": "https://github.com/ericlitman/open-pstack", - "repository": "https://github.com/ericlitman/open-pstack", + "homepage": "https://github.com/thisguymartin/pstack-flex", + "repository": "https://github.com/thisguymartin/pstack-flex", "license": "MIT", "keywords": [ "pstack", diff --git a/plugins/pstack/.codex-plugin/plugin.json b/plugins/pstack/.codex-plugin/plugin.json index 4bc05459..d87d532b 100644 --- a/plugins/pstack/.codex-plugin/plugin.json +++ b/plugins/pstack/.codex-plugin/plugin.json @@ -5,8 +5,8 @@ "author": { "name": "Lauren Tan" }, - "homepage": "https://github.com/ericlitman/open-pstack", - "repository": "https://github.com/ericlitman/open-pstack", + "homepage": "https://github.com/thisguymartin/pstack-flex", + "repository": "https://github.com/thisguymartin/pstack-flex", "license": "MIT", "keywords": [ "pstack", @@ -23,7 +23,7 @@ "displayName": "pstack", "shortDescription": "Rigorous, parallelizable agent workflows: go deep first, write less, verify everything", "longDescription": "pstack guides agent work through poteto-mode's principles, parallel design exploration (architect/arena), adversarial multi-model review (interrogate), root-cause debugging, prose deslopping, and verified delivery. Skills are shared with the Claude Code build; on Codex, tool names resolve via the codex-tools mapping.", - "developerName": "Lauren Tan (original), open-pstack maintainers", + "developerName": "Lauren Tan (original), pstack-flex maintainers", "category": "Developer Tools", "capabilities": [ "Interactive", @@ -34,7 +34,7 @@ "Work on this in poteto-mode.", "Interrogate this diff with multiple models." ], - "websiteURL": "https://github.com/ericlitman/open-pstack", + "websiteURL": "https://github.com/thisguymartin/pstack-flex", "brandColor": "#7C3AED" } } diff --git a/plugins/pstack/skills/poteto-mode/scripts/bun.lock b/plugins/pstack/skills/poteto-mode/scripts/bun.lock index b46f3ea9..56c665fd 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/bun.lock +++ b/plugins/pstack/skills/poteto-mode/scripts/bun.lock @@ -3,7 +3,7 @@ "configVersion": 1, "workspaces": { "": { - "name": "@open-pstack/poteto-mode-tools", + "name": "@pstack-flex/poteto-mode-tools", "dependencies": { "commander": "14.0.0", }, diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.test.ts b/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.test.ts index 4ede0431..8e41ca84 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.test.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.test.ts @@ -121,6 +121,7 @@ describe("rollout", () => { expect(flagged(childThread, childMeta(), ask("Use pstack:monitor."))).toBe(false); expect(flagged(parentThread, rootMeta, shell("pstack-runner --parent codex --provider minimax"))).toBe(true); expect(flagged(parentThread, rootMeta, shell("cat ~/.codex/plugins/cache/open-pstack/pstack/1.5.0/skills/arena/SKILL.md"))).toBe(true); + expect(flagged(parentThread, rootMeta, shell("cat ~/.codex/plugins/cache/pstack-flex/pstack/1.5.0/skills/arena/SKILL.md"))).toBe(true); expect(flagged(parentThread, rootMeta, shell("ls"))).toBe(false); }); diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.ts b/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.ts index 7e8320b4..701551b9 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.ts +++ b/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.ts @@ -38,7 +38,7 @@ const IGNORED_TYPES = new Set([ ]); /** Paths and commands only pstack's skills and runner use. */ -const PSTACK_MARKERS = ["pstack-runner", "open-pstack/pstack/", "/pstack/skills/"]; +const PSTACK_MARKERS = ["pstack-runner", "pstack-flex/pstack/", "open-pstack/pstack/", "/pstack/skills/"]; const PSTACK_WORD = /\bpstack\b/i; const IGNORED_ITEMS = new Set(["ghost_snapshot"]); diff --git a/plugins/pstack/skills/poteto-mode/scripts/package.json b/plugins/pstack/skills/poteto-mode/scripts/package.json index 6aa871c2..30e33c80 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/package.json +++ b/plugins/pstack/skills/poteto-mode/scripts/package.json @@ -1,5 +1,5 @@ { - "name": "@open-pstack/poteto-mode-tools", + "name": "@pstack-flex/poteto-mode-tools", "private": true, "type": "module", "scripts": { From 7628aa4f544bcb577a89b6f09071a0f8bbbe40d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 01:48:33 +0000 Subject: [PATCH 2/5] refactor(monitor): move the agent monitor into its own plugin The monitor was 82% of the fork's added code but no pstack skill depends on it. It now ships as pstack-monitor, a second plugin in the pstack-flex marketplace, with its own manifests, version, Bun package, and CI steps. Users install it only if they want it, and monitor fixes no longer need a pstack release. The lane journal is the only interface between the two plugins. monitor/lane-contract.ts restates the runner's journal and receipt shapes; lane-contract.test.ts fails at typecheck when they drift, and a static check fails if monitor code imports the runner directly. version.ts reads the new plugin's manifest. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LSWNeBzAMt5FQZVziZCGLs --- .agents/plugins/marketplace.json | 21 +++++- .claude-plugin/marketplace.json | 17 +++++ .github/workflows/ci.yml | 11 ++- .gitignore | 1 + CHANGES.md | 6 ++ NOTICE.md | 2 +- README.md | 13 +++- UPSTREAM-FLEX.md | 2 +- docs/USAGE.md | 15 ++-- docs/reference.md | 11 +-- .../pstack-monitor/.claude-plugin/plugin.json | 13 ++++ .../pstack-monitor/.codex-plugin/plugin.json | 23 +++++++ plugins/pstack-monitor/scripts/bun.lock | 62 +++++++++++++++++ .../scripts/monitor/adapter.ts | 0 .../scripts/monitor/adapters/blocks.ts | 0 .../scripts/monitor/adapters/claude.test.ts | 0 .../scripts/monitor/adapters/claude.ts | 0 .../scripts/monitor/adapters/codex.test.ts | 0 .../scripts/monitor/adapters/codex.ts | 0 .../scripts/monitor/adapters/lane.test.ts | 0 .../scripts/monitor/adapters/lane.ts | 7 +- .../scripts/monitor/assets.test.ts | 0 .../scripts/monitor/assets.ts | 0 .../scripts/monitor/cli.ts | 0 .../scripts/monitor/daemon.ts | 0 .../scripts/monitor/doctor.ts | 0 .../scripts/monitor/domain.ts | 2 +- .../scripts/monitor/format.test.ts | 0 .../scripts/monitor/format.ts | 0 .../scripts/monitor/fs.ts | 0 .../scripts/monitor/graph.ts | 0 .../scripts/monitor/index.test.ts | 0 .../scripts/monitor/index.ts | 0 .../scripts/monitor/journal.test.ts | 0 .../scripts/monitor/journal.ts | 0 .../scripts/monitor/json.ts | 0 .../scripts/monitor/lane-contract.test.ts | 27 ++++++++ .../scripts/monitor/lane-contract.ts | 68 +++++++++++++++++++ .../scripts/monitor/layout.test.ts | 0 .../scripts/monitor/layout.ts | 0 .../scripts/monitor/monitor.ts | 0 .../scripts/monitor/probe.ts | 0 .../scripts/monitor/pstack-monitor | 0 .../scripts/monitor/record.ts | 0 .../scripts/monitor/server.test.ts | 0 .../scripts/monitor/server.ts | 0 .../scripts/monitor/sources.ts | 7 +- .../scripts/monitor/store.test.ts | 0 .../scripts/monitor/store.ts | 0 .../scripts/monitor/tail.test.ts | 0 .../scripts/monitor/tail.ts | 0 .../scripts/monitor/tsconfig.json | 0 .../scripts/monitor/version.ts | 2 +- .../scripts/monitor/web/app.css | 0 .../scripts/monitor/web/canvas.ts | 0 .../scripts/monitor/web/dom.ts | 0 .../scripts/monitor/web/index.html | 0 .../scripts/monitor/web/main.ts | 0 .../scripts/monitor/web/panel.ts | 0 .../scripts/monitor/web/rail.ts | 0 .../scripts/monitor/web/tsconfig.json | 0 .../scripts/monitor/wire.ts | 0 plugins/pstack-monitor/scripts/package.json | 13 ++++ .../skills/monitor/SKILL.md | 2 +- .../skills/poteto-mode/scripts/package.json | 4 +- tests/skill-collision-repro.sh | 17 +++++ 66 files changed, 320 insertions(+), 26 deletions(-) create mode 100644 plugins/pstack-monitor/.claude-plugin/plugin.json create mode 100644 plugins/pstack-monitor/.codex-plugin/plugin.json create mode 100644 plugins/pstack-monitor/scripts/bun.lock rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapter.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/blocks.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/claude.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/claude.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/codex.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/codex.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/lane.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/adapters/lane.ts (97%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/assets.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/assets.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/cli.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/daemon.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/doctor.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/domain.ts (99%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/format.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/format.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/fs.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/graph.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/index.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/index.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/journal.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/journal.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/json.ts (100%) create mode 100644 plugins/pstack-monitor/scripts/monitor/lane-contract.test.ts create mode 100644 plugins/pstack-monitor/scripts/monitor/lane-contract.ts rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/layout.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/layout.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/monitor.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/probe.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/pstack-monitor (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/record.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/server.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/server.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/sources.ts (81%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/store.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/store.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/tail.test.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/tail.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/tsconfig.json (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/version.ts (95%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/app.css (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/canvas.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/dom.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/index.html (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/main.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/panel.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/rail.ts (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/web/tsconfig.json (100%) rename plugins/{pstack/skills/poteto-mode => pstack-monitor}/scripts/monitor/wire.ts (100%) create mode 100644 plugins/pstack-monitor/scripts/package.json rename plugins/{pstack => pstack-monitor}/skills/monitor/SKILL.md (92%) diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 2b0d2473..281617fb 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -1,7 +1,7 @@ { "name": "pstack-flex", "interface": { - "displayName": "pstack" + "displayName": "pstack-flex" }, "plugins": [ { @@ -13,7 +13,24 @@ "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL", - "products": ["CODEX"] + "products": [ + "CODEX" + ] + }, + "category": "Developer Tools" + }, + { + "name": "pstack-monitor", + "source": { + "source": "local", + "path": "./plugins/pstack-monitor" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL", + "products": [ + "CODEX" + ] }, "category": "Developer Tools" } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index afd3c0e6..b959b48c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -24,6 +24,23 @@ "subagents", "unslop" ] + }, + { + "name": "pstack-monitor", + "source": "./plugins/pstack-monitor", + "description": "Optional companion: a live, read-only local view of pstack sessions, subagents, and model lanes.", + "version": "1.0.0", + "author": { + "name": "Martin Patino" + }, + "homepage": "https://github.com/thisguymartin/pstack-flex", + "license": "MIT", + "keywords": [ + "pstack", + "monitor", + "agents", + "observability" + ] } ] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 126d94b4..9ccac698 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,16 @@ jobs: - name: Typecheck working-directory: plugins/pstack/skills/poteto-mode/scripts run: bun run typecheck + - name: Install monitor + working-directory: plugins/pstack-monitor/scripts + run: bun install --frozen-lockfile + - name: Test monitor + working-directory: plugins/pstack-monitor/scripts + run: bun run test + - name: Typecheck monitor + working-directory: plugins/pstack-monitor/scripts + run: bun run typecheck - name: Validate manifests and static invariants run: | - bun -e 'for (const path of [".claude-plugin/marketplace.json", ".agents/plugins/marketplace.json", "plugins/pstack/.claude-plugin/plugin.json", "plugins/pstack/.codex-plugin/plugin.json"]) JSON.parse(await Bun.file(path).text())' + bun -e 'for (const path of [".claude-plugin/marketplace.json", ".agents/plugins/marketplace.json", "plugins/pstack/.claude-plugin/plugin.json", "plugins/pstack/.codex-plugin/plugin.json", "plugins/pstack-monitor/.claude-plugin/plugin.json", "plugins/pstack-monitor/.codex-plugin/plugin.json"]) JSON.parse(await Bun.file(path).text())' PSTACK_STATIC_ONLY=1 bash tests/skill-collision-repro.sh diff --git a/.gitignore b/.gitignore index 008e57b5..9a792d12 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ # bun deps for the vendored poteto-mode scripts (bootstrap.ts installs them on first run) plugins/pstack/skills/poteto-mode/scripts/node_modules/ +plugins/pstack-monitor/scripts/node_modules/ diff --git a/CHANGES.md b/CHANGES.md index 360df52b..a0acef5f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # CHANGES — applied substitutions +## Unreleased: pstack-flex becomes its own distribution + +- The marketplace is now `pstack-flex` (was `open-pstack`) in both the Claude Code and Codex marketplace files. Install with `pstack@pstack-flex`. The plugin keeps the name `pstack`, so skill names such as `pstack:poteto-mode` are unchanged. Manifests, package names, and docs point at `thisguymartin/pstack-flex`; attribution to open-pstack, pstack-claude, and Cursor pstack stays in README and NOTICE. +- The agent monitor moves out of the `pstack` plugin into its own plugin, `pstack-monitor`, in the same marketplace. It has its own manifests, version (1.0.0), Bun package, and CI steps. Its skill is now `pstack-monitor:monitor` and its launcher is `scripts/monitor/pstack-monitor` under that plugin. Users who want the monitor install it separately. +- The monitor reaches the pstack plugin only through the lane journal. `scripts/monitor/lane-contract.ts` restates the journal's shapes, a repository test fails when they drift from the runner's types, and a static check fails if monitor code imports the runner directly. + ## Unreleased: agent monitor - Add `/pstack:monitor` and `pstack-monitor`, a read-only local server that draws pstack work on this machine as a live node canvas: each session that runs pstack, every subagent it spawns, and external Codex, Grok, DeepSeek, and MiniMax lanes, with each agent's model and messages. `--all` or **Show all** shows every session. Tracked in [pstack-flex #23](https://github.com/thisguymartin/pstack-flex/issues/23). diff --git a/NOTICE.md b/NOTICE.md index 6f261609..e1114255 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -58,7 +58,7 @@ Files authored for this port (not derived from upstream): - `plugins/pstack/skills/babysit/SKILL.md` (independently authored; workflow informed by Cursor's public `/babysit` behavior) - `plugins/pstack/agents/pstack-fable-*.md` and `plugins/pstack/agents/pstack-opus-*.md` (Claude-native frontier lanes at each selectable effort) - `plugins/pstack/hooks/hooks.json`, `plugins/pstack/hooks/session-start`, and `plugins/pstack/hooks/session-start-context.md` (the SessionStart hook and its opt-in gate) -- `plugins/pstack/skills/monitor/SKILL.md` and `plugins/pstack/skills/poteto-mode/scripts/monitor/` (the pstack-flex agent monitor) +- `plugins/pstack-monitor/` (the pstack-flex agent monitor plugin) - `NOTICE.md` (this file) - `README.md` - `CHANGES.md` diff --git a/README.md b/README.md index df6f2eed..55a6936e 100644 --- a/README.md +++ b/README.md @@ -146,13 +146,20 @@ That is the main workflow. The other skills are there when poteto-mode needs the ### 3. Watch your agents (optional) -The monitor does not start by itself. Start it when you want to watch a run: +The monitor is a separate plugin in the same marketplace, so install it only if you want it: ```text -/pstack:monitor +/plugin install pstack-monitor@pstack-flex # Claude Code +codex plugin add pstack-monitor@pstack-flex # Codex ``` -In Codex, ask for `pstack:monitor`. It prints a link to a local page that draws each session running pstack, the agents it spawned, the external lanes pstack launched, and what each one is doing now, live. It keeps running in the background until you say "pstack, kill the monitor" or restart the machine. See [Watching your agents](docs/USAGE.md#watching-your-agents-the-monitor) for the terminal commands and troubleshooting. +It does not start by itself. Start it when you want to watch a run: + +```text +/pstack-monitor:monitor +``` + +In Codex, ask for `pstack-monitor:monitor`. It prints a link to a local page that draws each session running pstack, the agents it spawned, the external lanes pstack launched, and what each one is doing now, live. It keeps running in the background until you say "pstack, kill the monitor" or restart the machine. See [Watching your agents](docs/USAGE.md#watching-your-agents-the-monitor) for the terminal commands and troubleshooting. ## Useful skills diff --git a/UPSTREAM-FLEX.md b/UPSTREAM-FLEX.md index 891e5b18..89d1d30c 100644 --- a/UPSTREAM-FLEX.md +++ b/UPSTREAM-FLEX.md @@ -37,7 +37,7 @@ All flex changes are additive and live in port-owned files so upstream merges st - The assignment-first restructure of `skills/setup-pstack/SKILL.md` - `docs/LANES.md`, this file, the README fork section, and the NOTICE/LICENSE/CHANGES additions - `plugins/pstack/hooks/session-start-context.md`, which the fork rewrote from open-pstack's auto-fire mandate into an opt-in gate, and the docs lines that describe it -- The agent monitor: `plugins/pstack/skills/monitor/`, `plugins/pstack/skills/poteto-mode/scripts/monitor/`, the `monitor` entries in the scripts `package.json`, and the "Agent monitor" section of `docs/reference.md`. Upstream has no equivalent, so none of these conflict on a sync. +- The agent monitor plugin: everything under `plugins/pstack-monitor/`, its marketplace entries, its CI steps, and the "Agent monitor" section of `docs/reference.md`. Upstream has no equivalent, so none of these conflict on a sync. - The lane journal: `runner/flex-journal.ts` and its test (new), its call sites in `runner/{types,run,cli}.ts` (an optional stdout callback on the model run, the journal opened after output reservation and finished on both return paths, and the `--label` flag), the journal tests in `run.test.ts`, and the pstack-flex paragraph after the invocation block in `references/provider-dispatch.md`. On a sync, keep these call sites; they change no receipt or exit status. Every upstream skill body is byte-unchanged except for the default-descriptor mentions listed above. Since [#17](https://github.com/thisguymartin/pstack-flex/issues/17), the stock matrix carries three fork-owned GPT-6 rows and the first-run sheet uses them, so those two surfaces conflict on every upstream sync and are resolved by hand: keep the fork's rows and defaults, take upstream's wording for everything else. diff --git a/docs/USAGE.md b/docs/USAGE.md index 0d77bcdd..ca4058af 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -237,18 +237,25 @@ Status comes from evidence: ### Start and stop +The monitor ships as its own plugin, `pstack-monitor`, in the same marketplace. Install it once: + +```text +/plugin install pstack-monitor@pstack-flex # Claude Code +codex plugin add pstack-monitor@pstack-flex # Codex +``` + Nothing starts the monitor for you. Start it once; it runs in the background until you stop it or restart the machine. ```text -/pstack:monitor # Claude Code -Use pstack:monitor. # Codex +/pstack-monitor:monitor # Claude Code +Use pstack-monitor:monitor. # Codex ``` Either prints a link such as `http://127.0.0.1:47317/?token=…`. The token changes on every start. From a terminal, run the launcher inside the installed plugin (it needs Bun): ```shell -~/.claude/plugins/cache/open-pstack/pstack//skills/poteto-mode/scripts/monitor/pstack-monitor start --parent claude -~/.codex/plugins/cache/open-pstack/pstack//skills/poteto-mode/scripts/monitor/pstack-monitor start --parent codex +~/.claude/plugins/cache/pstack-flex/pstack-monitor//scripts/monitor/pstack-monitor start --parent claude +~/.codex/plugins/cache/pstack-flex/pstack-monitor//scripts/monitor/pstack-monitor start --parent codex ``` Options: `--all`, `--port ` (default 47317), `--hours ` (default 24), `--focus `. diff --git a/docs/reference.md b/docs/reference.md index c1034c75..81073a85 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -56,14 +56,17 @@ The marketplace install is the normal user path. Direct links are only for testi . ├── .claude-plugin/marketplace.json # Claude Code marketplace manifest (repo root) ├── .agents/plugins/marketplace.json # Codex marketplace manifest (repo root) -├── plugins/pstack/ # the plugin itself +├── plugins/pstack/ # the pstack plugin │ ├── .claude-plugin/plugin.json # Claude Code manifest │ ├── .codex-plugin/plugin.json # Codex manifest (skills: ./skills/) │ ├── skills/ # 55 skills shared by Claude Code and Codex │ │ ├── poteto-mode/references/{codex-tools,provider-dispatch}.md # tool + provider routing -│ │ └── poteto-mode/scripts/ # bun/bash/node tooling: watch-pr, orch, runner, monitor, check-plan.mjs, worktree-audit.sh +│ │ └── poteto-mode/scripts/ # bun/bash/node tooling: watch-pr, orch, runner, check-plan.mjs, worktree-audit.sh │ ├── hooks/ # SessionStart opt-in gate: pstack runs only on request (Claude Code and Codex) │ └── agents/ # Claude subagents, including native Fable and Opus lanes at each selectable effort +├── plugins/pstack-monitor/ # optional agent monitor plugin (its own manifests, skill, and Bun package) +│ ├── skills/monitor/SKILL.md # start, stop, status, doctor +│ └── scripts/monitor/ # server, adapters, web UI, lane-contract.ts (the lane journal interface) ├── tests/skill-collision-repro.sh # native-skill package invariants and Claude invocation checks ├── LICENSE # pstack upstream MIT ├── LICENSE-cursor-team-kit # cursor-team-kit upstream MIT @@ -151,11 +154,11 @@ The table uses the short upstream names. Claude Code exposes each native skill w | `/fix-merge-conflicts` | non-interactively resolve merge conflicts, validate, finalize | | `/get-pr-comments` | fetch and summarize review comments from the active PR | | `/what-did-i-get-done` | summarize authored commits over a user-chosen period | -| `/monitor` | open a live local view of running agents, their spawn tree, and each agent's activity (pstack-flex) | +| `/pstack-monitor:monitor` | open a live local view of running agents, their spawn tree, and each agent's activity (separate `pstack-monitor` plugin) | ## Agent monitor -`/monitor` starts `skills/poteto-mode/scripts/monitor/pstack-monitor`, a read-only server on `127.0.0.1` (default port 47317), and prints a link with a per-start access token. The page draws one session's agents as a node canvas and streams any agent's timeline in a resizable side panel. +The monitor is a separate plugin, `plugins/pstack-monitor/`, installed as `pstack-monitor@pstack-flex`. Its only interface with the pstack plugin is the lane journal, restated in `scripts/monitor/lane-contract.ts`; a repository test fails when that copy drifts from the runner's types. `/pstack-monitor:monitor` starts `scripts/monitor/pstack-monitor`, a read-only server on `127.0.0.1` (default port 47317), and prints a link with a per-start access token. The page draws one session's agents as a node canvas and streams any agent's timeline in a resizable side panel. - **Scope.** The server indexes every Claude Code and Codex session and marks a spawn tree as pstack work when any member shows pstack evidence: `attributionPlugin`/`attributionSkill`/`attributionAgent` on Claude records, a `Skill` or `Agent` call naming `pstack:`, a `/pstack:` command, a `pstack:` subagent sidecar, a Codex root prompt naming pstack or a call into `pstack-runner` or pstack's skills, or any runner lane. The page shows marked trees by default; **Show all** or `?all=1` (`start --all`) shows everything. - **Sources.** Claude Code session and subagent transcripts, `.meta.json` sidecars, and `~/.claude/sessions/.json` process records; Codex rollouts under `~/.codex/sessions/`. No hook, no harness setting. `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, and `PSTACK_FLEX_MONITOR_DIR` move them. diff --git a/plugins/pstack-monitor/.claude-plugin/plugin.json b/plugins/pstack-monitor/.claude-plugin/plugin.json new file mode 100644 index 00000000..6bda2c14 --- /dev/null +++ b/plugins/pstack-monitor/.claude-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "pstack-monitor", + "displayName": "pstack monitor", + "version": "1.0.0", + "description": "A live, read-only local view of pstack work: Claude Code and Codex sessions, their subagents, and pstack's external model lanes, with what each agent is doing now.", + "author": { + "name": "Martin Patino" + }, + "homepage": "https://github.com/thisguymartin/pstack-flex", + "repository": "https://github.com/thisguymartin/pstack-flex", + "license": "MIT", + "keywords": ["pstack", "monitor", "agents", "observability", "subagents"] +} diff --git a/plugins/pstack-monitor/.codex-plugin/plugin.json b/plugins/pstack-monitor/.codex-plugin/plugin.json new file mode 100644 index 00000000..191fbc9d --- /dev/null +++ b/plugins/pstack-monitor/.codex-plugin/plugin.json @@ -0,0 +1,23 @@ +{ + "name": "pstack-monitor", + "version": "1.0.0", + "description": "A live, read-only local view of pstack work: Claude Code and Codex sessions, their subagents, and pstack's external model lanes, with what each agent is doing now.", + "author": { + "name": "Martin Patino" + }, + "homepage": "https://github.com/thisguymartin/pstack-flex", + "repository": "https://github.com/thisguymartin/pstack-flex", + "license": "MIT", + "keywords": ["pstack", "monitor", "agents", "observability", "subagents"], + "skills": "./skills/", + "interface": { + "displayName": "pstack monitor", + "shortDescription": "Watch pstack's agents and model lanes live in a local browser view", + "longDescription": "Starts a read-only local server that draws each pstack session, its subagents, and its external Codex, Grok, DeepSeek, and MiniMax lanes as a live graph. Optional companion to the pstack plugin.", + "developerName": "pstack-flex maintainers", + "category": "Developer Tools", + "capabilities": ["Interactive", "Read"], + "defaultPrompt": ["Show me the agents."], + "websiteURL": "https://github.com/thisguymartin/pstack-flex" + } +} diff --git a/plugins/pstack-monitor/scripts/bun.lock b/plugins/pstack-monitor/scripts/bun.lock new file mode 100644 index 00000000..2984ebb2 --- /dev/null +++ b/plugins/pstack-monitor/scripts/bun.lock @@ -0,0 +1,62 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "@pstack-flex/monitor", + "devDependencies": { + "bun-types": "1.3.14", + "typescript": "7.0.2", + }, + }, + }, + "packages": { + "@types/node": ["@types/node@26.6.4", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg=="], + + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + + "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], + } +} diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapter.ts b/plugins/pstack-monitor/scripts/monitor/adapter.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapter.ts rename to plugins/pstack-monitor/scripts/monitor/adapter.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/blocks.ts b/plugins/pstack-monitor/scripts/monitor/adapters/blocks.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/blocks.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/blocks.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/claude.test.ts b/plugins/pstack-monitor/scripts/monitor/adapters/claude.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/claude.test.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/claude.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/claude.ts b/plugins/pstack-monitor/scripts/monitor/adapters/claude.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/claude.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/claude.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.test.ts b/plugins/pstack-monitor/scripts/monitor/adapters/codex.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.test.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/codex.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.ts b/plugins/pstack-monitor/scripts/monitor/adapters/codex.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/codex.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/codex.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/lane.test.ts b/plugins/pstack-monitor/scripts/monitor/adapters/lane.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/lane.test.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/lane.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/lane.ts b/plugins/pstack-monitor/scripts/monitor/adapters/lane.ts similarity index 97% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/lane.ts rename to plugins/pstack-monitor/scripts/monitor/adapters/lane.ts index c1eaa560..6e9ae362 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/monitor/adapters/lane.ts +++ b/plugins/pstack-monitor/scripts/monitor/adapters/lane.ts @@ -1,6 +1,5 @@ import { relative, sep } from "node:path"; -import type { LaneRecord } from "../../runner/flex-journal.ts"; -import type { ReceiptStatus, RunnerReceipt } from "../../runner/types.ts"; +import type { LaneReceipt, LaneRecord, ReceiptStatus } from "../lane-contract.ts"; import { NOTHING, parsed, @@ -227,7 +226,7 @@ export function laneAdapter(root: string): Adapter { if (record === null) return problem({ kind: "shape", recordType: location.file, detail: "not a JSON object" }); const id = laneId(location.lane); if (location.file === "lane.json") return laneRecord(path, id, record as Partial); - return laneReceipt(id, record as Partial); + return laneReceipt(id, record as Partial); }, removed(path: string) { @@ -290,7 +289,7 @@ const RECEIPT_STATUSES = new Set([ "malformed-output", ]); -function laneReceipt(id: AgentId, receipt: Partial): Parsed { +function laneReceipt(id: AgentId, receipt: Partial): Parsed { const status = text(receipt.status); const provider = text(receipt.provider); if (status === null || !RECEIPT_STATUSES.has(status) || provider === null) { diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/assets.test.ts b/plugins/pstack-monitor/scripts/monitor/assets.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/assets.test.ts rename to plugins/pstack-monitor/scripts/monitor/assets.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/assets.ts b/plugins/pstack-monitor/scripts/monitor/assets.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/assets.ts rename to plugins/pstack-monitor/scripts/monitor/assets.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/cli.ts b/plugins/pstack-monitor/scripts/monitor/cli.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/cli.ts rename to plugins/pstack-monitor/scripts/monitor/cli.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/daemon.ts b/plugins/pstack-monitor/scripts/monitor/daemon.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/daemon.ts rename to plugins/pstack-monitor/scripts/monitor/daemon.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/doctor.ts b/plugins/pstack-monitor/scripts/monitor/doctor.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/doctor.ts rename to plugins/pstack-monitor/scripts/monitor/doctor.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/domain.ts b/plugins/pstack-monitor/scripts/monitor/domain.ts similarity index 99% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/domain.ts rename to plugins/pstack-monitor/scripts/monitor/domain.ts index 8654817f..d467f951 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/monitor/domain.ts +++ b/plugins/pstack-monitor/scripts/monitor/domain.ts @@ -1,4 +1,4 @@ -import type { AccessMode, NormalizedUsage, ReceiptStatus } from "../runner/types.ts"; +import type { AccessMode, NormalizedUsage, ReceiptStatus } from "./lane-contract.ts"; // pstack-flex addition. Shared by the monitor server and the browser bundle, // so this file imports nothing from Bun, Node, or the DOM. diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/format.test.ts b/plugins/pstack-monitor/scripts/monitor/format.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/format.test.ts rename to plugins/pstack-monitor/scripts/monitor/format.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/format.ts b/plugins/pstack-monitor/scripts/monitor/format.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/format.ts rename to plugins/pstack-monitor/scripts/monitor/format.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/fs.ts b/plugins/pstack-monitor/scripts/monitor/fs.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/fs.ts rename to plugins/pstack-monitor/scripts/monitor/fs.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/graph.ts b/plugins/pstack-monitor/scripts/monitor/graph.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/graph.ts rename to plugins/pstack-monitor/scripts/monitor/graph.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/index.test.ts b/plugins/pstack-monitor/scripts/monitor/index.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/index.test.ts rename to plugins/pstack-monitor/scripts/monitor/index.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/index.ts b/plugins/pstack-monitor/scripts/monitor/index.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/index.ts rename to plugins/pstack-monitor/scripts/monitor/index.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/journal.test.ts b/plugins/pstack-monitor/scripts/monitor/journal.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/journal.test.ts rename to plugins/pstack-monitor/scripts/monitor/journal.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/journal.ts b/plugins/pstack-monitor/scripts/monitor/journal.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/journal.ts rename to plugins/pstack-monitor/scripts/monitor/journal.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/json.ts b/plugins/pstack-monitor/scripts/monitor/json.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/json.ts rename to plugins/pstack-monitor/scripts/monitor/json.ts diff --git a/plugins/pstack-monitor/scripts/monitor/lane-contract.test.ts b/plugins/pstack-monitor/scripts/monitor/lane-contract.test.ts new file mode 100644 index 00000000..64fa8edd --- /dev/null +++ b/plugins/pstack-monitor/scripts/monitor/lane-contract.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "bun:test"; +import * as runnerJournal from "../../../pstack/skills/poteto-mode/scripts/runner/flex-journal.ts"; +import type * as Runner from "../../../pstack/skills/poteto-mode/scripts/runner/types.ts"; +import { LANES_DIR_VAR, type LaneReceipt, type LaneRecord } from "./lane-contract.ts"; +import { lanesRoot } from "./sources.ts"; +import type * as Monitor from "./lane-contract.ts"; + +// Repository-only check: the installed plugins never see each other's files. +type Same = [A] extends [B] ? ([B] extends [A] ? true : false) : false; +const same = (): T => true as T; + +same>(); +same>(); +same>(); +same>(); +same>(); +same>(); +same>(); +same>>(); + +describe("lane journal contract", () => { + it("reads the lanes directory the runner writes", () => { + expect(LANES_DIR_VAR).toBe(runnerJournal.LANES_DIR_VAR); + expect(lanesRoot({})).toBe(runnerJournal.lanesRoot({})); + expect(lanesRoot({ [LANES_DIR_VAR]: "/tmp/lanes" })).toBe(runnerJournal.lanesRoot({ [LANES_DIR_VAR]: "/tmp/lanes" })); + }); +}); diff --git a/plugins/pstack-monitor/scripts/monitor/lane-contract.ts b/plugins/pstack-monitor/scripts/monitor/lane-contract.ts new file mode 100644 index 00000000..ef4caa61 --- /dev/null +++ b/plugins/pstack-monitor/scripts/monitor/lane-contract.ts @@ -0,0 +1,68 @@ +// pstack-flex addition. The lane journal is the only interface between the +// pstack plugin's runner (which writes it) and this plugin (which reads it). +// The two plugins install separately, so this file restates the runner's +// shapes; lane-contract.test.ts fails in the repository when they drift. + +// Types only: the web page imports these through domain.ts and has no Node types. +// lanesRoot lives in sources.ts. + +export const LANES_DIR_VAR = "PSTACK_FLEX_LANES_DIR"; + +export type Parent = "claude" | "codex"; +export type Provider = "claude" | "codex" | "grok" | "deepseek" | "minimax"; +export type Effort = "low" | "medium" | "high" | "xhigh" | "max"; +export type AccessMode = "read-only" | "isolated-write"; + +export type ReceiptStatus = + | "complete" + | "cancelled" + | "unavailable-cli" + | "unauthenticated" + | "unavailable-model" + | "timed-out" + | "child-failed" + | "malformed-output"; + +export interface NormalizedUsage { + readonly inputTokens?: number; + readonly cachedInputTokens?: number; + readonly cacheCreationInputTokens?: number; + readonly outputTokens?: number; + readonly reasoningTokens?: number; + readonly totalTokens?: number; +} + +/** `lane.json`: written once when the lane starts. */ +export interface LaneRecord { + readonly schemaVersion: 1; + readonly laneId: string; + readonly runnerPid: number; + readonly startedAt: string; + readonly parent: Parent; + readonly parentSessionId: string | null; + readonly provider: Provider; + readonly model: string; + readonly effort: Effort; + readonly mode: AccessMode; + readonly label: string | null; + readonly cwd: string; + readonly promptPath: string; + readonly promptHead: string | null; + readonly outputPath: string; + readonly receiptPath: string; +} + +/** The fields of `receipt.json` (a copy of the runner's receipt) that the monitor reads. */ +export interface LaneReceipt { + readonly schemaVersion: 1; + readonly status: ReceiptStatus; + readonly parent: Parent; + readonly provider: Provider; + readonly model: string; + readonly effort: Effort; + readonly mode: AccessMode; + readonly completedAt: string; + readonly reportedModel: string | null; + readonly usage: NormalizedUsage | null; + readonly error: { readonly message: string; readonly evidence: string } | null; +} diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/layout.test.ts b/plugins/pstack-monitor/scripts/monitor/layout.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/layout.test.ts rename to plugins/pstack-monitor/scripts/monitor/layout.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/layout.ts b/plugins/pstack-monitor/scripts/monitor/layout.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/layout.ts rename to plugins/pstack-monitor/scripts/monitor/layout.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/monitor.ts b/plugins/pstack-monitor/scripts/monitor/monitor.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/monitor.ts rename to plugins/pstack-monitor/scripts/monitor/monitor.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/probe.ts b/plugins/pstack-monitor/scripts/monitor/probe.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/probe.ts rename to plugins/pstack-monitor/scripts/monitor/probe.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/pstack-monitor b/plugins/pstack-monitor/scripts/monitor/pstack-monitor similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/pstack-monitor rename to plugins/pstack-monitor/scripts/monitor/pstack-monitor diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/record.ts b/plugins/pstack-monitor/scripts/monitor/record.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/record.ts rename to plugins/pstack-monitor/scripts/monitor/record.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/server.test.ts b/plugins/pstack-monitor/scripts/monitor/server.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/server.test.ts rename to plugins/pstack-monitor/scripts/monitor/server.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/server.ts b/plugins/pstack-monitor/scripts/monitor/server.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/server.ts rename to plugins/pstack-monitor/scripts/monitor/server.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/sources.ts b/plugins/pstack-monitor/scripts/monitor/sources.ts similarity index 81% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/sources.ts rename to plugins/pstack-monitor/scripts/monitor/sources.ts index 8e04c130..81ef40d1 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/monitor/sources.ts +++ b/plugins/pstack-monitor/scripts/monitor/sources.ts @@ -1,6 +1,6 @@ import { homedir } from "node:os"; import { join } from "node:path"; -import { lanesRoot } from "../runner/flex-journal.ts"; +import { LANES_DIR_VAR } from "./lane-contract.ts"; import type { Adapter } from "./adapter.ts"; import { claudeAdapter } from "./adapters/claude.ts"; import { codexAdapter } from "./adapters/codex.ts"; @@ -22,6 +22,11 @@ function configured(env: NodeJS.ProcessEnv, name: string): string | null { return value !== undefined && value.trim().length > 0 ? value : null; } +/** The runner's lane journal directory; must match the runner's own lanesRoot. */ +export function lanesRoot(env: NodeJS.ProcessEnv = process.env): string { + return configured(env, LANES_DIR_VAR) ?? join(homedir(), ".pstack-flex", "lanes"); +} + export function homes(env: NodeJS.ProcessEnv = process.env): Homes { const home = homedir(); return { diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/store.test.ts b/plugins/pstack-monitor/scripts/monitor/store.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/store.test.ts rename to plugins/pstack-monitor/scripts/monitor/store.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/store.ts b/plugins/pstack-monitor/scripts/monitor/store.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/store.ts rename to plugins/pstack-monitor/scripts/monitor/store.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/tail.test.ts b/plugins/pstack-monitor/scripts/monitor/tail.test.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/tail.test.ts rename to plugins/pstack-monitor/scripts/monitor/tail.test.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/tail.ts b/plugins/pstack-monitor/scripts/monitor/tail.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/tail.ts rename to plugins/pstack-monitor/scripts/monitor/tail.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/tsconfig.json b/plugins/pstack-monitor/scripts/monitor/tsconfig.json similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/tsconfig.json rename to plugins/pstack-monitor/scripts/monitor/tsconfig.json diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/version.ts b/plugins/pstack-monitor/scripts/monitor/version.ts similarity index 95% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/version.ts rename to plugins/pstack-monitor/scripts/monitor/version.ts index ae70948a..3eeeabb3 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/monitor/version.ts +++ b/plugins/pstack-monitor/scripts/monitor/version.ts @@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url"; // code changes, so `start` can replace a server left over from another build. const MONITOR_DIR = fileURLToPath(new URL(".", import.meta.url)); -const PLUGIN_DIR = join(MONITOR_DIR, "..", "..", "..", ".."); +const PLUGIN_DIR = join(MONITOR_DIR, "..", ".."); function pluginVersion(): string { for (const manifest of [".claude-plugin", ".codex-plugin"]) { diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/app.css b/plugins/pstack-monitor/scripts/monitor/web/app.css similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/app.css rename to plugins/pstack-monitor/scripts/monitor/web/app.css diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/canvas.ts b/plugins/pstack-monitor/scripts/monitor/web/canvas.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/canvas.ts rename to plugins/pstack-monitor/scripts/monitor/web/canvas.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/dom.ts b/plugins/pstack-monitor/scripts/monitor/web/dom.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/dom.ts rename to plugins/pstack-monitor/scripts/monitor/web/dom.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/index.html b/plugins/pstack-monitor/scripts/monitor/web/index.html similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/index.html rename to plugins/pstack-monitor/scripts/monitor/web/index.html diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/main.ts b/plugins/pstack-monitor/scripts/monitor/web/main.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/main.ts rename to plugins/pstack-monitor/scripts/monitor/web/main.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/panel.ts b/plugins/pstack-monitor/scripts/monitor/web/panel.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/panel.ts rename to plugins/pstack-monitor/scripts/monitor/web/panel.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/rail.ts b/plugins/pstack-monitor/scripts/monitor/web/rail.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/rail.ts rename to plugins/pstack-monitor/scripts/monitor/web/rail.ts diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/web/tsconfig.json b/plugins/pstack-monitor/scripts/monitor/web/tsconfig.json similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/web/tsconfig.json rename to plugins/pstack-monitor/scripts/monitor/web/tsconfig.json diff --git a/plugins/pstack/skills/poteto-mode/scripts/monitor/wire.ts b/plugins/pstack-monitor/scripts/monitor/wire.ts similarity index 100% rename from plugins/pstack/skills/poteto-mode/scripts/monitor/wire.ts rename to plugins/pstack-monitor/scripts/monitor/wire.ts diff --git a/plugins/pstack-monitor/scripts/package.json b/plugins/pstack-monitor/scripts/package.json new file mode 100644 index 00000000..49b53ff8 --- /dev/null +++ b/plugins/pstack-monitor/scripts/package.json @@ -0,0 +1,13 @@ +{ + "name": "@pstack-flex/monitor", + "private": true, + "type": "module", + "scripts": { + "test": "\"$npm_execpath\" test --parallel monitor", + "typecheck": "tsc --project monitor/tsconfig.json --noEmit --strict && tsc --project monitor/web/tsconfig.json --noEmit --strict" + }, + "devDependencies": { + "bun-types": "1.3.14", + "typescript": "7.0.2" + } +} diff --git a/plugins/pstack/skills/monitor/SKILL.md b/plugins/pstack-monitor/skills/monitor/SKILL.md similarity index 92% rename from plugins/pstack/skills/monitor/SKILL.md rename to plugins/pstack-monitor/skills/monitor/SKILL.md index ce1f2c5b..26513cd2 100644 --- a/plugins/pstack/skills/monitor/SKILL.md +++ b/plugins/pstack-monitor/skills/monitor/SKILL.md @@ -14,7 +14,7 @@ Run one command and relay its output. | stop, kill, close, or shut down the monitor | `pstack-monitor stop` | | check whether it is running, or get the link again | `pstack-monitor status` | -The launcher is `skills/poteto-mode/scripts/monitor/pstack-monitor` under the installed plugin. Pass the harness you are running in as `--parent`. +The launcher is `scripts/monitor/pstack-monitor` under the installed `pstack-monitor` plugin. Pass the harness you are running in as `--parent`. `start` returns at once and prints one link. Give the user that link exactly as printed. The link carries an access token: never paste it anywhere else, and never open it with a fetch tool. diff --git a/plugins/pstack/skills/poteto-mode/scripts/package.json b/plugins/pstack/skills/poteto-mode/scripts/package.json index 30e33c80..a63c436a 100644 --- a/plugins/pstack/skills/poteto-mode/scripts/package.json +++ b/plugins/pstack/skills/poteto-mode/scripts/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "scripts": { - "test": "\"$npm_execpath\" test --parallel bootstrap orch watch-pr runner check-plan monitor", - "typecheck": "tsc --project watch-pr/tsconfig.json --noEmit --strict && tsc --project runner/tsconfig.json --noEmit --strict && tsc --project check-plan.tsconfig.json --noEmit --strict && tsc --project monitor/tsconfig.json --noEmit --strict && tsc --project monitor/web/tsconfig.json --noEmit --strict" + "test": "\"$npm_execpath\" test --parallel bootstrap orch watch-pr runner check-plan", + "typecheck": "tsc --project watch-pr/tsconfig.json --noEmit --strict && tsc --project runner/tsconfig.json --noEmit --strict && tsc --project check-plan.tsconfig.json --noEmit --strict" }, "dependencies": { "commander": "14.0.0" diff --git a/tests/skill-collision-repro.sh b/tests/skill-collision-repro.sh index b9c237d7..8922c7a0 100755 --- a/tests/skill-collision-repro.sh +++ b/tests/skill-collision-repro.sh @@ -45,6 +45,23 @@ else fail=1 fi +mvc="$(verof "$repo/plugins/pstack-monitor/.claude-plugin/plugin.json")" +mvx="$(verof "$repo/plugins/pstack-monitor/.codex-plugin/plugin.json")" +mvm="$(sed -n '/"name": "pstack-monitor"/,/}/p' "$repo/.claude-plugin/marketplace.json" | verof /dev/stdin)" +if [ -n "$mvc" ] && [ "$mvc" = "$mvx" ] && [ "$mvc" = "$mvm" ]; then + note "ok: pstack-monitor version matches across its 2 manifests and the marketplace ($mvc)" +else + note "FAIL: pstack-monitor version differs: claude-plugin=$mvc codex-plugin=$mvx marketplace=$mvm" + fail=1 +fi + +if grep -rqE 'from "[^"]*/runner/' "$repo/plugins/pstack-monitor/scripts/monitor" --include='*.ts' --exclude='*.test.ts'; then + note "FAIL: pstack-monitor imports the pstack runner directly; use monitor/lane-contract.ts" + fail=1 +else + note "ok: pstack-monitor reaches the runner only through the lane journal contract" +fi + # Active configuration must use Claude's rolling family aliases. Concrete # provider reports may still appear in runner fixtures, but no shipped # descriptor, native-agent model field, or live test invocation may pin one. From aa11f19ab7bc5ebfe09592123bbf0a18ad86ef0c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 01:49:25 +0000 Subject: [PATCH 3/5] fix: correct stale docs and tighten Codex pstack detection - codex-tools.md said the default panel keeps four-provider diversity. It runs four lanes on three providers; Fable and Opus are both Claude. - LANES.md said OpenRouter needs a local translator. Per issue #7, OpenRouter documents a direct Claude Code connection but guarantees it only for Anthropic models, so other models must pass the #7 probes before a lane ships. - The monitor's Codex rule matched "pstack" inside "pstack-flex" and "pstack-monitor", so talking about this repo marked a Codex session as pstack work. It now matches pstack only as its own word. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LSWNeBzAMt5FQZVziZCGLs --- CHANGES.md | 1 + docs/LANES.md | 2 +- .../pstack-monitor/scripts/monitor/adapters/codex.test.ts | 7 +++++-- plugins/pstack-monitor/scripts/monitor/adapters/codex.ts | 3 ++- .../pstack/skills/poteto-mode/references/codex-tools.md | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a0acef5f..fd57f9c7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ - The marketplace is now `pstack-flex` (was `open-pstack`) in both the Claude Code and Codex marketplace files. Install with `pstack@pstack-flex`. The plugin keeps the name `pstack`, so skill names such as `pstack:poteto-mode` are unchanged. Manifests, package names, and docs point at `thisguymartin/pstack-flex`; attribution to open-pstack, pstack-claude, and Cursor pstack stays in README and NOTICE. - The agent monitor moves out of the `pstack` plugin into its own plugin, `pstack-monitor`, in the same marketplace. It has its own manifests, version (1.0.0), Bun package, and CI steps. Its skill is now `pstack-monitor:monitor` and its launcher is `scripts/monitor/pstack-monitor` under that plugin. Users who want the monitor install it separately. - The monitor reaches the pstack plugin only through the lane journal. `scripts/monitor/lane-contract.ts` restates the journal's shapes, a repository test fails when they drift from the runner's types, and a static check fails if monitor code imports the runner directly. +- Fixes: `codex-tools.md` now says the default panel runs four lanes across three providers (it said four providers). `docs/LANES.md` replaces the stale claim that OpenRouter needs a local translator with OpenRouter's documented Claude Code connection and the probes from [#7](https://github.com/thisguymartin/pstack-flex/issues/7). The monitor's Codex rule no longer treats "pstack-flex" or "pstack-monitor" in a prompt as a request for pstack. ## Unreleased: agent monitor diff --git a/docs/LANES.md b/docs/LANES.md index ca4d8d86..36156629 100644 --- a/docs/LANES.md +++ b/docs/LANES.md @@ -126,7 +126,7 @@ Quality note: this trades peak capability for cost control. The hardest-task rol ## Optional lanes -- **OpenRouter (off by default).** OpenRouter has no Anthropic-format endpoint, so a lane needs a local translator that serves `/v1/messages` — musistudio/claude-code-router or a version-pinned LiteLLM — with `ANTHROPIC_BASE_URL` pointed at it. That is one extra long-running local process, which is why it is documented rather than shipped. Expect roughly a 5.5% credit fee on top of provider list prices. If you build it, model it as another gateway provider in `flex-providers.ts`. +- **OpenRouter (not shipped).** OpenRouter documents a direct Claude Code connection (`ANTHROPIC_BASE_URL=https://openrouter.ai/api`, `ANTHROPIC_AUTH_TOKEN` from `OPENROUTER_API_KEY`, and an explicitly empty `ANTHROPIC_API_KEY`), so no local translator is needed. It guarantees that route only for Anthropic models, so DeepSeek, MiniMax, and other catalog models through OpenRouter must pass the live probes in [issue #7](https://github.com/thisguymartin/pstack-flex/issues/7) (tool call, second turn, effort, model identity) before a lane ships. If it does, model it as another gateway provider in `flex-providers.ts`. Expect a credit fee on top of provider list prices; check OpenRouter's current pricing page. - **Local via Ollama (planned).** Ollama serves an Anthropic-compatible API since v0.14, so a `local` gateway provider pointed at it is the natural next lane: full compute control, zero per-token cost, your hardware. Not wired in yet. ## Adding a gateway provider diff --git a/plugins/pstack-monitor/scripts/monitor/adapters/codex.test.ts b/plugins/pstack-monitor/scripts/monitor/adapters/codex.test.ts index 8e41ca84..b30027d3 100644 --- a/plugins/pstack-monitor/scripts/monitor/adapters/codex.test.ts +++ b/plugins/pstack-monitor/scripts/monitor/adapters/codex.test.ts @@ -116,9 +116,12 @@ describe("rollout", () => { const rootMeta = record(0, "session_meta", { id: parentThread, cwd: "/repo", source: "cli", cli_version: "0.160.0" }); const ask = (text: string) => record(1, "response_item", { type: "message", role: "user", content: [{ type: "input_text", text }] }); const shell = (cmd: string) => record(1, "response_item", { type: "function_call", name: "exec_command", arguments: JSON.stringify({ cmd }), call_id: "c1" }); - expect(flagged(parentThread, rootMeta, ask("Use pstack:monitor."))).toBe(true); + expect(flagged(parentThread, rootMeta, ask("Use pstack:poteto-mode."))).toBe(true); + expect(flagged(parentThread, rootMeta, ask("Use pstack for this bug"))).toBe(true); expect(flagged(parentThread, rootMeta, ask("Fix the parser"))).toBe(false); - expect(flagged(childThread, childMeta(), ask("Use pstack:monitor."))).toBe(false); + expect(flagged(parentThread, rootMeta, ask("Fix the typo in the pstack-flex README"))).toBe(false); + expect(flagged(parentThread, rootMeta, ask("Use pstack-monitor:monitor."))).toBe(false); + expect(flagged(childThread, childMeta(), ask("Use pstack:poteto-mode."))).toBe(false); expect(flagged(parentThread, rootMeta, shell("pstack-runner --parent codex --provider minimax"))).toBe(true); expect(flagged(parentThread, rootMeta, shell("cat ~/.codex/plugins/cache/open-pstack/pstack/1.5.0/skills/arena/SKILL.md"))).toBe(true); expect(flagged(parentThread, rootMeta, shell("cat ~/.codex/plugins/cache/pstack-flex/pstack/1.5.0/skills/arena/SKILL.md"))).toBe(true); diff --git a/plugins/pstack-monitor/scripts/monitor/adapters/codex.ts b/plugins/pstack-monitor/scripts/monitor/adapters/codex.ts index 701551b9..16ddeb32 100644 --- a/plugins/pstack-monitor/scripts/monitor/adapters/codex.ts +++ b/plugins/pstack-monitor/scripts/monitor/adapters/codex.ts @@ -39,7 +39,8 @@ const IGNORED_TYPES = new Set([ /** Paths and commands only pstack's skills and runner use. */ const PSTACK_MARKERS = ["pstack-runner", "pstack-flex/pstack/", "open-pstack/pstack/", "/pstack/skills/"]; -const PSTACK_WORD = /\bpstack\b/i; +// "pstack" as its own word; "pstack-flex" (the repo) and "pstack-monitor" (this plugin) are not requests for pstack. +const PSTACK_WORD = /\bpstack(?![-\w])/i; const IGNORED_ITEMS = new Set(["ghost_snapshot"]); diff --git a/plugins/pstack/skills/poteto-mode/references/codex-tools.md b/plugins/pstack/skills/poteto-mode/references/codex-tools.md index b7b2c9f3..68e49981 100644 --- a/plugins/pstack/skills/poteto-mode/references/codex-tools.md +++ b/plugins/pstack/skills/poteto-mode/references/codex-tools.md @@ -42,7 +42,7 @@ poteto-mode's Subagents section sets Claude-specific defaults (`subagent_type: " ## Models and providers -Do not replace every configured entry with a Codex model. `/setup-pstack` writes portable descriptors such as `claude:fable@max`, `codex:gpt-5.6-sol@max`, and `grok:grok-4.7@xhigh`. In a Codex parent, only `codex:*` is native. Route Claude and Grok descriptors through the external launcher exactly as `provider-dispatch.md` specifies. The current default panel intentionally keeps four-provider frontier diversity and contains no older GPT or Claude substitute. pstack-flex gateway descriptors (`deepseek:*`, `minimax:*`) also always route through the external launcher in a Codex parent; they are never `spawn_agent` lanes. +Do not replace every configured entry with a Codex model. `/setup-pstack` writes portable descriptors such as `claude:fable@max`, `codex:gpt-5.6-sol@max`, and `grok:grok-4.7@xhigh`. In a Codex parent, only `codex:*` is native. Route Claude and Grok descriptors through the external launcher exactly as `provider-dispatch.md` specifies. The current default panel runs four lanes across three providers (Fable and Opus are both Claude) and contains no older GPT or Claude substitute. pstack-flex gateway descriptors (`deepseek:*`, `minimax:*`) also always route through the external launcher in a Codex parent; they are never `spawn_agent` lanes. ## Claude built-in skills pstack references From 1f3be9fb432c89fbb8a8b9fc970f74f25392221a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 01:51:02 +0000 Subject: [PATCH 4/5] feat(pstack): add the intake and diff-behavior skills intake (#10) turns GitHub issues into ready-to-run poteto-mode briefs. It reads the issue and every comment, grounds it with how, picks one playbook, and writes a brief with an observable exit condition, a unit/live/perf verification plan, and numbered open questions. Briefs with open product questions are parked; ready ones get a worktree. It is read-only and launches poteto-mode only when asked. diff-behavior (#15) builds trunk and head in separate worktrees, runs one scenario set on both through swarm, normalizes, and classifies every difference as intended, unintended, or noise. Noise is fixed in the normalizer and rerun; ratios are never computed between unlike scenarios. Both are new fork-owned skills. No upstream skill body changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LSWNeBzAMt5FQZVziZCGLs --- CHANGES.md | 1 + README.md | 4 +- UPSTREAM-FLEX.md | 1 + docs/reference.md | 4 +- plugins/pstack/skills/diff-behavior/SKILL.md | 75 +++++++++++++++++++ .../references/report-template.md | 32 ++++++++ plugins/pstack/skills/intake/SKILL.md | 69 +++++++++++++++++ .../intake/references/brief-template.md | 42 +++++++++++ 8 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 plugins/pstack/skills/diff-behavior/SKILL.md create mode 100644 plugins/pstack/skills/diff-behavior/references/report-template.md create mode 100644 plugins/pstack/skills/intake/SKILL.md create mode 100644 plugins/pstack/skills/intake/references/brief-template.md diff --git a/CHANGES.md b/CHANGES.md index fd57f9c7..c63173bb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ - The marketplace is now `pstack-flex` (was `open-pstack`) in both the Claude Code and Codex marketplace files. Install with `pstack@pstack-flex`. The plugin keeps the name `pstack`, so skill names such as `pstack:poteto-mode` are unchanged. Manifests, package names, and docs point at `thisguymartin/pstack-flex`; attribution to open-pstack, pstack-claude, and Cursor pstack stays in README and NOTICE. - The agent monitor moves out of the `pstack` plugin into its own plugin, `pstack-monitor`, in the same marketplace. It has its own manifests, version (1.0.0), Bun package, and CI steps. Its skill is now `pstack-monitor:monitor` and its launcher is `scripts/monitor/pstack-monitor` under that plugin. Users who want the monitor install it separately. - The monitor reaches the pstack plugin only through the lane journal. `scripts/monitor/lane-contract.ts` restates the journal's shapes, a repository test fails when they drift from the runner's types, and a static check fails if monitor code imports the runner directly. +- New skills: `intake` ([#10](https://github.com/thisguymartin/pstack-flex/issues/10)) turns GitHub issues into ready-to-run poteto-mode briefs with a playbook, an observable exit condition, a verification plan, and a worktree; it is read-only and parks briefs with open product questions. `diff-behavior` ([#15](https://github.com/thisguymartin/pstack-flex/issues/15)) runs the same scenarios on trunk and head through `swarm`, normalizes, and classifies every difference as intended, unintended, or noise. Neither changes an upstream skill body; wiring them into poteto-mode and the multi-phase-plan regression lane is a follow-up. - Fixes: `codex-tools.md` now says the default panel runs four lanes across three providers (it said four providers). `docs/LANES.md` replaces the stale claim that OpenRouter needs a local translator with OpenRouter's documented Claude Code connection and the probes from [#7](https://github.com/thisguymartin/pstack-flex/issues/7). The monitor's Codex rule no longer treats "pstack-flex" or "pstack-monitor" in a prompt as a request for pstack. ## Unreleased: agent monitor diff --git a/README.md b/README.md index 55a6936e..7bc02647 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,9 @@ In Codex, ask for `pstack-monitor:monitor`. It prints a link to a local page tha | `maintain-verification-skill` | The project's verification instructions no longer match the product. | | `babysit` | A pull request needs CI failures and review comments handled until it is ready. | | `reflect` | A hard task is finished and its lessons should improve the next run. | -| `monitor` | You want to watch your agents work: a live graph of each pstack session, its subagents and model lanes, and what each is doing now. | +| `intake` | You have GitHub issues and want each turned into a ready-to-run brief with a playbook, an observable exit condition, and a worktree. | +| `diff-behavior` | You want to know what a change did from the outside: the same scenarios on trunk and head, with every unclaimed difference flagged. | +| `pstack-monitor:monitor` | You want to watch your agents work: a live graph of each pstack session, its subagents and model lanes, and what each is doing now. Separate plugin. | Plugin skills include `pstack:` in their name. In Claude Code, invoke a native skill such as `/pstack:architect`. In Codex, ask for the skill, such as `Use pstack:architect for this design.` See the [technical reference](docs/reference.md) for the full list. diff --git a/UPSTREAM-FLEX.md b/UPSTREAM-FLEX.md index 89d1d30c..53c1f3d3 100644 --- a/UPSTREAM-FLEX.md +++ b/UPSTREAM-FLEX.md @@ -37,6 +37,7 @@ All flex changes are additive and live in port-owned files so upstream merges st - The assignment-first restructure of `skills/setup-pstack/SKILL.md` - `docs/LANES.md`, this file, the README fork section, and the NOTICE/LICENSE/CHANGES additions - `plugins/pstack/hooks/session-start-context.md`, which the fork rewrote from open-pstack's auto-fire mandate into an opt-in gate, and the docs lines that describe it +- The `intake` and `diff-behavior` skills: `plugins/pstack/skills/intake/` and `plugins/pstack/skills/diff-behavior/`. Upstream has no equivalent, so they never conflict. - The agent monitor plugin: everything under `plugins/pstack-monitor/`, its marketplace entries, its CI steps, and the "Agent monitor" section of `docs/reference.md`. Upstream has no equivalent, so none of these conflict on a sync. - The lane journal: `runner/flex-journal.ts` and its test (new), its call sites in `runner/{types,run,cli}.ts` (an optional stdout callback on the model run, the journal opened after output reservation and finished on both return paths, and the `--label` flag), the journal tests in `run.test.ts`, and the pstack-flex paragraph after the invocation block in `references/provider-dispatch.md`. On a sync, keep these call sites; they change no receipt or exit status. diff --git a/docs/reference.md b/docs/reference.md index 81073a85..21400e47 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -59,7 +59,7 @@ The marketplace install is the normal user path. Direct links are only for testi ├── plugins/pstack/ # the pstack plugin │ ├── .claude-plugin/plugin.json # Claude Code manifest │ ├── .codex-plugin/plugin.json # Codex manifest (skills: ./skills/) -│ ├── skills/ # 55 skills shared by Claude Code and Codex +│ ├── skills/ # 56 skills shared by Claude Code and Codex │ │ ├── poteto-mode/references/{codex-tools,provider-dispatch}.md # tool + provider routing │ │ └── poteto-mode/scripts/ # bun/bash/node tooling: watch-pr, orch, runner, check-plan.mjs, worktree-audit.sh │ ├── hooks/ # SessionStart opt-in gate: pstack runs only on request (Claude Code and Codex) @@ -140,6 +140,8 @@ The table uses the short upstream names. Claude Code exposes each native skill w | `/figure-it-out` | design a rigorous, auditable playbook for a task no bundled playbook fits | | `/show-me-your-work` | log decisions to a reviewable tsv decision trail | | `/blast-radius` | find what a change could break beyond the diff and prove safety by running code | +| `/intake` | turn GitHub issues into ready-to-run poteto-mode briefs with a playbook, exit condition, and worktree (pstack-flex) | +| `/diff-behavior` | run the same scenarios on trunk and head and classify every observable difference as intended, unintended, or noise (pstack-flex) | | `/recall` | catch up on recent working context from chat history, live state, and the shared record | | `/setup-pstack` | configure pstack per-role model choices and per-family requested effort | | `/unslop` | clean up writing by removing AI tells | diff --git a/plugins/pstack/skills/diff-behavior/SKILL.md b/plugins/pstack/skills/diff-behavior/SKILL.md new file mode 100644 index 00000000..d2775e16 --- /dev/null +++ b/plugins/pstack/skills/diff-behavior/SKILL.md @@ -0,0 +1,75 @@ +--- +name: diff-behavior +description: "Run the same scenarios against trunk and head, list every observable difference in output, UI, logs, timing, and errors, and flag the ones the change didn't claim. Use for /diff-behavior, 'what did this PR change in behavior', 'prove no behavior change', a dependency bump, a migration, or a perf claim to confirm." +--- + +# Diff behavior + +A diff shows what changed in the code. This skill shows what changed in behavior, and whether all of it was intended. Both sides always run. A head-only run is not a diff. + +Companion to **blast-radius**. Blast radius proves the one fact a change is safe because of. Diff behavior drives the whole load-bearing surface on both builds and lists what differs. + +**Dispatch contract.** Scenario workers run through the **swarm** skill, which resolves `swarm workers` through [`provider-dispatch.md`](../poteto-mode/references/provider-dispatch.md). Workers never pick a model. No implicit timeout. A dropout is named by provider, model, and receipt, and never replaced. On Codex, resolve Claude tool names via [`codex-tools.md`](../poteto-mode/references/codex-tools.md). + +## Start + +Open a todolist with one entry per phase before starting. + +1. Build both sides +2. Pick scenarios +3. Run both +4. Normalize and diff +5. Classify +6. Report + +## Phase A: Build both sides + +1. Name the base (trunk by default, or the PR's base branch) and the head. Record both exact SHAs. +2. Create one worktree per side. Never use the primary checkout. +3. Build each side the same way: same command, same env, same config, same seed data. Note anything that cannot match, such as a migration that only head has. + +## Phase B: Pick scenarios + +1. Start from the project's verification skill and its feature map when one exists. When it does not, say so, and suggest the **create-verification-skill** skill as a follow-up. +2. Add scenarios from **how** on the touched surface and from every claim in the PR description or brief. +3. Keep the load-bearing scenarios even when the diff looks unrelated. Finding unexpected changes is the point. +4. Each scenario names what it captures: + + | Capture | Example | + | --- | --- | + | stdout and exit code | a CLI command | + | HTTP response | status, headers that matter, body | + | Screenshot | one UI state, compared by image diff as in the Visual parity playbook | + | Log lines | the lines a scenario emits, filtered by component | + | Metric | latency, memory, row count, with sample count and method | + | Files written | path and contents | + +5. Write the scenario set to `/scenarios.md` so it can be rerun. `` defaults to `.pstack/diff-behavior//`. + +## Phase C: Run both + +1. Run the **swarm** skill as a partition with one worker per scenario. Each worker runs base, then head, and captures both sides to `//base/` and `//head/`. Each brief names both SHAs and both worktree paths. +2. For timing scenarios, interleave base and head runs so drift hits both sides equally, and take enough samples to clear the noise (median or p95 of N, as in the Perf issue playbook). +3. Use the same seed, clock, and fixtures on both sides where the app allows it. + +## Phase D: Normalize and diff + +1. Strip what the app does not promise: timestamps, generated ids, temp paths, ordering of unordered collections. Put every rule in `/normalize.md` with its reason. +2. Diff what is left. Compare screenshots by image diff. Report metrics as base value, head value, and ratio. +3. Never compute a ratio between unlike scenarios. When base lacks the feature, record that and report head's absolute value against a stated budget instead. + +## Phase E: Classify + +Give every difference exactly one class: + +- **Intended.** A claim in the PR description or brief covers it. Cite the claim. +- **Unintended.** No claim covers it. This list is the deliverable. +- **Noise.** A normalizer gap. Fix the rule in `normalize.md` and rerun that scenario. Noise is never waved off. + +Zero unintended differences is a real result. Say so plainly. + +## Phase F: Report + +Write `/report.md` from [`references/report-template.md`](references/report-template.md). Post it to the PR only when asked. + +**Reply:** base and head SHAs, scenario count, the unintended differences first with evidence paths, then intended and the count of identical scenarios, any dropouts, and the rerun command for the scenario set. diff --git a/plugins/pstack/skills/diff-behavior/references/report-template.md b/plugins/pstack/skills/diff-behavior/references/report-template.md new file mode 100644 index 00000000..37c701ec --- /dev/null +++ b/plugins/pstack/skills/diff-behavior/references/report-template.md @@ -0,0 +1,32 @@ +# Behavior diff: + +- Base: `` () +- Head: `` () +- Scenarios: ( identical, intended, unintended) +- Scenario set: `/scenarios.md` +- Normalizer rules: `/normalize.md` + +## Unintended differences + +| Scenario | Base | Head | Difference | Evidence | +| --- | --- | --- | --- | --- | +| | | | | `//` | + +## Intended differences + +| Scenario | Base | Head | Difference | Claim it matches | Evidence | +| --- | --- | --- | --- | --- | --- | + +## Metrics + +| Scenario | Method | Base | Head | Ratio | +| --- | --- | --- | --- | --- | +| | | | | | + +## Identical + + + +## Dropouts and gaps + + diff --git a/plugins/pstack/skills/intake/SKILL.md b/plugins/pstack/skills/intake/SKILL.md new file mode 100644 index 00000000..dd46204a --- /dev/null +++ b/plugins/pstack/skills/intake/SKILL.md @@ -0,0 +1,69 @@ +--- +name: intake +description: "Turn one or more GitHub issues into ready-to-run poteto-mode briefs: read the issue and its comments, ground it in the repo with how, pick a playbook, write a brief with an observable exit condition, and prepare a worktree. Use for /intake, 'intake #41 #42', 'turn these issues into briefs', or before autopilot-stack or a batch of unattended work." +--- + +# Intake + +Every pstack run starts from a brief. Intake writes it from the issue, so the human reviews a brief instead of writing a prompt. + +Intake is read-only. It never edits product code. Launching poteto-mode on a brief is a separate, explicit step. + +**Dispatch contract.** Grounding runs `how`, which resolves its `how explorer` and `how explainer` roles through [`provider-dispatch.md`](../poteto-mode/references/provider-dispatch.md). Intake adds no route of its own. Children never pick a model. No implicit timeout and no fallback model. + +**Platform note.** Forge commands below use GitHub CLI (`gh`), the default forge in the Babysit and Shipping playbooks. On Codex, resolve Claude tool names via [`codex-tools.md`](../poteto-mode/references/codex-tools.md). + +## Start + +Open a todolist with one entry per phase, plus one entry per issue under Phase B. + +1. Collect +2. Ground and brief, per issue +3. Gate +4. Hand off + +## Phase A: Collect + +1. Resolve the repository once with `gh repo view --json nameWithOwner` and record it as ``. Pass `--repo "$repo"` to every `gh` command. +2. Read each issue in full: `gh issue view --repo "$repo" --json number,title,body,labels,comments,state,url`. Read every comment. Comments often hold the real decision ("we agreed on OIDC only"). +3. Read linked PRs and referenced issues when the body or comments point at them. A closed duplicate or a reverted PR is part of the spec. +4. Skip closed issues and say so. + +## Phase B: Ground and brief, per issue + +Run issues in parallel when there are several. Each issue gets its own grounding and its own brief. + +1. **Ground.** Run the **how** skill on the subsystem the issue names. Collect file pointers, entry points, the test command, and the project's verification skill if one exists (`.claude/skills/verify-*` or the equivalent). Keep pointers, not pasted code (the **guard-the-context-window** principle skill). +2. **Classify.** Pick exactly one poteto-mode playbook and say why in one sentence: + + | The issue asks for | Playbook | + | --- | --- | + | A defect with a symptom to reproduce | `bug-fix` | + | New or changed behavior | `feature` | + | Same behavior, different structure | `refactoring` | + | Measured slowness | `perf-issue` | + | A question, not a change | `investigation` | + | A large or cross-cutting change, or nothing above fits | the **figure-it-out** skill | + +3. **Write the brief.** Copy [`references/brief-template.md`](references/brief-template.md) to `/issue-.md` and fill every field. `` defaults to `.pstack/intake//` in the repository. Add `.pstack/` to `.git/info/exclude` so briefs stay out of commits unless the operator asks to keep them. + - The exit condition is observable: a command, a test name, a URL and expected response, or a screenshot. "Works" is not an exit condition. + - The verification plan names unit, live, and perf boxes. A box that does not apply says `n/a: `. + - Every ambiguity the issue leaves open becomes a numbered question. Do not answer product questions yourself. + - When the issue carries a repro (sample input and expected output), turn it into the failing-test candidate for the **tdd** skill and say so. + +## Phase C: Gate + +Mark each brief **ready** or **needs a human**. + +- **Ready.** Scope is clear, the exit condition is observable, and no open question changes the design. +- **Needs a human.** An open question changes what gets built, or the exit condition needs a number nobody gave (for example "make search faster" with no target). + +The **never-block-on-the-human** principle covers mechanics, not intent. Product and preference calls go to the operator. When the operator is present, ask the questions in one batch. When running unattended, post the questions as one comment on the issue with `gh issue comment --repo "$repo" --body-file `, ending with a line that says an agent wrote it, and leave the brief parked. + +## Phase D: Hand off + +1. For each **ready** brief, prepare a worktree off the default branch: `git worktree add -b issue-- /issue- origin/`. Record the path in the brief. Writers never use the primary checkout. +2. Default: stop here and return the briefs for review. +3. When the operator asked intake to run the briefs, start each one in its worktree as `Use pstack:poteto-mode. Follow the brief at .` For several ready briefs, hand the set to the Autopilot-stack playbook (`../poteto-mode/playbooks/autopilot-stack.md`) instead of starting them one by one. + +**Reply:** one row per issue: issue link, playbook, ready or needs a human, brief path, worktree path, and the open questions. Then the single next action for the operator. diff --git a/plugins/pstack/skills/intake/references/brief-template.md b/plugins/pstack/skills/intake/references/brief-template.md new file mode 100644 index 00000000..0b220304 --- /dev/null +++ b/plugins/pstack/skills/intake/references/brief-template.md @@ -0,0 +1,42 @@ +# Brief: (#) + +- Issue: +- Playbook: , because +- Status: +- Worktree: + +## Goal + + + +## Scope + +In scope: +- + +Out of scope: +- + +## Exit condition + + + +## Verification plan + +- Unit: +- Live: +- Perf: + +## Grounding + +- Entry points: +- Test command: +- Verification skill: + +## Decisions found in the thread + +- + +## Open questions + +1. From e6772e0169b645ab883f44969d24f6e1939f92d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 01:52:41 +0000 Subject: [PATCH 5/5] docs: add the live-gate runbook and a one-command local gate scripts/check.sh runs every local check a pull request needs for both plugins: install, tests, strict typecheck, manifest parse, static invariants, and claude plugin validate when the CLI is present. docs/LIVE-GATE.md explains how to install the exact candidate in Claude Code and Codex, which live checks to run per kind of change, the evidence format for the pull request, and the merged PRs that still need a live test. AGENTS.md and README point at both. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LSWNeBzAMt5FQZVziZCGLs --- AGENTS.md | 4 +-- README.md | 2 +- docs/LIVE-GATE.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++ scripts/check.sh | 66 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 docs/LIVE-GATE.md create mode 100755 scripts/check.sh diff --git a/AGENTS.md b/AGENTS.md index b1852655..0ec25f83 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,8 +4,8 @@ Track all durable work in this repository's GitHub Issues. Do not create a paral Cursor's `cursor/plugins/pstack` tree is the content upstream. Keep one shared skill tree for Claude Code and Codex; adapt harness primitives at the existing mapping boundaries instead of forking skills or adding compatibility layers. The parent harness resolves provider routing once. Children do not detect or reroute themselves. -Before opening a pull request, run the Bun tests, strict typecheck, static invariants, and plugin validation. +Before opening a pull request, run the Bun tests, strict typecheck, static invariants, and plugin validation: `bash scripts/check.sh` runs all of them for both plugins. -Nothing merges, tags, releases, or rolls out until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness. Unit tests, validators, source inspection, and self-reports do not satisfy this gate. Record the installed version, surface, action, and observed result in the pull request template. A pull request without that evidence remains a draft. +Nothing merges, tags, releases, or rolls out until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness. Unit tests, validators, source inspection, and self-reports do not satisfy this gate. Record the installed version, surface, action, and observed result in the pull request template. A pull request without that evidence remains a draft. `docs/LIVE-GATE.md` lists the steps per change and the evidence format. Do not add an implicit runtime timeout or a weaker-model fallback. diff --git a/README.md b/README.md index 7bc02647..5b25065e 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ Also kept here: [the original README](README-UPSTREAM.md), unchanged; [the techn Fixes for Claude Code or Codex, new lanes, and help bringing over new pstack releases are welcome. Search this repository's [GitHub Issues](https://github.com/thisguymartin/pstack-flex/issues) before opening a new one. For changes to upstream-derived content, explain why the change belongs here instead of in open-pstack or Lauren's original project. -Read [UPSTREAM.md](UPSTREAM.md) and [UPSTREAM-FLEX.md](UPSTREAM-FLEX.md) before changing content brought over from either upstream. Pull requests must keep one shared skill tree for Claude Code and Codex and pass the repository's tests, type checks, plugin validation, and static checks. Nothing merges until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness; the [pull request template](.github/pull_request_template.md) records that evidence, and a PR without it stays a draft. Adding a gateway provider has its own checklist in [docs/LANES.md](docs/LANES.md#adding-a-gateway-provider). +Read [UPSTREAM.md](UPSTREAM.md) and [UPSTREAM-FLEX.md](UPSTREAM-FLEX.md) before changing content brought over from either upstream. Pull requests must keep one shared skill tree for Claude Code and Codex and pass the repository's tests, type checks, plugin validation, and static checks. Nothing merges until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness; the [pull request template](.github/pull_request_template.md) records that evidence, and a PR without it stays a draft. Run `bash scripts/check.sh` for the local checks and follow [docs/LIVE-GATE.md](docs/LIVE-GATE.md) for the live test. Adding a gateway provider has its own checklist in [docs/LANES.md](docs/LANES.md#adding-a-gateway-provider). ## License diff --git a/docs/LIVE-GATE.md b/docs/LIVE-GATE.md new file mode 100644 index 00000000..73233f97 --- /dev/null +++ b/docs/LIVE-GATE.md @@ -0,0 +1,74 @@ +# Live gate + +AGENTS.md says nothing merges, tags, releases, or rolls out until the exact candidate is installed and the changed behavior passes a live test from the real user surface in every affected harness. Unit tests, validators, and source reading do not count. This page is how to run that test and where to record it. + +There are two halves: + +1. **Local gate.** One command, runs anywhere: `bash scripts/check.sh`. It installs both Bun packages, runs their tests and strict typechecks, parses every manifest, runs the static invariants, and runs `claude plugin validate` on the marketplace and both plugins when the `claude` CLI is present. +2. **Live gate.** You, in a real Claude Code and a real Codex session, with the candidate installed. Steps below. + +## 1. Install the exact candidate + +Push the branch first, so both harnesses install the same commit. + +Claude Code: check out the branch, then add that checkout as the marketplace (inside a session): + +```text +! git clone -b https://github.com/thisguymartin/pstack-flex ~/src/pstack-flex-candidate +/plugin marketplace add ~/src/pstack-flex-candidate +/plugin install pstack@pstack-flex +/plugin install pstack-monitor@pstack-flex +/reload-plugins +``` + +Codex (shell): + +```shell +codex plugin marketplace add thisguymartin/pstack-flex --ref +codex plugin add pstack@pstack-flex +codex plugin add pstack-monitor@pstack-flex +``` + +Start a new session in each harness afterwards. Record the installed version: `pstack` and `pstack-monitor` versions from the plugin list, plus `claude --version` and `codex --version`. + +If you had open-pstack or an older pstack-flex installed under the `open-pstack` marketplace name, remove it first so only one plugin named `pstack` is active. + +## 2. Run the checks for what changed + +Run the rows that match the change. A change that touches the runner or the model sheet runs rows A to C in both harnesses. + +| Row | Action | Pass when | +| --- | --- | --- | +| A. Opt-in gate | In a fresh session, ask for a two-line fix without naming pstack. Then ask again with "Use pstack for this." | The first request runs no `pstack:` skill. The second enters `pstack:poteto-mode`. | +| B. Setup | Run `/pstack:setup-pstack` (Claude Code) or `Use pstack:setup-pstack.` (Codex). Keep defaults or change one role. | Every assigned family probes `complete`, the sheet is written to `~/.claude/pstack-models.md` or `~/.codex/pstack-models.md`, and a failed probe writes nothing. | +| C. Mixed panel | `Use pstack:interrogate on the last commit.` | Each configured reviewer returns, external lanes write receipts with `status: complete`, and any missing CLI shows as a named dropout, not a substitute. | +| D. Gateway lane | With `DEEPSEEK_API_KEY` or `MINIMAX_API_KEY` exported, assign one role to that family in setup and run it once. | The receipt shows `status: complete`, the requested model, and `costUsd: null`. | +| E. Monitor | `/pstack-monitor:monitor` (Claude Code) or `Use pstack-monitor:monitor.` (Codex). Run a small arena while it is open. Then ask to stop it. | A link prints; the pstack session shows with its lanes; an external lane appears while it runs; stop prints `pstack-monitor stopped`. | +| F. intake | In a scratch repo with a real issue: `Use pstack:intake for #.` | A brief appears under `.pstack/intake/`, with one playbook, an observable exit condition, and open questions when the issue is vague. No product code changes. | +| G. diff-behavior | In a scratch repo, make a branch that changes one scenario on purpose and one by accident. `Use pstack:diff-behavior on this branch.` | The report lists the accidental change as unintended and the deliberate one as intended, with evidence for both sides. | + +## 3. Record the evidence + +Paste this into the pull request under "Live evidence", one block per harness: + +```text +Harness: Claude Code | Codex +Installed: pstack @ , pstack-monitor @ +Row : +Observed: +Result: pass | fail +``` + +A pull request without this stays a draft. + +## Outstanding live tests + +These merged without an installed live test. Clear them with one session per harness on current `main`, then record the results in a tracking issue and tick the rows. + +| PR | Change | Rows to run | Status | +| --- | --- | --- | --- | +| #16 | GPT-6 families and first-run defaults | B, C | not run | +| #19, #20 | Opt-in gate (Claude Code and Codex) | A | not run | +| #22 | open-pstack 1.5.0 merge, setup step order, grok-4.7 pin | B, C | not run | +| #24, #25 | Agent monitor and pstack-only view | E | checkout build only, not installed | +| this branch | Rename, monitor plugin split, intake, diff-behavior | A to G | not run | diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 00000000..94747394 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Runs every local check a pull request needs before review: install, +# tests, strict typecheck for both plugins, manifest parse, static +# invariants, and Claude plugin validation when the claude CLI is present. +# This is the local half of the gate. The live half is docs/LIVE-GATE.md. +set -uo pipefail + +repo="$(cd "$(dirname "$0")/.." && pwd)" +failed=() + +step() { + local name="$1"; shift + printf '\n== %s\n' "$name" + if "$@"; then + printf 'ok: %s\n' "$name" + else + printf 'FAIL: %s\n' "$name" + failed+=("$name") + fi +} + +in_dir() { (cd "$1" && shift && "$@"); } + +for pkg in plugins/pstack/skills/poteto-mode/scripts plugins/pstack-monitor/scripts; do + step "install $pkg" in_dir "$repo/$pkg" bun install --frozen-lockfile + step "test $pkg" in_dir "$repo/$pkg" bun run test + step "typecheck $pkg" in_dir "$repo/$pkg" bun run typecheck +done + +manifests=( + .claude-plugin/marketplace.json + .agents/plugins/marketplace.json + plugins/pstack/.claude-plugin/plugin.json + plugins/pstack/.codex-plugin/plugin.json + plugins/pstack-monitor/.claude-plugin/plugin.json + plugins/pstack-monitor/.codex-plugin/plugin.json +) +parse_manifests() { + local path + for path in "${manifests[@]}"; do + bun -e "JSON.parse(await Bun.file('$repo/$path').text())" || { echo "invalid JSON: $path"; return 1; } + done +} +step "manifests parse" parse_manifests +step "static invariants" env PSTACK_STATIC_ONLY=1 bash "$repo/tests/skill-collision-repro.sh" + +if command -v claude >/dev/null 2>&1; then + for target in . plugins/pstack plugins/pstack-monitor; do + step "claude plugin validate $target" claude plugin validate "$repo/$target" + done +else + printf '\nskip: claude plugin validate (claude CLI not installed)\n' +fi + +if [ "$(id -u)" = "0" ]; then + printf '\nnote: running as root. Two runner tests that make a file unreadable with chmod fail under root; CI runs as a normal user.\n' +fi + +printf '\n' +if [ "${#failed[@]}" -eq 0 ]; then + echo "local gate: all checks passed" +else + echo "local gate: ${#failed[@]} failed" + printf ' - %s\n' "${failed[@]}" + exit 1 +fi