pitroom 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pitroom",
3
3
  "description": "A free pit crew for your expensive coding agent: the superpowers development workflow, run by cheap workers. Delegate research, fixes, tests, reviews and whole plans from Claude Code, Codex or any agent to OpenCode, Codex or Claude Code workers, with receipts that prove the savings.",
4
- "version": "0.16.0",
4
+ "version": "0.18.0",
5
5
  "author": {
6
6
  "name": "TahaAtas"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pitroom",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "A free pit crew for your expensive coding agent: delegate research, fixes, tests, reviews and parallel investigations to cheap worker agents running your own models, with verified answers, exact diffs and cost receipts.",
5
5
  "author": {
6
6
  "name": "TahaAtas"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.0
4
+
5
+ ### Windows
6
+ - The test suite runs on `windows-latest` (Node 22 and 24) in CI. Ten tests that need `sh`, POSIX file modes or the git guard are skipped there.
7
+ - `pitroom install` makes a `pitroom.cmd` launcher; `pitroom doctor` starts it through a shell and warns that the git guard is not available.
8
+ - Fixes found by running there: a directory spelled `RUNNER~1` and the same one spelled `runneradmin` were taken for two places (isolate runs started in a directory that did not exist), so paths are compared as the file system names them; a bare `ipc.ts:12` reference was not matched to `src\main\ipc.ts`; the secret-looking files in a warning used backslashes; a missing `--verify` command (`is not recognized`) was shown as a failing test; `statusline --then` needs a shell that exists; the Tailwind build step used a symlink; run records could not be replaced while `watch` or the dashboard had them open (`EPERM`), so it retries.
9
+ - A spawn that fails because the working directory is missing says so, instead of "<command> not found".
10
+ - `.gitattributes` checks out with LF everywhere.
11
+
12
+ ### Dashboard
13
+ - **Stop and Discard from the page.** A running card has a Stop button and a finished, not yet applied `--isolate` card has Discard, each with a confirmation. These are the dashboard's first writes, and the only ones: it still never starts a run or applies a patch. `POST /api/run/<id>/stop` and `/discard` need the secret the page was served with (random per start, compared in constant time) in `x-pitroom-token` and the dash's own `Origin`; anything else is 403, a POST elsewhere is 403, another method 405. A run that cannot be stopped or discarded (not active, not isolate, already applied or discarded) is 409.
14
+ - `pitroom stop` and the dashboard share one `stopRun`.
15
+
16
+ ### Fixes
17
+ - A stop request is recorded in a file of its own (`stop-requested` in the run's folder), not only in `meta.json`: the run's process writes `meta.json` too, and its write could drop the flag, so a stopped run showed as "worker process exited unexpectedly" (found on Windows, where the process dies at once on the signal).
18
+ - `pitroom dash --detach` waits up to 20 s for the server (it was 6 s) while it imports old runs into the history.
19
+
20
+ ## 0.17.0
21
+
22
+ ### Answer cache
23
+ - The same read question on the same code gets the earlier answer back at once instead of a new worker run (`pitroom ⟲ cached answer · … · no worker ran`). The key is the question (whitespace does not count), where it was asked, the attached files' contents, where the worker reads (a clean snapshot or in place) and the project's exact state: HEAD and a tree of every working file, uncommitted and untracked included (made with git's own index, so unchanged files are not re-hashed). Only answers that held up are reused: a finished read run with an answer, every reference verified, nothing written, and no audit still running or disagreeing, for `cacheDays` (default 7; 0 turns it off; `PITROOM_CACHE_DAYS`). Follow-ups, web runs, plan tasks, reviews, audits, `--audit`, `--verify`, crews and changes are never cached; `--fresh` asks a worker anyway and its answer is reused from then on. Git-ignored files and the worker/model are not part of the key (documented; `--fresh` for those). With `--bg --json` the earlier run comes back already done, marked `"cached": true`.
24
+
25
+ ### MCP server
26
+ - **Smaller tool definitions.** Ten tools instead of eighteen, nothing lost: parallel work is `pitroom_run` with `tasks` (was `pitroom_crew`), every report is `pitroom_info` with a `topic` (`runs`, `history`, `stats`, `savings`, `models`, `cooldown`, `config` (new), `doctor`; were seven tools), `pitroom_show` gives a running run's progress (was `pitroom_status`, and `run` now defaults to the latest), and `pitroom_stop` with `cooldowns` clears the cooldowns. Descriptions are shorter. The definitions sit in the client's context for the whole session: about 1.9k tokens now, 3.3k before. An option that does not go with a topic is an error that names the ones that do.
27
+ - **Run notifications.** A client can subscribe to a run's resource (`resources/subscribe` on `pitroom://run/<id>` or its `/patch`) and is told when the run changes state (for `/patch` also when it changed more files) or is gone (`notifications/resources/updated`); every client is told when the newest run changes (`notifications/resources/list_changed`; runs starting within one check are one notification). Read from the run records every 2 s, only while a client can be told; at most 100 subscriptions per client. An event stream nobody uses (no request, no event for an hour) is closed with its session. Over HTTP a GET with `Accept: text/event-stream` opens the session's event stream, where these arrive (one stream per session, kept alive with comments); a client without one is told once it opens it. Few clients act on these yet; the tools work the same without them.
28
+
29
+ ### Fixes
30
+ - `pitroom review` in a linked git worktree: the review package was written to the worktree's git directory, which lies outside the worktree, and a sandboxed reviewer (OpenCode) was refused reading it, so the review had nothing to review. There it now goes to `<worktree>/.pitroom/` (added once to the repository's `info/exclude`, so `git status` stays clean) and is removed when the review ends. Found by having Pitroom review its own branches from a worktree.
31
+
32
+ ### MCP Registry
33
+ - `server.json` describes Pitroom for the [MCP Registry](https://registry.modelcontextprotocol.io) (`io.github.ATASTECH/pitroom`: the npm package, stdio, `pitroom mcp`), and `package.json` has the `mcpName` the registry checks. `npm run bump` keeps its version in step, and turns an `## Unreleased` CHANGELOG section into the version's entry.
34
+
35
+ ### From real-world tests (Codex driving Pitroom over MCP)
36
+ - **`--verify` under an MCP client.** An app that starts Pitroom often passes a bare `PATH`, and `npm test` was "command not found" (exit 127). The verify command now also finds the Node running Pitroom (npm and npx sit next to it) and the usual install places, after your own `PATH`; a command that still is not found is reported as "could not run (exit 127: command not found)" with what to do, not as a failing test.
37
+ - **A failed `--verify` is no longer shown as success.** The report says `⚠ done · verify failed` instead of `✔ done`, an MCP result is an error (`isError`, as is a read-only violation), and the dashboard shows a warning icon on the card and lists the run under **Needs attention**.
38
+ - **Audits check the question, not only the references.** The auditor must check every condition the question sets (scope, exclusions, "all", "only", counts), look for missing and extra items in a list and recount a count; AGREE needs all of them. In the tests an answer that listed 8 files outside the asked directory got AGREE from two of three auditors, because its references were valid.
39
+ - **Skills over MCP.** An agent using the MCP tools did not think of Pitroom's skills. The server's instructions now name them and say to use the matching one, `pitroom_run` points to `using-pitroom`, and every skill is also a prompt of the same name (its text, with how the CLI commands in it map to the tools), for clients that have no skills.
40
+ - **`pitroom review --range A..B` shows the branch's own change**, from where it left A, as a pull request does. When A had moved on since (another PR merged), the diff also showed A's newer commits as if the branch undid them.
41
+ - An OpenCode model answering "Endpoint is unavailable" (HTTP 400) is treated as rate-limited: it cools down and the next worker runs (this already worked; it now has a test).
42
+
3
43
  ## 0.16.0
4
44
 
5
45
  ### MCP server
package/README.md CHANGED
@@ -150,7 +150,7 @@ Every run prints a receipt, and `pitroom savings` adds them up (`--card` writes
150
150
 
151
151
  ## Dashboard
152
152
 
153
- `pitroom dash --detach` prints the address of a live page on `127.0.0.1` (read-only, this machine only). It is for the places where hook messages and status lines do not reach, such as the Claude Code and Codex apps: open it in a browser or in the app's own browser pane. The skills tell your agent to start it and give you the address when it runs workers in the background.
153
+ `pitroom dash --detach` prints the address of a live page on `127.0.0.1` (this machine only; it reads your runs and changes nothing but the two actions below). It is for the places where hook messages and status lines do not reach, such as the Claude Code and Codex apps: open it in a browser or in the app's own browser pane. The skills tell your agent to start it and give you the address when it runs workers in the background.
154
154
 
155
155
  The screenshots below show sample data (an imaginary `shop-api` project), not a real one.
156
156
 
@@ -170,6 +170,8 @@ The screenshots below show sample data (an imaginary `shop-api` project), not a
170
170
 
171
171
  <p align="center"><img src="docs/dash-history.png" width="49%" alt="The History tab: a search box, filters and every past run as a card"> <img src="docs/dash-stats.png" width="49%" alt="The Stats tab: totals, runs per day, and per worker and model its logo, success rate and how many of its audited answers another worker confirmed"></p>
172
172
 
173
+ **Stop and Discard.** A running card has a Stop button, and a finished `--isolate` card that is not applied yet has Discard (the same as `pitroom stop` and `pitroom discard`; the patch file stays). Each asks once more. Nothing else can be done from the page: it never starts a run and never applies a patch. A POST needs the secret the page was served with (it changes every time the dashboard starts) and the page's own origin, so a web page on another site cannot press them.
174
+
173
175
  The dashboard is a React app built once into `dist/ui` and served as two static files, so the CLI still has no runtime dependencies. It follows your system's light or dark theme (a button switches it).
174
176
 
175
177
  ---
@@ -241,6 +243,15 @@ To repeat it on your own repository: `pitroom crew -d <repo> -g bench "<question
241
243
 
242
244
  ---
243
245
 
246
+ ## Platforms
247
+
248
+ macOS and Linux are the main platforms. **Windows** runs the same test suite in CI (Windows Server, Node 22 and 24, with the fake workers; the worker CLIs themselves are not run there), with these differences:
249
+
250
+ - **No git guard.** Pitroom's shim that blocks a worker's history changes and pushes is a `sh` script and is not available on Windows; `pitroom doctor` says so. Read-only runs are still limited by each worker CLI's own rules and `--isolate` keeps edits in a copy, so prefer `--isolate` and check the patch before `apply`.
251
+ - **The launcher is `pitroom.cmd`** (`%USERPROFILE%\.local\bin`), started with the Node that ran `pitroom install` or the one in `PITROOM_NODE`; it does not search for another Node.
252
+ - **Line endings.** Pitroom compares and applies patches byte for byte. Keep `core.autocrlf` off (or an `eol=lf` in `.gitattributes`, as this repository has); a checkout converted to CRLF has not been tested with `apply`.
253
+ - A read snapshot's files are not made read-only (Windows has no POSIX modes), and a `--verify` command runs in `cmd.exe`, which reports a missing command as `is not recognized`.
254
+
244
255
  ## Quick start
245
256
 
246
257
  You need Node.js 22.13+ and at least one worker CLI: [OpenCode](https://opencode.ai) v2+ (the default worker, with free models), [Codex CLI](https://github.com/openai/codex), [Claude Code](https://claude.com/claude-code) or [Gemini CLI](https://github.com/google-gemini/gemini-cli) (beta; it needs an API key from [Google AI Studio](https://aistudio.google.com/apikey), a Google account sign-in no longer works for it).
@@ -273,7 +284,7 @@ npm i -g pitroom
273
284
 
274
285
  **Any agent that speaks MCP (Cursor, Claude Desktop, and the three above too)**
275
286
 
276
- Pitroom is also an [MCP](https://modelcontextprotocol.io) server on stdio: tools instead of shell commands and skills (`pitroom_run`, `pitroom_crew`, `pitroom_wait`, `pitroom_review`, `pitroom_audit`, `pitroom_apply`, `pitroom_revert`, … 18 in all, covering the commands an agent needs), the runs as resources, and four prompts. After `npm i -g pitroom`, let Pitroom register itself in the clients it finds (Claude Code, Codex, Gemini CLI, Cursor, Claude Desktop):
287
+ Pitroom is also an [MCP](https://modelcontextprotocol.io) server on stdio: ten tools instead of shell commands and skills (`pitroom_run`, `pitroom_wait`, `pitroom_show`, `pitroom_info`, `pitroom_review`, `pitroom_audit`, `pitroom_apply`, `pitroom_discard`, `pitroom_revert`, `pitroom_stop`), the runs as resources, and four prompts. The tool definitions sit in the client's context for the whole session, so they are kept small: about 1.9k tokens. It is listed in the [MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.ATASTECH/pitroom`, so clients that browse the registry can find it. After `npm i -g pitroom`, let Pitroom register itself in the clients it finds (Claude Code, Codex, Gemini CLI, Cursor, Claude Desktop):
277
288
 
278
289
  ```bash
279
290
  pitroom install --mcp # also links the skills; add --no-skills to skip that
@@ -297,10 +308,10 @@ or, for Cursor (`~/.cursor/mcp.json`), Claude Desktop (`claude_desktop_config.js
297
308
 
298
309
  Each tool runs the matching `pitroom` command, so every rule of the CLI applies unchanged (permission profiles, git guard, isolation, read snapshots). A run is waited for up to `waitSeconds` (default 50), then comes back as "still running" with its id for `pitroom_wait`; keep it below your client's tool timeout. The server works in the directory it is started in: the client's for stdio, or the one `-d DIR` names (Claude Desktop starts it elsewhere, so give it `"args": ["mcp", "-d", "/path/to/project"]`); the tools' `dir` argument picks another per call.
299
310
 
300
- - **Progress.** While a call waits for a run, a client that sent a progress token gets a `notifications/progress` every few seconds ("running · read · 12s · 3 steps, 2 tool calls · last: …"), and a client that resets its timeout when progress comes in (the protocol allows it; not every client does) does not give up on a long run. **Cancelling** a `pitroom_run`, `pitroom_crew`, `pitroom_review` or `pitroom_audit` call stops the run it started (a cancelled `pitroom_wait` only stops waiting).
301
- - **Resources.** The latest runs are listed as `pitroom://run/<id>` (the report) and, for runs that changed files, `pitroom://run/<id>/patch` (the exact diff), so a client can attach one to a conversation.
302
- - **Prompts.** `research`, `implement`, `review` and `crew` say how to use Pitroom for that job (in Claude Code they appear as `/mcp__pitroom__research`, …).
303
- - **Also as tools:** `pitroom_crew` (independent tasks in parallel), `pitroom_list`, `pitroom_history` (find an earlier answer), `pitroom_stats` and `pitroom_models` (pick a worker), `pitroom_savings`, `pitroom_cooldown`, `pitroom_doctor`, `pitroom_revert`, and a `continue` option on `pitroom_run` for a follow-up.
311
+ - **Progress.** While a call waits for a run, a client that sent a progress token gets a `notifications/progress` every few seconds ("running · read · 12s · 3 steps, 2 tool calls · last: …"), and a client that resets its timeout when progress comes in (the protocol allows it; not every client does) does not give up on a long run. **Cancelling** a `pitroom_run`, `pitroom_review` or `pitroom_audit` call stops the run it started (a cancelled `pitroom_wait` only stops waiting).
312
+ - **Resources.** The latest runs are listed as `pitroom://run/<id>` (the report) and, for runs that changed files, `pitroom://run/<id>/patch` (the exact diff), so a client can attach one to a conversation. A client can **subscribe** to a run (`resources/subscribe`) and is told when it changes state (`notifications/resources/updated`), and every client is told when the newest run changes (`notifications/resources/list_changed`), so it need not poll; over HTTP these arrive on the session's event stream (a GET).
313
+ - **Prompts.** `research`, `implement`, `review` and `crew` say how to use Pitroom for that job (in Claude Code they appear as `/mcp__pitroom__research`, …), and every skill is a prompt of the same name (`using-pitroom`, `pitroom-research`, …), so a client without skills gets the same guidance. The server's instructions tell the agent to use the skills alongside the tools.
314
+ - **More than runs:** `pitroom_run` takes `tasks` (independent tasks in parallel, one worker each) and `continue` (a follow-up in the same worker session); `pitroom_info` reports without changing anything, by `topic`: `runs`, `history` (find an earlier answer before asking again), `stats` and `models` (pick a worker), `savings`, `cooldown`, `config`, `doctor`; `pitroom_stop` with `cooldowns` tries rate-limited models again.
304
315
  - **Over HTTP.** `pitroom mcp --http [--port N]` (default 7117) serves the same tools at `http://127.0.0.1:7117/mcp` for clients that connect to a URL, for example `claude mcp add --transport http pitroom http://127.0.0.1:7117/mcp --header "Authorization: Bearer $(cat ~/.local/state/pitroom/mcp-token)"` (the command is printed when it starts). It is a local service: it listens on 127.0.0.1 only, needs the bearer token (made on first start, kept at `<state dir>/mcp-token` with owner-only permissions, or set with `PITROOM_MCP_TOKEN`), and refuses a Host or Origin that is not local, so a web page cannot use it. Anyone who has the token can run workers as you, so keep it private. It works in the directory it was started in (printed when it starts; `-d DIR` for another). Each client has its own session; a call that asks for progress is answered as an event stream. Ending a session or stopping the server ends the waiting, not the runs: collect them with `pitroom_wait` or the CLI. `pitroom install --mcp` registers the stdio command, which needs no token and no running process.
305
316
 
306
317
  **Any other agent (a plain shell) or just the CLI**
@@ -373,6 +384,8 @@ pitroom uninstall && npm rm -g pitroom
373
384
 
374
385
  **Read snapshots.** With secret-looking files in the directory, a read run reads a snapshot of your project's current state (uncommitted and untracked files included) without them and without git-ignored files, made from git objects, kept once per state and shared by every read run on it (twenty workers, one snapshot), and cleaned up after three days or by `pitroom clean`. Nothing is copied back: a read run has nothing to land. Its files are read-only, and the paths a worker cites are turned back into your project's. With no secret-looking files nothing changes: the worker runs in place. `--in-place` (or `"readIn": "project"` in the config) reads the directory itself, e.g. for a task that needs build output; `"readIn": "snapshot"` always uses one.
375
386
 
387
+ **Answer cache.** Ask the same read question on the same code and you get the earlier answer back at once, with no worker run: `pitroom ⟲ cached answer · the same question on the same code as run … · no worker ran`. "The same code" is exact: the commit and every working file, uncommitted and untracked ones included (git-ignored files are not code), so any edit makes it a new question. Only answers that held up are reused (the run finished, every file:line reference checked out, no audit disputed it) and only for 7 days (`"cacheDays"` in the config, `0` turns it off, or `PITROOM_CACHE_DAYS`). Follow-ups, web runs, plan tasks, reviews, audits, `--verify` runs, parallel work (crews) and changes are never answered from the cache, and `--fresh` asks a worker anyway (its answer is the one reused from then on). Not part of "the same code": git-ignored files (build output, installed dependencies) and the worker or model that answered; when a question depends on them, use `--fresh`.
388
+
376
389
  ```bash
377
390
  pitroom run "Find every place we build SQL strings by hand; file:line and risk"
378
391
  pitroom run -i --link node_modules --verify "npm test" "Fix the off-by-one in paginate() and add a test"
package/dist/lib.mjs CHANGED
@@ -1384,7 +1384,7 @@ function verifyRefs(refs, dirs) {
1384
1384
  if (direct) return [direct];
1385
1385
  if (path7.isAbsolute(ref.file)) return [];
1386
1386
  const wanted = ref.file.replace(/^(\.{1,2}\/)+/, "");
1387
- return (byName().get(path7.basename(wanted)) ?? []).filter((f) => f.endsWith(`/${wanted}`) || path7.basename(f) === wanted).slice(0, 20);
1387
+ return (byName().get(path7.basename(wanted)) ?? []).filter((f) => f.split(path7.sep).join("/").endsWith(`/${wanted}`) || path7.basename(f) === wanted).slice(0, 20);
1388
1388
  };
1389
1389
  const check = (file, ref) => {
1390
1390
  const lines = load(file);