osdy-pi 1.2.0 → 1.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 (44) hide show
  1. package/README.md +160 -30
  2. package/bin/osdy-pi.mjs +29 -6
  3. package/bin/osdy.mjs +43 -0
  4. package/extensions/osdy-pi/account-profiles.test.ts +111 -97
  5. package/extensions/osdy-pi/account-profiles.ts +91 -162
  6. package/extensions/osdy-pi/animation.test.ts +84 -0
  7. package/extensions/osdy-pi/animation.ts +17 -8
  8. package/extensions/osdy-pi/codex-usage-ui.test.ts +1145 -0
  9. package/extensions/osdy-pi/codex-usage-ui.ts +728 -0
  10. package/extensions/osdy-pi/codex-usage.test.ts +276 -0
  11. package/extensions/osdy-pi/codex-usage.ts +276 -0
  12. package/extensions/osdy-pi/constants.ts +134 -34
  13. package/extensions/osdy-pi/context-usage-ui.test.ts +168 -0
  14. package/extensions/osdy-pi/context-usage-ui.ts +60 -0
  15. package/extensions/osdy-pi/editor-settings.test.ts +121 -23
  16. package/extensions/osdy-pi/editor-settings.ts +39 -3
  17. package/extensions/osdy-pi/metrics.test.ts +95 -0
  18. package/extensions/osdy-pi/metrics.ts +39 -1
  19. package/extensions/osdy-pi/profile-label.ts +2 -2
  20. package/extensions/osdy-pi/runtime-helpers.test.ts +220 -1
  21. package/extensions/osdy-pi/runtime-helpers.ts +42 -19
  22. package/extensions/osdy-pi/runtime.test.ts +355 -7
  23. package/extensions/osdy-pi/runtime.ts +344 -66
  24. package/extensions/osdy-pi/types.ts +26 -1
  25. package/extensions/osdy-pi/ui.test.ts +128 -0
  26. package/extensions/osdy-pi/ui.ts +57 -40
  27. package/package.json +5 -2
  28. package/scripts/osdy-pi-account-profiles.mjs +521 -76
  29. package/scripts/osdy-pi-gentle-coexistence.mjs +200 -0
  30. package/scripts/osdy-pi-profile-setup.mjs +175 -0
  31. package/themes/osdy-pi-catppuccin-frappe.json +6 -2
  32. package/themes/osdy-pi-catppuccin-latte.json +6 -2
  33. package/themes/osdy-pi-catppuccin-macchiato.json +6 -2
  34. package/themes/osdy-pi-catppuccin-mocha.json +6 -2
  35. package/themes/osdy-pi-dark.json +6 -2
  36. package/themes/osdy-pi-dracula.json +6 -2
  37. package/themes/osdy-pi-kanagawa-dragon.json +6 -2
  38. package/themes/osdy-pi-kanagawa-lotus.json +6 -2
  39. package/themes/osdy-pi-kanagawa-wave.json +6 -2
  40. package/themes/osdy-pi-lucent-orange.json +6 -2
  41. package/themes/osdy-pi-matrix.json +6 -2
  42. package/themes/osdy-pi-new.json +6 -2
  43. package/themes/osdy-pi-sexy.json +6 -2
  44. package/themes/osdy-pi-tokyo-night.json +6 -2
