gentle-pi 3.5.0 → 3.5.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.
package/README.md CHANGED
@@ -12,8 +12,8 @@
12
12
  <a href="https://www.npmjs.com/package/gentle-pi"><img src="https://img.shields.io/npm/v/gentle-pi?style=for-the-badge&labelColor=1A1218&color=F095C8" alt="npm"></a>
13
13
  <a href="https://pi.dev/packages/gentle-pi"><img src="https://img.shields.io/badge/Pi-native-F095C8?style=for-the-badge&labelColor=1A1218" alt="Pi-native package"></a>
14
14
  <a href="LICENSE"><img src="https://img.shields.io/npm/l/gentle-pi?style=for-the-badge&labelColor=1A1218&color=F095C8" alt="MIT license"></a>
15
- <a href="https://github.com/Gentleman-Programming/gentle-pi/stargazers"><img src="https://img.shields.io/github/stars/Gentleman-Programming/gentle-pi?style=for-the-badge&labelColor=1A1218&color=F095C8" alt="GitHub stars"></a>
16
- <a href="https://github.com/Gentleman-Programming/gentle-pi"><img src="https://img.shields.io/github/last-commit/Gentleman-Programming/gentle-pi?style=for-the-badge&labelColor=1A1218&color=D7A0B8" alt="Last commit"></a>
15
+ <a href="https://github.com/Gentleman-Programming/gentle-shell/stargazers"><img src="https://img.shields.io/github/stars/Gentleman-Programming/gentle-shell?style=for-the-badge&labelColor=1A1218&color=F095C8" alt="GitHub stars"></a>
16
+ <a href="https://github.com/Gentleman-Programming/gentle-shell"><img src="https://img.shields.io/github/last-commit/Gentleman-Programming/gentle-shell?style=for-the-badge&labelColor=1A1218&color=D7A0B8" alt="Last commit"></a>
17
17
  </p>
18
18
 
19
19
  <p align="center">
@@ -37,7 +37,7 @@
37
37
  <p align="center"><strong>BUILT FOR PI</strong> &nbsp;·&nbsp; Coding-agent workspace &nbsp;·&nbsp; Focused agents &nbsp;·&nbsp; ODD</p>
38
38
 
39
39
  <p align="center">
40
- <a href="https://github.com/Gentleman-Programming/gentle-pi/stargazers"><strong>★ Star gentle-shell on GitHub</strong></a>
40
+ <a href="https://github.com/Gentleman-Programming/gentle-shell/stargazers"><strong>★ Star gentle-shell on GitHub</strong></a>
41
41
  </p>
42
42
 
43
43
  <div align="center">
@@ -185,13 +185,13 @@ Extension commands are only useful if you can find them. `alt+k` opens a curated
185
185
 
186
186
  ---
187
187
 
188
- ### What's new in v2.6.0
188
+ ### What's new in v3.5
189
189
 
