@danypops/pi-packed 0.13.2 → 0.14.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.
- package/README.md +1 -1
- package/extension/src/reload.ts +25 -0
- package/extension/src/resource-config.ts +2 -2
- package/extension/src/tui.ts +41 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The extension connects to Packed's authenticated user daemon and starts the pack
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
13
13
|
- `/packed` -- a floating overlay panel: every installed Pi package, with update availability. Mnemonics follow lazy.nvim's own convention (uppercase acts on every row, lowercase on the one under the cursor):
|
|
14
|
-
- `u` / `U` -- update the selected package / update every outdated package, one combined confirmation and one reload. `U`'s progress bar appears inline next to the package currently updating -- the list stays visible throughout, nothing swaps to a separate screen.
|
|
14
|
+
- `u` / `U` -- update the selected package / update every outdated package, one combined confirmation and one reload. `U`'s progress bar appears inline next to the package currently updating -- the list stays visible throughout, nothing swaps to a separate screen. A reload is confirmed separately from the update itself -- decline it to defer: the update already happened, only picking it up in this Pi session is deferred until `/reload`.
|
|
15
15
|
- `x` -- remove the selected package.
|
|
16
16
|
- `d` -- disable (or re-enable) the selected package's own extensions.
|
|
17
17
|
- `c` -- jump to resource config for the selected package (skills, prompts, themes).
|
package/extension/src/reload.ts
CHANGED
|
@@ -14,3 +14,28 @@ export function reloadWarning(operation: PackageOperation): string {
|
|
|
14
14
|
? "This will require a Pi reload (/reload) to deactivate it."
|
|
15
15
|
: "This will likely require a Pi reload (/reload) to activate its resources.";
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export interface ReloadConfirmContext {
|
|
19
|
+
hasUI: boolean;
|
|
20
|
+
ui: { confirm(title: string, message: string): Promise<boolean> };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The second, separate decision point: reloadWarning above is a still-just-
|
|
25
|
+
* likely warning shown before a mutation runs; this is asked only once the
|
|
26
|
+
* mutation has actually succeeded and a reload is now definitely needed,
|
|
27
|
+
* not merely predicted. Declining defers it -- the mutation itself already
|
|
28
|
+
* happened (the package really did install/update/toggle); only Pi's own
|
|
29
|
+
* currently-loaded resources are stale until /reload runs later. No-UI
|
|
30
|
+
* contexts reload immediately: there's no one to ask, and staying silently
|
|
31
|
+
* stale forever is worse than reloading.
|
|
32
|
+
*
|
|
33
|
+
* Extracted from resource-config.ts's own pre-existing pendingReload gate
|
|
34
|
+
* (the first surface to implement this pattern) so every mutation surface
|
|
35
|
+
* shares identical wording and behavior instead of drifting apart --
|
|
36
|
+
* reload.ts's whole reason for existing.
|
|
37
|
+
*/
|
|
38
|
+
export async function confirmReload(ctx: ReloadConfirmContext): Promise<boolean> {
|
|
39
|
+
if (!ctx.hasUI) return true;
|
|
40
|
+
return ctx.ui.confirm("Reload Pi now?", "This change only takes effect after a reload.");
|
|
41
|
+
}
|
|
@@ -14,6 +14,7 @@ import { Container, Input, Spacer, truncateToWidth, visibleWidth } from "@earend
|
|
|
14
14
|
import type { PackageResources, ResourceField } from "./packed.js";
|
|
15
15
|
import type { Natives } from "./packed.js";
|
|
16
16
|
import { packagePermissionDecision } from "./permission.js";
|
|
17
|
+
import { confirmReload } from "./reload.js";
|
|
17
18
|
|
|
18
19
|
type Scope = "global" | "project";
|
|
19
20
|
const RESOURCE_FIELDS = ["extensions", "skills", "prompts", "themes"] as const satisfies readonly ResourceField[];
|
|
@@ -132,8 +133,7 @@ export async function showResourceConfig(ctx: ExtensionCommandContext, natives:
|
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
if (!pendingReload) return;
|
|
135
|
-
|
|
136
|
-
if (confirmed) await ctx.reload();
|
|
136
|
+
if (await confirmReload(ctx)) await ctx.reload();
|
|
137
137
|
else ctx.ui.notify("Extension changes pending -- run /reload when ready.", "warning");
|
|
138
138
|
}
|
|
139
139
|
|
package/extension/src/tui.ts
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
* per-row selection styling baked into each cell since Table's own
|
|
20
20
|
* cellStyle is column-wide, not row-wide) inside this panel's own
|
|
21
21
|
* scroll-window slice -- Table deliberately owns no pagination of its
|
|
22
|
-
* own, so the visible-window-around-selectedIndex math stays here.
|
|
22
|
+
* own, so the visible-window-around-selectedIndex math stays here. Every
|
|
23
|
+
* mutation that actually changes something asks confirmReload separately
|
|
24
|
+
* from the earlier mutation-approval confirm -- declining defers the
|
|
25
|
+
* reload (the mutation itself already happened) and keeps the panel open
|
|
26
|
+
* with refreshed rows instead of ending the session. All
|
|
23
27
|
* data flows through the packed CLI (thin seam).
|
|
24
28
|
*/
|
|
25
29
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
@@ -34,6 +38,7 @@ import { showPackedSettings } from "./security-tui.js";
|
|
|
34
38
|
import { showResourceConfig, applyResourceToggle } from "./resource-config.js";
|
|
35
39
|
import { showDiscoverPanel } from "./discover.js";
|
|
36
40
|
import { menuTheme } from "./menu-theme.js";
|
|
41
|
+
import { confirmReload } from "./reload.js";
|
|
37
42
|
|
|
38
43
|
interface PanelAction {
|
|
39
44
|
type: "update" | "remove" | "disable" | "config" | "find" | "refresh" | "settings";
|
|
@@ -43,8 +48,11 @@ interface PanelAction {
|
|
|
43
48
|
/** Outcome of a confirmed row action, resolved after any real mutation and
|
|
44
49
|
* reload decision -- "changed" means the daemon state changed and Pi has
|
|
45
50
|
* already been reloaded (the caller should stop showing the stale panel);
|
|
51
|
+
* "deferred" means the mutation itself genuinely happened but the user
|
|
52
|
+
* declined confirmReload's separate reload gate -- Pi's session is still
|
|
53
|
+
* alive and the panel should refresh its rows and keep running, not close;
|
|
46
54
|
* "unchanged"/"cancelled" mean the panel keeps running as-is. */
|
|
47
|
-
export type PackageChoiceOutcome = "changed" | "unchanged" | "cancelled";
|
|
55
|
+
export type PackageChoiceOutcome = "changed" | "unchanged" | "cancelled" | "deferred";
|
|
48
56
|
|
|
49
57
|
export async function applyPackageChoice(
|
|
50
58
|
choice: string | undefined,
|
|
@@ -70,6 +78,10 @@ export async function applyPackageChoice(
|
|
|
70
78
|
return "unchanged";
|
|
71
79
|
}
|
|
72
80
|
const transition = outcome.previousVersion && outcome.currentVersion ? ` (${outcome.previousVersion} → ${outcome.currentVersion})` : "";
|
|
81
|
+
if (!(await confirmReload(ctx))) {
|
|
82
|
+
ctx.ui.notify(`Updated ${row.name}${transition}; reload pending -- run /reload when ready.`, "warning");
|
|
83
|
+
return "deferred";
|
|
84
|
+
}
|
|
73
85
|
ctx.ui.notify(`Updated ${row.name}${transition}; reloading Pi resources.`, "info");
|
|
74
86
|
await ctx.reload();
|
|
75
87
|
return "changed";
|
|
@@ -86,6 +98,10 @@ export async function applyPackageChoice(
|
|
|
86
98
|
return "cancelled";
|
|
87
99
|
}
|
|
88
100
|
await natives.remove(row.name, approval.approved);
|
|
101
|
+
if (!(await confirmReload(ctx))) {
|
|
102
|
+
ctx.ui.notify(`Removed ${row.name}; reload pending -- run /reload when ready.`, "warning");
|
|
103
|
+
return "deferred";
|
|
104
|
+
}
|
|
89
105
|
ctx.ui.notify(`Removed ${row.name}; reloading Pi resources.`, "info");
|
|
90
106
|
await ctx.reload();
|
|
91
107
|
return "changed";
|
|
@@ -156,7 +172,12 @@ async function approveAndRunUpdateAll(
|
|
|
156
172
|
ctx.ui.notify(failedNames.length > 0 ? `No packages updated; ${failedNames.length} failed.` : "All packages already up to date.", failedNames.length > 0 ? "warning" : "info");
|
|
157
173
|
return failedNames.length > 0 ? "cancelled" : "unchanged";
|
|
158
174
|
}
|
|
159
|
-
|
|
175
|
+
const failedSuffix = failedNames.length > 0 ? `, ${failedNames.length} failed` : "";
|
|
176
|
+
if (!(await confirmReload(ctx))) {
|
|
177
|
+
ctx.ui.notify(`Updated ${changed} package(s)${failedSuffix}; reload pending -- run /reload when ready.`, "warning");
|
|
178
|
+
return "deferred";
|
|
179
|
+
}
|
|
180
|
+
ctx.ui.notify(`Updated ${changed} package(s)${failedSuffix}; reloading Pi resources.`, "info");
|
|
160
181
|
await ctx.reload();
|
|
161
182
|
return "changed";
|
|
162
183
|
}
|
|
@@ -239,7 +260,12 @@ export async function applyDisableExtensions(row: Row, natives: Natives, ctx: Ex
|
|
|
239
260
|
else if (outcome === "cancelled") return toggled > 0 ? "changed" : "cancelled";
|
|
240
261
|
}
|
|
241
262
|
if (toggled === 0) return "unchanged";
|
|
242
|
-
|
|
263
|
+
const verb = disabling ? "Disabled" : "Enabled";
|
|
264
|
+
if (!(await confirmReload(ctx))) {
|
|
265
|
+
ctx.ui.notify(`${verb} ${toggled} extension(s) for ${row.name}; reload pending -- run /reload when ready.`, "warning");
|
|
266
|
+
return "deferred";
|
|
267
|
+
}
|
|
268
|
+
ctx.ui.notify(`${verb} ${toggled} extension(s) for ${row.name}; reloading Pi resources.`, "info");
|
|
243
269
|
await ctx.reload();
|
|
244
270
|
return "changed";
|
|
245
271
|
}
|
|
@@ -287,6 +313,14 @@ export async function showPackedPanel(ctx: ExtensionCommandContext, natives: Nat
|
|
|
287
313
|
return;
|
|
288
314
|
}
|
|
289
315
|
|
|
316
|
+
// A deferred reload means the mutation itself genuinely happened but
|
|
317
|
+
// ctx.reload() was declined -- the session is still alive, so refresh
|
|
318
|
+
// rows (real on-disk versions) and keep the panel open, same as "refresh".
|
|
319
|
+
async function refreshRows(): Promise<void> {
|
|
320
|
+
({ rows, error } = await loadRows(natives));
|
|
321
|
+
if (error) ctx.ui.notify(`refresh failed: ${error}`, "error");
|
|
322
|
+
}
|
|
323
|
+
|
|
290
324
|
// Panel loop: actions resolve the component, run outside it, then reopen.
|
|
291
325
|
// U/updateAll is handled entirely inside renderPanel itself (progress
|
|
292
326
|
// rendered on the same already-open overlay) and never reaches here.
|
|
@@ -295,8 +329,7 @@ export async function showPackedPanel(ctx: ExtensionCommandContext, natives: Nat
|
|
|
295
329
|
if (!action) return; // closed
|
|
296
330
|
|
|
297
331
|
if (action.type === "refresh") {
|
|
298
|
-
|
|
299
|
-
if (error) ctx.ui.notify(`refresh failed: ${error}`, "error");
|
|
332
|
+
await refreshRows();
|
|
300
333
|
continue;
|
|
301
334
|
}
|
|
302
335
|
|
|
@@ -321,11 +354,13 @@ export async function showPackedPanel(ctx: ExtensionCommandContext, natives: Nat
|
|
|
321
354
|
if (action.type === "disable") {
|
|
322
355
|
const outcome = await applyDisableExtensions(row, natives, ctx);
|
|
323
356
|
if (outcome === "changed") return;
|
|
357
|
+
if (outcome === "deferred") await refreshRows();
|
|
324
358
|
continue;
|
|
325
359
|
}
|
|
326
360
|
|
|
327
361
|
const outcome = await applyPackageChoice(action.type === "update" ? `Update to ${row.latest}` : "Remove", row, natives, ctx);
|
|
328
362
|
if (outcome === "changed") return; // ctx.reload() already replaced the session
|
|
363
|
+
if (outcome === "deferred") await refreshRows();
|
|
329
364
|
}
|
|
330
365
|
}
|
|
331
366
|
|