pier-setup 0.2.7 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +78 -7
  2. package/install.mjs +39 -39
  3. package/package.json +5 -3
package/README.md CHANGED
@@ -26,11 +26,18 @@ pier ships as **two halves**, installed separately:
26
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`; TUI widget is an activity-anchored window (the in_progress entry and its surrounding context stay visible as work moves down the list); `/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
- - **Soft write-locks**: per-pane lock tokens on write paths; cross-pane conflicts warn or block
29
+ - **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
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)
30
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)
31
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
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
+ - **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
+ - **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
32
37
  - **Isolated worktree subagents (`isolate`)**: heavy parallel writers get a fresh git worktree (branch `pier/<slug>` from your HEAD under `~/.herdr/worktrees/<repo>/`) with commit discipline in the prompt; settlement carries a diff summary (commits since base, files changed, uncommitted count); merge with `git merge --no-ff` and the worktree auto-removes once merged and clean — the branch stays for audit
33
- - **Long-task lifecycle (01a03c0d review)**: observation timeout is now an inactivity budget (working slices renew it — healthy >10min tasks are no longer killed); follow_up messages deliver via steer at tool-call gaps (supplementary contracts arrive in seconds, not after the whole run); GC waits for the settlement notice to be delivered before closing panes; takeover detection attributes recent machine injections first; ledger rows carry a `via` tag, closed rows inherit their outcome, zombie running rows are swept on startup; SUBS snapshots are hash-gated. Tunables: `PI_HERDR_SUBAGENT_TIMEOUT_MS` (inactivity seconds), `PI_HERDR_INJECT_GRACE_MS`, `PI_HERDR_OBSERVE_MS`
38
+ - **Worktree collection is ownership-scoped**: only worktrees this session registered are collected, and the running process's own directory is never a candidate — the earlier prefix match on `refs/heads/pier/` could delete a parallel session's worktree out from under it. Sweeping branches the session never registered is opt-in (`PIER_ISOLATE_SWEEP_ORPHANS=1`)
39
+ - **Ops dashboard + sidebar view (herdr 0.9)**: `herdr plugin pane open --plugin pier.workbench --entrypoint dashboard` opens a live pane/tab/agent board (roles, states, todo progress); the plugin also registers a `Pier` sidebar agent view that lists every detected agent (no harness filter) with attention-first ordering
40
+ - **Long-task lifecycle (01a03c0d review)**: observation timeout is now an inactivity budget (working slices renew it — healthy >10min tasks are no longer killed); follow_up messages deliver via steer at tool-call gaps (supplementary contracts arrive in seconds, not after the whole run); GC waits for the settlement notice to be delivered before closing panes; takeover detection attributes recent machine injections first; ledger rows carry a `via` tag, closed rows inherit their outcome, zombie running rows are swept on startup; SUBS snapshots are hash-gated. Tunables: `PIER_SUBAGENT_TIMEOUT_MS` (inactivity ms), `PIER_SETTLEMENT_WINDOW_MS` (ms), `PIER_OBSERVATION_WINDOW_MS` (ms)
34
41
  - **Settlement notice folding**: subagent settlements no longer flood back when a long main run ends — they inject at turn gaps, up to 3 shown, the rest folded with pointers
35
42
 
36
43
  ## Installation
@@ -38,8 +45,8 @@ pier ships as **two halves**, installed separately:
38
45
  ### Requirements
39
46
 
40
47
  - Node ≥ 22
41
- - pi ≥ 0.84 (`@earendil-works/pi-coding-agent`)
42
- - herdr ≥ 0.8.0 (macOS / Linux / Windows; Windows is preview beta)
48
+ - pi ≥ 0.84.4 (`@earendil-works/pi-coding-agent`)
49
+ - herdr ≥ 0.9.0 (macOS / Linux / Windows; Windows is preview beta)
43
50
 
44
51
  ### One-shot install (recommended)
45
52
 
