pier-setup 0.3.1 → 0.4.2
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/README.md +24 -11
- package/install.mjs +63 -11
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -23,10 +23,13 @@ pier ships as **two halves**, installed separately:
|
|
|
23
23
|
|
|
24
24
|
### Highlights
|
|
25
25
|
|
|
26
|
-
- **Todo loop**: `todo_write` with full-replace semantics, session JSONL as source of truth, correct branch rollback; live pane-title projection `▶i ○p ■b ✓c (N/M) · current task`;
|
|
26
|
+
- **Todo loop**: `todo_write` with full-replace semantics, session JSONL as source of truth, correct branch rollback; live pane-title projection `▶i ○p ■b ✓c (N/M) · current task`; `/todos` command (including unblock)
|
|
27
27
|
- **Todo anti-freeze**: an all-completed list unchanged for ≥6 turns flips the read hook from recitation to a rewrite warning; after ≥1h it archives — first offering one rewrite window with old entries as reference, then (if ignored) a final notice that clears the list (rm persisted to JSONL; the empty guard takes over — multi-step work must re-track); `/todos` still shows history, session JSONL untouched
|
|
28
28
|
- **Interactive subagents**: every subagent = an isolated pi session in its own pane (separate context window); **a human can enter that pane and talk to it directly** (fix bugs, take over, answer ask_user_question)
|
|
29
|
-
- **
|
|
29
|
+
- **Delegation identity** (D111): spawn hands the worker a `PI_HERDR_DELEGATION` context (parent pane/session, taskId, task, role), replayed env > worker marker > branch fold; the worker's system prompt gains a `pier-delegation` section (identity + one stable delivery contract), its transcript opens with an "⟵ delegated by master …" card, and its pane title carries an `↳` prefix; `pi-parent`/`pi-task` herdr tokens ride their own report batch, the dashboard groups workers under their master, and blocked alerts name the edge (`worker ← master`)
|
|
30
|
+
- **Task slugs** (D112): spawn allocates a task slug (numbered on collision); tool outputs print `slug@paneId` and `agentId` accepts the bare slug, bare pane id, or the combined form (the slug part survives a revive that changed the pane). A master bash call that sleeps ≥60 s while background workers run gets a note that the settlement notice arrives on its own (D125; the earlier `report_progress` tool was never called and is gone)
|
|
31
|
+
- **Unified TUI status strip** (D113/D114): the resident widget is one line per agent — self as `▶ current item ✓done/total`, each running subagent as `↳ slug ▶ current item ✓d/n` (blocked shows the task + reason; settled rows never occupy the strip; a fully completed self list hides the widget instead of showing a bare `✓N/N`); workers push their full todo list over the pipe on every `todo_write` (event-driven, no polling), and `/pier-tree` opens the complete delegation tree (your todos + every worker's full list + settled nodes) in the shared Pier **popup** — the same singleton panel as `/dashboard`, opened on its `Tree` tab (inside the popup, `1`/`2`/`Tab` switch views; an explicit `/pier-tree overlay` keeps the independent full-window pane; non-Herdr falls back to the in-pane notify view); `/dashboard` opens the same panel on its `Dashboard` tab
|
|
32
|
+
- **Subagent output preview**: `subagent(action: "output", agentId)` returns what a running background subagent has printed since the previous call — an in-memory per-pane cursor computes the delta (`agent.read`, falling back to `pane.read` on older herdr), and the result carries status, revision, truncation and a `restart` flag for scrolled/cleared buffers, so the master can notice a stuck worker instead of waiting for settlement. TUI chrome (frames, footer) is stripped; once the worker has finished, the complete transcript report replaces the pane's lossy rendered copy, and a later settlement notice points at that delivery instead of repeating it (D124)
|
|
30
33
|
- **Soft write-locks**: per-pane lock beacons on `write`/`edit` paths; cross-pane conflicts warn (default) or block (`PI_HERDR_WRITE_LOCK=1`). The warning lists every holder; `/locks` is the human view of the same table (`herdr agent list` exposes the raw tokens to agents)
|
|
31
34
|
- **Role profiles**: built-in `master` / `worker-default`; custom roles as `.pi-herdr/roles/<name>.json`; toolset converges per role (deny rules are not bypassable)
|
|
32
35
|
- **Human gate**: subagent `ask_user_question` → sidebar blocked marker + notification; manual takeover (ESC-interrupt then typing) is heuristically detected so the master pauses/returns management automatically
|
|
@@ -45,8 +48,8 @@ pier ships as **two halves**, installed separately:
|
|
|
45
48
|
### Requirements
|
|
46
49
|
|
|
47
50
|
- Node ≥ 22
|
|
48
|
-
- pi ≥
|
|
49
|
-
- herdr ≥ 0.9.
|
|
51
|
+
- pi ≥ 1.3.0 (`@earendil-works/pi-coding-agent`) — `before_agent_start.messageType` and resumed-session active tools
|
|
52
|
+
- herdr ≥ 0.9.5 (macOS / Linux / Windows; Windows is preview beta) — native `pane.focused`, self-reported resume, and shutdown-safe session saves
|
|
50
53
|
|
|
51
54
|
### One-shot install (recommended)
|
|
52
55
|
|
|
@@ -162,16 +165,15 @@ Runtime policies and operational limits are centralized in `runtime-policy.ts` (
|
|
|
162
165
|
|---|---|---|---|
|
|
163
166
|
| `PIER_SUBAGENT_TIMEOUT_MS` | `600000` | ms | Subagent inactivity budget / overall timeout before forced termination |
|
|
164
167
|
| `PIER_SETTLEMENT_WINDOW_MS` | `60000` | ms | Settlement notice window, machine injection grace period, and takeover idle threshold |
|
|
165
|
-
| `PIER_OBSERVATION_WINDOW_MS` | `30000` | ms | Post-settle observation window before auto-consuming finished subagents |
|
|
168
|
+
| `PIER_OBSERVATION_WINDOW_MS` | `30000` | ms | Post-settle observation window before auto-consuming finished subagents; skipped when the worker's transcript already holds a finalized answer and its pane is idle |
|
|
166
169
|
| `PIER_FOREGROUND_PATIENCE_MS` | `300000` | ms | Foreground execution patience before auto-demoting a subagent to background |
|
|
167
170
|
| `PIER_GC_TICK_MS` | `30000` | ms | Subagent garbage collection ticker interval |
|
|
168
171
|
| `PIER_POLL_INTERVAL_MS` | `30000` | ms | State observation polling interval for subagent state transitions |
|
|
169
172
|
| `PIER_READY_TIMEOUT_MS` | `90000` | ms | Subagent pane pipe readiness wait (exponential backoff; a pane that exited fails immediately with its last output attached) |
|
|
170
173
|
| `PIER_SESSION_TTL_SECONDS` | `600` | s | Session retention TTL after subagent exit before GC cleanup |
|
|
171
174
|
| `PIER_GIT_TIMEOUT_MS` | `10000` | ms | Execution timeout for git operations (worktree creation, diff summary, cleanup) |
|
|
172
|
-
| `PIER_FOCUS_POLL_MS` | `1500` / `0` | ms | Focus sampling for heat layout (`0` disables). Default 1500ms on Herdr <0.9.1; 0 (event-first) on 0.9.1+ |
|
|
173
175
|
| `PIER_TERM_READ_MAX` | `8000` | chars | Maximum terminal buffer characters read per operation |
|
|
174
|
-
| `PIER_TERM_IDLE_MS` | `
|
|
176
|
+
| `PIER_TERM_IDLE_MS` | `300000` | ms | Idle time before the second terminal-cleanup nudge (one is sent at turn end for a terminal used that turn) |
|
|
175
177
|
| `PIER_TODO_GRACE_MS` | `30000` | ms | Settle grace before the unfinished-todo reminder |
|
|
176
178
|
| `PIER_TRACE` | – | flag | Write diagnostics (tool renderers, swallowed errors) to stderr |
|
|
177
179
|
| `PIER_JEV_ENABLE` | `0` | flag | Jev decision layer (TypeSafe System One classification) master switch; every call site fails open — see `docs/rfc-jev-integration.md` |
|
|
@@ -189,15 +191,26 @@ processes stay as they are (`PI_HERDR_SUBAGENT`, `PI_HERDR_ROLE_MANIFEST`, `PI_H
|
|
|
189
191
|
`/pier-config doctor` lists every option with its effective value and where it came from, plus the
|
|
190
192
|
errors pier deliberately swallowed this session.
|
|
191
193
|
|
|
192
|
-
**Focus heat:** Herdr 0.9.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
**Focus heat:** Herdr 0.9.5 delivers `pane.focused` natively, so pier starts no per-pane sampler;
|
|
195
|
+
the workbench receives the event directly. `PIER_WORKBENCH_ROOT` points at a relocated plugin checkout.
|
|
196
|
+
|
|
197
|
+
**Upstream tracking:** pi 1.3 / herdr 0.9.5 impact, version-floor cleanup, and the next extension
|
|
198
|
+
directions (remote machines, native worktrees, plugin actions):
|
|
199
|
+
**[docs/rfc-upstream-absorption-2026-10.md](docs/rfc-upstream-absorption-2026-10.md)**.
|
|
196
200
|
|
|
197
201
|
### Efficiency mechanisms (D100–D103, opt-in)
|
|
198
202
|
|
|
199
203
|
Three token-saving mechanisms ship **disabled by default** and are safe to try one at a time:
|
|
200
204
|
|
|
205
|
+
**Origin — credit where due**: the mechanism ideas and names come from NVIDIA's
|
|
206
|
+
[SoL-Pi](https://github.com/NVlabs/SoL-Pi) ([paper](https://arxiv.org/abs/2609.20519) ·
|
|
207
|
+
[blog](https://nvlabs.github.io/SoL-Pi/)) — four harness-level efficiency mechanisms surfaced by
|
|
208
|
+
scaled auto-research loops. pier adopts three of them (ObservationPack / EPR / OCC; the fourth,
|
|
209
|
+
Action Fusion, is not adopted) and re-implements them on its own stack, extending the originals
|
|
210
|
+
with a jev decision layer (small System One classifications at the diagnostic gate, excerpt-window
|
|
211
|
+
pick, and settle attribution — [rfc-jev-integration.md](docs/rfc-jev-integration.md)), role gating,
|
|
212
|
+
and the 4-plane efficiency config with per-mechanism JSONL telemetry (D100–D103).
|
|
213
|
+
|
|
201
214
|
- **ObservationPack** — large tool outputs are projected as placeholders after the first `fullSends` requests, with paged retrieval via `obs_recall` (session JSONL stays untouched).
|
|
202
215
|
- **EPR** — long diagnostic (`bash` test/build) logs are reduced in-process to a byte-verified evidence receipt; the raw log is archived first and stays readable.
|
|
203
216
|
- **OCC** — `todo_write` boundaries drive pi's native compaction under a KV-cache cost model, carrying unfinished tasks across the compaction.
|
package/install.mjs
CHANGED
|
@@ -100,6 +100,11 @@ function tryRun(cmd, args) {
|
|
|
100
100
|
try { run(cmd, args); return true; } catch (e) { log(` (${e.message.split('\n')[0]})`); return false; }
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/** Same as tryRun without the diagnostic line, for feature probes whose failure is expected. */
|
|
104
|
+
function tryRunQuiet(cmd, args) {
|
|
105
|
+
try { run(cmd, args); return true; } catch { return false; }
|
|
106
|
+
}
|
|
107
|
+
|
|
103
108
|
function parseVersion(s) {
|
|
104
109
|
const m = String(s).match(/(\d+)\.(\d+)\.(\d+)/);
|
|
105
110
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
@@ -145,13 +150,13 @@ function checkEnv() {
|
|
|
145
150
|
if (major < 22) die(`node ≥ 22 required, found ${process.versions.node}`);
|
|
146
151
|
log(`✓ node ${process.versions.node}`);
|
|
147
152
|
const piVersion = (() => { try { return parseVersion(run('pi', ['--version'])); } catch { return null; } })();
|
|
148
|
-
if (!piVersion || !geVersion(piVersion, [
|
|
149
|
-
die(`pi ≥
|
|
153
|
+
if (!piVersion || !geVersion(piVersion, [1, 3, 0])) {
|
|
154
|
+
die(`pi ≥ 1.3.0 required${piVersion ? `, found ${piVersion.join('.')}` : ' (pi not found or not runnable)'}. Install: npm i -g @earendil-works/pi-coding-agent`);
|
|
150
155
|
}
|
|
151
156
|
log(`✓ pi ${piVersion.join('.')}`);
|
|
152
157
|
const herdrVersion = (() => { try { return parseVersion(run('herdr', ['--version'])); } catch { return null; } })();
|
|
153
|
-
if (!herdrVersion || !geVersion(herdrVersion, [0, 9,
|
|
154
|
-
die(`herdr ≥ 0.9.
|
|
158
|
+
if (!herdrVersion || !geVersion(herdrVersion, [0, 9, 5])) {
|
|
159
|
+
die(`herdr ≥ 0.9.5 required${herdrVersion ? `, found ${herdrVersion.join('.')}` : ' (herdr not found)'}. Install: https://herdr.dev/`);
|
|
155
160
|
}
|
|
156
161
|
log(`✓ herdr ${herdrVersion.join('.')}`);
|
|
157
162
|
return { piVersion, herdrVersion };
|
|
@@ -245,16 +250,57 @@ function pkgDirFromExtPath(extPath) {
|
|
|
245
250
|
return null;
|
|
246
251
|
}
|
|
247
252
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
const HERDR_PLUGIN_ID = 'pier.workbench';
|
|
254
|
+
const HERDR_PLUGIN_NAME = 'pier-workbench';
|
|
255
|
+
const NOT_INSTALLED = Object.freeze({ installed: false, spec: null, sha: null, version: null, source: null, enabled: false });
|
|
256
|
+
|
|
257
|
+
/** `plugin list --json` (herdr 0.9.5+): returns undefined for a malformed payload, null when the
|
|
258
|
+
* plugin is absent, so the caller can fall back to the text listing on older binaries. */
|
|
259
|
+
function parseHerdrPluginJson(out) {
|
|
260
|
+
let parsed;
|
|
261
|
+
try { parsed = JSON.parse(out); } catch { return undefined; }
|
|
262
|
+
const plugins = parsed?.result?.plugins ?? parsed?.plugins;
|
|
263
|
+
if (!Array.isArray(plugins)) return undefined;
|
|
264
|
+
const p = plugins.find((x) => x && (x.plugin_id === HERDR_PLUGIN_ID || x.name === HERDR_PLUGIN_NAME));
|
|
265
|
+
if (!p) return null;
|
|
266
|
+
const source = p.source && typeof p.source === 'object' ? p.source : {};
|
|
267
|
+
const kind = typeof source.kind === 'string' ? source.kind : null;
|
|
268
|
+
const root = typeof p.plugin_root === 'string' ? p.plugin_root : null;
|
|
269
|
+
const commit = typeof source.commit === 'string' ? source.commit : null;
|
|
270
|
+
const spec = kind === 'local'
|
|
271
|
+
? `local:${root ?? p.manifest_path ?? ''}`
|
|
272
|
+
: (typeof p.version === 'string' ? `v${p.version}` : (kind ?? 'installed'));
|
|
273
|
+
return {
|
|
274
|
+
installed: true,
|
|
275
|
+
spec,
|
|
276
|
+
sha: commit ? commit.slice(0, 7) : null,
|
|
277
|
+
version: typeof p.version === 'string' ? p.version : null,
|
|
278
|
+
source: kind,
|
|
279
|
+
enabled: p.enabled === true,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function parseHerdrPluginText(out) {
|
|
284
|
+
const line = out.split(/\r?\n/).find((l) => l.includes(HERDR_PLUGIN_ID));
|
|
285
|
+
if (!line) return NOT_INSTALLED;
|
|
253
286
|
const bracket = line.match(/\[([^\]]+)\]/);
|
|
254
287
|
const spec = bracket ? bracket[1] : line.trim();
|
|
255
288
|
const at = spec.lastIndexOf('@');
|
|
256
289
|
const fullSha = at >= 0 && !spec.startsWith('local:') ? spec.slice(at + 1) : null;
|
|
257
|
-
return { installed: true, spec, sha: fullSha ? fullSha.slice(0, 7) : null };
|
|
290
|
+
return { installed: true, spec, sha: fullSha ? fullSha.slice(0, 7) : null, version: null, source: spec.startsWith('local:') ? 'local' : null, enabled: false };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function herdrPluginInfo() {
|
|
294
|
+
try {
|
|
295
|
+
const out = run('herdr', ['plugin', 'list', '--json']);
|
|
296
|
+
const parsed = parseHerdrPluginJson(out);
|
|
297
|
+
if (parsed !== undefined) return parsed ?? NOT_INSTALLED;
|
|
298
|
+
} catch { /* older herdr or no server; fall through to the text listing */ }
|
|
299
|
+
try {
|
|
300
|
+
return parseHerdrPluginText(run('herdr', ['plugin', 'list']));
|
|
301
|
+
} catch {
|
|
302
|
+
return NOT_INSTALLED;
|
|
303
|
+
}
|
|
258
304
|
}
|
|
259
305
|
|
|
260
306
|
function collectStatus() {
|
|
@@ -328,7 +374,13 @@ function update() {
|
|
|
328
374
|
if (tryRun('pi', ['update', piSource()])) log(`✓ pi extension updated (${piSource()})`);
|
|
329
375
|
else if (tryRun('pi', ['install', piSource()])) log(`✓ pi extension installed (${piSource()})`);
|
|
330
376
|
else { console.error('✗ pi update/install failed'); log(` manual: pi update ${piSource()}`); process.exitCode = 1; }
|
|
331
|
-
|
|
377
|
+
// 0.9.4+ updates the GitHub checkout in place without disturbing running plugin panes.
|
|
378
|
+
// Only try it for an installed, non-local plugin; otherwise install from the configured spec.
|
|
379
|
+
const existing = herdrPluginInfo();
|
|
380
|
+
const updated = (existing.installed && existing.source !== 'local'
|
|
381
|
+
&& tryRunQuiet('herdr', ['plugin', 'update', HERDR_PLUGIN_ID, '--yes']))
|
|
382
|
+
|| tryRunQuiet('herdr', ['plugin', 'install', HERDR_SPEC, '--yes']);
|
|
383
|
+
if (updated) log(`✓ herdr plugin updated (${HERDR_SPEC})`);
|
|
332
384
|
else { console.error('✗ herdr plugin update failed'); log(` manual: herdr plugin install ${HERDR_SPEC} --yes`); process.exitCode = 1; }
|
|
333
385
|
}
|
|
334
386
|
log('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pier-setup",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "One-shot installer for pier: install / update / version / uninstall for pi-pier + pier.workbench",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
"packages/*"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
|
+
"build:workbench": "tsc -p packages/pier-workbench/tsconfig.build.json",
|
|
34
35
|
"typecheck": "tsc --noEmit -p packages/pier-ext/tsconfig.json",
|
|
35
|
-
"test": "npm run typecheck && node --test \"packages/*/test/*.test.ts\"",
|
|
36
|
+
"test": "npm run build:workbench && npm run typecheck && node --test \"packages/*/test/*.test.ts\"",
|
|
36
37
|
"test:mutation": "stryker run",
|
|
37
38
|
"prepare": "node ./install.mjs --prepare"
|
|
38
39
|
},
|
|
@@ -42,7 +43,6 @@
|
|
|
42
43
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.2",
|
|
43
44
|
"@deepseek-ai/cordis-plugin-timer": "^1.1.3",
|
|
44
45
|
"@stryker-mutator/core": "^10.0.0",
|
|
45
|
-
"@stryker-mutator/typescript-checker": "^10.0.0",
|
|
46
46
|
"typescript": "~5.9.3"
|
|
47
47
|
},
|
|
48
48
|
"pi": {
|