pier-setup 0.3.0 → 0.3.1

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.
Files changed (3) hide show
  1. package/README.md +13 -23
  2. package/install.mjs +15 -97
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -19,7 +19,7 @@ pier ships as **two halves**, installed separately:
19
19
  | Half | Package | Role |
20
20
  |---|---|---|
21
21
  | **pi extension** | `packages/pier-ext` (npm: [`pi-pier`](https://www.npmjs.com/package/pi-pier), [pi.dev gallery](https://pi.dev/packages/pi-pier)) | Injects `todo_write` / `subagent` / `terminal` / `ask_user_question` tools into a pi session, and reports pane state over the herdr socket API |
22
- | **herdr plugin** | `packages/pier-workbench` (`pier.workbench`) | Workspace bootstrap, blocked human-gate notifications, focus heat layout (focused pane grows in place) |
22
+ | **herdr plugin** | `packages/pier-workbench` (`pier.workbench`) | Blocked human-gate notifications, focus heat layout (focused pane grows in place), ops dashboard + sidebar agent view |
23
23
 
24
24
  ### Highlights
25
25
 
@@ -30,7 +30,7 @@ pier ships as **two halves**, installed separately:
30
30
  - **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
31
  - **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
32
  - **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
33
- - **Ask picker**: a `multi: true` question renders as an interactive toggle list (space toggles a row, `a` toggles all, enter confirms, esc declines, live selection count) with the free-text row pinned last; `allowOther: false` gives pure choice. The typed-index prompt remains the fallback for RPC mode and older pi
33
+ - **Ask picker**: every choice question renders through one pier-owned picker in the TUI — single-select (arrows + enter/space pick the highlighted row) and `multi: true` toggle list (checkboxes: space toggles, `a` toggles all, live selection count) share the same framed chrome, `recommended` seeds the cursor (and pre-checks in multi), and the free-text row stays last; `allowOther: false` gives pure choice. The host select dialog and typed-index prompts remain the fallbacks for RPC mode and older pi
34
34
  - **Blocking dialogs mark the pane**: pi's `ui_prompt_start` / `ui_prompt_end` events cover the inherently blocking `ctx.ui` dialogs (`select` / `confirm` / `input` / `editor`), so a dialog from *any* extension marks the pane blocked and emits the `herdr:blocked` edge; nested gates coalesce, and the ask tool no longer needs its 5s refresh heartbeat. `custom` is deliberately excluded — pi routes resident overlays through it too (pier's own slim-frame overlay never calls `done()`), which used to leave a working pane marked blocked for the whole session
35
35
  - **Role gate stops early**: a denied worker tool call returns `terminate`, so a batch whose results are all terminating ends without another model round trip
36
36
  - **Transcript cards**: pier's own session entries (todo edits, subagent/terminal registries, role manifests, soft approvals) and its reminder messages render as compact cards instead of raw JSON — entry/message renderers, no pi-tui dependency
@@ -45,7 +45,7 @@ pier ships as **two halves**, installed separately:
45
45
  ### Requirements
46
46
 
47
47
  - Node ≥ 22
48
- - pi ≥ 0.84.4 (`@earendil-works/pi-coding-agent`)
48
+ - pi ≥ 0.86.0 (`@earendil-works/pi-coding-agent`) — dynamic toolset (transcript-backed `setActiveTools` deltas) and role switching require it
49
49
  - herdr ≥ 0.9.0 (macOS / Linux / Windows; Windows is preview beta)
50
50
 
51
51
  ### One-shot install (recommended)
@@ -56,7 +56,7 @@ No clone needed — run the npm package directly:
56
56
  npx pier-setup@latest # user mode: pi install npm:pi-pier + herdr plugin install
57
57
  npx pier-setup@latest version # local vs npm latest (installer / pi-pier / herdr plugin)
58
58
  npx pier-setup@latest update # refresh both halves in place (does not uninstall first)
59
- npx pier-setup@latest uninstall # --purge also drops boot-config.json
59
+ npx pier-setup@latest uninstall
60
60
 
61
61
  npm i -g pier-setup # or install globally, then: pier-setup / version / update
62
62
  ```
@@ -70,15 +70,13 @@ Dev mode still requires cloning the repo:
70
70
  git clone https://github.com/July24/pier && cd pier
71
71
  node install.mjs install --dev # local-path pi install + herdr plugin link; code changes are live
72
72
  node install.mjs version --dev
73
- node install.mjs update --dev # rewrite boot-config only; pull the repo yourself
73
+ node install.mjs update --dev # re-check env + versions; pull the repo yourself
74
74
  ```
75
75
 
76
- The script verifies the environment (node / pi / herdr versions), probes pi's node
77
- and cli.js absolute paths, generates boot-config.json (user mode stores it in the
78
- herdr plugin config dir, so reinstalls don't lose it), and registers both halves.
76
+ The script verifies the environment (node / pi / herdr versions) and registers both halves.
79
77
 
80
78
  `update` re-runs `pi update npm:pi-pier` (falls back to `pi install`) and
81
- `herdr plugin install … --yes`, then rewrites boot-config. Override sources with
79
+ `herdr plugin install … --yes`. Override sources with
82
80
  `--pi-spec=` / `--herdr-spec=` (npm publishing or forks). `pier-setup --help`
83
81
  lists every command.
84
82
 
@@ -131,15 +129,7 @@ pi overwrites tools/commands by name; event listeners stack. Two todo or subagen
131
129
  **Designed coexistence:** herdr’s official `herdr:pi` reporter. Keep it. pier emits `herdr:blocked` so that plugin remains lifecycle authority.
132
130
 
133
131
  - **other agents inside herdr (claude code / codex etc.)**: tabs without a pi pane
134
- are excluded from heat reflow; blocked notifications are pi-only; master-tab
135
- auto-bootstrap can be disabled with `autoBootstrap: false` in boot-config.
136
-
137
- ### Bootstrap config (workbench half)
138
-
139
- Master-tab bootstrap needs local node / pi paths: `pier-setup` generates
140
- them automatically; manually, copy `packages/pier-workbench/scripts/boot-config.example.json`
141
- (placeholders for both macOS and Windows). User mode reads it from
142
- `herdr plugin config-dir pier.workbench`; dev mode from `packages/pier-workbench/scripts/boot-config.json`.
132
+ are excluded from heat reflow; blocked notifications are pi-only.
143
133
 
144
134
  ### Usage
145
135
 
@@ -153,7 +143,7 @@ them automatically; manually, copy `packages/pier-workbench/scripts/boot-config.
153
143
  ```
154
144
  packages/
155
145
  pier-ext/ # pi extension (npm: pi-pier): todo/subagent tools, herdr client, vocab authority, skill
156
- pier-workbench/ # herdr plugin (pier.workbench): workspace bootstrap + blocked notify + heat layout
146
+ pier-workbench/ # herdr plugin (pier.workbench): blocked notify + heat layout + ops dashboard
157
147
  docs/ # install guide, configuration overview, role profile docs, sidebar role config
158
148
  ```
159
149
 
@@ -161,7 +151,7 @@ docs/ # install guide, configuration overview, role profile docs, s
161
151
 
162
152
  ```sh
163
153
  npm install --ignore-scripts
164
- npm test # node --test, 809 unit tests (planner / todo replay / anti-freeze staleness / session tail / GC / lifecycle / renderers / ask picker / subagent output / jev decision layer: diagnostic gate, notice ranking, excerpt windows)
154
+ npm test # node --test, 733 unit tests (planner / todo replay / anti-freeze staleness / session tail / GC / lifecycle / renderers / ask picker / subagent output / jev decision layer: diagnostic gate, notice ranking, excerpt windows)
165
155
  ```
166
156
 
167
157
  ## Configuration
@@ -218,10 +208,10 @@ PI_HERDR_OBS_PACK_ENABLE=1 PI_HERDR_OBS_PACK_LOG=1 pi # start here
218
208
 
219
209
  Full quick start, what to watch in `efficiency-logs/*.jsonl`, rollback steps and a feedback template: **[docs/efficiency-trial.md](docs/efficiency-trial.md)**.
220
210
 
221
- Everything above (plus roles, pi settings, boot-config and the `PIER_*`/`PI_HERDR_*` env knobs) is also inspectable in-session:
211
+ Everything above (plus roles, pi settings and the `PIER_*`/`PI_HERDR_*` env knobs) is also inspectable in-session:
222
212
 
223
213
  ```text
224
- /pier-config # 5-plane index; hands a guided change to the agent
214
+ /pier-config # 4-plane index; hands a guided change to the agent
225
215
  /pier-config show all # effective value + source (env > workspace > user > default) per key
226
216
  /pier-config check # validation across planes · /pier-config doc → written report
227
217
  ```
@@ -241,6 +231,6 @@ MIT
241
231
 
242
232
  ---
243
233
 
244
- > 💡 **For contributors**: `.gitignore` is tracked in the repository. Note that `packages/pier-workbench/scripts/boot-config.json` is machine-local (see the `.example.json` template), and `docs/research/` contains local research notes ignored by `.gitignore`.
234
+ > 💡 **For contributors**: `.gitignore` is tracked in the repository. `docs/research/` contains local research notes ignored by `.gitignore`.
245
235
  >
246
236
  > **Naming convention**: the brand is **pier** (repo/packages/plugin); runtime protocol identifiers keep the **`pi-herdr`** prefix (`.pi-herdr/roles/` dir, `pi-herdr.subs` session custom entries, `~/.pi/agent/herdr-pi/roles/` user dir) — they persist with user sessions/config files, renaming would break existing data, so they are the compatibility layer.
package/install.mjs CHANGED
@@ -3,25 +3,23 @@
3
3
  * pier one-shot installer / uninstaller / updater / version inspector (cross-platform: macOS / Linux / Windows).
4
4
  *
5
5
  * Usage:
6
- * node install.mjs install [--dev] [--hmr-dev] [--force] # Install (default command, 'install' can be omitted)
7
- * node install.mjs update [--dev] [--hmr-dev] # Refresh both halves in place to latest release (no prior uninstall)
8
- * node install.mjs version [--json] # Local vs npm latest
9
- * node install.mjs uninstall [--dev] [--purge] # Uninstall; --purge deletes boot-config.json as well
6
+ * node install.mjs install [--dev] # Install (default command, 'install' can be omitted)
7
+ * node install.mjs update [--dev] # Refresh both halves in place to latest release (no prior uninstall)
8
+ * node install.mjs version [--json] # Local vs npm latest
9
+ * node install.mjs uninstall [--dev] # Uninstall both halves
10
10
  * node install.mjs --prepare # npm prepare: hooksPath + repo root bin link
11
11
  * node install.mjs --help
12
12
  *
13
13
  * Modes:
14
14
  * User mode (default): pi install npm:pi-pier
15
15
  * + herdr plugin install July24/pier/packages/pier-workbench --yes
16
- * boot-config.json written to herdr plugin config-dir; extPath points to pi-installed
17
- * pi-pier (~/.pi/agent/npm/...), not the repo path inside pier-setup package.
18
16
  * --dev Development mode: pi install <repo>/packages/pier-ext + herdr plugin link <repo>/packages/pier-workbench
19
- * (linked directory is live; code edits take effect immediately; update only rewrites boot-config).
17
+ * (linked directory is live; code edits take effect immediately).
20
18
  * Distribution specs can be overridden via --pi-spec= / --herdr-spec= (for npm publish or fork scenarios).
21
19
  * Failure semantics: Each step provides manual equivalent command; step failure does not abort reporting (exitCode=1).
22
20
  */
23
21
  import { execFileSync } from 'node:child_process';
24
- import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
22
+ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
25
23
  import { homedir } from 'node:os';
26
24
  import { dirname, join, resolve } from 'node:path';
27
25
  import { fileURLToPath } from 'node:url';
@@ -29,7 +27,6 @@ import { fileURLToPath } from 'node:url';
29
27
  const ROOT = resolve(dirname(fileURLToPath(import.meta.url)));
30
28
  const EXT_DIR = join(ROOT, 'packages', 'pier-ext');
31
29
  const WB_DIR = join(ROOT, 'packages', 'pier-workbench');
32
- const DEV_BOOT_CONFIG = join(WB_DIR, 'scripts', 'boot-config.json');
33
30
  const EXT_PATH = join(EXT_DIR, 'src', 'index.ts');
34
31
  const IS_WIN = process.platform === 'win32';
35
32
  const COMMANDS = ['install', 'uninstall', 'update', 'version', 'help'];
@@ -141,10 +138,6 @@ function npmLatest(name) {
141
138
  }
142
139
  }
143
140
 
144
- /** Herdr plugin config directory (boot-config destination in user mode; semantic path exists once plugin is registered). */
145
- function herdrConfigDir() {
146
- try { return run('herdr', ['plugin', 'config-dir', 'pier.workbench']); } catch { return null; }
147
- }
148
141
 
149
142
  /* ── Environment check (pre-install) ────────────────────────────────── */
150
143
  function checkEnv() {
@@ -152,8 +145,8 @@ function checkEnv() {
152
145
  if (major < 22) die(`node ≥ 22 required, found ${process.versions.node}`);
153
146
  log(`✓ node ${process.versions.node}`);
154
147
  const piVersion = (() => { try { return parseVersion(run('pi', ['--version'])); } catch { return null; } })();
155
- if (!piVersion || !geVersion(piVersion, [0, 84, 4])) {
156
- die(`pi ≥ 0.84.4 required${piVersion ? `, found ${piVersion.join('.')}` : ' (pi not found or not runnable)'}. Install: npm i -g @earendil-works/pi-coding-agent`);
148
+ if (!piVersion || !geVersion(piVersion, [0, 86, 0])) {
149
+ die(`pi ≥ 0.86.0 required${piVersion ? `, found ${piVersion.join('.')}` : ' (pi not found or not runnable)'}. Install: npm i -g @earendil-works/pi-coding-agent`);
157
150
  }
158
151
  log(`✓ pi ${piVersion.join('.')}`);
159
152
  const herdrVersion = (() => { try { return parseVersion(run('herdr', ['--version'])); } catch { return null; } })();
@@ -167,26 +160,6 @@ function checkEnv() {
167
160
  /** pi extension source: user mode uses git/npm spec, dev mode uses local directory. */
168
161
  const piSource = () => (dev ? EXT_DIR : PI_SPEC);
169
162
 
170
- /* ── boot-config detection and writing ────────────────────────────── */
171
- function probePiRuntime() {
172
- const piBin = which('pi');
173
- // bin is a node shim: realpath resolves to .../pi-coding-agent/dist/cli.js (npm layout, isomorphic win/posix)
174
- if (piBin) {
175
- try {
176
- const real = realpathSync(piBin);
177
- if (real.endsWith('cli.js') && existsSync(real)) {
178
- return { piCli: real, piNode: process.execPath };
179
- }
180
- } catch { /* fall back to fallback */ }
181
- }
182
- // npm root -g fallback: when pi is not in PATH (e.g. nvm shim), construct from global npm layout
183
- try {
184
- const gRoot = run('npm', ['root', '-g']);
185
- const cand = join(gRoot, '@earendil-works', 'pi-coding-agent', 'dist', 'cli.js');
186
- if (existsSync(cand)) return { piCli: cand, piNode: process.execPath };
187
- } catch { /* npm unavailable */ }
188
- return null;
189
- }
190
163
 
191
164
  function piAgentDir() {
192
165
  return process.env.PI_CODING_AGENT_DIR || join(homedir(), '.pi', 'agent');
@@ -260,47 +233,6 @@ function resolveUserExtPath() {
260
233
  return extPathFromPiList();
261
234
  }
262
235
 
263
- function writeBootConfig(hmrDev, force) {
264
- const probed = probePiRuntime();
265
- if (!probed) die('cannot locate pi cli.js (tried: pi shim realpath, npm root -g). Fill boot-config.json manually.');
266
- let extPath;
267
- if (dev) {
268
- if (!existsSync(EXT_PATH)) die(`pier-ext entry missing: ${EXT_PATH} (repo layout broken?)`);
269
- extPath = EXT_PATH;
270
- } else {
271
- extPath = resolveUserExtPath();
272
- if (!extPath) die(`cannot locate installed pier-ext (npm:pi-pier / git clone). Did \`pi install ${piSource()}\` succeed?`);
273
- }
274
-
275
- const config = {
276
- mainTabLabel: 'main',
277
- piNode: probed.piNode,
278
- piCli: probed.piCli,
279
- extPath,
280
- workbenchPluginId: 'pier.workbench',
281
- hmrDev,
282
- };
283
-
284
- const target = dev ? DEV_BOOT_CONFIG : join(herdrConfigDir() ?? die('cannot resolve herdr plugin config-dir (is pier.workbench installed?)'), 'boot-config.json');
285
- if (existsSync(target) && !force) {
286
- let existing = null;
287
- try { existing = JSON.parse(readFileSync(target, 'utf8')); } catch { /* treated as corrupted */ }
288
- if (existing) {
289
- log(`• ${target} exists, diff (existing → new):`);
290
- for (const k of Object.keys(config)) {
291
- if (existing[k] !== config[k]) log(` ${k}: ${JSON.stringify(existing[k])} → ${JSON.stringify(config[k])}`);
292
- }
293
- log(' kept existing (use --force to overwrite)');
294
- return;
295
- }
296
- }
297
- mkdirSync(dirname(target), { recursive: true });
298
- writeFileSync(target, JSON.stringify(config, null, 2) + '\n');
299
- log(`✓ boot-config.json ${force ? 'overwritten (--force)' : 'written'} → ${target}`);
300
- log(` piNode = ${config.piNode}`);
301
- log(` piCli = ${config.piCli}`);
302
- log(` extPath = ${config.extPath}`);
303
- }
304
236
 
305
237
  function pkgDirFromExtPath(extPath) {
306
238
  let d = dirname(extPath);
@@ -344,17 +276,17 @@ function usage() {
344
276
  log(`pier-setup — install / update / inspect pier (pi-pier + pier.workbench)
345
277
 
346
278
  Usage:
347
- pier-setup [install] [--dev] [--hmr-dev] [--force]
348
- pier-setup update [--dev] [--hmr-dev]
279
+ pier-setup [install] [--dev]
280
+ pier-setup update [--dev]
349
281
  pier-setup version [--json]
350
- pier-setup uninstall [--dev] [--purge]
282
+ pier-setup uninstall [--dev]
351
283
 
352
284
  npx pier-setup@latest # recommended user install
353
285
  npx pier-setup@latest update # refresh both halves to newest release
354
286
  npx pier-setup@latest version # local vs npm latest
355
287
 
356
288
  --pi-spec= / --herdr-spec= override distribution sources
357
- --dev local link (clone); update only rewrites boot-config`);
289
+ --dev local link (clone); the linked directories are live`);
358
290
  }
359
291
 
360
292
  function version() {
@@ -390,25 +322,20 @@ function update() {
390
322
  if (latestSetup && me && cmpSemver(me, latestSetup) < 0) {
391
323
  log(`⚠ installer ${me} < latest ${latestSetup} — re-run: npx pier-setup@latest update`);
392
324
  }
393
- const hmrDev = flags.has('--hmr-dev');
394
325
  if (dev) {
395
- log('dev mode: local link is live; pull the repo yourself. Refreshing boot-config only.');
396
- writeBootConfig(hmrDev, true);
326
+ log('dev mode: local link is live; pull the repo yourself (no installer-managed files to refresh).');
397
327
  } else {
398
328
  if (tryRun('pi', ['update', piSource()])) log(`✓ pi extension updated (${piSource()})`);
399
329
  else if (tryRun('pi', ['install', piSource()])) log(`✓ pi extension installed (${piSource()})`);
400
330
  else { console.error('✗ pi update/install failed'); log(` manual: pi update ${piSource()}`); process.exitCode = 1; }
401
331
  if (tryRun('herdr', ['plugin', 'install', HERDR_SPEC, '--yes'])) log(`✓ herdr plugin updated (${HERDR_SPEC})`);
402
332
  else { console.error('✗ herdr plugin update failed'); log(` manual: herdr plugin install ${HERDR_SPEC} --yes`); process.exitCode = 1; }
403
- writeBootConfig(hmrDev, true);
404
333
  }
405
334
  log('');
406
335
  version();
407
336
  }
408
337
 
409
338
  function install() {
410
- const hmrDev = flags.has('--hmr-dev');
411
- const force = flags.has('--force');
412
339
  checkEnv();
413
340
  removeLegacyGitRegistration();
414
341
 
@@ -417,40 +344,31 @@ function install() {
417
344
  else { console.error('✗ pi install failed'); log(` manual: pi install ${EXT_DIR}`); process.exitCode = 1; }
418
345
  if (tryRun('herdr', ['plugin', 'link', WB_DIR])) log('✓ herdr plugin linked (packages/pier-workbench, local)');
419
346
  else { console.error('✗ herdr plugin link failed'); log(` manual: herdr plugin link ${WB_DIR}`); process.exitCode = 1; }
420
- writeBootConfig(hmrDev, force);
421
347
  } else {
422
348
  if (tryRun('pi', ['install', piSource()])) log(`✓ pi extension installed (${PI_SPEC})`);
423
349
  else { console.error('✗ pi install failed'); log(` manual: pi install ${PI_SPEC}`); process.exitCode = 1; }
424
350
  if (tryRun('herdr', ['plugin', 'install', HERDR_SPEC, '--yes'])) log(`✓ herdr plugin installed (${HERDR_SPEC})`);
425
351
  else { console.error('✗ herdr plugin install failed'); log(` manual: herdr plugin install ${HERDR_SPEC}`); process.exitCode = 1; }
426
- writeBootConfig(hmrDev, force);
427
352
  }
428
353
 
429
354
  log(`
430
355
  pier installed (${dev ? 'dev mode: local paths, code changes are live' : 'user mode: managed checkouts'}). Next:
431
- 1. start herdr, open/create a workspace — the main tab auto-bootstraps a pi session
356
+ 1. start herdr, open/create a workspace and run \`pi\` inside a pane
432
357
  2. inside pi, ask the model to use todo_write / subagent tools`);
433
358
  }
434
359
 
435
360
  function uninstall() {
436
- const purge = flags.has('--purge');
437
361
  if (tryRun('pi', ['remove', piSource()])) log('✓ pi extension removed');
438
362
  else { console.error('✗ pi remove failed'); log(` manual: pi remove ${piSource()}`); process.exitCode = 1; }
439
363
 
440
364
  if (dev) {
441
365
  if (tryRun('herdr', ['plugin', 'unlink', 'pier.workbench'])) log('✓ herdr plugin unlinked (files kept)');
442
366
  else { console.error('✗ herdr plugin unlink failed'); log(' manual: herdr plugin unlink pier.workbench'); process.exitCode = 1; }
443
- if (purge && existsSync(DEV_BOOT_CONFIG)) { rmSync(DEV_BOOT_CONFIG); log('✓ dev boot-config.json purged'); }
444
367
  } else {
445
368
  if (tryRun('herdr', ['plugin', 'uninstall', 'pier.workbench'])) log('✓ herdr plugin uninstalled (managed checkout removed)');
446
369
  else { console.error('✗ herdr plugin uninstall failed'); log(' manual: herdr plugin uninstall pier.workbench'); process.exitCode = 1; }
447
- if (purge) {
448
- const dir = herdrConfigDir();
449
- const f = dir ? join(dir, 'boot-config.json') : null;
450
- if (f && existsSync(f)) { rmSync(f); log('✓ boot-config.json purged'); }
451
- }
452
370
  }
453
- log(`\npier uninstalled.${purge ? '' : ' (boot-config.json kept; use --purge to remove)'}`);
371
+ log('\npier uninstalled.');
454
372
  }
455
373
 
456
374
  if (command === 'help' || flags.has('--help') || flags.has('-h')) usage();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pier-setup",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
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",