@@ -147,16 +154,80 @@ them automatically; manually, copy `packages/pier-workbench/scripts/boot-config.
147
154
  packages/
148
155
  pier-ext/ # pi extension (npm: pi-pier): todo/subagent tools, herdr client, vocab authority, skill
149
156
  pier-workbench/ # herdr plugin (pier.workbench): workspace bootstrap + blocked notify + heat layout
150
- docs/ # install guide, role profile docs, sidebar role config
157
+ docs/ # install guide, configuration overview, role profile docs, sidebar role config
151
158
  ```
152
159
 
153
160
  ## Testing
154
161
 
155
162
  ```sh
156
163
  npm install --ignore-scripts
157
- npm test # node --test, 278 unit tests (planner / todo replay / anti-freeze staleness / session tail / GC / lifecycle)
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)
158
165
  ```
159
166
 
167
+ ## Configuration
168
+
169
+ Runtime policies and operational limits are centralized in `runtime-policy.ts` (with terminal read limits in `terminal.ts`). All timeouts use millisecond units unless specified otherwise:
170
+
171
+ | Variable | Default | Unit | Purpose |
172
+ |---|---|---|---|
173
+ | `PIER_SUBAGENT_TIMEOUT_MS` | `600000` | ms | Subagent inactivity budget / overall timeout before forced termination |
174
+ | `PIER_SETTLEMENT_WINDOW_MS` | `60000` | ms | Settlement notice window, machine injection grace period, and takeover idle threshold |
175
+ | `PIER_OBSERVATION_WINDOW_MS` | `30000` | ms | Post-settle observation window before auto-consuming finished subagents |
176
+ | `PIER_FOREGROUND_PATIENCE_MS` | `300000` | ms | Foreground execution patience before auto-demoting a subagent to background |
177
+ | `PIER_GC_TICK_MS` | `30000` | ms | Subagent garbage collection ticker interval |
178
+ | `PIER_POLL_INTERVAL_MS` | `30000` | ms | State observation polling interval for subagent state transitions |
179
+ | `PIER_READY_TIMEOUT_MS` | `90000` | ms | Subagent pane pipe readiness wait (exponential backoff; a pane that exited fails immediately with its last output attached) |
180
+ | `PIER_SESSION_TTL_SECONDS` | `600` | s | Session retention TTL after subagent exit before GC cleanup |
181
+ | `PIER_GIT_TIMEOUT_MS` | `10000` | ms | Execution timeout for git operations (worktree creation, diff summary, cleanup) |
182
+ | `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+ |
183
+ | `PIER_TERM_READ_MAX` | `8000` | chars | Maximum terminal buffer characters read per operation |
184
+ | `PIER_TERM_IDLE_MS` | `1800000` | ms | Idle time before pier nudges about an open terminal |
185
+ | `PIER_TODO_GRACE_MS` | `30000` | ms | Settle grace before the unfinished-todo reminder |
186
+ | `PIER_TRACE` | – | flag | Write diagnostics (tool renderers, swallowed errors) to stderr |
187
+ | `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` |
188
+ | `PIER_JEV_LOG` | `0` | flag | Write `efficiency-logs/jev.jsonl` (metadata + hashes only, never bodies) |
189
+ | `PIER_JEV_API_KEY` | – | – | API key; precedence env > config `jev.apiKey` > `TYPESAFE_API_KEY` |
190
+ | `PIER_JEV_MODEL` | `jev-1.13.0` | – | Pinned versioned model id (aliases drift silently and skew tuned thresholds) |
191
+ | `PIER_JEV_TIMEOUT_MS` | `2000` | ms | Total per-call budget (AbortController hard kill) |
192
+ | `PIER_JEV_MIN_CONFIDENCE` | `0.6` | – | Minimum Choice/Score confidence to adopt an answer |
193
+ | `PIER_JEV_BASE_URL` | – | – | API root override (relay/gateway) |
194
+
195
+ **Naming:** `PIER_*` is the canonical namespace for pier options; the historical `PI_HERDR_*` spelling
196
+ of the same knob is still read as an alias (an empty value counts as unset). Names handed to child
197
+ processes stay as they are (`PI_HERDR_SUBAGENT`, `PI_HERDR_ROLE_MANIFEST`, `PI_HERDR_TUI`,
198
+ `PI_HERDR_META_KEY`), because renaming them would split a running worker from its parent.
199
+ `/pier-config doctor` lists every option with its effective value and where it came from, plus the
200
+ errors pier deliberately swallowed this session.
201
+
202
+ **Focus heat:** Herdr 0.9.0 resolved mouse focus in the client and did not deliver `pane.focused`
203
+ to plugins, so each pane sampled `layout.export → focused_pane_id` and replayed the workbench
204
+ event. Herdr 0.9.1+ delivers `pane.focused` natively, so the poller defaults off. Override with
205
+ `PIER_FOCUS_POLL_MS`; `PIER_WORKBENCH_ROOT` points at a relocated plugin checkout.
206
+
207
+ ### Efficiency mechanisms (D100–D103, opt-in)
208
+
209
+ Three token-saving mechanisms ship **disabled by default** and are safe to try one at a time:
210
+
211
+ - **ObservationPack** — large tool outputs are projected as placeholders after the first `fullSends` requests, with paged retrieval via `obs_recall` (session JSONL stays untouched).
212
+ - **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.
213
+ - **OCC** — `todo_write` boundaries drive pi's native compaction under a KV-cache cost model, carrying unfinished tasks across the compaction.
214
+
215
+ ```bash
216
+ PI_HERDR_OBS_PACK_ENABLE=1 PI_HERDR_OBS_PACK_LOG=1 pi # start here
217
+ ```
218
+
219
+ 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
+
221
+ Everything above (plus roles, pi settings, boot-config and the `PIER_*`/`PI_HERDR_*` env knobs) is also inspectable in-session:
222
+
223
+ ```text
224
+ /pier-config # 5-plane index; hands a guided change to the agent
225
+ /pier-config show all # effective value + source (env > workspace > user > default) per key
226
+ /pier-config check # validation across planes · /pier-config doc → written report
227
+ ```
228
+
229
+ Read-only by design — see **[docs/configuration.md](docs/configuration.md)** for the plane map and the "which plane do I edit?" table.
230
+
160
231
  ## Design principles
161
232
 
162
233
  - **Session JSONL is the single source of truth**: todos, delegations, and registries replay from session branches — restart and branch switches stay correct
@@ -170,6 +241,6 @@ MIT
170
241
 
171
242
  ---
172
243
 
173
- > 💡 **For contributors**: `.gitignore` is deliberately **not committed** to this repository (ignore rules are per-environment). Maintain your own rules based on `README.md` / `README_ZH.md`; `packages/pier-workbench/scripts/boot-config.json` is also machine-local (see the `.example.json` template).
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`.
174
245
  >
