pi-extmgr 0.1.28 → 0.2.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 +18 -8
- package/package.json +12 -4
- package/src/commands/auto-update.ts +1 -1
- package/src/commands/history.ts +2 -31
- package/src/constants.ts +0 -8
- package/src/extensions/discovery.ts +121 -39
- package/src/index.ts +0 -4
- package/src/packages/discovery.ts +34 -0
- package/src/packages/extensions.ts +55 -98
- package/src/packages/install.ts +85 -56
- package/src/packages/management.ts +25 -38
- package/src/types/index.ts +4 -2
- package/src/ui/footer.ts +49 -29
- package/src/ui/help.ts +15 -11
- package/src/ui/remote.ts +704 -112
- package/src/ui/unified.ts +922 -311
- package/src/utils/auto-update.ts +34 -29
- package/src/utils/cache.ts +205 -34
- package/src/utils/duration.ts +132 -0
- package/src/utils/format.ts +0 -30
- package/src/utils/fs.ts +8 -4
- package/src/utils/history.ts +43 -7
- package/src/utils/mode.ts +1 -1
- package/src/utils/notify.ts +0 -14
- package/src/utils/package-source.ts +2 -5
- package/src/utils/path-identity.ts +7 -0
- package/src/utils/relative-path-selection.ts +100 -0
- package/src/utils/settings.ts +4 -63
- package/src/utils/retry.ts +0 -49
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { UI } from "../constants.js";
|
|
11
11
|
import { type InstalledPackage } from "../types/index.js";
|
|
12
12
|
import { runTaskWithLoader } from "../ui/async-task.js";
|
|
13
|
+
import { parseChoiceByLabel } from "../utils/command.js";
|
|
13
14
|
import { formatInstalledPackageLabel } from "../utils/format.js";
|
|
14
15
|
import { logPackageRemove, logPackageUpdate } from "../utils/history.js";
|
|
15
16
|
import { requireUI } from "../utils/mode.js";
|
|
@@ -34,17 +35,13 @@ export interface PackageMutationOutcome {
|
|
|
34
35
|
reloaded: boolean;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
const NO_PACKAGE_MUTATION_OUTCOME: PackageMutationOutcome = {
|
|
38
|
-
reloaded: false,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
38
|
const BULK_UPDATE_LABEL = "all packages";
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
39
|
+
const REMOVAL_SCOPE_CHOICES = {
|
|
40
|
+
both: "Both global + project",
|
|
41
|
+
global: "Global only",
|
|
42
|
+
project: "Project only",
|
|
43
|
+
cancel: "Cancel",
|
|
44
|
+
} as const;
|
|
48
45
|
|
|
49
46
|
function getProgressMessage(event: ProgressEvent, fallback: string): string {
|
|
50
47
|
return event.message?.trim() || fallback;
|
|
@@ -70,7 +67,7 @@ async function updatePackageInternal(
|
|
|
70
67
|
logPackageUpdate(pi, source, source, undefined, true);
|
|
71
68
|
clearUpdatesAvailable(pi, ctx, [updateIdentity]);
|
|
72
69
|
void updateExtmgrStatus(ctx, pi);
|
|
73
|
-
return
|
|
70
|
+
return { reloaded: false };
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
await runTaskWithLoader(
|
|
@@ -94,7 +91,7 @@ async function updatePackageInternal(
|
|
|
94
91
|
logPackageUpdate(pi, source, source, undefined, false, errorMsg);
|
|
95
92
|
notifyError(ctx, errorMsg);
|
|
96
93
|
void updateExtmgrStatus(ctx, pi);
|
|
97
|
-
return
|
|
94
|
+
return { reloaded: false };
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
logPackageUpdate(pi, source, source, undefined, true);
|
|
@@ -105,7 +102,7 @@ async function updatePackageInternal(
|
|
|
105
102
|
if (!reloaded) {
|
|
106
103
|
void updateExtmgrStatus(ctx, pi);
|
|
107
104
|
}
|
|
108
|
-
return
|
|
105
|
+
return { reloaded };
|
|
109
106
|
}
|
|
110
107
|
|
|
111
108
|
async function updatePackagesInternal(
|
|
@@ -121,7 +118,7 @@ async function updatePackagesInternal(
|
|
|
121
118
|
logPackageUpdate(pi, BULK_UPDATE_LABEL, BULK_UPDATE_LABEL, undefined, true);
|
|
122
119
|
clearUpdatesAvailable(pi, ctx);
|
|
123
120
|
void updateExtmgrStatus(ctx, pi);
|
|
124
|
-
return
|
|
121
|
+
return { reloaded: false };
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
await runTaskWithLoader(
|
|
@@ -145,7 +142,7 @@ async function updatePackagesInternal(
|
|
|
145
142
|
logPackageUpdate(pi, BULK_UPDATE_LABEL, BULK_UPDATE_LABEL, undefined, false, errorMsg);
|
|
146
143
|
notifyError(ctx, errorMsg);
|
|
147
144
|
void updateExtmgrStatus(ctx, pi);
|
|
148
|
-
return
|
|
145
|
+
return { reloaded: false };
|
|
149
146
|
}
|
|
150
147
|
|
|
151
148
|
logPackageUpdate(pi, BULK_UPDATE_LABEL, BULK_UPDATE_LABEL, undefined, true);
|
|
@@ -156,7 +153,7 @@ async function updatePackagesInternal(
|
|
|
156
153
|
if (!reloaded) {
|
|
157
154
|
void updateExtmgrStatus(ctx, pi);
|
|
158
155
|
}
|
|
159
|
-
return
|
|
156
|
+
return { reloaded };
|
|
160
157
|
}
|
|
161
158
|
|
|
162
159
|
export async function updatePackage(
|
|
@@ -230,25 +227,15 @@ interface RemovalTarget {
|
|
|
230
227
|
name: string;
|
|
231
228
|
}
|
|
232
229
|
|
|
233
|
-
function scopeChoiceFromLabel(choice: string | undefined): RemovalScopeChoice {
|
|
234
|
-
if (!choice || choice === "Cancel") return "cancel";
|
|
235
|
-
if (choice.includes("Both")) return "both";
|
|
236
|
-
if (choice.includes("Global")) return "global";
|
|
237
|
-
if (choice.includes("Project")) return "project";
|
|
238
|
-
return "cancel";
|
|
239
|
-
}
|
|
240
|
-
|
|
241
230
|
async function selectRemovalScope(ctx: ExtensionCommandContext): Promise<RemovalScopeChoice> {
|
|
242
231
|
if (!ctx.hasUI) return "global";
|
|
243
232
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return scopeChoiceFromLabel(choice);
|
|
233
|
+
return (
|
|
234
|
+
parseChoiceByLabel(
|
|
235
|
+
REMOVAL_SCOPE_CHOICES,
|
|
236
|
+
await ctx.ui.select("Remove scope", Object.values(REMOVAL_SCOPE_CHOICES))
|
|
237
|
+
) ?? "cancel"
|
|
238
|
+
);
|
|
252
239
|
}
|
|
253
240
|
|
|
254
241
|
function buildRemovalTargets(
|
|
@@ -374,18 +361,18 @@ async function removePackageInternal(
|
|
|
374
361
|
|
|
375
362
|
if (scopeChoice === "cancel") {
|
|
376
363
|
notify(ctx, "Removal cancelled.", "info");
|
|
377
|
-
return
|
|
364
|
+
return { reloaded: false };
|
|
378
365
|
}
|
|
379
366
|
|
|
380
367
|
if (matching.length === 0) {
|
|
381
368
|
notify(ctx, `${source} is not installed.`, "info");
|
|
382
|
-
return
|
|
369
|
+
return { reloaded: false };
|
|
383
370
|
}
|
|
384
371
|
|
|
385
372
|
const targets = buildRemovalTargets(matching, ctx.hasUI, scopeChoice);
|
|
386
373
|
if (targets.length === 0) {
|
|
387
374
|
notify(ctx, "Nothing to remove.", "info");
|
|
388
|
-
return
|
|
375
|
+
return { reloaded: false };
|
|
389
376
|
}
|
|
390
377
|
|
|
391
378
|
const confirmed = await confirmAction(
|
|
@@ -396,7 +383,7 @@ async function removePackageInternal(
|
|
|
396
383
|
);
|
|
397
384
|
if (!confirmed) {
|
|
398
385
|
notify(ctx, "Removal cancelled.", "info");
|
|
399
|
-
return
|
|
386
|
+
return { reloaded: false };
|
|
400
387
|
}
|
|
401
388
|
|
|
402
389
|
const results = await executeRemovalTargets(targets, ctx, pi);
|
|
@@ -424,7 +411,7 @@ async function removePackageInternal(
|
|
|
424
411
|
|
|
425
412
|
if (successfulRemovalCount === 0) {
|
|
426
413
|
void updateExtmgrStatus(ctx, pi);
|
|
427
|
-
return
|
|
414
|
+
return { reloaded: false };
|
|
428
415
|
}
|
|
429
416
|
|
|
430
417
|
const reloaded = await confirmReload(ctx, "Removal complete.");
|
|
@@ -432,7 +419,7 @@ async function removePackageInternal(
|
|
|
432
419
|
void updateExtmgrStatus(ctx, pi);
|
|
433
420
|
}
|
|
434
421
|
|
|
435
|
-
return
|
|
422
|
+
return { reloaded };
|
|
436
423
|
}
|
|
437
424
|
|
|
438
425
|
export async function removePackage(
|
package/src/types/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface NpmPackage {
|
|
|
19
19
|
name: string;
|
|
20
20
|
version?: string | undefined;
|
|
21
21
|
description?: string | undefined;
|
|
22
|
+
author?: string | undefined;
|
|
22
23
|
keywords?: string[] | undefined;
|
|
23
24
|
date?: string | undefined;
|
|
24
25
|
size?: number | undefined; // Package size in bytes
|
|
@@ -64,6 +65,7 @@ export interface PackageUnifiedItem {
|
|
|
64
65
|
displayName: string;
|
|
65
66
|
scope: Scope;
|
|
66
67
|
source: string;
|
|
68
|
+
resolvedPath?: string | undefined;
|
|
67
69
|
version?: string | undefined;
|
|
68
70
|
description?: string | undefined;
|
|
69
71
|
size?: number | undefined; // Package size in bytes
|
|
@@ -97,7 +99,7 @@ export type BrowseAction =
|
|
|
97
99
|
| { type: "prev" }
|
|
98
100
|
| { type: "next" }
|
|
99
101
|
| { type: "refresh" }
|
|
102
|
+
| { type: "search"; query: string }
|
|
103
|
+
| { type: "install" }
|
|
100
104
|
| { type: "menu" }
|
|
101
|
-
| { type: "main" }
|
|
102
|
-
| { type: "help" }
|
|
103
105
|
| { type: "cancel" };
|
package/src/ui/footer.ts
CHANGED
|
@@ -4,18 +4,24 @@
|
|
|
4
4
|
import { type State, type UnifiedItem } from "../types/index.js";
|
|
5
5
|
|
|
6
6
|
export interface FooterState {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
selectedType?: UnifiedItem["type"];
|
|
8
|
+
pendingChanges: number;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
export function buildFooterState(
|
|
12
|
+
staged: Map<string, State>,
|
|
13
|
+
byId: Map<string, UnifiedItem>,
|
|
14
|
+
selectedItem?: UnifiedItem
|
|
15
|
+
): FooterState {
|
|
16
|
+
const state: FooterState = {
|
|
17
|
+
pendingChanges: getPendingToggleChangeCount(staged, byId),
|
|
18
18
|
};
|
|
19
|
+
|
|
20
|
+
if (selectedItem) {
|
|
21
|
+
state.selectedType = selectedItem.type;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return state;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
export function getPendingToggleChangeCount(
|
|
@@ -37,27 +43,41 @@ export function getPendingToggleChangeCount(
|
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
/**
|
|
40
|
-
* Build keyboard shortcuts text for the footer.
|
|
46
|
+
* Build contextual keyboard shortcuts text for the footer.
|
|
41
47
|
*/
|
|
42
48
|
export function buildFooterShortcuts(state: FooterState): string {
|
|
43
49
|
const parts: string[] = [];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
|
|
51
|
+
if (state.selectedType === "local") {
|
|
52
|
+
parts.push("Space toggle");
|
|
53
|
+
parts.push("Enter/A actions");
|
|
54
|
+
parts.push("V details");
|
|
55
|
+
parts.push("X remove");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (state.selectedType === "package") {
|
|
59
|
+
parts.push("Enter/A actions");
|
|
60
|
+
parts.push("V details");
|
|
61
|
+
parts.push("c configure");
|
|
62
|
+
parts.push("u update");
|
|
63
|
+
parts.push("X remove");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (state.pendingChanges > 0) {
|
|
67
|
+
parts.push(`S save (${state.pendingChanges})`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
parts.push("/ search");
|
|
71
|
+
parts.push("Tab filters");
|
|
72
|
+
parts.push("1-5 filters");
|
|
73
|
+
parts.push("i install");
|
|
74
|
+
parts.push("f remote search");
|
|
75
|
+
parts.push("U update all");
|
|
76
|
+
parts.push("t auto-update");
|
|
77
|
+
parts.push("P palette");
|
|
78
|
+
parts.push("R browse");
|
|
79
|
+
parts.push("? help");
|
|
80
|
+
parts.push("Esc clear/cancel");
|
|
81
|
+
|
|
82
|
+
return parts.join(" · ");
|
|
63
83
|
}
|
package/src/ui/help.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Help display
|
|
3
3
|
*/
|
|
4
4
|
import { type ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { notify } from "../utils/notify.js";
|
|
5
6
|
|
|
6
7
|
export function showHelp(ctx: ExtensionCommandContext): void {
|
|
7
8
|
const lines = [
|
|
@@ -9,25 +10,33 @@ export function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
9
10
|
"",
|
|
10
11
|
"Unified View:",
|
|
11
12
|
" Local extensions and npm/git packages are displayed together",
|
|
13
|
+
" The list is grouped into Local extensions and Installed packages sections",
|
|
14
|
+
" Rows stay compact; details for the selected item appear below the list",
|
|
12
15
|
" Local extensions show ● enabled / ○ disabled with G/P scope",
|
|
13
|
-
" Packages show
|
|
16
|
+
" Packages show a source-type icon with name@version, scope, and size when known",
|
|
14
17
|
"",
|
|
15
18
|
"Navigation:",
|
|
16
19
|
" ↑↓ Navigate list",
|
|
17
|
-
"
|
|
20
|
+
" PageUp/Down Jump through longer lists",
|
|
21
|
+
" Home/End Jump to top or bottom",
|
|
22
|
+
" Space Toggle selected local extension enabled/disabled",
|
|
18
23
|
" S Save changes to local extensions",
|
|
19
|
-
" Enter/A Open actions for selected
|
|
24
|
+
" Enter/A Open actions for the selected item",
|
|
25
|
+
" / or Ctrl+F Search visible items",
|
|
26
|
+
" Tab/Shift+Tab Cycle filters",
|
|
27
|
+
" 1-5 Quick filters: All / Local / Packages / Updates / Disabled",
|
|
20
28
|
" c Configure selected package extensions (reload after save)",
|
|
21
29
|
" u Update selected package",
|
|
30
|
+
" V View full details for the selected item",
|
|
22
31
|
" X Remove selected item (package or local extension)",
|
|
23
32
|
" i Quick install by source",
|
|
24
|
-
" f
|
|
33
|
+
" f Remote package search",
|
|
25
34
|
" U Update all packages",
|
|
26
35
|
" t Auto-update wizard",
|
|
27
36
|
" P/M Quick actions palette",
|
|
28
37
|
" R Browse remote packages",
|
|
29
38
|
" ?/H Show this help",
|
|
30
|
-
" Esc
|
|
39
|
+
" Esc Clear search or cancel",
|
|
31
40
|
"",
|
|
32
41
|
"Extension Sources:",
|
|
33
42
|
" - ~/.pi/agent/extensions/ (global - G)",
|
|
@@ -49,10 +58,5 @@ export function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
49
58
|
" /extensions auto-update Show or change update schedule",
|
|
50
59
|
];
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
if (ctx.hasUI) {
|
|
54
|
-
ctx.ui.notify(output, "info");
|
|
55
|
-
} else {
|
|
56
|
-
console.log(output);
|
|
57
|
-
}
|
|
61
|
+
notify(ctx, lines.join("\n"), "info");
|
|
58
62
|
}
|