pi-extmgr 0.1.2 → 0.1.4

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
@@ -1,430 +1,83 @@
1
- # 🔧 pi-extmgr
1
+ # pi-extmgr
2
2
 
3
- <img width="2560" height="369" alt="image" src="https://i.imgur.com/nP5rJPC.png" />
4
-
5
- > Enhanced UI/UX for managing Pi extensions and discovering community packages
3
+ <img width="2560" height="369" alt="image" src="https://i.imgur.com/tTD31v8.png" />
6
4
 
7
5
  [![CI](https://github.com/ayagmar/pi-extmgr/actions/workflows/ci.yml/badge.svg)](https://github.com/ayagmar/pi-extmgr/actions/workflows/ci.yml)
8
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
7
 
10
- **pi-extmgr** transforms extension management in Pi from a command-line chore into a delightful interactive experience. Browse, install, and manage extensions with an intuitive TUI interface, smart autocomplete, and one-click operations.
11
-
12
- <!-- Replace with actual demo GIF/Screenshot when available -->
13
- <!-- ![Demo](demo.gif) -->
14
-
15
- ## ✨ Features
16
-
17
- ### 🎨 Interactive TUI Interface
18
-
19
- - **Beautiful themed interface** with color-coded status indicators
20
- - **Unified view** - local extensions and npm/git packages in one screen
21
- - **Keyboard-driven navigation** - fast and efficient
22
- - **Real-time previews** with package descriptions
23
- - **Context-aware help** - press `?` anywhere for shortcuts
24
-
25
- ### 📋 Unified Extension Manager
26
-
27
- All your extensions in one place:
28
-
29
- - **Local extensions**: `● enabled` / `○ disabled` with `[G]` global or `[P]` project scope
30
- - **Installed packages**: `◆` npm / `◇` git icon with name@version and size info
31
- - **Visual distinction** between toggle-able locals and action-based packages
32
- - **Smart deduplication** - packages already managed as local extensions are hidden
33
- - **Theme adaptive UI** - Works consistently in dark and light themes
34
-
35
- ### 🔍 Smart Package Discovery
36
-
37
- - **Browse community packages** with pagination (20 per page)
38
- - **Cached search results** - npm metadata cached for 24 hours for fast navigation
39
- - **Keyword filtering** - automatically shows `pi-package` tagged npm packages
40
- - **Detailed package info** - view version, author, homepage, and install size
41
-
42
- ### 📦 Flexible Installation
43
-
44
- - **Multiple source support**: npm, git, local paths
45
- - **Two install modes**:
46
- - **Managed** (npm) - Auto-updates with `pi update`, stored in pi's package cache
47
- - **Standalone** (local) - Full package directory to `~/.pi/agent/extensions/{package}/`
48
- - **Multi-file extension support** - Local install copies entire package directory, preserving imports
49
- - **Auto-extract** from npm tarballs for local installs
50
- - **One-click reload** after installation
51
-
52
- ### ⚡ Quick Extension Management
53
-
54
- - **Enable/disable extensions** with staging (preview before applying)
55
- - **Package actions** - update/remove/view details without leaving the manager
56
- - **Visual change indicators** (`*`) show pending modifications
57
- - **Bulk operations** - update all packages at once
58
- - **Scope indicators**: Global (G) vs Project (P) for all items
59
-
60
- ### 📊 Extension Change History
8
+ A better way to manage Pi extensions. Browse, install, enable/disable, and remove extensions from one place.
61
9
 
62
- Track all your extension management actions:
63
-
64
- - **Automatic logging** - Every toggle, install, update, and remove is recorded
65
- - **Session persistence** - View change history with `/extensions history`
66
- - **Statistics** - View totals by action type with `/extensions stats`
67
- - **Error tracking** - Failed operations are logged with error details
68
-
69
- ### 🎯 Quality of Life
70
-
71
- - **Tab autocomplete** for all subcommands
72
- - **Status bar integration** - shows installed package count
73
- - **Keyboard shortcut**: `Ctrl+Shift+E` opens extension manager
74
- - **Non-interactive mode** - works in scripts and CI
75
- - **Parallel data loading** - local extensions and packages fetched simultaneously
76
- - **Metadata caching** - npm package info cached for 24 hours for faster browsing
77
-
78
- ## 🚀 Installation
10
+ ## Install
79
11
 
80
12
  ```bash
81
13
  pi install npm:pi-extmgr
82
14
  ```
83
15
 
84
- Then reload Pi:
16
+ Then reload Pi with `/reload`.
85
17
 
86
- ```
87
- /reload
88
- ```
18
+ ## What it does
89
19
 
90
- ## 📖 Usage
20
+ - **Unified view**: See all your local extensions and installed npm/git packages in one list
21
+ - **Toggle extensions**: Enable/disable local extensions with Space/Enter, save with `S`
22
+ - **Package actions**: Update or remove installed packages with `A`
23
+ - **Browse community**: Search and install from npm (`R` to browse)
24
+ - **History tracking**: See what you've changed with `/extensions history`
91
25
 
92
- ### Interactive Mode (Recommended)
26
+ ## Usage
27
+
28
+ Open the manager:
93
29
 
94
30
  ```
95
- /extensions # Open unified interactive manager
31
+ /extensions
96
32
  ```
97
33
 
98
- The unified view displays:
99
-
100
- - **Local extensions** first (toggle-able)
101
- - **Installed packages** second (action-based)
102
- - Sorted alphabetically within each group
103
-
104
- #### Keyboard Shortcuts
105
-
106
- | Key | Action |
107
- | ------------- | --------------------------------------------------- |
108
- | `↑↓` | Navigate items |
109
- | `Space/Enter` | Toggle local extension on/off |
110
- | `S` | Save changes to local extensions |
111
- | `A` | Actions on selected package (update/remove/details) |
112
- | `R` | Browse remote packages |
113
- | `?` / `H` | Show help |
114
- | `Esc` | Cancel / Exit |
115
-
116
- **Staged Changes**: Toggle extensions on/off without immediate effect. Press `S` to apply all changes at once. Pending changes show `*` next to the extension name.
117
-
118
- #### Package Actions
119
-
120
- When a package is selected, press `A` to:
121
-
122
- - **Update package** - fetch latest version
123
- - **Remove package** - uninstall completely
124
- - **View details** - see version, source, scope
125
- - **Back to manager** - return to unified view
126
-
127
- #### Community Package Browser
34
+ ### In the manager
128
35
 
129
- Browse and install from npm:
36
+ | Key | Action |
37
+ | ------------- | ------------------------------------------------ |
38
+ | `↑↓` | Navigate |
39
+ | `Space/Enter` | Toggle local extension on/off |
40
+ | `S` | Save changes |
41
+ | `A` | Actions on selected package (update/remove/view) |
42
+ | `R` | Browse remote packages |
43
+ | `?` / `H` | Help |
44
+ | `Esc` | Exit |
130
45
 
131
- | Key | Action |
132
- | ------- | -------------------- |
133
- | `↑↓` | Navigate packages |
134
- | `Enter` | View package details |
135
- | `N` | Next page |
136
- | `P` | Previous page |
137
- | `R` | Refresh search |
138
- | `Esc` | Cancel |
139
-
140
- ### Command Reference
46
+ ### Commands
141
47
 
142
48
  ```bash
143
- # Unified Manager (Recommended)
144
- /extensions # Open unified interactive manager
145
-
146
- # Legacy Commands
147
- /extensions list # List local extensions (text output)
148
- /extensions installed # Redirects to unified view
149
-
150
- # Package Discovery
151
- /extensions remote # Browse community packages
152
- /extensions packages # Alias for remote
153
- /extensions search <query> # Search npm for packages
154
-
155
- # Package Management
156
- /extensions install <source> # Install from npm/git/path
157
- /extensions remove [source] # Remove package (interactive if no source)
158
- /extensions uninstall [source]# Alias for remove
159
-
160
- # History & Stats
161
- /extensions history # View recent extension changes
162
- /extensions stats # View extension manager statistics
163
- /extensions clear-cache # Clear metadata cache
49
+ /extensions # Open the manager
50
+ /extensions search <query> # Search npm
51
+ /extensions install <source> # Install package
52
+ /extensions remove [source] # Remove package
53
+ /extensions history # View change history
54
+ /extensions stats # View statistics
55
+ /extensions clear-cache # Clear metadata cache
164
56
  ```
165
57
 
166
- ### Install Sources
58
+ ### Install sources
167
59
 
168
60
  ```bash
169
- # npm packages (auto-detected if no prefix)
170
- /extensions install npm:some-package
61
+ /extensions install npm:package-name
171
62
  /extensions install @scope/package
172
-
173
- # Git repositories
174
63
  /extensions install git:https://github.com/user/repo.git
175
-
176
- # GitHub single-file extensions (.ts files)
177
- # Automatically converts blob URLs to raw and downloads directly
178
64
  /extensions install https://github.com/user/repo/blob/main/extension.ts
179
-
180
- # Local paths
181
65
  /extensions install /path/to/extension.ts
182
- /extensions install ./my-extension/
183
- ```
184
-
185
- ### Non-Interactive Mode
186
-
187
- All commands work in non-interactive environments (CI, scripts):
188
-
189
- ```bash
190
- # These work without UI
191
- /extensions list
192
- /extensions installed
193
-
194
- # These require arguments in non-interactive mode
195
- /extensions install npm:package-name
196
- /extensions remove npm:package-name
197
- ```
198
-
199
- ## 🎮 Keyboard Shortcuts
200
-
201
- ### Global
202
-
203
- - `Ctrl+Shift+E` - Open Extensions Manager
204
-
205
- ### In Unified Manager
206
-
207
- | Key | Action |
208
- | -------------- | ------------------------------- |
209
- | `↑/↓` or `K/J` | Navigate |
210
- | `Enter/Space` | Toggle local extension |
211
- | `S` | Save changes |
212
- | `A` | Package actions (update/remove) |
213
- | `R` | Browse remote packages |
214
- | `?` / `H` | Help |
215
- | `Esc` | Cancel / Exit |
216
-
217
- ## 🏗️ Extension Discovery
218
-
219
- pi-extmgr discovers extensions from multiple sources:
220
-
221
- ### Local Extensions
222
-
223
- ```
224
- ~/.pi/agent/extensions/ # Global
225
- ├── my-extension.ts
226
- ├── disabled-extension.ts.disabled
227
- └── my-extension/
228
- └── index.ts
229
-
230
- ./.pi/extensions/ # Project
231
- ├── project-tool.ts
232
- └── local-helper/
233
- └── index.ts
234
- ```
235
-
236
- ### Installed Packages
237
-
238
- Managed by `pi install`:
239
-
240
- - npm packages (`npm:package@version`)
241
- - git packages (`git:https://...`)
242
- - Stored in pi's package cache
243
-
244
- **Naming**: Append `.disabled` to disable a local extension without removing it.
245
-
246
- ## 🔧 Configuration
247
-
248
- No configuration needed! But you can customize your Pi theme to change the appearance:
249
-
250
- ```typescript
251
- // In your theme extension
252
- export default function myTheme(pi: ExtensionAPI) {
253
- pi.registerTheme({
254
- name: "my-theme",
255
- colors: {
256
- accent: "#00ff00",
257
- success: "#00aa00",
258
- error: "#ff0000",
259
- warning: "#ffaa00",
260
- // ... other colors
261
- },
262
- });
263
- }
264
- ```
265
-
266
- ## 📝 Example Workflows
267
-
268
- ### Managing All Extensions
269
-
270
- 1. Press `Ctrl+Shift+E` or type `/extensions`
271
- 2. See all local extensions and installed packages in one view
272
- 3. Navigate with `↑↓`
273
- 4. For local extensions: press `Space` to toggle on/off
274
- 5. For packages: press `A` for actions (update/remove)
275
- 6. Press `S` to save any changes to local extensions
276
- 7. Confirm reload to apply changes
277
-
278
- ### Installing a New Extension
279
-
280
- 1. Type `/extensions` to open manager
281
- 2. Press `R` for remote packages
282
- 3. Browse or search for the extension
283
- 4. Press `Enter` on the desired package
284
- 5. Choose install mode:
285
- - **"Install via npm (managed)"** - Uses pi's package manager. Auto-updates with `pi update`. Best for most users.
286
- - **"Install locally (standalone)"** - Copies entire package to `~/.pi/agent/extensions/{package}/`. Supports multi-file extensions with imports. Manual updates required.
287
- 6. Confirm installation
288
- 7. Choose to reload Pi to activate
289
-
290
- **Local Install Directory Structure:**
291
-
292
- ```
293
- ~/.pi/agent/extensions/
294
- └── pi-some-extension/ # Full package directory
295
- ├── index.ts # Entry point
296
- ├── utils.ts # Helper (imports work!)
297
- └── package.json # Original package.json preserved
298
- ```
299
-
300
- ### Updating a Package
301
-
302
- 1. Type `/extensions` to open unified manager
303
- 2. Navigate to the installed package
304
- 3. Press `A` for actions
305
- 4. Select "Update package"
306
- 5. Confirm reload if updated
307
-
308
- ### Disabling a Local Extension Temporarily
309
-
310
- 1. Type `/extensions` to open manager
311
- 2. Navigate to the local extension with `↑↓`
312
- 3. Press `Space` to toggle it off
313
- 4. Press `S` to save
314
- 5. Confirm reload
315
-
316
- The extension remains installed but won't load until re-enabled.
317
-
318
- ### Removing a Package
319
-
320
- 1. Type `/extensions` to open unified manager
321
- 2. Navigate to the installed package with `↑↓`
322
- 3. Press `A` for actions
323
- 4. Select "Remove package"
324
- 5. Confirm removal
325
-
326
- **Important**: Unlike install/update, removing a package requires a **full restart** of pi (not just `/reload`). The extension files are deleted, but the extension remains loaded in memory until you exit and restart pi.
327
-
328
- ### Updating All Packages
329
-
330
- 1. Type `/extensions` to open unified manager
331
- 2. Navigate to any installed package
332
- 3. Press `A` for actions
333
- 4. Select "Update package"
334
- 5. Or use command: `/extensions install npm:pi-extmgr` then select "[Update all packages]"
335
-
336
- ### Viewing Change History
337
-
338
- See what you've done in the current session:
339
-
340
- ```
341
- /extensions history
342
- ```
343
-
344
- Output shows recent actions like:
345
-
346
- ```
347
- [12:34:56] ✓ global:my-extension: enabled → disabled
348
- [12:35:10] ✓ Installed pi-some-package@1.2.3
349
- [12:36:22] ✓ Updated pi-other-package → @2.0.0
350
- ```
351
-
352
- ### Viewing Statistics
353
-
354
- Get a summary of your extension management activity:
355
-
356
- ```
357
- /extensions stats
358
- ```
359
-
360
- Shows installed package count, total changes, and breakdown by action type.
361
-
362
- ## 🐛 Troubleshooting
363
-
364
- ### Commands not showing after install
365
-
366
- Make sure to reload Pi:
367
-
368
- ```
369
- /reload
66
+ /extensions install ./local-folder/
370
67
  ```
371
68
 
372
- ### Extension not appearing in list
373
-
374
- Check that the file has a `.ts` or `.js` extension and is in one of the discovery paths:
375
-
376
- - `~/.pi/agent/extensions/` (global)
377
- - `.pi/extensions/` (project)
69
+ ## Tips
378
70
 
379
- ### Package installation fails
380
-
381
- - Check npm is installed and accessible
382
- - For git installs, ensure git is available
383
- - Verify the package has the `pi-package` keyword for browsing
384
-
385
- ### Commands still work after removing package
386
-
387
- This is expected behavior. When you remove a package:
388
-
389
- 1. The files are deleted from disk
390
- 2. The extension remains loaded in pi's memory
391
- 3. Commands continue to work until you **restart pi**\n
392
- A full restart (Ctrl+C or `/quit`) is required to fully unload removed extensions. `/reload` is only sufficient for install/update operations.
393
-
394
- ### Back to manager closes everything
395
-
396
- Fixed! Pressing "Back to manager" now correctly returns to the unified view instead of closing.
397
-
398
- ## 🤝 Contributing
399
-
400
- Contributions welcome! Please ensure:
401
-
402
- 1. Run `pnpm run check` before committing
403
- 2. Husky pre-commit hooks will validate automatically
404
- 3. Follow existing code style
405
-
406
- ```bash
407
- # Setup
408
- git clone https://github.com/ayagmar/pi-extmgr.git
409
- cd pi-extmgr
410
- pnpm install
411
-
412
- # Development
413
- pnpm run typecheck # Type checking
414
- pnpm run lint # Linting
415
- pnpm run check # Full validation
416
-
417
- # Test in Pi
418
- pi install ./index.ts
419
- /reload
420
- ```
71
+ - **Staged changes**: Toggle extensions on/off, then press `S` to apply all at once. A `*` shows pending changes.
72
+ - **Two install modes**:
73
+ - **Managed** (npm): Auto-updates with `pi update`, stored in pi's package cache
74
+ - **Local** (standalone): Copies to `~/.pi/agent/extensions/{package}/`, supports multi-file extensions
75
+ - **Remove requires restart**: After removing a package, you need to fully restart Pi (not just `/reload`) for it to be completely unloaded.
421
76
 
422
- ## 📄 License
77
+ ## Keyboard shortcut
423
78
 
424
- MIT © [ayagmar](https://github.com/ayagmar)
79
+ Press `Ctrl+Shift+E` anywhere to open the extension manager.
425
80
 
426
- ---
81
+ ## License
427
82
 
428
- <p align="center">
429
- Made with ❤️ for the Pi community
430
- </p>
83
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-extmgr",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Enhanced UX for managing local Pi extensions and community packages",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -30,7 +30,7 @@
30
30
  "./src/index.ts"
31
31
  ],
32
32
  "video": "https://github.com/ayagmar/pi-extmgr/releases/download/v0.0.2/Screencast_20260207_013142.mp4",
33
- "image": "https://i.imgur.com/nP5rJPC.png"
33
+ "image": "https://i.imgur.com/tTD31v8.png"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@mariozechner/pi-coding-agent": "*",
package/src/ui/remote.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
5
5
  import type { BrowseAction, NpmPackage } from "../types/index.js";
6
6
  import { PAGE_SIZE } from "../constants.js";
7
- import { truncate } from "../utils/format.js";
7
+ import { truncate, dynamicTruncate } from "../utils/format.js";
8
8
  import {
9
9
  searchNpmPackages,
10
10
  getSearchCache,
@@ -119,9 +119,11 @@ export async function browseRemotePackages(
119
119
  : `Search: ${truncate(query, 40)}`;
120
120
 
121
121
  // Use simple select instead of custom component to avoid hanging issues
122
+ // Calculate reserved space: name (~25) + version (~10) + separator (3) = ~40 chars
123
+ const reservedSpace = 40;
122
124
  const selectItems: string[] = packages.map(
123
125
  (p) =>
124
- `${p.name}${p.version ? ` @${p.version}` : ""} - ${truncate(p.description || "No description", 50)}`
126
+ `${p.name}${p.version ? ` @${p.version}` : ""} - ${dynamicTruncate(p.description || "No description", reservedSpace)}`
125
127
  );
126
128
 
127
129
  // Add navigation options
package/src/ui/unified.ts CHANGED
@@ -21,7 +21,7 @@ import { showPackageActions } from "../packages/management.js";
21
21
  import { showRemote } from "./remote.js";
22
22
  import { showHelp } from "./help.js";
23
23
  import { discoverExtensions as discoverExt } from "../extensions/discovery.js";
24
- import { formatEntry as formatExtEntry, truncate } from "../utils/format.js";
24
+ import { formatEntry as formatExtEntry, dynamicTruncate } from "../utils/format.js";
25
25
  import {
26
26
  getStatusIcon,
27
27
  getPackageIcon,
@@ -291,8 +291,9 @@ function formatUnifiedItemLabel(
291
291
  const infoParts: string[] = [];
292
292
 
293
293
  // Show description if available
294
+ // Reserved space: icon (2) + scope (3) + name (~25) + version (~10) + separator (3) = ~43 chars
294
295
  if (item.description) {
295
- infoParts.push(truncate(item.description, 40));
296
+ infoParts.push(dynamicTruncate(item.description, 43));
296
297
  } else if (item.source?.startsWith("npm:")) {
297
298
  infoParts.push("npm");
298
299
  } else if (item.source?.startsWith("git:")) {
@@ -8,6 +8,36 @@ export function truncate(text: string, maxLength: number): string {
8
8
  return text.slice(0, maxLength - 3) + "...";
9
9
  }
10
10
 
11
+ /**
12
+ * Get the terminal width, with a minimum fallback
13
+ */
14
+ export function getTerminalWidth(minWidth = 80): number {
15
+ return Math.max(minWidth, process.stdout.columns || minWidth);
16
+ }
17
+
18
+ /**
19
+ * Calculate available space for description based on fixed-width elements
20
+ */
21
+ export function getDescriptionWidth(
22
+ totalWidth: number,
23
+ reservedSpace: number,
24
+ minDescWidth = 20
25
+ ): number {
26
+ return Math.max(minDescWidth, totalWidth - reservedSpace);
27
+ }
28
+
29
+ /**
30
+ * Dynamic truncate that adapts to available terminal width
31
+ * @param text - Text to truncate
32
+ * @param reservedSpace - Space taken by fixed elements (icons, name, version, etc.)
33
+ * @param minWidth - Minimum terminal width to consider
34
+ */
35
+ export function dynamicTruncate(text: string, reservedSpace: number, minWidth = 80): string {
36
+ const termWidth = getTerminalWidth(minWidth);
37
+ const maxDescWidth = getDescriptionWidth(termWidth, reservedSpace);
38
+ return truncate(text, maxDescWidth);
39
+ }
40
+
11
41
  export function formatEntry(entry: ExtensionEntry): string {
12
42
  const state = entry.state === "enabled" ? "on " : "off";
13
43
  const scope = entry.scope === "global" ? "G" : "P";