175
246
  > **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
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * pier 一键安装/卸载/更新/查版本(跨平台:macOS / Linux / Windows)。
3
+ * pier one-shot installer / uninstaller / updater / version inspector (cross-platform: macOS / Linux / Windows).
4
4
  *
5
- * 用法:
6
- * node install.mjs install [--dev] [--hmr-dev] [--force] # 安装(默认命令,可省略 install)
7
- * node install.mjs update [--dev] [--hmr-dev] # 原地刷新两半区到最新发行(不先卸载)
8
- * node install.mjs version [--json] # 本地 vs npm latest
9
- * node install.mjs uninstall [--dev] [--purge] # 卸载;--purge 连 boot-config.json 一起删
10
- * node install.mjs --prepare # npm prepare:hooksPath + 仓库根 bin 链接
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
10
+ * node install.mjs --prepare # npm prepare: hooksPath + repo root bin link
11
11
  * node install.mjs --help
12
12
  *
13
- * 模式:
14
- * 用户模式(默认):pi install npm:pi-pier
13
+ * Modes:
14
+ * User mode (default): pi install npm:pi-pier
15
15
  * + herdr plugin install July24/pier/packages/pier-workbench --yes
16
- * boot-config.json 写 herdr plugin config-dir;extPath 指向 pi 已安装的
17
- * pi-pier(~/.pi/agent/npm/...),不是 pier-setup 包内的仓库路径。
18
- * --dev 开发模式:pi install <repo>/packages/pier-ext + herdr plugin link <repo>/packages/pier-workbench
19
- * (link 目录是活的,改码即生效;update 只重写 boot-config)。
20
- * 发行规格可用 --pi-spec= / --herdr-spec= 覆盖(npm 发布或 fork 场景)。
21
- * 失败语义:每步给出手动等价命令;步骤失败不阻断报告(exitCode=1)。
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
+ * --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).
20
+ * Distribution specs can be overridden via --pi-spec= / --herdr-spec= (for npm publish or fork scenarios).
21
+ * Failure semantics: Each step provides manual equivalent command; step failure does not abort reporting (exitCode=1).
22
22
  */