package/README.md CHANGED
@@ -4,27 +4,114 @@ Osdy Pi gives [Pi](https://github.com/earendil-works/pi) a themed, responsive te
4
4
 
5
5
  <img width="1857" height="847" alt="Osdy Pi interface" src="https://github.com/user-attachments/assets/028eeb14-3f43-4f1c-9603-0c55a8d2856d" />
6
6
 
7
+ ## Release highlights
8
+
9
+ | Area | What ships |
10
+ | --- | --- |
11
+ | Message cards | Pi-native, theme-aware user and assistant cards; assistant accents follow the theme and user accents are white. |
12
+ | Header and mascot | A theme-aware `neon` header and selectable `bts` mascot, both responsive and persisted independently. |
13
+ | Accounts | `/osdy-account` switches Codex profiles in place with atomic activation, rollback, bounded auth files, and process-safe locks. |
14
+ | Quota | `/usage` and compact bars emphasize remaining Codex quota at warning (40% or less) and error (15% or less) thresholds. |
15
+
16
+ ## Prerequisites
17
+
18
+ - [Pi](https://github.com/earendil-works/pi)
19
+ - Node.js **22.19.0 or later**
20
+ - Git, for Osdy Pi's working-tree and diff features (and for Git-based installs)
21
+
7
22
  ## Quick start
8
23
 
9
- Install from npm:
24
+ 1. Install Osdy Pi from npm:
25
+
26
+ ```bash
27
+ pi install npm:osdy-pi
28
+ ```
29
+
30
+ Or install directly from GitHub:
31
+
32
+ ```bash
33
+ pi install git:github.com/OsdyOrtiz/Osdy-Pi
34
+ ```
35
+
36
+ 2. Start Pi:
37
+
38
+ ```bash
39
+ pi
40
+ ```
41
+
42
+ Osdy Pi's own extension and all **14 themes** are bundled with this installation—do not install them separately. Launch through `osdy-pi` (or `npm run pi:dev`) to activate a valid Osdy default account into Pi's shared agent directory before Pi starts. With no default, Pi remains unmanaged; run `/osdy-account` to create a profile and establish a default. On session start, Osdy Pi restores its persisted enabled state when a UI is available and preserves your selected Pi theme. When disabled, it leaves Gentle Shell (or Pi's native UI) untouched.
43
+
44
+ ### Optional: isolated `osdy` command
45
+
46
+ Use this path when you want **installed, normal Pi** with Osdy-specific settings and sessions, without changing the official Pi profile. `pi install` loads package resources in Pi; it does not guarantee that package executables are available on your shell's `PATH`. To use the packaged commands, install the CLI separately (or run the scripts directly from a checkout):
10
47
 
11
48
  ```bash
12
- pi install npm:osdy-pi
49
+ npm install --global osdy-pi
50
+ osdy-pi setup
51
+ osdy
13
52
  ```
14
53
 
15
- Or install directly from GitHub:
54
+ From an Osdy Pi checkout, `node bin/osdy-pi.mjs setup` or `node bin/osdy.mjs setup` works without a global CLI installation; `node bin/osdy.mjs` launches Pi. `osdy-pi setup` is explicit and repeatable; `osdy` also checks/reconciles the isolated profile before each launch and forwards its arguments to the installed `pi` executable. The default source is `~/.pi/agent`, the isolated destination is `~/.pi/osdy-agent`, and the Osdy source is the package containing the CLI. Set `OSDY_PI_SOURCE_AGENT_DIR`, `OSDY_PI_AGENT_DIR`, or `OSDY_PI_EXTENSION_ROOT` to override them with absolute paths. The setup needs one valid local `gentle-pi` checkout declared as an absolute source in official Pi's package settings; if none or several match, set `GENTLE_PI_EXTENSION_ROOT=/absolute/path/to/gentle-pi` before setup and launch. Missing or invalid Gentle sources fail with an actionable error rather than silently omitting Gentle. No Pi fork, build, or PATH shim is required.
55
+
56
+ **What is isolated?** Official `~/.pi/agent/settings.json` is read but never rewritten by `osdy-pi setup` or `osdy`. Osdy keeps its own settings, selected theme, sessions, crash logs, and command history. Other existing top-level official resources are linked into the isolated profile only when absent—including `auth.json`, so credentials are **shared**, not isolated. Existing isolated files and symlinks are not replaced; the managed isolated settings are reconciled atomically. Named Codex accounts under `/osdy-account` are a separate feature, not this profile separation.
57
+
58
+ **Already have `~/.local/bin/osdy`?** Check `type -a osdy` (or `command -v osdy`) before using the new command: a home-local launcher earlier on `PATH` still wins. Setup does **not** remove or replace it. To try the packaged launcher without changing PATH, use `node "$(npm root -g)/osdy-pi/bin/osdy.mjs" --version` after installing the CLI; `node "$(npm root -g)/osdy-pi/bin/osdy.mjs" setup` runs the same explicit setup. Compare `pi --version` and `osdy --version` once the intended command resolves. Keep the old launcher until you confirm which executable you want; migration/removal is manual.
59
+
60
+ ## Optional: complete Osdy Pi suite
61
+
62
+ Add these maintained extensions after installing Osdy Pi. They are optional; each extends Pi independently.
63
+
64
+ | Package | Purpose |
65
+ | --- | --- |
66
+ | `gentle-engram` | Persistent memory shared across sessions, compactions, and MCP agents. |
67
+ | `pi-intercom` | Brokered communication between Pi agents and sessions. |
68
+ | `@juicesharp/rpiv-ask-user-question` | Structured, typed questionnaires when an agent needs clarification. |
69
+ | `pi-web-access` | Web search and URL fetching, plus repository, PDF, YouTube, and local-video analysis. |
70
+ | `pi-lens` | Real-time code feedback through LSP, linters, formatters, type checking, and structural analysis. |
71
+ | `pi-btw` | Parallel side conversations through `/btw`. |
72
+ | `@open-pets/pi` | OpenPets integration for Pi. |
73
+ | `pi-playwright` | Playwright browser-automation skills. |
74
+ | `pi-mcp-adapter` | MCP server and tool adapter for Pi. |
75
+ | `pi-subagents-j0k3r` | Markdown-defined subagents, delegation tools, history, and model profiles. |
76
+ | `@juicesharp/rpiv-todo` | A persistent live todo overlay for the agent. |
77
+ | `gentle-pi` | The Gentle senior-architect harness, with SDD/OpenSpec, subagents, TDD evidence, and skills. |
78
+
79
+ Install the ordinary npm packages once:
16
80
 
17
81
  ```bash
18
- pi install git:github.com/OsdyOrtiz/Osdy-Pi
82
+ pi install npm:gentle-engram
83
+ pi install npm:pi-intercom
84
+ pi install npm:@juicesharp/rpiv-ask-user-question
85
+ pi install npm:pi-web-access
86
+ pi install npm:pi-lens
87
+ pi install npm:pi-btw
88
+ pi install npm:@open-pets/pi
89
+ pi install npm:pi-playwright
90
+ pi install npm:pi-mcp-adapter
91
+ pi install npm:pi-subagents-j0k3r
92
+ pi install npm:@juicesharp/rpiv-todo
19
93
  ```
20
94
 
21
- Start Pi normally:
95
+ `gentle-pi` is deliberately not included in that package-install block: clone and register it through the coexistence setup below, which applies Osdy-specific exclusions instead of adding a duplicate ordinary package entry.
22
96
 
23
97
  ```bash
24
- pi
98
+ git clone https://github.com/Gentleman-Programming/gentle-pi.git
99
+ osdy-pi gentle setup "$(pwd)/gentle-pi"
25
100
  ```
26
101
 
27
- `pi install` installs Osdy Pi's extension resources. When you later run plain `pi`, a valid Osdy default account automatically hands off once to the bundled launcher and resumes the saved session under that profile. With no default, Pi remains unmanaged; run `/osdy-account` to create a profile and establish a default. On session start, Osdy Pi enables its UI when a UI is available and preserves your selected Pi theme.
102
+ ## Gentle coexistence setup
103
+
104
+ To load a local `gentle-pi` checkout while keeping Osdy Pi's UI authoritative, run:
105
+
106
+ ```bash
107
+ osdy-pi gentle setup /absolute/path/to/gentle-pi
108
+ ```
109
+
110
+ The command validates that the absolute source is a readable `gentle-pi` package with Gentle's todo and agents extensions before atomically updating `$PI_CODING_AGENT_DIR/settings.json` (or `~/.pi/agent/settings.json`). It registers the local package immediately before the first configured Osdy Pi package entry, so Gentle Shell initializes first, with exclusions only for Gentle's `gentle-todo.ts` and `gentle-agents.ts`, plus `themes: []`. Restart Pi or run `/reload` after setup.
111
+
112
+ Gentle Shell intentionally remains fully active underneath Osdy, including its footer and widgets. While Osdy is enabled, Osdy claims the footer and editor; disabling Osdy restores the editor Gentle Shell provided at session startup. Gentle's changes widget may coexist with Osdy's visual widgets.
113
+
114
+ The exclusions prevent Gentle's todo and agents extensions from competing with the suite. `@juicesharp/rpiv-todo` remains the authoritative todo overlay, `pi-subagents-j0k3r` remains the authoritative subagent system, and `@juicesharp/rpiv-ask-user-question` remains the authoritative structured-question plugin; their package entries are left untouched.
28
115
 
29
116
  ## OpenAI account profiles
30
117
 
@@ -36,7 +123,7 @@ Osdy Pi can keep multiple ChatGPT Plus/Pro accounts authenticated and let you ch
36
123
 
37
124
  | Action | Behavior |
38
125
  | --- | --- |
39
- | **Switch** | Restarts Pi safely with another profile, resumes the current saved session, and makes that profile the default. |
126
+ | **Switch** | Waits for idle, safely replaces Pi's canonical auth with the selected profile, and updates active/default together without restarting Pi. |
40
127
  | **Add** | Creates an isolated profile without restarting or changing the default. Select it with **Switch**, then run `/login`. |
41
128
  | **Default** | Shows, changes, or clears the profile used by future plain installed `pi` and `npm run pi:dev` launches. It does not switch the current process. |
42
129
  | **Rename** | Renames an inactive profile. The default follows the new name when applicable. |
@@ -71,7 +158,7 @@ osdy-pi account remove personal --confirm personal --replacement work
71
158
  osdy-pi account default --clear
72
159
  ```
73
160
 
74
- Profile names accept lowercase letters, numbers, and hyphens, up to 63 characters. Spaces, paths, uppercase letters, and the reserved names `default`, `profiles`, and `auth.json` are rejected. `account add` opens a profile for login but does not change the default.
161
+ Profile names accept ASCII letters, numbers, and hyphens, up to 63 characters, preserving their spelling (for example, `Personal` or `WORK`). Spaces, paths, and the reserved names `default`, `profiles`, and `auth.json` (in any casing) are rejected. Profile identity is case-insensitive, so names that differ only by casing cannot coexist. `account add` opens a profile for login but does not change the default.
75
162
 
76
163
  ### See and switch the active account
77
164
 
@@ -81,7 +168,7 @@ When Pi was launched through a profile, Osdy Pi shows its profile name in the ed
81
168
  - **Extended/framed editor:** `personal` replaces the `Osdy-Pi` title.
82
169
  - **No managed profile:** the existing model line and `Osdy-Pi` title remain unchanged.
83
170
 
84
- `account use <name>` saves that existing profile as the default before it starts Pi. Inside Pi, `/osdy-account` does the same after you select another profile. Osdy Pi waits for active work to finish, starts the replacement Pi with the current saved session, confirms that the new Pi process started, and only then closes the previous process. This is a controlled restart, not an in-process credential swap.
171
+ `account use <name>` activates the profile in Pi's shared agent directory, saves it as active/default, then starts Pi. Inside Pi, `/osdy-account` waits for active work to finish and swaps the canonical `auth.json` in place; the next request resolves the new account without a spawn, shutdown, session handoff, or restart.
85
172
 
86
173
  To resume a specific session directly from the terminal:
87
174
 
@@ -89,7 +176,9 @@ To resume a specific session directly from the terminal:
89
176
  osdy-pi account use work -- --session /absolute/path/to/session.jsonl
90
177
  ```
91
178
 
92
- > **Privacy:** only Pi's managed `auth.json` is isolated per profile. Session history, settings, installed packages, and extension resources are shared, so every profile can access that local state. Osdy Pi never reads, copies, prints, or passes OAuth credentials. It keeps Pi's canonical `openai-codex` provider and delegates authentication to Pi's built-in `/login` flow.
179
+ > **Privacy:** each profile keeps a private `auth.json`; Pi runs against its canonical shared `auth.json`. Osdy Pi atomically copies opaque auth files between those private locations only to activate or preserve an account; it never parses, prints, logs, or passes OAuth credentials. Session history, settings, installed packages, and extension resources are shared, so every profile can access that local state. It keeps Pi's canonical `openai-codex` provider and delegates authentication to Pi's built-in `/login` flow.
180
+
181
+ Account switching is serialized in-process and protected by a private cross-process lock. Osdy Pi accepts only bounded regular auth files, refuses symlink-based auth sources, writes replacements with private permissions, and restores the previous canonical auth and account metadata when activation fails. A separate namespace lock protects concurrent profile creation, rename, and removal. Launchers activate the selected auth before starting Pi while continuing to use the shared agent directory; existing managed-directory links from earlier profile layouts are resolved for compatibility.
93
182
 
94
183
  ### Rename and permanently remove profiles
95
184
 
@@ -104,9 +193,10 @@ Before rename or removal, close this Pi process when it uses the target and **ma
104
193
  | Area | Included behavior |
105
194
  | --- | --- |
106
195
  | Themes | 14 built-in themes, including Osdy, Kanagawa, Dracula, Catppuccin, Matrix, and Lucent Orange palettes |
107
- | Header | Selectable `osdy-theme` and `classic` header/mascot styles |
196
+ | Header and mascot | Independently selectable `osdy-theme`/`neon` headers and `current`/`bts` mascots |
197
+ | Messages | Pi-native, theme-aware user and assistant message cards |
108
198
  | Input | Responsive auto editor by default, with selectable simple Pi-native or extended framed modes |
109
- | Status | Custom working spinner, responsive footer metrics, and dynamic extension statuses |
199
+ | Status | Custom working spinner, responsive footer metrics, dynamic extension statuses, and Codex subscription quota with low-capacity emphasis |
110
200
  | Git | Working-tree summary and a centered, filterable diff panel |
111
201
  | Audio | Optional event sounds on macOS and Windows |
112
202
 
@@ -149,7 +239,16 @@ Or set the theme in Pi's `settings.json`:
149
239
 
150
240
  ### Header, mascot, and animation
151
241
 
152
- `osdy-theme` is the default header style; `classic` is the alternative. In normal mode, both styles render their full selected header and mascot. The header animation and mascot edge glow resolve through the active theme, so each installed palette supplies its own accents. Use `/osdy-pi osdy-theme` or `/osdy-pi classic`, or their direct aliases `/osdy-pi-osdy-theme` and `/osdy-pi-classic`.
242
+ Header and mascot choices are independent:
243
+
244
+ | Element | Choices | Command |
245
+ | --- | --- | --- |
246
+ | Header | `osdy-theme` (default), `neon` | `/osdy-pi header osdy-theme|neon` |
247
+ | Mascot | `current` (default), `bts` | `/osdy-pi mascot current|bts` |
248
+
249
+ Both commands update the UI immediately. Add `status` instead of a choice to inspect the current selection. Choices persist in the global Osdy Pi settings across reloads and sessions. The old top-level style commands and `classic` header aliases are no longer registered.
250
+
251
+ In normal mode, the selected header and mascot render side by side. The Neon header derives its highlights from the active theme rather than using one fixed palette. Each mascot keeps its own tone map while its animated edge glow can resolve through theme colors. Header and mascot scaling remain independent, so any combination follows the same responsive layout rules.
153
252
 
154
253
  Animation is enabled by default with an intro animation. Configure it through `OSDY_PI_ANIMATION`:
155
254
 
@@ -167,31 +266,51 @@ Animation is enabled by default with an intro animation. Configure it through `O
167
266
  | Compact (72+ columns) | Proportionally scaled mascot above a readable header, reduced only when needed | Selected editor mode and Git behavior | Same editor-aware footer behavior as normal/small modes |
168
267
  | Small (<72 columns) | Mascot only; art and tone map scale proportionally | Pi native editor for every editor mode; Git summary hidden | Model + styled thinking level, usage, path/branch, then dynamic extension statuses (except Pi Lens) |
169
268
 
269
+ In the extended framed editor, the active thinking level is rendered in bold in the top-right model metadata so it remains easy to scan.
270
+
170
271
  Small and compact modes trim only fully empty mascot-art and tone-map margins before applying one proportional width-and-height scale; mascot width starts near four-fifths of the available width. The header moves below the mascot as soon as side-by-side width would force the mascot into an additional width-limited reduction. Compact headers retain their source art when it fits and reduce proportionally only when a width or row bound requires it. Compact headers and mascots share a bounded terminal-row budget, so the header is omitted rather than collapsed into an unreadable one-row logo when there is not enough vertical space. The small-mode footer places the model and styled bare thinking level above usage, path/branch, and dynamic extension statuses. Pi Lens's footer status is hidden in small mode, but Pi Lens continues running. Usage includes input/output/cache-read/cache-write tokens, cost, and context. Extension statuses are supplied dynamically by Pi/extensions and may include Osdy Pi, MCP, or LSP; they are not hardcoded.
171
272
 
172
- The editor mode and working-tree visibility preference persist globally across Pi reloads and sessions, shared by all projects. They are saved in `$PI_CODING_AGENT_DIR/extensions/osdy-pi/settings.json`, or `~/.pi/agent/extensions/osdy-pi/settings.json` when `PI_CODING_AGENT_DIR` is unset. The selected editor mode and working-tree placement are restored when the terminal moves normal → small → normal.
273
+ The enabled state, editor mode, working-tree visibility preference, header, and mascot persist globally across Pi reloads and sessions, shared by all projects. They are saved in `$PI_CODING_AGENT_DIR/extensions/osdy-pi/settings.json`, or `~/.pi/agent/extensions/osdy-pi/settings.json` when `PI_CODING_AGENT_DIR` is unset. Existing settings without the new visual fields safely default to `osdy-theme` and `current`; the former `raccoon` mascot value migrates to `bts`. The selected editor mode and working-tree placement are restored when the terminal moves normal → small → normal.
173
274
 
174
275
  ## Commands
175
276
 
176
277
  | Group | Command |
177
278
  | --- | --- |
178
279
  | Main | `/osdy-pi` |
179
- | Main | `/osdy-pi enable\|disable\|status` |
280
+ | Main | `/osdy-pi enable\|disable\|on\|off\|status` |
180
281
  | Accounts | `/osdy-account` |
181
- | Header | `/osdy-pi osdy-theme\|classic` |
282
+ | Header | `/osdy-pi header osdy-theme\|neon\|status` |
283
+ | Mascot | `/osdy-pi mascot current\|bts\|status` |
182
284
  | Editor | `/osdy-pi editor auto\|extended\|simple\|on\|off\|toggle\|status` |
183
285
  | Working tree | `/osdy-pi working-tree on\|off\|toggle\|status` |
184
286
  | Working tree | `/osdy-pi working-tree position top\|bottom\|status` |
185
287
  | Audio | `/osdy-pi sound setup` |
186
288
  | Diff | `/osdy-pi diff` |
187
- | Alias | `/osdy-pi-osdy-theme` |
188
- | Alias | `/osdy-pi-classic` |
289
+ | Codex subscription | `/usage` |
290
+
291
+ `/osdy-pi` reports status. `enable` (or `on`) applies the Osdy Pi UI without changing the selected Pi theme; `disable` (or `off`) restores the Gentle Shell or Pi UI captured at session startup while preserving that theme. The enabled state, editor mode, working-tree visibility, and sound configuration persist globally.
292
+
293
+ ### Codex subscription usage
189
294
 
190
- `/osdy-pi` reports status. `enable` applies the Osdy Pi UI without changing the selected Pi theme; `disable` restores Pi's built-in header, editor, footer, and working row while preserving that theme. The editor mode, working-tree visibility, and sound configuration persist globally; other UI toggles are current-session desired state.
295
+ Run `/usage` to open the Codex subscription dashboard for the active managed `openai-codex` profile and model. Press `r` to refresh; `esc` or `q` closes it. The dashboard shows these controls at the bottom.
296
+
297
+ The main windows are labeled **Session** and **Weekly** (the API/domain remains primary/secondary). Their remaining-capacity bars are full at 100% remaining and empty at 0%; labels use the active theme's bold accent, Session uses the accent fill, Weekly uses `mdLink`, and empty segments are muted. The remaining percentage stays muted above 40%, changes to the theme's warning color at 40% or less, and changes to its error color at 15% or less. The same thresholds apply in the `/usage` dashboard and compact footer/editor quota bars. A double themed frame, section dividers, and spacing separate the display. Detail cards show each duration and relative, local, and UTC reset times. Plan, availability, credits/reset count, and additional buckets appear only when the service supplies them; absent optional values are omitted.
298
+
299
+ At wide widths, the modal pairs the Session and Weekly detail cards. Below 72 content columns, cards and bars stack and account/model data wraps. The native footer and extended editor also show compact Session/Weekly remaining-capacity bars below model and thinking metadata whenever a current Codex snapshot exists. Wide widths combine those bars; narrow widths stack them. Additional buckets appear only in `/usage`.
300
+
301
+ Usage loads once when the session starts and refreshes when the modal opens or `r` is pressed; it does not poll. A refresh clears the previous snapshot before authentication resolves, and shutdown clears state, so quota data cannot cross account or profile boundaries.
302
+
303
+ > **Privacy:** OAuth is resolved only through Pi's `modelRegistry`. Osdy Pi does not read `auth.json`, persist or log credentials, or display account IDs, tokens, response bodies, or endpoint internals in the UI. Requests use a fixed HTTPS endpoint with bounded timeout, response size, and redirects.
304
+
305
+ ## Native message cards
306
+
307
+ Osdy Pi delegates conversation rendering to Pi's native message-card components. It does not install a Markdown transformer or inject card markup into assistant responses. This preserves Pi's own streaming, selection, and Markdown behavior while allowing every bundled Osdy theme to style the native cards.
308
+
309
+ User and assistant cards have separate background, text, and accent tokens. Assistant accents follow each theme's primary accent; user accents are white for a consistent visual distinction. Disabling Osdy Pi continues to restore the underlying Gentle Shell or Pi presentation normally.
191
310
 
192
311
  ## Editor and working indicator
193
312
 
194
- The default `auto` editor mode preserves the responsive behavior: it uses the framed editor when space permits and Pi's native editor on small terminals. Select `simple` for Pi's native editor at every width, or `extended` to request the framed editor explicitly: `/osdy-pi editor auto|extended|simple`. Small terminals always use Pi's native editor, including when `extended` is selected. The legacy commands remain compatible where feasible: `on` maps to `extended`, `off` maps to `simple`, and `toggle` switches between extended and simple.
313
+ The default `auto` editor mode preserves the responsive behavior: it uses the framed editor when space permits and Pi's native editor on small terminals. Select `simple` for Pi's native editor at every width, or `extended` to request the framed editor explicitly: `/osdy-pi editor auto|extended|simple`. Simple mode actually unmounts the custom editor component rather than hiding it. Small terminals always use Pi's native editor, including when `extended` is selected. The legacy commands remain compatible where feasible: `on` maps to `extended`, `off` maps to `simple`, and `toggle` switches between extended and simple.
195
314
 
196
315
  In auto or extended mode at a non-small width, the framed editor shows the model and thinking level in its title and session usage in its footer. For an account-profile launch, the left title shows the active profile name instead of `Osdy-Pi`. When the native editor is effective (simple mode or any small terminal), the Osdy footer instead shows model, active profile when present, thinking, and usage rows before its path/branch and status rows. It uses the currently active Pi/Osdy theme palette; no separate editor theme selector exists. Usage covers input, output, cache read, cache write when present, cost, and context. If Pi supports autocomplete, the editor uses Pi's native autocomplete rendering while the completion UI is visible.
197
316
 
@@ -199,7 +318,7 @@ A custom spinner appears above the editor while work is active. Osdy Pi hides Pi
199
318
 
200
319
  ## Working tree and diff
201
320
 
202
- The working-tree summary is enabled by default. It reads the repository state at session start, including existing changes, and reports staged, unstaged, and untracked counts with total `+/-` changes. It also has clean and unavailable states. After successful `edit`, `write`, `ast_grep_replace`, or `bash` tool execution, it refreshes.
321
+ The working-tree summary is enabled by default. It reads the repository state at session start, including existing changes, and reports staged, unstaged, and untracked counts with total `+/-` changes. It also has clean and unavailable states. `working-tree off` unregisters the widget completely, and a persisted disabled preference leaves it unmounted when the next session starts. `working-tree on` remounts and refreshes it without requiring a restart. After successful `edit`, `write`, `ast_grep_replace`, or `bash` tool execution, it refreshes.
203
322
 
204
323
  Use `working-tree position top` or `bottom` to place the summary above or below the editor. The widget supplies trailing blank space and adds leading separation when it is below the editor or the spinner is active, keeping the surrounding layout readable without promising a fixed number of blank lines in every state.
205
324
 
@@ -215,7 +334,7 @@ Use `working-tree position top` or `bottom` to place the summary above or below
215
334
 
216
335
  ## Audio notifications
217
336
 
218
- Osdy Pi can play readable `.mp3` or `.wav` files on macOS and Windows. Other platforms safely skip playback.
337
+ Osdy Pi can play readable `.mp3` or `.wav` files on macOS and Windows. Other platforms safely skip playback. macOS playback requires the system `afplay` command; Windows playback requires `powershell.exe` and the Windows Media Player COM component (`WMPlayer.OCX`).
219
338
 
220
339
  | Event | Current meaning |
221
340
  | --- | --- |
@@ -248,16 +367,22 @@ Precedence is startup flag, then saved global setting, then unconfigured. Empty
248
367
 
249
368
  For the normal in-Pi development flow, no global `osdy-pi` link is required:
250
369
 
251
- 1. Start the local extension:
370
+ 1. Install the checkout's dependencies:
371
+
372
+ ```bash
373
+ npm install
374
+ ```
375
+
376
+ 2. Start the local extension:
252
377
 
253
378
  ```bash
254
379
  npm run pi:dev
255
380
  ```
256
381
 
257
- 2. Inside Pi, run `/osdy-account`.
258
- 3. Choose **Add**, enter a profile name, then choose **Switch** and select it.
259
- 4. After the managed restart, run `/login` and choose **ChatGPT Plus/Pro (Codex)**.
260
- 5. From then on, `npm run pi:dev` starts the default profile automatically. Use `/osdy-account` for every profile-management action.
382
+ 3. Inside Pi, run `/osdy-account`.
383
+ 4. Choose **Add**, enter a profile name, then choose **Switch** and select it.
384
+ 5. Without restarting, run `/login` and choose **ChatGPT Plus/Pro (Codex)**.
385
+ 6. From then on, `npm run pi:dev` starts the default profile automatically. Use `/osdy-account` for every profile-management action.
261
386
 
262
387
  The terminal interface remains available for recovery and automated testing:
263
388
 
@@ -270,7 +395,7 @@ npm run pi:dev -- account rename personal private
270
395
  npm run pi:dev -- account remove private --confirm private
271
396
  ```
272
397
 
273
- `npm run pi:dev` launches `pi -e <absolute repository root>` with `PI_CODING_AGENT_DIR=<absolute repository root>/.pi-dev` when no default is set. If its `.pi-dev` metadata names a valid profile, it routes through this checkout's local launcher and starts that profile while retaining `-e <absolute repository root>`. The development `.pi-dev` profile store and the installed Pi profile store are separate. For an installed package, plain `pi` loads extension resources; at startup Osdy Pi hands off through its bundled launcher when the installed store has a valid default, while no default leaves ordinary unmanaged Pi running. Account commands use this checkout's local launcher, so no global `osdy-pi` link is needed. The development extension root is inherited by profile launches, keeping the local extension loaded after an `/osdy-account` handoff. The manual equivalent is:
398
+ `npm run pi:dev` launches `pi -e <absolute repository root>` with `PI_CODING_AGENT_DIR=<absolute repository root>/.pi-dev`. If its `.pi-dev` metadata names a valid profile, it activates that profile's auth before launching Pi and retains `-e <absolute repository root>`. The development `.pi-dev` profile store and the installed Pi profile store are separate. Account commands use this checkout's local launcher, so no global `osdy-pi` link is needed. The development extension root remains loaded after an in-process `/osdy-account` switch. The manual equivalent is:
274
399
 
275
400
  ```bash
276
401
  PI_CODING_AGENT_DIR="$PWD/.pi-dev" OSDY_PI_DEV_EXTENSION_ROOT="$PWD" pi -e "$PWD"
@@ -301,15 +426,20 @@ npm run pi:dev
301
426
  - The Git summary reports unavailable when Git commands cannot read a working tree.
302
427
  - Diff patches depend on readable repository files; a file whose patch cannot load shows the reported error in the panel.
303
428
  - Audio playback is limited to macOS and Windows and to readable `.mp3`/`.wav` files.
429
+ - `/usage` requires an active `openai-codex` login. Run `/login` and choose **ChatGPT Plus/Pro (Codex)**; run `/reload` after local extension changes.
430
+ - A `401` indicates an expired session, while a `403` means usage is unavailable. Optional backend fields may be absent and simply do not render.
304
431
 
305
432
  ## Disable, uninstall, and license
306
433
 
307
- Temporarily turn off the custom UI:
434
+ Turn off the custom UI persistently (including across `/reload`):
308
435
 
309
436
  ```text
310
437
  /osdy-pi disable
438
+ # Alias: /osdy-pi off
311
439
  ```
312
440
 
441
+ Restore it with `/osdy-pi enable` or `/osdy-pi on`.
442
+
313
443
  To remove the package, use Pi's package-management command for installed packages.
314
444
 
315
445
  MIT
package/bin/osdy-pi.mjs CHANGED
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { setupOsdyProfile } from "../scripts/osdy-pi-profile-setup.mjs";
7
+ import { configureGentleCoexistence } from "../scripts/osdy-pi-gentle-coexistence.mjs";
3
8
  import {
4
9
  clearDefaultAccount,
5
10
  ensureProfileLayout,
@@ -12,7 +17,7 @@ import {
12
17
  removeProfile,
13
18
  renameProfile,
14
19
  setDefaultAccount,
15
- validateExistingProfile,
20
+ switchAccountAuth,
16
21
  } from "../scripts/osdy-pi-account-profiles.mjs";
17
22
 
18
23
  function writeStdout(message) {
@@ -25,7 +30,7 @@ function writeStderr(message) {
25
30
 
26
31
  function printUsage() {
27
32
  writeStderr(
28
- "Usage: osdy-pi account list | create <name> | add <name> | rename <old> <new> | remove <name> --confirm <name> [--replacement <other>] | use <name> [-- <pi args...] | default [<name> | --clear]",
33
+ "Usage: osdy-pi setup | gentle setup <absolute-source-path> | account list | create <name> | add <name> | rename <old> <new> | remove <name> --confirm <name> [--replacement <other>] | use <name> [-- <pi args...] | default [<name> | --clear]",
29
34
  );
30
35
  }
31
36
 
@@ -65,13 +70,31 @@ function startPi(plan) {
65
70
 
66
71
  try {
67
72
  const args = process.argv.slice(2);
68
- const sharedAgentDir = getSharedAgentDir();
69
- if (args.length === 0) {
73
+ if (args[0] === "setup") {
74
+ if (args.length !== 1) throw new Error("Usage: osdy-pi setup");
75
+ const home = homedir();
76
+ const result = await setupOsdyProfile({
77
+ officialDir: process.env.OSDY_PI_SOURCE_AGENT_DIR || join(home, ".pi", "agent"),
78
+ profileDir: process.env.OSDY_PI_AGENT_DIR || join(home, ".pi", "osdy-agent"),
79
+ osdyRoot: process.env.OSDY_PI_EXTENSION_ROOT || resolve(dirname(fileURLToPath(import.meta.url)), ".."),
80
+ gentleRoot: process.env.GENTLE_PI_EXTENSION_ROOT || undefined,
81
+ });
82
+ writeStdout(`Isolated Osdy profile ${result.status}: ${result.profileDir}`);
83
+ } else if (args[0] === "gentle") {
84
+ if (args[1] !== "setup" || args.length !== 3)
85
+ throw new Error("Usage: osdy-pi gentle setup <absolute-source-path>");
86
+ const result = await configureGentleCoexistence(args[2]);
87
+ writeStdout(
88
+ `Gentle coexistence ${result.status}. Restart Pi or run /reload to apply it.`,
89
+ );
90
+ } else if (args.length === 0) {
91
+ const sharedAgentDir = getSharedAgentDir();
70
92
  const result = await planDefaultLaunch(sharedAgentDir);
71
93
  if (result.defaultAccount.status === "invalid")
72
94
  writeStderr("Ignoring invalid Osdy Pi default account metadata.");
73
95
  startPi(result.plan);
74
96
  } else {
97
+ const sharedAgentDir = getSharedAgentDir();
75
98
  const command = parseAccountCommand(args);
76
99
  if (command.action === "list") {
77
100
  const profiles = await listProfiles(sharedAgentDir);
@@ -104,6 +127,7 @@ try {
104
127
  writeStdout(
105
128
  "Account profile is ready. In Pi, run /login and select ChatGPT Plus/Pro (Codex).",
106
129
  );
130
+ await switchAccountAuth(sharedAgentDir, command.name);
107
131
  startPi(planPiLaunch(sharedAgentDir, command.name));
108
132
  } else if (command.action === "rename") {
109
133
  await renameProfile(sharedAgentDir, command.oldName, command.newName);
@@ -115,8 +139,7 @@ try {
115
139
  });
116
140
  writeStdout(`Account profile ${command.name} permanently removed.`);
117
141
  } else {
118
- await validateExistingProfile(sharedAgentDir, command.name);
119
- await setDefaultAccount(sharedAgentDir, command.name);
142
+ await switchAccountAuth(sharedAgentDir, command.name);
120
143
  startPi(planPiLaunch(sharedAgentDir, command.name, command.piArgs));
121
144
  }
122
145
  }
package/bin/osdy.mjs ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ /* global process, console */
3
+ import { spawn } from "node:child_process";
4
+ import { constants as osConstants, homedir } from "node:os";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { setupOsdyProfile } from "../scripts/osdy-pi-profile-setup.mjs";
8
+
9
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10
+ const home = homedir();
11
+ const officialDir = process.env.OSDY_PI_SOURCE_AGENT_DIR || join(home, ".pi", "agent");
12
+ const profileDir = process.env.OSDY_PI_AGENT_DIR || join(home, ".pi", "osdy-agent");
13
+ const osdyRoot = process.env.OSDY_PI_EXTENSION_ROOT || packageRoot;
14
+ const gentleRoot = process.env.GENTLE_PI_EXTENSION_ROOT || undefined;
15
+
16
+ try {
17
+ if (process.argv[2] === "setup" && process.argv.length !== 3)
18
+ throw new Error("Usage: osdy setup");
19
+ const result = await setupOsdyProfile({ officialDir, profileDir, osdyRoot, gentleRoot });
20
+ if (process.argv[2] === "setup") {
21
+ console.log(`Isolated Osdy profile ${result.status}: ${result.profileDir}`);
22
+ } else {
23
+ const child = spawn("pi", process.argv.slice(2), {
24
+ stdio: "inherit",
25
+ env: {
26
+ ...process.env,
27
+ PI_CODING_AGENT_DIR: result.profileDir,
28
+ OSDY_PI_DEV_EXTENSION_ROOT: osdyRoot,
29
+ },
30
+ });
31
+ child.on("error", (error) => {
32
+ console.error(`osdy: Could not launch installed pi from PATH: ${error.message}`);
33
+ process.exitCode = 1;
34
+ });
35
+ child.on("exit", (code, signal) => {
36
+ if (signal) process.exitCode = 128 + (osConstants.signals[signal] ?? 1);
37
+ else process.exitCode = code ?? 1;
38
+ });
39
+ }
40
+ } catch (error) {
41
+ console.error(`osdy: ${error instanceof Error ? error.message : String(error)}`);
42
+ process.exitCode = 1;
43
+ }