pier-setup 0.4.1 → 0.4.3
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 +7 -3
- package/install.mjs +67 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,9 +27,9 @@ pier ships as **two halves**, installed separately:
|
|
|
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
|
|
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
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
|
|
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)
|
|
33
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)
|
|
34
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)
|
|
35
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
|
|
@@ -77,6 +77,10 @@ node install.mjs update --dev # re-check env + versions; pull the repo yourse
|
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
The script verifies the environment (node / pi / herdr versions) and registers both halves.
|
|
80
|
+
pier must be registered in pi from exactly one source: `install`/`update` drops the legacy
|
|
81
|
+
`git:github.com/July24/pier` registration automatically, and stops (exit 1, nothing changed)
|
|
82
|
+
when pier is already registered from another source - e.g. a `--dev` checkout before a user-mode
|
|
83
|
+
install - printing the `pi remove …` to run. `version` reports the source pi actually loads.
|
|
80
84
|
|
|
81
85
|
`update` re-runs `pi update npm:pi-pier` (falls back to `pi install`) and
|
|
82
86
|
`herdr plugin install … --yes`. Override sources with
|
|
@@ -165,7 +169,7 @@ Runtime policies and operational limits are centralized in `runtime-policy.ts` (
|
|
|
165
169
|
|---|---|---|---|
|
|
166
170
|
| `PIER_SUBAGENT_TIMEOUT_MS` | `600000` | ms | Subagent inactivity budget / overall timeout before forced termination |
|
|
167
171
|
| `PIER_SETTLEMENT_WINDOW_MS` | `60000` | ms | Settlement notice window, machine injection grace period, and takeover idle threshold |
|
|
168
|
-
| `PIER_OBSERVATION_WINDOW_MS` | `30000` | ms | Post-settle observation window before auto-consuming finished subagents |
|
|
172
|
+
| `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 |
|
|
169
173
|
| `PIER_FOREGROUND_PATIENCE_MS` | `300000` | ms | Foreground execution patience before auto-demoting a subagent to background |
|
|
170
174
|
| `PIER_GC_TICK_MS` | `30000` | ms | Subagent garbage collection ticker interval |
|
|
171
175
|
| `PIER_POLL_INTERVAL_MS` | `30000` | ms | State observation polling interval for subagent state transitions |
|
package/install.mjs
CHANGED
|
@@ -127,14 +127,17 @@ function cmpSemver(a, b) {
|
|
|
127
127
|
return 0;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
function
|
|
130
|
+
function readPkgJson(dir) {
|
|
131
131
|
try {
|
|
132
|
-
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'))
|
|
132
|
+
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
|
133
133
|
} catch {
|
|
134
134
|
return null;
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
const readPkgVersion = (dir) => readPkgJson(dir)?.version ?? null;
|
|
139
|
+
const readPkgName = (dir) => readPkgJson(dir)?.name ?? null;
|
|
140
|
+
|
|
138
141
|
function npmLatest(name) {
|
|
139
142
|
try {
|
|
140
143
|
return run('npm', ['view', name, 'version'], { timeout: 15000 });
|
|
@@ -181,12 +184,56 @@ function registeredPiPackages() {
|
|
|
181
184
|
} catch { return []; }
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
/**
|
|
185
|
-
function
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
/** A `packages` entry is a string spec or `{ source }`; local paths are relative to the agent dir. */
|
|
188
|
+
function packageSource(entry) {
|
|
189
|
+
return typeof entry === 'string' ? entry : (typeof entry?.source === 'string' ? entry.source : null);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function localPackageDir(src) {
|
|
193
|
+
if (/^(npm|git|https?|ssh):/.test(src)) return null;
|
|
194
|
+
return resolve(piAgentDir(), src.startsWith('~/') ? join(homedir(), src.slice(2)) : src);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Registered sources that load pier's extension (npm, git, or a local checkout of pi-pier). */
|
|
198
|
+
function registeredPierSources() {
|
|
199
|
+
return registeredPiPackages().map(packageSource).filter((src) => {
|
|
200
|
+
if (!src) return false;
|
|
201
|
+
if (npmNameFromSpec(src) === 'pi-pier' || /^git:.*july24\/pier/i.test(src)) return true;
|
|
202
|
+
const dir = localPackageDir(src);
|
|
203
|
+
return dir !== null && readPkgName(dir) === 'pi-pier';
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function sameSource(registered, target) {
|
|
208
|
+
const dir = localPackageDir(registered);
|
|
209
|
+
if (dir !== null) return localPackageDir(target) === dir;
|
|
210
|
+
const name = npmNameFromSpec(registered);
|
|
211
|
+
return name !== null ? npmNameFromSpec(target) === name : registered === target;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Human/CLI form of a registered source: local entries become absolute (`pi remove` takes them). */
|
|
215
|
+
function displaySource(src) {
|
|
216
|
+
const dir = localPackageDir(src);
|
|
217
|
+
return dir !== null ? dir : src;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Before install/update: drop the legacy git-mode registration (unless it is the explicit target),
|
|
222
|
+
* and refuse when pier is registered from any other source — two registrations load every pier
|
|
223
|
+
* tool twice and pi rejects the duplicate at launch.
|
|
224
|
+
*/
|
|
225
|
+
function reconcilePierRegistrations() {
|
|
226
|
+
for (const src of registeredPierSources()) {
|
|
227
|
+
if (sameSource(src, piSource())) continue;
|
|
228
|
+
if (src === LEGACY_GIT_SPEC) {
|
|
229
|
+
if (tryRun('pi', ['remove', LEGACY_GIT_SPEC])) log(`✓ removed legacy git-mode registration (${LEGACY_GIT_SPEC}); using ${piSource()}`);
|
|
230
|
+
else { console.error('✗ failed to remove legacy git-mode registration'); log(` manual: pi remove ${LEGACY_GIT_SPEC}`); process.exitCode = 1; }
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const switchHint = localPackageDir(src) !== null && !dev ? ', or keep it and rerun with --dev'
|
|
234
|
+
: dev && localPackageDir(src) === null ? ', or keep it and rerun without --dev' : '';
|
|
235
|
+
die(`pier is already registered in pi from ${displaySource(src)}; adding ${piSource()} would load every pier tool twice.\n Remove it first: pi remove ${displaySource(src)}${switchHint}`);
|
|
236
|
+
}
|
|
190
237
|
}
|
|
191
238
|
|
|
192
239
|
function npmNameFromSpec(spec) {
|
|
@@ -305,13 +352,20 @@ function herdrPluginInfo() {
|
|
|
305
352
|
|
|
306
353
|
function collectStatus() {
|
|
307
354
|
const installer = readPkgVersion(ROOT);
|
|
308
|
-
|
|
309
|
-
const
|
|
355
|
+
// Report what pi actually loads, not the spec this run would install.
|
|
356
|
+
const registered = registeredPierSources();
|
|
357
|
+
const registeredDir = registered.map(localPackageDir).find((d) => d !== null);
|
|
358
|
+
const extDir = dev
|
|
359
|
+
? (existsSync(EXT_PATH) ? EXT_DIR : null)
|
|
360
|
+
: registeredDir ?? (() => { const p = resolveUserExtPath(); return p ? pkgDirFromExtPath(p) : null; })();
|
|
361
|
+
const spec = registered.length > 0
|
|
362
|
+
? registered.map((src) => (localPackageDir(src) !== null ? `local:${localPackageDir(src)}` : src)).join(', ')
|
|
363
|
+
: piSource();
|
|
310
364
|
return {
|
|
311
365
|
product: (extDir ? readPkgVersion(extDir) : null) || installer,
|
|
312
366
|
mode: dev ? 'dev' : 'user',
|
|
313
367
|
installer: { name: 'pier-setup', version: installer },
|
|
314
|
-
piExt: { spec
|
|
368
|
+
piExt: { spec, version: extDir ? readPkgVersion(extDir) : null, path: extDir },
|
|
315
369
|
herdr: herdrPluginInfo(),
|
|
316
370
|
latest: { 'pier-setup': npmLatest('pier-setup'), 'pi-pier': npmLatest('pi-pier') },
|
|
317
371
|
};
|
|
@@ -362,7 +416,7 @@ function version() {
|
|
|
362
416
|
|
|
363
417
|
function update() {
|
|
364
418
|
checkEnv();
|
|
365
|
-
|
|
419
|
+
reconcilePierRegistrations();
|
|
366
420
|
const me = readPkgVersion(ROOT);
|
|
367
421
|
const latestSetup = npmLatest('pier-setup');
|
|
368
422
|
if (latestSetup && me && cmpSemver(me, latestSetup) < 0) {
|
|
@@ -389,7 +443,7 @@ function update() {
|
|
|
389
443
|
|
|
390
444
|
function install() {
|
|
391
445
|
checkEnv();
|
|
392
|
-
|
|
446
|
+
reconcilePierRegistrations();
|
|
393
447
|
|
|
394
448
|
if (dev) {
|
|
395
449
|
if (tryRun('pi', ['install', piSource()])) log('✓ pi extension installed (packages/pier-ext, local)');
|