23
23
  import { execFileSync } from 'node:child_process';
24
24
  import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
@@ -66,17 +66,17 @@ const command = COMMANDS.includes(argv[0]) ? argv.shift() : null;
66
66
  const flags = new Set(argv.filter((a) => (a.startsWith('--') && !a.includes('=')) || a === '-h' || a === '-v'));
67
67
  const optValue = (name) => argv.find((a) => a.startsWith(`--${name}=`))?.split('=').slice(1).join('=') ?? null;
68
68
  const dev = flags.has('--dev');
69
- const PI_SPEC = optValue('pi-spec') ?? 'npm:pi-pier'; // 用户模式默认 npm 发行版;--pi-spec=git:github.com/July24/pier 可跟随仓库 main
69
+ const PI_SPEC = optValue('pi-spec') ?? 'npm:pi-pier'; // User mode defaults to npm release; --pi-spec=git:github.com/July24/pier tracks repo main
70
70
  const HERDR_SPEC = optValue('herdr-spec') ?? 'July24/pier/packages/pier-workbench';
71
71
 
72
72
  const log = (m) => console.log(m);
73
73
  const die = (m) => { console.error(`✗ ${m}`); process.exit(1); };
74
74
 
75
- /* ── 工具函数 ────────────────────────────────────────────────────── */
75
+ /* ── Utility functions ────────────────────────────────────────────── */
76
76
  function which(bin) {
77
77
  try {
78
78
  const out = execFileSync(IS_WIN ? 'where' : 'which', [bin], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
79
- // where 可能多行(PATH 逐条命中),取第一条存在的
79
+ // 'where' may return multiple lines (one per PATH hit); pick the first existing line
80
80
  for (const line of out.split('\n').map((l) => l.trim()).filter(Boolean)) {
81
81
  if (existsSync(line)) return line;
82
82
  }
@@ -87,9 +87,9 @@ function which(bin) {
87
87
  }
88
88
 
89
89
  function run(cmd, args, opts = {}) {
90
- // win32: npm 全局 bin 是 .cmd shim,无 shell 直接 spawn 会 ENOENT/EINVAL,须走 cmd.exe;
91
- // 且 shell:true 时 Node 把 args 按空格裸拼接(不加引号),含空格的路径参数会被拆碎,故预拼接并加引号。
92
- // POSIX 走原生分支,保持 execFileSync(cmd, args) 原语义。
90
+ // win32: npm global bin is a .cmd shim; spawning directly without shell triggers ENOENT/EINVAL, must run via cmd.exe;
91
+ // also when shell:true, Node concatenates args with bare spaces (unquoted), breaking path args with spaces, so pre-join and quote.
92
+ // POSIX uses native branch, preserving execFileSync(cmd, args) semantics.
93
93
  if (IS_WIN) {
94
94
  const cmdline = [cmd, ...args]
95
95
  .map((a) => (/[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a))
@@ -141,50 +141,50 @@ function npmLatest(name) {
141
141
  }
142
142
  }
143
143
 
144
- /** herdr 插件配置目录(用户模式 boot-config 落点;插件已注册才存在路径语义)。 */
144
+ /** Herdr plugin config directory (boot-config destination in user mode; semantic path exists once plugin is registered). */
145
145
  function herdrConfigDir() {
146
146
  try { return run('herdr', ['plugin', 'config-dir', 'pier.workbench']); } catch { return null; }
147
147
  }
148
148
 
149
- /* ── 环境校验(install 前置) ────────────────────────────────────── */
149
+ /* ── Environment check (pre-install) ────────────────────────────────── */
150
150
  function checkEnv() {
151
151
  const [major] = process.versions.node.split('.').map(Number);
152
152
  if (major < 22) die(`node ≥ 22 required, found ${process.versions.node}`);
153
153
  log(`✓ node ${process.versions.node}`);
154
154
  const piVersion = (() => { try { return parseVersion(run('pi', ['--version'])); } catch { return null; } })();
155
- if (!piVersion || !geVersion(piVersion, [0, 84, 0])) {
156
- die(`pi ≥ 0.84 required${piVersion ? `, found ${piVersion.join('.')}` : ' (pi not found or not runnable)'}. Install: npm i -g @earendil-works/pi-coding-agent`);
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`);
157
157
  }
158
158
  log(`✓ pi ${piVersion.join('.')}`);
159
159
  const herdrVersion = (() => { try { return parseVersion(run('herdr', ['--version'])); } catch { return null; } })();
160
- if (!herdrVersion || !geVersion(herdrVersion, [0, 8, 0])) {
161
- die(`herdr ≥ 0.8.0 required${herdrVersion ? `, found ${herdrVersion.join('.')}` : ' (herdr not found)'}. Install: https://herdr.dev/`);
160
+ if (!herdrVersion || !geVersion(herdrVersion, [0, 9, 0])) {
161
+ die(`herdr ≥ 0.9.0 required${herdrVersion ? `, found ${herdrVersion.join('.')}` : ' (herdr not found)'}. Install: https://herdr.dev/`);
162
162
  }
163
163
  log(`✓ herdr ${herdrVersion.join('.')}`);
164
164
  return { piVersion, herdrVersion };
165
165
  }
166
166
 
167
- /** pi 半区源:用户模式 git 规格,dev 模式本地目录。 */
167
+ /** pi extension source: user mode uses git/npm spec, dev mode uses local directory. */
168
168
  const piSource = () => (dev ? EXT_DIR : PI_SPEC);
169
169
 
170
- /* ── boot-config 探测与写入 ──────────────────────────────────────── */
170
+ /* ── boot-config detection and writing ────────────────────────────── */
171
171
  function probePiRuntime() {
172
172
  const piBin = which('pi');
173
- // bin 是 node shim:realpath 落到 .../pi-coding-agent/dist/cli.js(npm 布局,win/posix 同构)
173
+ // bin is a node shim: realpath resolves to .../pi-coding-agent/dist/cli.js (npm layout, isomorphic win/posix)
174
174
  if (piBin) {
175
175
  try {
176
176
  const real = realpathSync(piBin);
177
177
  if (real.endsWith('cli.js') && existsSync(real)) {
178
178
  return { piCli: real, piNode: process.execPath };
179
179
  }
180
- } catch { /* 落到兜底 */ }
180
+ } catch { /* fall back to fallback */ }
181
181
  }
182
- // npm root -g 兜底:pi 不在 PATH(nvm shim 等)时按全局布局拼
182
+ // npm root -g fallback: when pi is not in PATH (e.g. nvm shim), construct from global npm layout
183
183
  try {
184
184
  const gRoot = run('npm', ['root', '-g']);
185
185
  const cand = join(gRoot, '@earendil-works', 'pi-coding-agent', 'dist', 'cli.js');
186
186
  if (existsSync(cand)) return { piCli: cand, piNode: process.execPath };
187
- } catch { /* npm 不可用 */ }
187
+ } catch { /* npm unavailable */ }
188
188
  return null;
189
189
  }
190
190
 
@@ -192,9 +192,9 @@ function piAgentDir() {
192
192
  return process.env.PI_CODING_AGENT_DIR || join(homedir(), '.pi', 'agent');
193
193
  }
194
194
 
195
- /* ── 旧版 git 模式注册迁移 ──────────────────────────────────────── */
196
- // 早期 `pi install git:github.com/July24/pier` 的遗留注册与 npm:pi-pier 并存时,
197
- // 两份扩展会注册同名工具(todo_write / ask_user_question…),pi 启动即报 conflict。
195
+ /* ── Legacy git-mode registration migration ────────────────────────── */
196
+ // When legacy `pi install git:github.com/July24/pier` coexists with npm:pi-pier,
197
+ // both extensions register identical tools (todo_write / ask_user_question...), causing conflict at pi launch.
198
198
  const LEGACY_GIT_SPEC = 'git:github.com/July24/pier';
199
199
 
200
200
  function registeredPiPackages() {
@@ -203,7 +203,7 @@ function registeredPiPackages() {
203
203
  } catch { return []; }
204
204
  }
205
205
 
206
- /** install/update 前清理旧 git 模式注册;--pi-spec 显式指定该 git 源时视为目标源,不动。 */
206
+ /** Clean up legacy git-mode registration before install/update; if --pi-spec explicitly specifies that git source, treat it as target and keep it. */
207
207
  function removeLegacyGitRegistration() {
208
208
  if (piSource() === LEGACY_GIT_SPEC) return;
209
209
  if (!registeredPiPackages().includes(LEGACY_GIT_SPEC)) return;
@@ -284,7 +284,7 @@ function writeBootConfig(hmrDev, force) {
284
284
  const target = dev ? DEV_BOOT_CONFIG : join(herdrConfigDir() ?? die('cannot resolve herdr plugin config-dir (is pier.workbench installed?)'), 'boot-config.json');
285
285
  if (existsSync(target) && !force) {
286
286
  let existing = null;
287
- try { existing = JSON.parse(readFileSync(target, 'utf8')); } catch { /* 视为损坏 */ }
287
+ try { existing = JSON.parse(readFileSync(target, 'utf8')); } catch { /* treated as corrupted */ }
288
288
  if (existing) {
289
289
  log(`• ${target} exists, diff (existing → new):`);
290
290
  for (const k of Object.keys(config)) {
@@ -339,7 +339,7 @@ function collectStatus() {
339
339
  };
340
340
  }
341
341
 
342
- /* ── 命令 ────────────────────────────────────────────────────────── */
342
+ /* ── Commands ─────────────────────────────────────────────────────── */
343
343
  function usage() {
344
344
  log(`pier-setup — install / update / inspect pier (pi-pier + pier.workbench)
345
345
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pier-setup",
3
- "version": "0.2.7",
3
+ "version": "0.3.0",
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,7 +31,8 @@
31
31
  "packages/*"
32
32
  ],
33
33
  "scripts": {
34
- "test": "node --test \"packages/*/test/*.test.ts\"",
34
+ "typecheck": "tsc --noEmit -p packages/pier-ext/tsconfig.json",
35
+ "test": "npm run typecheck && node --test \"packages/*/test/*.test.ts\"",
35
36
  "test:mutation": "stryker run",
36
37
  "prepare": "node ./install.mjs --prepare"
37
38
  },
@@ -41,7 +42,8 @@
41
42
  "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
42
43
  "@deepseek-ai/cordis-plugin-timer": "^1.1.3",
43
44
  "@stryker-mutator/core": "^10.0.0",
44
- "@stryker-mutator/typescript-checker": "^10.0.0"
45
+ "@stryker-mutator/typescript-checker": "^10.0.0",
46
+ "typescript": "~5.9.3"
45
47
  },
46
48
  "pi": {
47
49
  "extensions": [