pitroom 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +6 -3
- package/dist/lib.mjs +23 -0
- package/dist/pitroom.mjs +559 -294
- package/dist/ui/app.js +1 -1
- package/docs/backends.md +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
|
@@ -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.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TahaAtas"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pitroom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Read snapshots
|
|
6
|
+
- A read run in a directory that holds secret-looking files (`.env`, `prod.env`, private keys and keystores, `.netrc`, `credentials.json`, `secrets.json`) now reads a **clean snapshot** of your project instead of the directory, so those files are simply not there, whatever the worker CLI would have allowed. Before, the only protection was the worker's prompt and a warning (OpenCode and Claude Code refuse `.env` in their file tools, Codex does not, Gemini CLI 0.62 ignores our deny rules). Checked live with Gemini: `server.pem` in the snapshot came back "File not found".
|
|
7
|
+
- The snapshot is your project's current state (uncommitted and untracked files included) minus git-ignored files and anything secret-looking, a tracked secret too. It is made from git objects, kept once per state and shared by every read run on it (twenty workers, one snapshot), and nothing is copied back: a read run has nothing to land. Its files are read-only. Unused ones are removed after three days, beyond the newest six, or by `pitroom clean`. On an 8,000-file repository the first snapshot took about 3 seconds (167 MB) and later runs 0.6 seconds.
|
|
8
|
+
- With no secret-looking files nothing changes: the worker runs in place. `--in-place` (or `"readIn": "project"`, env `PITROOM_READ_IN`) reads the directory itself, for example when a task needs build output; `"readIn": "snapshot"` always uses one. The paths a worker cites are turned back into your project's, follow-ups stay in the snapshot, and edits (`-i`, `-w`) and reviews are not affected. If a snapshot cannot be made the run falls back to the directory and says so.
|
|
9
|
+
- More names count as secret: names ending `.env`, `.key`, `.jks`, `.keystore`, and `secrets.json|yml|toml` (templates such as `.env.example` still do not).
|
|
10
|
+
|
|
11
|
+
### Tests
|
|
12
|
+
- The warning test covers both ways: a snapshot by default, the old heads-up when reading in place.
|
|
13
|
+
|
|
14
|
+
## 0.12.0
|
|
15
|
+
|
|
16
|
+
### Cooldowns
|
|
17
|
+
- A model that says "rate limited" (a free daily quota used up, an overloaded provider) is remembered for a while, so the next runs go straight to the fallback instead of each one trying the exhausted model first and waiting for it to fail. The wait comes from the provider's message when it gives one (`retry in 4h28m`), else a guess: 4 hours for a daily quota, 20 minutes for overload; at least a minute, at most a day.
|
|
18
|
+
- A model on cooldown is skipped only while another worker is left to run; if it is the only one it is still tried. Cooldowns expire on their own.
|
|
19
|
+
- The report says `skipped: … (cooling down until 14:05: …)`, the dashboard card shows "Skipped …", `pitroom doctor` warns with the time, and `pitroom cooldown` lists them (`--clear` tries them again). Only rate-limit failures count, not a missing model or a sign-in problem.
|
|
20
|
+
|
|
3
21
|
## 0.11.0
|
|
4
22
|
|
|
5
23
|
### Gemini CLI as your primary agent
|
package/README.md
CHANGED
|
@@ -334,6 +334,8 @@ pitroom uninstall && npm rm -g pitroom
|
|
|
334
334
|
| **isolate** | `-i` | code changes you want to review first | untouched until `pitroom apply` |
|
|
335
335
|
| **write** | `-w` | quick in-place edits | edited now; `pitroom revert` undoes exactly |
|
|
336
336
|
|
|
337
|
+
**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.
|
|
338
|
+
|
|
337
339
|
```bash
|
|
338
340
|
pitroom run "Find every place we build SQL strings by hand; file:line and risk"
|
|
339
341
|
pitroom run -i --link node_modules --verify "npm test" "Fix the off-by-one in paginate() and add a test"
|
|
@@ -457,6 +459,7 @@ pitroom crew [-i] [-g NAME] "task 1" "task 2" … (or --task-file with --- sep
|
|
|
457
459
|
pitroom run -i --plan PLAN --step N [--tier T] ["notes"]
|
|
458
460
|
pitroom review [run | --range A..B [--plan PLAN]] [--tier T | -W T] [--bg]
|
|
459
461
|
pitroom audit RUN [-W worker]
|
|
462
|
+
pitroom cooldown [--clear]
|
|
460
463
|
pitroom plan status PLAN [--json] · pitroom plan note PLAN "Task N: …"
|
|
461
464
|
pitroom status|wait|watch [run… | -g NAME] wait: --any --brief --timeout · watch: --json|--brief --interval
|
|
462
465
|
pitroom dash [--detach] [--port N] [--open] [--stop] a live page of the runs on 127.0.0.1
|
|
@@ -539,7 +542,7 @@ pitroom run -m nvidia/z-ai/glm-5.3 "…" # another model on th
|
|
|
539
542
|
| OpenCode (v2+) | ✅ | per-run permission rules | reported | private `--standalone` server per run |
|
|
540
543
|
| Codex CLI | ✅ | OS sandbox (`read-only` / `workspace-write`) | tokens only | `codex login`; your `~/.codex/config.toml` is ignored for workers (its MCP servers run outside the sandbox); models take `#effort` |
|
|
541
544
|
| Claude Code | ✅ | tool allowlist (`--restricted --safe-mode`, `dontAsk`) | reported | `claude auth login`; its default is often Opus, so prefer `-W claude:haiku` |
|
|
542
|
-
| Gemini CLI | beta | `--approval-mode plan` plus Pitroom's policy rules | tokens only | an API key from Google AI Studio (sign in with `gemini` or set `GEMINI_API_KEY`; Google account sign-in is refused by Google); workers run in a private Gemini home without your hooks, MCP servers and skills; secret-file rules are not enforced by Gemini 0.62,
|
|
545
|
+
| Gemini CLI | beta | `--approval-mode plan` plus Pitroom's policy rules | tokens only | an API key from Google AI Studio (sign in with `gemini` or set `GEMINI_API_KEY`; Google account sign-in is refused by Google); workers run in a private Gemini home without your hooks, MCP servers and skills; secret-file rules are not enforced by Gemini 0.62, but a read run with secret-looking files reads a [clean snapshot](#modes) without them; pin a model, e.g. `-W gemini:gemini-3.8-flash`; no `--continue` (see [the notes](docs/backends.md#gemini-cli-notes)) |
|
|
543
546
|
|
|
544
547
|
### Models, costs and effort
|
|
545
548
|
|
|
@@ -554,7 +557,7 @@ claude haiku low/medium/high/xhigh/max ? - -
|
|
|
554
557
|
|
|
555
558
|
Pitroom cannot know vendor prices and does not fetch them, so a cost is what you enter: `"costs": {"codex:gpt-6-sol": 1, "codex:gpt-6.1-sol": 2}` in the config, in any unit (they are only compared). `pitroom doctor` then prints the cost of the models in use and says when you priced a cheaper one of the same worker. A target that names only an effort (`codex:#low`) uses the model from `models`; a Codex or Claude Code worker with no pinned model gets a warning, because it would run the vendor's own default, which can change and cost more. `--effort LEVEL` sets the level for one run; your agent picks model and level from `pitroom models` (cheapest that fits: `low` for lookups, `medium` for ordinary changes, `high` for reviews).
|
|
556
559
|
|
|
557
|
-
Fallbacks cross backends (e.g. `"fallback": ["codex:#low", "opencode"]`): a worker that is rate-limited, logged out or missing its model hands the task to the next. Follow-ups (`--continue`) always stay on the worker that owns the session. Writing an adapter: [docs/backends.md](docs/backends.md).
|
|
560
|
+
Fallbacks cross backends (e.g. `"fallback": ["codex:#low", "opencode"]`): a worker that is rate-limited, logged out or missing its model hands the task to the next. Follow-ups (`--continue`) always stay on the worker that owns the session. Writing an adapter: [docs/backends.md](docs/backends.md). A model that says "rate limited" (a daily quota used up, an overloaded provider) is remembered for the time its message gives, or a guess: the next runs skip it while a fallback is left, so they do not each wait for it to fail. `pitroom cooldown` lists what is skipped and until when, `--clear` tries it again, and `doctor` shows it too.
|
|
558
561
|
|
|
559
562
|
---
|
|
560
563
|
|
|
@@ -611,7 +614,7 @@ Or put defaults in `~/.config/pitroom/config.json` (flags and env still win); `p
|
|
|
611
614
|
|
|
612
615
|
**Can the worker break my repo?** It can't touch git history, refs or your index (permission profile + git guard), can't run bulk deletes, and write-mode edits are revertible with one command. The guard is not an OS sandbox: git called by absolute path from inside a script, or non-git tools, are outside its reach, which is why `--isolate` exists: nothing reaches your tree until you apply it.
|
|
613
616
|
|
|
614
|
-
**Can the worker read my `.env`?** In the directory
|
|
617
|
+
**Can the worker read my `.env`?** In a read run, no: when the directory holds secret-looking files (`.env`, `prod.env`, private keys and keystores, `.netrc`, `credentials.json`, `secrets.json`), the worker reads a **clean snapshot** of your project instead (see below), where they are simply not present, whatever the worker CLI would have allowed. Edits (`-i`, `-w`) and reviews are different: an isolated copy (`-i`) leaves out git-ignored files, so a git-ignored `.env` is not in it, but one that is not ignored would be copied, and Pitroom warns when it sees such files (`PITROOM_NO_SECRET_WARNING=1` silences the warning). A free model may be hosted by a third party: keep real secrets out of what you delegate.
|
|
615
618
|
|
|
616
619
|
**What if the worker fails?** Pitroom exits non-zero with the real cause (for example a default model that no longer exists) and your agent simply continues on its own. `pitroom doctor` diagnoses setup problems.
|
|
617
620
|
|
package/dist/lib.mjs
CHANGED
|
@@ -1495,6 +1495,27 @@ function fill(template, values) {
|
|
|
1495
1495
|
return template.replace(/\{\{([A-Z_]+)\}\}/g, (_, key) => values[key]);
|
|
1496
1496
|
}
|
|
1497
1497
|
|
|
1498
|
+
// src/core/cooldown.ts
|
|
1499
|
+
var MAX_MS = 24 * 36e5;
|
|
1500
|
+
var OVERLOAD_MS = 20 * 6e4;
|
|
1501
|
+
var DAILY_MS = 4 * 36e5;
|
|
1502
|
+
function retryAfterMs(message) {
|
|
1503
|
+
const compact = /(?:retry|try again|resets?|available again)[^0-9]{0,24}((?:\d+(?:\.\d+)?\s*[dhms]\s*)+)/i.exec(message)?.[1];
|
|
1504
|
+
const words = /(?:retry|try again|resets?)[^0-9]{0,24}(\d+(?:\.\d+)?)\s*(second|minute|hour)s?/i.exec(message);
|
|
1505
|
+
let ms = 0;
|
|
1506
|
+
if (compact) {
|
|
1507
|
+
for (const [, n, unit] of compact.matchAll(/(\d+(?:\.\d+)?)\s*([dhms])/gi)) ms += Number(n) * { d: 864e5, h: 36e5, m: 6e4, s: 1e3 }[unit.toLowerCase()];
|
|
1508
|
+
} else if (words) {
|
|
1509
|
+
ms = Number(words[1]) * { second: 1e3, minute: 6e4, hour: 36e5 }[words[2].toLowerCase()];
|
|
1510
|
+
}
|
|
1511
|
+
return ms > 0 ? ms : void 0;
|
|
1512
|
+
}
|
|
1513
|
+
function cooldownMs(message) {
|
|
1514
|
+
const told = retryAfterMs(message);
|
|
1515
|
+
if (told !== void 0) return Math.min(Math.max(told, 6e4), MAX_MS);
|
|
1516
|
+
return /daily|per.?day|per day|exhausted your/i.test(message) ? DAILY_MS : OVERLOAD_MS;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1498
1519
|
// src/core/answers.ts
|
|
1499
1520
|
var TASK_STATUSES = ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"];
|
|
1500
1521
|
function parseStatus(text) {
|
|
@@ -1598,6 +1619,7 @@ export {
|
|
|
1598
1619
|
allBackends,
|
|
1599
1620
|
backendIds,
|
|
1600
1621
|
brief,
|
|
1622
|
+
cooldownMs,
|
|
1601
1623
|
describeTarget,
|
|
1602
1624
|
extractRefs,
|
|
1603
1625
|
fill,
|
|
@@ -1612,5 +1634,6 @@ export {
|
|
|
1612
1634
|
parseVerdict,
|
|
1613
1635
|
planName,
|
|
1614
1636
|
planTask,
|
|
1637
|
+
retryAfterMs,
|
|
1615
1638
|
verifyRefs
|
|
1616
1639
|
};
|