190
- The [v2.6.0 release](https://github.com/Gentleman-Programming/gentle-pi/releases/tag/v2.6.0) brings a more persistent, inspectable Pi workspace:
190
+ The [v3.5.1 release](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v3.5.1) makes Gentle Shell runnable on its own:
191
191
 
192
- - **Shell:** `/gentle:changes` groups captured write/edit changes from the current agent session and its subagents, without startup repository scans; fullscreen navigation, sidebars, and mouse support stay available. See the [capture limits and shell-command coverage](docs/gentle-shell.md#what-appears-in-changes).
193
- - **Agents and profiles:** the Agents view shows orchestrator/session hierarchy, retained completion, abort, and lost-exit history, parent-child handoff, and model, effort, and usage observability. Named `/gentle:profiles` atomically route the orchestrator independently from packaged and review roles; applying one replaces the routing of every agent, a repository can be pinned to a profile with `p` so its subagent launches stop following the globally active profile, and the panel shows the routing the runtime actually uses even when `models.json` is sparse.
194
- - **Control and recovery:** native SDD requires parent-confirmed preflight; native review supports intended-untracked selection, consent, and provider continuations. Subsystems install with explicit recovery guidance when npm lifecycle scripts were skipped; Pi Git installs are recognized globally; custom ask responses are opt-in. Windows keeps child consoles hidden and fixes ownership mode; Gentle Todo keeps the next pending task visible when collapsed.
192
+ - **Standalone launcher:** `npm i -g gentle-pi` installs `gentle-shell`, which opens Pi with the Gentle Shell package loaded from its own home (`~/.gentle-shell/agent`) or, with `--link`, from your existing `~/.pi/agent`; `gentle-shell install npm:<pkg>` and the other pi subcommands run against the selected home. A bundled or `PATH` pi is used, never a modified one.
193
+ - **Link mode take-over:** when `~/.pi/agent` already declares gentle-pi as a path package, the launcher takes over extension loading (`--no-extensions` plus explicit `-e` for every other declared package and loose extension) so tools never register twice.
194
+ - **Interactive RPC hosts:** with `GENTLE_SHELL_INTERACTIVE_HOST=1` and `--mode rpc`, ask-user tools use pi's RPC dialogs and gentle-agents publishes live subagent activity for the desktop app. See the [reference](docs/readme-reference.md#interactive-rpc-hosts).
195
195
 
196
196
  ---
197
197
 
@@ -203,13 +203,38 @@ The [v2.6.0 release](https://github.com/Gentleman-Programming/gentle-pi/releases
203
203
 
204
204
  ## Get started
205
205
 
206
- Install the stable release, restart Pi, then synchronize the installed assets.
207
-
208
206
  > **Naming transition:** The product is called `gentle-shell`; the current npm package and repository remain `gentle-pi` until migration.
209
207
 
208
+ ### Path A: standalone `gentle-shell` (recommended, no pi changes)
209
+
210
+ `gentle-shell` opens Pi with the Gentle Shell package loaded, without installing it into your pi agent or editing its `settings.json`.
211
+
212
+ ```bash
213
+ npm i -g gentle-pi
214
+
215
+ # Own home, never touches your pi install
216
+ gentle-shell
217
+
218
+ # Reuse your pi sign-ins, models and chats instead
219
+ gentle-shell --link
220
+ ```
221
+
222
+ `gentle-shell` alone starts in its own home, `~/.gentle-shell/agent`. `gentle-shell --link` reuses `~/.pi/agent` as-is.
223
+
210
224
  ```bash
211
- # Published stable release: v2.6.0
212
- pi install npm:gentle-pi@2.6.0
225
+ # Make --link the default
226
+ gentle-shell home link
227
+ ```
228
+
229
+ Every other argument is forwarded to pi unchanged, for example `gentle-shell --mode rpc` or `gentle-shell -p "..."`. Full flags, env vars, and modes: **[launcher reference](docs/readme-reference.md#gentle-shell-launcher)**.
230
+
231
+ ### Path B: inside an existing pi
232
+
233
+ Install the stable release into an existing pi agent, restart Pi, then synchronize the installed assets.
234
+
235
+ ```bash
236
+ # Published stable release: v3.5.1
237
+ pi install npm:gentle-pi@3.5.1
213
238
 
214
239
  # Restart Pi, then run:
215
240
  gentle-ai sync
@@ -218,7 +243,7 @@ gentle-ai sync
218
243
  pi
219
244
  ```
220
245
 
221
- See the [v2.6.0 release notes](https://github.com/Gentleman-Programming/gentle-pi/releases/tag/v2.6.0) for version-specific changes.
246
+ See the [v3.5.1 release notes](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v3.5.1) for version-specific changes.
222
247
 
223
248
  ```text
224
249
  /gentle:status
@@ -233,29 +258,6 @@ See the [v2.6.0 release notes](https://github.com/Gentleman-Programming/gentle-p
233
258
 
234
259
  For prerequisites, source-checkout instructions, full install behavior, and release policy, use the **[installation reference](docs/readme-reference.md#install)**. For everyday work, describe the outcome and follow [ODD](#odd--the-everyday-workflow).
235
260
 
236
- ### Without touching your pi
237
-
238
- `gentle-shell` opens Pi with the Gentle Shell package loaded, without installing it into your pi agent or editing its `settings.json`.
239
-
240
- ```bash
241
- npm i -g gentle-pi
242
-
243
- # Own home, never touches your pi install
244
- gentle-shell
245
-
246
- # Reuse your pi sign-ins, models and chats instead
247
- gentle-shell --link
248
- ```
249
-
250
- `gentle-shell` alone starts in its own home, `~/.gentle-shell/agent`. `gentle-shell --link` reuses `~/.pi/agent` as-is.
251
-
252
- ```bash
253
- # Make --link the default
254
- gentle-shell home link
255
- ```
256
-
257
- Every other argument is forwarded to pi unchanged, for example `gentle-shell --mode rpc` or `gentle-shell -p "..."`. Full flags, env vars, and modes: **[launcher reference](docs/readme-reference.md#gentle-shell-launcher)**.
258
-
259
261
  <p align="right"><a href="#top">Back to top ↑</a></p>
260
262
 
261
263
  <p align="center">
@@ -287,17 +289,17 @@ Start with the product-facing destination, then move into the operational refere
287
289
  This project is built in public. Bring a real workflow, a sharp question, a bug report, or a small improvement that makes the next person’s work clearer.
288
290
 
289
291
  <p align="center">
290
- <a href="https://github.com/Gentleman-Programming/gentle-pi/issues"><img src="https://img.shields.io/badge/Issues-join%20the%20conversation-F095C8?style=for-the-badge&labelColor=1A1218" alt="GitHub issues"></a>
291
- <a href="https://github.com/Gentleman-Programming/gentle-pi/graphs/contributors"><img src="https://img.shields.io/badge/Contributors-thank%20you-D7A0B8?style=for-the-badge&labelColor=1A1218" alt="Contributors"></a>
292
+ <a href="https://github.com/Gentleman-Programming/gentle-shell/issues"><img src="https://img.shields.io/badge/Issues-join%20the%20conversation-F095C8?style=for-the-badge&labelColor=1A1218" alt="GitHub issues"></a>
293
+ <a href="https://github.com/Gentleman-Programming/gentle-shell/graphs/contributors"><img src="https://img.shields.io/badge/Contributors-thank%20you-D7A0B8?style=for-the-badge&labelColor=1A1218" alt="Contributors"></a>
292
294
  <a href="https://discord.com/invite/gentleman-programming-769863833996754944"><img src="https://img.shields.io/badge/Discord-Gentleman%20Programming-F095C8?style=for-the-badge&labelColor=1A1218" alt="Gentleman Programming Discord"></a>
293
295
  </p>
294
296
 
295
297
  <p align="center">
296
- <a href="https://github.com/Gentleman-Programming/gentle-pi/graphs/contributors"><img src="https://contrib.rocks/image?repo=Gentleman-Programming/gentle-pi" alt="gentle-shell contributors"></a>
298
+ <a href="https://github.com/Gentleman-Programming/gentle-shell/graphs/contributors"><img src="https://contrib.rocks/image?repo=Gentleman-Programming/gentle-shell" alt="gentle-shell contributors"></a>
297
299
  </p>
298
300
 
299
- - Open an [issue](https://github.com/Gentleman-Programming/gentle-pi/issues) with the context needed to reproduce or understand the idea.
300
- - See the people shaping the project in the [contributors graph](https://github.com/Gentleman-Programming/gentle-pi/graphs/contributors).
301
+ - Open an [issue](https://github.com/Gentleman-Programming/gentle-shell/issues) with the context needed to reproduce or understand the idea.
302
+ - See the people shaping the project in the [contributors graph](https://github.com/Gentleman-Programming/gentle-shell/graphs/contributors).
301
303
  - Follow [Gentleman Programming](https://github.com/Gentleman-Programming) for the wider ecosystem.
302
304
 
303
305
  <p align="right"><a href="#top">Back to top ↑</a></p>
@@ -4,7 +4,7 @@
4
4
  // resolution, pi resolution order, the version gate, and the pi invocation —
5
5
  // lives in that pure, unit-tested module; this file only wires it to the real
6
6
  // process, filesystem, and child process.
7
- import { accessSync, constants as fsConstants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { accessSync, constants as fsConstants, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "node:fs";
8
8
  import { createRequire } from "node:module";
9
9
  import { constants as osConstants, homedir } from "node:os";
10
10
  import { delimiter, dirname, join, resolve as resolvePath } from "node:path";
@@ -13,16 +13,19 @@ import { fileURLToPath } from "node:url";
13
13
  import {
14
14
  buildPiInvocation,
15
15
  checkPiVersion,
16
+ decideTakeOver,
16
17
  describeVersion,
18
+ discoverLooseExtensionEntries,
19
+ findGentlePiDeclaration,
17
20
  helpText,
18
21
  launcherConfigPath,
19
22
  missingPiMessage,
23
+ otherPackageInjections,
20
24
  parseLauncherArgs,
21
25
  parseLauncherConfig,
22
26
  planSpawn,
23
27
  resolveHome,
24
28
  resolvePiRuntime,
25
- settingsDeclareGentlePi,
26
29
  } from "../runtime/gentle-shell-launcher.mjs";
27
30
  import { installIsolatedTuiModeSetting } from "../scripts/install-tui-mode-setting.mjs";
28
31
 
@@ -83,7 +86,119 @@ function ownPackageVersion() {
83
86
  }
84
87
 
85
88
  function emptyArgs() {
86
- return { link: false, isolated: false, home: undefined, help: false, version: false, command: undefined, commandArgs: [], passthrough: [], error: undefined };
89
+ return {
90
+ link: false,
91
+ isolated: false,
92
+ home: undefined,
93
+ packageRoot: undefined,
94
+ help: false,
95
+ version: false,
96
+ command: undefined,
97
+ commandArgs: [],
98
+ passthrough: [],
99
+ piSubcommand: undefined,
100
+ error: undefined,
101
+ };
102
+ }
103
+
104
+ // package.json "name" reader injected into findGentlePiDeclaration: a
105
+ // missing or unreadable package.json, or a non-string "name", is never an
106
+ // error here — it just means that path package is not gentle-pi.
107
+ function readPackageName(dir) {
108
+ try {
109
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
110
+ return typeof pkg.name === "string" ? pkg.name : undefined;
111
+ } catch {
112
+ return undefined;
113
+ }
114
+ }
115
+
116
+ // Best-effort realpath: a directory that does not exist (yet, or ever)
117
+ // cannot be realpath'd, so the take-over decision falls back to comparing
118
+ // the raw path instead of failing.
119
+ function safeRealpath(path) {
120
+ try {
121
+ return realpathSync(path);
122
+ } catch {
123
+ return path;
124
+ }
125
+ }
126
+
127
+ // Used to filter the loose extension dirs a take-over re-injects: a missing
128
+ // path, or one that is not a directory (for example a stray file named
129
+ // "extensions"), is silently excluded rather than passed to pi as -e.
130
+ function isDirectory(path) {
131
+ try {
132
+ return statSync(path).isDirectory();
133
+ } catch {
134
+ return false;
135
+ }
136
+ }
137
+
138
+ // Real-fs adapter for discoverLooseExtensionEntries (lib/gentle-shell-launcher.ts):
139
+ // statSync-based isFile/isDirectory (not readdirSync's Dirent, which uses
140
+ // lstat and so would treat a symlinked file or directory as neither) so a
141
+ // symlinked loose extension resolves the same way pi's own fs.existsSync-based
142
+ // checks would.
143
+ const looseExtensionFs = {
144
+ readdir(dir) {
145
+ let names;
146
+ try {
147
+ names = readdirSync(dir);
148
+ } catch (error) {
149
+ // resolveLooseExtensionEntries only calls this once isDirectory(dir)
150
+ // has already confirmed the directory exists, so a failure here (for
151
+ // example EACCES) is a real read failure, not a missing directory.
152
+ // Warn instead of silently dropping every loose extension it would
153
+ // have contributed (R4-loose-extension-enumeration-fails-silently).
154
+ process.stderr.write(`gentle-shell: could not read loose extension directory ${dir}: ${error.message} (skipping)\n`);
155
+ return [];
156
+ }
157
+ return names.map((name) => {
158
+ const entryPath = join(dir, name);
159
+ try {
160
+ const entryStat = statSync(entryPath);
161
+ return { name, isFile: entryStat.isFile(), isDirectory: entryStat.isDirectory() };
162
+ } catch {
163
+ return { name, isFile: false, isDirectory: false };
164
+ }
165
+ });
166
+ },
167
+ exists: existsSync,
168
+ };
169
+
170
+ // A loose extensions directory that is itself a self-contained extension —
171
+ // a package.json declaring a non-empty "pi.extensions" manifest — is passed
172
+ // through as a single -e <dir> instead of being broken into per-file
173
+ // entries: pi's own module loader (jiti) resolves that case directly,
174
+ // exactly as it would for any other explicitly configured package path. A
175
+ // root-level index.ts/index.js is deliberately NOT treated as that same
176
+ // marker: pi's own discovery loads it as just another loose file, so
177
+ // collapsing the whole directory on its presence silently dropped sibling
178
+ // loose files like extra.ts (R4-loose-index-collapses-sibling-extensions).
179
+ function readPiManifestExtensions(dir) {
180
+ try {
181
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
182
+ return Array.isArray(pkg?.pi?.extensions) ? pkg.pi.extensions : undefined;
183
+ } catch {
184
+ return undefined;
185
+ }
186
+ }
187
+
188
+ function looseDirHasOwnEntryPoint(dir) {
189
+ const manifestExtensions = readPiManifestExtensions(dir);
190
+ return manifestExtensions !== undefined && manifestExtensions.length > 0;
191
+ }
192
+
193
+ // Resolves one candidate loose-extensions directory (<agentDir>/extensions or
194
+ // <cwd>/.pi/extensions) into the -e entries a take-over must re-inject: the
195
+ // directory itself when it is a self-contained extension, otherwise every
196
+ // loose file discoverLooseExtensionEntries finds inside it. A missing or
197
+ // non-directory candidate resolves to no entries.
198
+ function resolveLooseExtensionEntries(dir) {
199
+ if (!isDirectory(dir)) return [];
200
+ if (looseDirHasOwnEntryPoint(dir)) return [dir];
201
+ return discoverLooseExtensionEntries(dir, looseExtensionFs);
87
202
  }
88
203
 
89
204
  function loadConfig() {
@@ -165,17 +280,77 @@ async function main() {
165
280
  process.stderr.write(`gentle-shell: using a separate home at ${home.dir}. Run 'gentle-shell --link' to reuse your pi sign-ins and chats.\n`);
166
281
  }
167
282
 
168
- let linkDeclaresGentlePi = false;
169
- if (home.mode === "link") {
283
+ const packageRootExplicit = args.packageRoot !== undefined;
284
+ const effectivePackageRoot = packageRootExplicit ? resolvePath(args.packageRoot) : packageRoot;
285
+ // R4-forced-package-root-unvalidated / R3-005: an unvalidated --package-root
286
+ // forces a take-over (dropping normal extension discovery via
287
+ // --no-extensions) and then hands pi -e/--theme/--skill/--prompt-template
288
+ // flags pointing at directories that do not exist, turning an operator typo
289
+ // into an obscure pi loader failure instead of a clear launcher error.
290
+ if (packageRootExplicit && !isDirectory(effectivePackageRoot)) {
291
+ fail(`--package-root ${args.packageRoot} does not exist or is not a directory.`, 2);
292
+ }
293
+
294
+ let declaration;
295
+ let takeOver = false;
296
+ let otherPackagePaths = [];
297
+ let looseExtensionEntries = [];
298
+
299
+ // Only --link can read another gentle-pi declaration out of a real
300
+ // settings.json; isolated and --home homes never declare one, so they
301
+ // always get the plain injection (declaration stays undefined) unless
302
+ // --package-root itself forces a take-over below. A pi subcommand skips
303
+ // this whole block: buildPiInvocation ignores takeOver/declaration once
304
+ // piSubcommand is set, and running the take-over/loose-dir discovery
305
+ // anyway would still print a misleading "taking over gentle-pi..."
306
+ // message (and otherPackageInjections warnings) for a plain
307
+ // `gentle-shell install npm:x` that never actually takes anything over.
308
+ if (home.mode === "link" && args.piSubcommand === undefined) {
170
309
  const settingsText = readJsonIfExists(join(home.dir, "settings.json"));
171
- linkDeclaresGentlePi = settingsDeclareGentlePi(settingsText);
310
+ declaration = findGentlePiDeclaration(settingsText, { agentDir: home.dir, readPackageName });
311
+ const realEffectivePackageRoot = safeRealpath(effectivePackageRoot);
312
+ const realDeclaredDir = declaration?.kind === "path" ? safeRealpath(declaration.dir) : undefined;
313
+ takeOver = decideTakeOver({
314
+ declaration,
315
+ realPackageRoot: realEffectivePackageRoot,
316
+ realDeclaredDir,
317
+ packageRootExplicit,
318
+ });
319
+ if (takeOver) {
320
+ const skip = declaration ?? { kind: "path", dir: realEffectivePackageRoot };
321
+ const injections = otherPackageInjections({ settingsText, agentDir: home.dir, skip, isDirectory, realpath: safeRealpath });
322
+ otherPackagePaths = injections.paths;
323
+ for (const warning of injections.warnings) process.stderr.write(`${warning}\n`);
324
+ // --no-extensions drops pi's normal settings-driven extension
325
+ // discovery, which also covers loose (non-package) extensions
326
+ // under <agentDir>/extensions and the project-local
327
+ // <cwd>/.pi/extensions. Re-injecting either directory wholesale
328
+ // as `-e <dir>` does not work for a directory of loose files: pi's
329
+ // -e flag hands the path straight to its module loader with no
330
+ // directory-discovery pass, so a bare directory of loose files
331
+ // fails with "Cannot find module ...". Resolve each candidate
332
+ // into its actual loose file entries (or pass it through
333
+ // unchanged when it is itself a self-contained extension) so a
334
+ // take-over does not silently stop loading them.
335
+ looseExtensionEntries = [join(home.dir, "extensions"), join(process.cwd(), ".pi", "extensions")].flatMap(resolveLooseExtensionEntries);
336
+ const declaredFrom = declaration === undefined ? "the requested package root" : declaration.kind === "npm" ? "npm:gentle-pi" : declaration.dir;
337
+ process.stderr.write(
338
+ `gentle-shell: taking over gentle-pi from ${declaredFrom} for this run (settings unchanged; its skills, prompts, and themes still load alongside this launcher's).\n`,
339
+ );
340
+ }
172
341
  }
342
+ // Isolated and --home homes have no declaration to take over: declaration
343
+ // stays undefined and buildPiInvocation injects effectivePackageRoot the
344
+ // same way it always has, --package-root included.
173
345
 
174
346
  const invocation = buildPiInvocation({
175
347
  runtime,
176
348
  home,
177
- packageRoot,
178
- settingsDeclareGentlePi: home.mode === "link" ? linkDeclaresGentlePi : false,
349
+ packageRoot: effectivePackageRoot,
350
+ declaration,
351
+ takeOver,
352
+ otherPackagePaths,
353
+ looseExtensionEntries,
179
354
  passthrough: args.passthrough,
180
355
  piSubcommand: args.piSubcommand,
181
356
  baseEnv: process.env,
@@ -135,15 +135,33 @@ Callers own keyboard policy, theme state, and business actions.
135
135
 
136
136
  ## Install
137
137
 
138
+ Two paths reach the same package. Path A stays standalone; Path B installs into an existing pi.
139
+
140
+ ### Path A: standalone `gentle-shell` (recommended, no pi changes)
141
+
138
142
  ```bash
139
- pi install npm:gentle-pi@2.6.0
143
+ npm i -g gentle-pi
144
+
145
+ # Own home, never touches your pi install
146
+ gentle-shell
147
+
148
+ # Reuse your pi sign-ins, models and chats instead
149
+ gentle-shell --link
140
150
  ```
141
151
 
142
- The stable release is [`v2.6.0`](https://github.com/Gentleman-Programming/gentle-pi/releases/tag/v2.6.0). Restart Pi after installation, then run `gentle-ai sync`. That published release pairs with Gentle AI `v2.8.0` and provider contract `1.2.0`; capabilities `v2.5` are retained. The command above installs that exact published version.
152
+ `gentle-shell` alone starts in its own home, `~/.gentle-shell/agent`. `gentle-shell --link` reuses `~/.pi/agent` as-is. Run `gentle-shell home link` to make `--link` the default. Full flags, env vars, and modes: [gentle-shell launcher](#gentle-shell-launcher).
153
+
154
+ ### Path B: inside an existing pi
155
+
156
+ ```bash
157
+ pi install npm:gentle-pi@3.5.1
158
+ ```
159
+
160
+ The stable release is [`v3.5.1`](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v3.5.1). Restart Pi after installation, then run `gentle-ai sync`. That published release pairs with Gentle AI `v2.8.0` and provider contract `1.2.0`; capabilities `v2.5` are retained. The command above installs that exact published version.
143
161
 
144
162
  ### Source checkout
145
163
 
146
- This checkout prepares `gentle-pi` `3.4.0`; it is source state, not a published release. Its package-local native runtime pin is Gentle AI `v3.5.0`, distinct from the published `v2.6.0` pairing.
164
+ This checkout prepares `gentle-pi` `3.5.1`; it is source state, not a published release. Its package-local native runtime pin is Gentle AI `v3.5.0`, distinct from the published `v3.5.1` pairing.
147
165
 
148
166
  The native SDD status consumer accepts both the pinned producer's legacy
149
167
  `apply`/`verify`/`remediate`/`archive` instruction record and the classical
@@ -161,13 +179,13 @@ Classical direct-archive behavior is compatibility-tested with an identified
161
179
  upstream development build, not presented as a published fix or version bump.
162
180
  The complete classical flow awaits a compatible published native version; this
163
181
  change does not bump the pin. Ordinary attempt governance and research/planning simplification remain separate
164
- work under [SDD parity #1051](https://github.com/Gentleman-Programming/gentle-pi/issues/1051).
182
+ work under [SDD parity #1051](https://github.com/Gentleman-Programming/gentle-shell/issues/1051).
165
183
 
166
184
  ### Pi compatibility
167
185
 
168
186
  The current package requires Pi 0.85.1 or newer (development tests pin 0.85.1). Use the latest Pi release; gentle-pi does not update your installed Pi automatically. Children, including any `GENTLE_PI_AGENTS_PI` override, must emit `agent_settled`: `agent_end` records a run's output but is not completion because retries or queued continuations may follow.
169
187
 
170
- The [`v2.6.0` release](https://github.com/Gentleman-Programming/gentle-pi/releases/tag/v2.6.0) adds persistent registered worktrees and grouped `/gentle:changes` views; fuller workspace interaction details are in the [Gentle Shell reference](gentle-shell.md). It also adds named atomic `/gentle:profiles`, parent-confirmed native SDD preflight transport, native review intended-untracked selection and provider continuations, and opt-in custom ask responses. Pi recognizes its global Git-managed package path; subsystems install with explicit recovery guidance when npm lifecycle work was skipped. Windows keeps child consoles hidden and fixes ownership mode; Gentle Todo keeps the next pending task visible when collapsed.
188
+ The [`v2.6.0` release](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v2.6.0) added persistent registered worktrees and grouped `/gentle:changes` views; fuller workspace interaction details are in the [Gentle Shell reference](gentle-shell.md). It also adds named atomic `/gentle:profiles`, parent-confirmed native SDD preflight transport, native review intended-untracked selection and provider continuations, and opt-in custom ask responses. Pi recognizes its global Git-managed package path; subsystems install with explicit recovery guidance when npm lifecycle work was skipped. Windows keeps child consoles hidden and fixes ownership mode; Gentle Todo keeps the next pending task visible when collapsed.
171
189
 
172
190
  ### Install-time fullscreen
173
191
 
@@ -179,18 +197,30 @@ Malformed/nonobject JSON, symlink/nonregular settings, unsafe paths, or a busy s
179
197
 
180
198
  ### RDD history and opt-in
181
199
 
182
- Native RDD was introduced in `gentle-pi` `v0.15.0` on 2026-07-10 with bounded review transactions. The current stable release, [`v2.6.0`](https://github.com/Gentleman-Programming/gentle-pi/releases/tag/v2.6.0), includes native RDD:
200
+ Native RDD was introduced in `gentle-pi` `v0.15.0` on 2026-07-10 with bounded review transactions. The current stable release, [`v3.5.1`](https://github.com/Gentleman-Programming/gentle-shell/releases/tag/v3.5.1), includes native RDD:
183
201
 
184
202
  ```bash
185
203
  # Stable release
186
- pi install npm:gentle-pi@2.6.0
204
+ pi install npm:gentle-pi@3.5.1
187
205
  ```
188
206
 
189
207
  RDD remains opt-in. Enable it only through an explicit user decision with `/gentle:review-mode enable`; `status` lets you inspect the mode without changing it.
190
208
 
191
209
  The source checkout's RDD integration installs Gentle AI only into its private `.gentle-ai/` directory. Darwin and Linux use pinned release assets with asset and executable SHA-256 verification (signed archives for source pin `v3.5.0`; raw prerelease binaries only under a prerelease pin). Windows x64 and arm64 build the exact `v3.5.0` source tag with a local Go 1.25.10+ toolchain, a sealed Go environment, `GOTOOLCHAIN=local`, and `GOSUMDB=sum.golang.org`; it does not download Go automatically. Windows provenance is Go-toolchain plus SumDB evidence and postinstall tamper detection, **not** Authenticode or protection against a malicious joint binary-and-manifest replacement. Package-private locks coordinate cooperative concurrent or crashed installers; their tombstones fail closed. A malicious same-user process with write access to package-private `node_modules` is outside that protocol because it can already replace package code, binary, or manifest, and portable Node has no pathname-delete CAS. It never uses `PATH` or a global `gentle-ai` installation. For development or offline installs only, set `GENTLE_PI_SKIP_GENTLE_AI_INSTALL=1`; native review operations then fail closed with an actionable `package-local-binary-missing` error. To recover explicitly, if `GENTLE_PI_SKIP_GENTLE_AI_INSTALL` is set, remove or unset it before changing to the installed `gentle-pi` package directory. Then run `node scripts/install-gentle-ai.mjs`. This invokes the package-owned installer without relying on a global binary or npm configuration change. A missing binary can result from skipped lifecycle scripts, but does not prove that lifecycle scripts were disabled.
192
210
 
193
- Recommended companion packages:
211
+ Recommended companion packages, into the standalone `gentle-shell` home:
212
+
213
+ ```bash
214
+ gentle-shell install npm:pi-intercom
215
+ gentle-shell install npm:gentle-engram
216
+ gentle-shell install npm:pi-web-access
217
+ gentle-shell install npm:pi-lens
218
+ gentle-shell install npm:@juicesharp/rpiv-ask-user-question
219
+ ```
220
+
221
+ `--link` before the subcommand (for example `gentle-shell --link install npm:pi-intercom`) targets `~/.pi/agent` instead of the isolated home.
222
+
223
+ Or, when `gentle-pi` is installed inside an existing pi:
194
224
 
195
225
  ```bash
196
226
  pi install npm:pi-intercom
@@ -241,6 +271,7 @@ gentle-shell home [link|isolated|<path>]
241
271
  | `--link` | Home is `PI_CODING_AGENT_DIR` or `~/.pi/agent`. Reuses your existing pi sign-ins, models, and chats; never writes to its `settings.json`. |
242
272
  | `--isolated` | Home is `GENTLE_SHELL_HOME` or `~/.gentle-shell/agent`. No credential seeding. Default when nothing else is configured. |
243
273
  | `--home <path>` | Home is the given directory. |
274
+ | `--package-root <dir>` | Force this directory as the gentle-pi package to load, taking over from any conflicting package the target `settings.json` already declares (see "Loading the package" below). |
244
275
  | `--help`, `-h` | Print usage (flags, commands, env vars) and exit 0. |
245
276
  | `--version` | Print `gentle-shell <version>`, `pi <version>`, and `home <mode> <dir>`, then exit 0. |
246
277
  | `--` | Everything after is forwarded to pi verbatim, even text that looks like a `gentle-shell` flag. |
@@ -255,6 +286,8 @@ gentle-shell home [link|isolated|<path>]
255
286
 
256
287
  `gentle-shell install npm:<pkg>`, `gentle-shell remove ...`, `gentle-shell list`, `gentle-shell update ...`, `gentle-shell config`, and `gentle-shell auth ...` run pi's own commands against the resolved home — the `--isolated` home by default, or your own pi home with `--link`. A launcher flag before the subcommand (`--link`, `--isolated`, `--home <path>`) still selects which home the subcommand runs against. Running `gentle-shell install npm:gentle-pi` inside the isolated home is unnecessary: the launcher already loads the Gentle Shell package itself (see "Loading the package" below).
257
288
 
289
+ `gentle-shell update` and `gentle-shell list` follow that same home selection, so they inspect and update packages in whichever home the effective flag or persisted `home` config points to.
290
+
258
291
  ### pi runtime resolution
259
292
 
260
293
  1. `GENTLE_SHELL_PI` — path to a pi executable, when set to a non-empty value.
@@ -274,7 +307,25 @@ If none resolve, `gentle-shell` exits 1 naming all three options. Once a runtime
274
307
 
275
308
  ### Loading the package
276
309
 
277
- Unless the target home's `settings.json` already lists `npm:gentle-pi` in its `packages` array (checked only for `--link`), every invocation injects `-e <package root> --theme <root>/themes --skill <root>/skills --prompt-template <root>/prompts` ahead of the forwarded arguments, so the Gentle Shell extensions, themes, skills, and prompt templates load without a separate `pi install`. Isolated and `--home <path>` homes never declare the package, so they always get the injection — except when the forwarded arguments start with one of pi's own subcommands (`install`, `remove`, `uninstall`, `update`, `list`, `config`, `auth`): pi dispatches those on `argv[0]` before it parses any flags, so the injection is skipped entirely and pi sees the bare subcommand, e.g. `gentle-shell install npm:x` runs exactly `pi install npm:x`.
310
+ Unless the target home's `settings.json` already declares gentle-pi (checked only for `--link`), every invocation injects `-e <package root> --theme <root>/themes --skill <root>/skills --prompt-template <root>/prompts` ahead of the forwarded arguments, so the Gentle Shell extensions, themes, skills, and prompt templates load without a separate `pi install`. Isolated and `--home <path>` homes never declare the package, so they always get this injection — except when the forwarded arguments start with one of pi's own subcommands (`install`, `remove`, `uninstall`, `update`, `list`, `config`, `auth`): pi dispatches those on `argv[0]` before it parses any flags, so the injection — and any take-over below — is skipped entirely and pi sees the bare subcommand, e.g. `gentle-shell install npm:x` runs exactly `pi install npm:x`. A subcommand never triggers a take-over, even against a home whose settings declare a conflicting gentle-pi; see "Managing packages" above.
311
+
312
+ A declaration is recognized either as `npm:gentle-pi[@version]` in the `packages` array, or as a local path package (string or `{"source": "..."}` entry, relative or absolute) whose own `package.json` names it `"gentle-pi"` — the shape produced when gentle-pi is developed from a checkout and referenced by path in `settings.json` instead of installed via `pi install npm:gentle-pi`.
313
+
314
+ - **A pi subcommand as the first forwarded argument**: no injection and no take-over at all, regardless of any declaration — pi must see the bare subcommand as `argv[0]`.
315
+ - **npm declaration matching this launcher's own install**: no injection — pi already loads gentle-pi from the declared package.
316
+ - **No declaration at all, or a path declaration that resolves (after `realpath`) to this launcher's own package root**: the same plain injection as above.
317
+ - **A declaration that resolves to a *different* gentle-pi** (a different checkout declared by path, for example) **— take-over**: `gentle-shell` prints `taking over gentle-pi from <declared source> for this run (settings unchanged; its skills, prompts, and themes still load alongside this launcher's)` to stderr, then runs pi with `--no-extensions` followed by an explicit `-e <dir>` for every *other* package already in settings (npm entries resolve to `<agent dir>/npm/node_modules/<name>`; path entries resolve relative to the settings file), then loose extension entries for `<agent dir>/extensions` and the project-local `<cwd>/.pi/extensions` (each candidate directory only consulted when it already exists), and finally its own `-e <package root> --theme ... --skill ... --prompt-template ...`. `settings.json` itself is never modified, and every `-e` path — including the launcher's own package root — is injected at most once even if it would otherwise repeat.
318
+
319
+ A declared *other* package whose resolved directory does not actually exist (a hand-edited `settings.json`, a failed or interrupted `pi install`, or an npm store laid out anywhere other than `<agent dir>/npm/node_modules`) is skipped with a stderr warning naming the source and the resolved path, instead of being handed to pi as an unresolvable `-e` that would fail the whole launch with "Cannot find module".
320
+
321
+ `--no-extensions` disables pi's normal directory-discovery pass, and pi's `-e` flag hands a path straight to its module loader with no discovery of its own — passing a loose extensions directory as-is via `-e <dir>` fails with "Cannot find module" unless that directory is itself a self-contained extension. So each loose candidate directory is resolved before injection: a directory that is itself a self-contained extension (a `package.json` declaring a `pi.extensions` manifest) is passed through as a single `-e <dir>`; otherwise its direct `*.ts`/`*.js`/`*.mjs` files — including a root-level `index.ts`/`index.js`, which is just another loose file — and any `<subdir>/index.ts`/`index.js` are discovered individually — mirroring pi's own directory scan — and each is injected as its own `-e <file>`. Hidden entries (dotfiles) and `*.d.ts` declaration files are skipped, since neither was ever a runnable extension.
322
+
323
+ A git-sourced other package is skipped with a stderr warning, since its install directory cannot be derived without pi's own package manager; an object entry with `extensions` or `autoload` filters is still included but warned about, because the take-over cannot honor those filters for extension discovery — that package's skills, prompts, and themes still load normally through settings discovery, which `--no-extensions` does not affect.
324
+
325
+ **Known limitation**: the take-over never removes the original declaration from `settings.json`, so its skills, prompt templates, and themes are still discovered alongside this launcher's own — only its extensions are replaced by `--no-extensions` plus the injected `-e` flags above.
326
+ - **`--package-root <dir>`**: forces a take-over using `<dir>` as the package root, even when settings already declare a matching `npm:gentle-pi`, or when there is no declaration at all. Use it to test a different gentle-pi checkout against a home whose settings already point at another one. Has no effect when the forwarded arguments start with a pi subcommand, since a subcommand skips the take-over entirely. The take-over path itself is only ever reached for `--link`: with `--isolated` or `--home <path>`, `--package-root` still changes which directory is injected, but always through the same plain injection as "no declaration at all" above — no `--no-extensions`, and no other-package or loose-extension re-injection — since those homes never carry a `settings.json` declaration to take over from. `--package-root` must also name an existing directory; a missing or non-directory path fails fast with a clear error instead of launching pi with unresolvable flags.
327
+
328
+ This take-over exists because two gentle-pi copies loaded at once — the declared one plus this launcher's own injection — register the same tools and extensions twice, which pi reports as tool conflicts (for example `Tool ask_user_choice conflicts with ...`).
278
329
 
279
330
  ### First run in an isolated or custom home
280
331
 
@@ -375,7 +426,7 @@ Reconciliation is intentionally narrow: native code may quarantine only the boun
375
426
 
376
427
  Native lifecycle status remains informational. VALIDATE does not authorize delivery; commit, push, PR, and release commands follow ordinary repository policy. Recovery grants no new budget, and legacy graph bundle export/import is retired.
377
428
 
378
- This is the post-U8 boundary, not the final architecture. [Issue #191](https://github.com/Gentleman-Programming/gentle-pi/issues/191) is the immediate final unit in this same delivery: extract the remaining Pi command-projection and lifecycle-gate surface from `review-transaction.ts`, repoint runtime enforcement, then delete only dependencies proven unreachable without weakening graph-v1 Judgment Day. The branch-wide High-tier 4R runs after that extraction, before the single size-exception PR.
429
+ This is the post-U8 boundary, not the final architecture. [Issue #191](https://github.com/Gentleman-Programming/gentle-shell/issues/191) is the immediate final unit in this same delivery: extract the remaining Pi command-projection and lifecycle-gate surface from `review-transaction.ts`, repoint runtime enforcement, then delete only dependencies proven unreachable without weakening graph-v1 Judgment Day. The branch-wide High-tier 4R runs after that extraction, before the single size-exception PR.
379
430
 
380
431
  ### Review Lens Selection (architecture reference)
381
432
 
@@ -1138,10 +1189,10 @@ tag="v${version}"
1138
1189
  git fetch --no-tags origin "refs/tags/${tag}"
1139
1190
  test "$(git rev-parse 'FETCH_HEAD^{commit}')" = "$(git rev-parse "${tag}^{commit}")"
1140
1191
  gh workflow run publish.yml \
1141
- --repo Gentleman-Programming/gentle-pi \
1192
+ --repo Gentleman-Programming/gentle-shell \
1142
1193
  --ref main \
1143
1194
  -f tag="${tag}"
1144
- gh run watch <run-id> --repo Gentleman-Programming/gentle-pi --exit-status
1195
+ gh run watch <run-id> --repo Gentleman-Programming/gentle-shell --exit-status
1145
1196
  npm view gentle-pi@<version> version --registry=https://registry.npmjs.org/
1146
1197
  npm dist-tag ls gentle-pi --registry=https://registry.npmjs.org/
1147
1198
  ```