@nklisch/pi-enhanced 0.3.1 → 0.4.2
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/CHANGELOG.md +10 -0
- package/README.md +1 -0
- package/node_modules/@nklisch/pi-astral-pocket/LICENSE +3 -0
- package/node_modules/@nklisch/pi-astral-pocket/README.md +138 -0
- package/node_modules/@nklisch/pi-astral-pocket/package.json +54 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/activation.ts +44 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/config.ts +95 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/controller.ts +78 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/distiller.ts +271 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/guidance.ts +66 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/index.ts +222 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/provider.ts +128 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/scope.ts +33 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/sessions.ts +237 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/store.ts +427 -0
- package/node_modules/@nklisch/pi-astral-pocket/src/tools.ts +142 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-arm64.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-x64.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-arm64-gnu.node +0 -0
- package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
- package/node_modules/@nklisch/pi-conveniences/extensions/agents-context.ts +4 -6
- package/node_modules/@nklisch/pi-conveniences/extensions/context-window-footer.ts +38 -22
- package/node_modules/@nklisch/pi-conveniences/package.json +1 -1
- package/node_modules/@nklisch/pi-plugins/README.md +12 -6
- package/node_modules/@nklisch/pi-plugins/dist/catalog.js +11 -0
- package/node_modules/@nklisch/pi-plugins/dist/catalog.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/host.js +85 -29
- package/node_modules/@nklisch/pi-plugins/dist/host.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-layout.d.ts +4 -0
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-layout.js +24 -0
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-layout.js.map +1 -0
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.d.ts +6 -0
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.js +130 -47
- package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/plugin-metadata.d.ts +9 -0
- package/node_modules/@nklisch/pi-plugins/dist/plugin-metadata.js +37 -0
- package/node_modules/@nklisch/pi-plugins/dist/plugin-metadata.js.map +1 -0
- package/node_modules/@nklisch/pi-plugins/dist/runtime-discovery.js +2 -0
- package/node_modules/@nklisch/pi-plugins/dist/runtime-discovery.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/dist/types.d.ts +4 -1
- package/node_modules/@nklisch/pi-plugins/dist/types.js.map +1 -1
- package/node_modules/@nklisch/pi-plugins/package.json +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import type { ActivationState } from "./activation.js";
|
|
7
|
+
import { resolveProjectIdentity } from "./scope.js";
|
|
8
|
+
import { searchAstraSessions } from "./sessions.js";
|
|
9
|
+
import { readSummaryCapped, searchPocket, writeNote } from "./store.js";
|
|
10
|
+
|
|
11
|
+
const INACTIVE_MESSAGE =
|
|
12
|
+
"Pocket tools are only active in gpt-6-astra sessions with the pocket enabled (/pocket on).";
|
|
13
|
+
const DEFAULT_RECALL_LIMIT = 10;
|
|
14
|
+
export const MAX_RECALL_LIMIT_PER_SOURCE = 20;
|
|
15
|
+
|
|
16
|
+
function normalizeRecallLimit(value: unknown): number {
|
|
17
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_RECALL_LIMIT;
|
|
18
|
+
return Math.min(MAX_RECALL_LIMIT_PER_SOURCE, Math.max(1, Math.trunc(value)));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ToolDeps {
|
|
22
|
+
state: ActivationState;
|
|
23
|
+
/** Pocket root directory (~/.pi/agent/astral-pocket). */
|
|
24
|
+
root: string;
|
|
25
|
+
/** Sessions directory (~/.pi/agent/sessions). */
|
|
26
|
+
sessionsDir: string;
|
|
27
|
+
maxSessionAgeDays: () => number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function textResult(text: string) {
|
|
31
|
+
return { content: [{ type: "text" as const, text }], details: {} };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function registerPocketTools(pi: ExtensionAPI, deps: ToolDeps): void {
|
|
35
|
+
pi.registerTool({
|
|
36
|
+
name: "pocket_note",
|
|
37
|
+
label: "Pocket Note",
|
|
38
|
+
description:
|
|
39
|
+
"Write a durable note to your persistent pocket. Notes default to the current repository. Use global scope only for explicitly portable preferences or observations — never for secrets or ephemeral task state.",
|
|
40
|
+
promptSnippet: "Save a durable cross-session note to the astral pocket",
|
|
41
|
+
promptGuidelines: [
|
|
42
|
+
"Use pocket_note when you learn something durable (a decision and why, a project convention, a pitfall, a user preference) — not for ephemeral task state.",
|
|
43
|
+
"Never put secrets, credentials, tokens, or personal data in pocket notes.",
|
|
44
|
+
"Keep pocket_note project-scoped by default; use global scope only for a clearly general preference or conditional portable observation.",
|
|
45
|
+
],
|
|
46
|
+
parameters: Type.Object({
|
|
47
|
+
title: Type.String({ description: "Short note title" }),
|
|
48
|
+
body: Type.String({ description: "Note content — a few sentences is enough" }),
|
|
49
|
+
keywords: Type.Optional(Type.Array(Type.String(), { description: "2-5 recall keywords" })),
|
|
50
|
+
scope: Type.Optional(Type.Union([Type.Literal("project"), Type.Literal("global")], {
|
|
51
|
+
description: "Project by default. Use global only for explicitly portable preferences or observations.",
|
|
52
|
+
})),
|
|
53
|
+
}),
|
|
54
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
55
|
+
if (!deps.state.active) throw new Error(INACTIVE_MESSAGE);
|
|
56
|
+
if (signal?.aborted) throw new Error("Pocket note cancelled");
|
|
57
|
+
const project = ctx.cwd;
|
|
58
|
+
const scope = params.scope ?? "project";
|
|
59
|
+
const projectId = resolveProjectIdentity(project);
|
|
60
|
+
const fileName = await withFileMutationQueue(join(deps.root, "POCKET.md"), async () => {
|
|
61
|
+
if (signal?.aborted || !deps.state.active) throw new Error("Pocket note cancelled");
|
|
62
|
+
return writeNote(deps.root, {
|
|
63
|
+
title: params.title,
|
|
64
|
+
body: params.body,
|
|
65
|
+
keywords: params.keywords,
|
|
66
|
+
project,
|
|
67
|
+
projectId: scope === "project" ? projectId : undefined,
|
|
68
|
+
scope,
|
|
69
|
+
source: "agent",
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
return textResult(`Note saved to the pocket: notes/${fileName}`);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
pi.registerTool({
|
|
77
|
+
name: "pocket_recall",
|
|
78
|
+
label: "Pocket Recall",
|
|
79
|
+
description:
|
|
80
|
+
"Search current-repository and explicit global pocket notes plus current-repository Astra sessions. Summarized by default; pass full: true for larger excerpts or scope: all for intentional cross-repository precedent.",
|
|
81
|
+
promptSnippet: "Search pocket notes and past astra sessions",
|
|
82
|
+
promptGuidelines: [
|
|
83
|
+
"Use pocket_recall for the quick pocket pass: search with keywords from the pocket summary before deep repo exploration.",
|
|
84
|
+
"Keep recall cheap: at most 4-6 lookup steps, summarized results first, full: true only when you need exact commands or error text.",
|
|
85
|
+
],
|
|
86
|
+
parameters: Type.Object({
|
|
87
|
+
query: Type.String({ description: "Keywords to search for (all must match)" }),
|
|
88
|
+
source: Type.Optional(
|
|
89
|
+
Type.Union([Type.Literal("pocket"), Type.Literal("sessions"), Type.Literal("both")], {
|
|
90
|
+
description: "Where to search (default: both)",
|
|
91
|
+
}),
|
|
92
|
+
),
|
|
93
|
+
full: Type.Optional(Type.Boolean({ description: "Return larger excerpts (default: false)" })),
|
|
94
|
+
limit: Type.Optional(Type.Integer({
|
|
95
|
+
minimum: 1,
|
|
96
|
+
maximum: MAX_RECALL_LIMIT_PER_SOURCE,
|
|
97
|
+
description: "Max hits from each source (default: 10, maximum: 20)",
|
|
98
|
+
})),
|
|
99
|
+
scope: Type.Optional(Type.Union([Type.Literal("current"), Type.Literal("all")], {
|
|
100
|
+
description: "Current repository plus global notes by default; all includes foreign repositories as precedent.",
|
|
101
|
+
})),
|
|
102
|
+
}),
|
|
103
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
104
|
+
if (!deps.state.active) throw new Error(INACTIVE_MESSAGE);
|
|
105
|
+
const source = params.source ?? "both";
|
|
106
|
+
const limit = normalizeRecallLimit(params.limit);
|
|
107
|
+
const recallScope = params.scope ?? "current";
|
|
108
|
+
const projectId = resolveProjectIdentity(ctx.cwd);
|
|
109
|
+
const sections: string[] = [];
|
|
110
|
+
|
|
111
|
+
if (source === "pocket" || source === "both") {
|
|
112
|
+
const hits = searchPocket(deps.root, params.query, projectId, limit, params.full, recallScope);
|
|
113
|
+
sections.push(
|
|
114
|
+
hits.length === 0
|
|
115
|
+
? "Pocket notes: no matches."
|
|
116
|
+
: `Pocket notes (${hits.length}):\n${hits
|
|
117
|
+
.map((h) => `- ${h.title} [notes/${h.noteFile}]${h.project ? ` (${h.project})` : ""} · scope: ${h.scope}${recallScope === "all" && h.scope === "project" && resolveProjectIdentity(h.project) !== projectId ? " · cross-repository precedent" : ""}${h.source ? ` · ${h.source}` : ""}${h.date ? ` · ${h.date}` : ""}\n ${h.excerpt}`)
|
|
118
|
+
.join("\n")}`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (source === "sessions" || source === "both") {
|
|
123
|
+
const hits = await searchAstraSessions(deps.sessionsDir, params.query, {
|
|
124
|
+
full: params.full,
|
|
125
|
+
limit,
|
|
126
|
+
maxAgeDays: deps.maxSessionAgeDays(),
|
|
127
|
+
projectId,
|
|
128
|
+
recallScope,
|
|
129
|
+
});
|
|
130
|
+
sections.push(
|
|
131
|
+
hits.length === 0
|
|
132
|
+
? "Past astra sessions: no matches."
|
|
133
|
+
: `Past astra sessions (${hits.length}):\n${hits
|
|
134
|
+
.map((h) => `- [${h.kind}] ${h.timestamp} (${h.project})${recallScope === "all" && resolveProjectIdentity(h.project) !== projectId ? " · cross-repository precedent" : ""}\n ${h.excerpt}`)
|
|
135
|
+
.join("\n")}`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return textResult(sections.join("\n\n"));
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -38,16 +38,15 @@ export const RELATIVE_PATH = ".agents/AGENTS.md";
|
|
|
38
38
|
/** The exact opening tag emitted — also the idempotency sentinel. */
|
|
39
39
|
export const OPEN_TAG = `<project_instructions path="${RELATIVE_PATH}">`;
|
|
40
40
|
|
|
41
|
-
type SystemPromptOptions = { cwd?: string };
|
|
42
41
|
type BeforeAgentStartEvent = {
|
|
43
42
|
systemPrompt?: string;
|
|
44
|
-
systemPromptOptions?: SystemPromptOptions;
|
|
45
43
|
};
|
|
46
44
|
type PiApi = {
|
|
47
45
|
on?: (
|
|
48
46
|
event: "before_agent_start",
|
|
49
47
|
handler: (
|
|
50
48
|
event: BeforeAgentStartEvent,
|
|
49
|
+
ctx?: { cwd?: string },
|
|
51
50
|
) => { systemPrompt: string } | undefined | void,
|
|
52
51
|
) => void;
|
|
53
52
|
};
|
|
@@ -80,10 +79,9 @@ function readAgentsContext(cwd: string): string | null {
|
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
export default function agentsContextExtension(pi: PiApi): void {
|
|
83
|
-
pi.on?.("before_agent_start", (event) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
);
|
|
82
|
+
pi.on?.("before_agent_start", (event, ctx) => {
|
|
83
|
+
// Pi supplies the active workspace on the event context, not the event.
|
|
84
|
+
const content = readAgentsContext(ctx?.cwd ?? process.cwd());
|
|
87
85
|
if (!content) return;
|
|
88
86
|
const base = event?.systemPrompt ?? "";
|
|
89
87
|
// Idempotency / double-registration guard. The prompt is rebuilt from base
|
|
@@ -10,7 +10,7 @@ const BAR_SEGMENTS = 8;
|
|
|
10
10
|
const FOOTER_REAPPLY_DELAYS_MS = [0, 50, 250, 1000] as const;
|
|
11
11
|
const CATPPUCCIN_STATUS_ID = "catppuccin-tui";
|
|
12
12
|
const MODE_STATUS_IDS = new Set(["mode", "pi-model-modes"]);
|
|
13
|
-
const CODEX_STATUS_ID = "codex-
|
|
13
|
+
const CODEX_STATUS_ID = "codex-pool";
|
|
14
14
|
const SHOULD_DISABLE_CATPPUCCIN_PACKAGE_FOOTER =
|
|
15
15
|
process.env.PI_CONVENIENCES_DISABLE_CATPPUCCIN_FOOTER !== "0";
|
|
16
16
|
|
|
@@ -227,9 +227,8 @@ function formatFooterLine(width: number, left: string, right: string): string {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
function statusPriority(key: string): number {
|
|
230
|
-
if (key
|
|
231
|
-
|
|
232
|
-
return 2;
|
|
230
|
+
if (MODE_STATUS_IDS.has(key)) return 0;
|
|
231
|
+
return 1;
|
|
233
232
|
}
|
|
234
233
|
|
|
235
234
|
function formatStatusValue(key: string, rawValue: string): string | undefined {
|
|
@@ -244,6 +243,7 @@ function formatExtensionStatuses(
|
|
|
244
243
|
maxItems = 2,
|
|
245
244
|
): string | undefined {
|
|
246
245
|
const values = [...statuses.entries()]
|
|
246
|
+
.filter(([key]) => key !== CODEX_STATUS_ID)
|
|
247
247
|
.sort(([left], [right]) => statusPriority(left) - statusPriority(right) || left.localeCompare(right))
|
|
248
248
|
.map(([key, value]) => formatStatusValue(key, value))
|
|
249
249
|
.filter((value): value is string => value !== undefined)
|
|
@@ -258,6 +258,12 @@ function formatExtensionStatusByKey(statuses: ReadonlyMap<string, string>, key:
|
|
|
258
258
|
return value === undefined ? undefined : formatStatusValue(key, value);
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
function formatCompactCodexStatus(value: string | undefined): string | undefined {
|
|
262
|
+
if (!value) return undefined;
|
|
263
|
+
const match = /^codex (.+) · 5h (\S+) · 7d (\S+)$/.exec(value);
|
|
264
|
+
return match ? `codex ${match[1]} ${match[2]}/${match[3]}` : value;
|
|
265
|
+
}
|
|
266
|
+
|
|
261
267
|
function installFooter(ctx: ExtensionContext, pi: ExtensionAPI): void {
|
|
262
268
|
if (!ctx.hasUI || !enabled) return;
|
|
263
269
|
|
|
@@ -271,16 +277,17 @@ function installFooter(ctx: ExtensionContext, pi: ExtensionAPI): void {
|
|
|
271
277
|
try {
|
|
272
278
|
if (width <= 0) return [""];
|
|
273
279
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
const model = formatModelName(ctx.model?.id);
|
|
281
|
+
const modelLabel = formatModelLabel(theme, model, pi.getThinkingLevel());
|
|
282
|
+
const branch = footerData.getGitBranch();
|
|
283
|
+
const branchLabel = branch ? `git ${branch}` : "no git";
|
|
284
|
+
const folderLabel = formatFolderLabel(theme, ctx.cwd);
|
|
285
|
+
const snapshot = getContextSnapshot(ctx);
|
|
286
|
+
const extensionStatuses = footerData.getExtensionStatuses();
|
|
287
|
+
const otherStatuses = formatExtensionStatuses(theme, extensionStatuses);
|
|
288
|
+
const primaryOtherStatus = formatExtensionStatuses(theme, extensionStatuses, 1);
|
|
289
|
+
const codexStatus = formatExtensionStatusByKey(extensionStatuses, CODEX_STATUS_ID);
|
|
290
|
+
const codexCompact = formatCompactCodexStatus(codexStatus);
|
|
284
291
|
|
|
285
292
|
const leftFullSegments = [theme.fg("accent", "◆"), modelLabel];
|
|
286
293
|
if (folderLabel) leftFullSegments.push(theme.fg("dim", "•"), folderLabel);
|
|
@@ -290,21 +297,23 @@ function installFooter(ctx: ExtensionContext, pi: ExtensionAPI): void {
|
|
|
290
297
|
);
|
|
291
298
|
const leftFull = leftFullSegments.join(" ");
|
|
292
299
|
|
|
293
|
-
const rightFull = [formatContextFull(theme, snapshot)
|
|
300
|
+
const rightFull = [otherStatuses, codexStatus, formatContextFull(theme, snapshot)]
|
|
294
301
|
.filter(Boolean)
|
|
295
302
|
.join(` ${theme.fg("dim", "•")} `);
|
|
296
303
|
if (footerFits(width, leftFull, rightFull)) {
|
|
297
304
|
return [formatFooterLine(width, leftFull, rightFull)];
|
|
298
305
|
}
|
|
299
306
|
|
|
300
|
-
const rightMediumWithStatus = [formatContextMedium(theme, snapshot)
|
|
307
|
+
const rightMediumWithStatus = [otherStatuses, codexStatus, formatContextMedium(theme, snapshot)]
|
|
301
308
|
.filter(Boolean)
|
|
302
309
|
.join(` ${theme.fg("dim", "•")} `);
|
|
303
310
|
if (footerFits(width, leftFull, rightMediumWithStatus)) {
|
|
304
311
|
return [formatFooterLine(width, leftFull, rightMediumWithStatus)];
|
|
305
312
|
}
|
|
306
313
|
|
|
307
|
-
const rightMedium = formatContextMedium(theme, snapshot)
|
|
314
|
+
const rightMedium = [codexStatus, formatContextMedium(theme, snapshot)]
|
|
315
|
+
.filter(Boolean)
|
|
316
|
+
.join(` ${theme.fg("dim", "•")} `);
|
|
308
317
|
if (footerFits(width, leftFull, rightMedium)) {
|
|
309
318
|
return [formatFooterLine(width, leftFull, rightMedium)];
|
|
310
319
|
}
|
|
@@ -317,15 +326,22 @@ function installFooter(ctx: ExtensionContext, pi: ExtensionAPI): void {
|
|
|
317
326
|
);
|
|
318
327
|
const leftCompact = leftCompactSegments.join(" ");
|
|
319
328
|
const rightCompact = formatContextCompact(theme, snapshot);
|
|
320
|
-
const
|
|
329
|
+
const rightCompactWithStatuses = [primaryOtherStatus, codexCompact, rightCompact]
|
|
330
|
+
.filter(Boolean)
|
|
331
|
+
.join(` ${theme.fg("dim", "•")} `);
|
|
332
|
+
if (footerFits(width, leftCompact, rightCompactWithStatuses)) {
|
|
333
|
+
return [formatFooterLine(width, leftCompact, rightCompactWithStatuses)];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const rightCompactWithCodex = [codexCompact, rightCompact]
|
|
321
337
|
.filter(Boolean)
|
|
322
338
|
.join(` ${theme.fg("dim", "•")} `);
|
|
323
|
-
if (footerFits(width, leftCompact,
|
|
324
|
-
return [formatFooterLine(width, leftCompact,
|
|
339
|
+
if (footerFits(width, leftCompact, rightCompactWithCodex)) {
|
|
340
|
+
return [formatFooterLine(width, leftCompact, rightCompactWithCodex)];
|
|
325
341
|
}
|
|
326
342
|
|
|
327
|
-
if (
|
|
328
|
-
return [formatFooterLine(width, leftCompact,
|
|
343
|
+
if (codexCompact && footerFits(width, leftCompact, codexCompact)) {
|
|
344
|
+
return [formatFooterLine(width, leftCompact, codexCompact)];
|
|
329
345
|
}
|
|
330
346
|
|
|
331
347
|
if (footerFits(width, leftCompact, rightCompact)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nklisch/pi-conveniences",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Small drop-in quality-of-life conveniences for Pi — /exit, .agents/AGENTS.md context loading, context-window footer, and subagent model listing.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "nklisch"
|
|
@@ -15,7 +15,9 @@ Run `/plugins` with no arguments to open the keyboard-first manager. It provides
|
|
|
15
15
|
Installed, Discover, Marketplaces, and Issues views; search and plugin details;
|
|
16
16
|
and explicit multi-select batches. `Ctrl+F` focuses search, Space selects rows,
|
|
17
17
|
`a` selects all filtered rows, Enter opens details, `r` checks marketplaces,
|
|
18
|
-
and the footer shows the contextual action keys.
|
|
18
|
+
and the footer shows the contextual action keys. A theme-native frame separates
|
|
19
|
+
the manager from the transcript; Page Up/Down scrolls long views while arrow-key
|
|
20
|
+
navigation keeps the selected row visible.
|
|
19
21
|
|
|
20
22
|
The manager opens from local files. Marketplace checks run asynchronously with
|
|
21
23
|
bounded concurrency and timeouts, remain cancellable, and never hide the
|
|
@@ -49,10 +51,13 @@ The `.auto-update` marker is both the selection and the standing authorization
|
|
|
49
51
|
to replace that plugin's executable content.
|
|
50
52
|
|
|
51
53
|
Before plugin activation, Pi refreshes each affected marketplace once with a
|
|
52
|
-
bounded timeout. A marked plugin updates only when the
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
bounded timeout. A marked plugin updates only when the candidate bundle version
|
|
55
|
+
differs from its installed version. Native manifests supply versions even when
|
|
56
|
+
the marketplace omits them; catalog entries and installed receipts are fallbacks,
|
|
57
|
+
not replacements for a bundle’s own version. Remote candidates are checked with
|
|
58
|
+
bounded acquisition and installed from that same copy when needed. Missing
|
|
59
|
+
versions, offline sources, and item failures leave the installed copy unchanged
|
|
60
|
+
and do not block startup.
|
|
56
61
|
|
|
57
62
|
`/plugins update-marked` is the explicit escape path. It refreshes affected
|
|
58
63
|
marketplaces, force-updates every marked plugin—including unversioned entries—
|
|
@@ -74,7 +79,8 @@ The host stores no database or lifecycle ledger. Its durable layout is:
|
|
|
74
79
|
A plugin directory is installed; `.disabled` means disabled. The receipt is
|
|
75
80
|
descriptive only. `.check-on-open` stores the manager's optional refresh
|
|
76
81
|
preference; cursor, selection, progress, results, and errors are never stored.
|
|
77
|
-
Refresh and install/update stage a sibling directory and rename it into place
|
|
82
|
+
Refresh and install/update stage a sibling directory and rename it into place,
|
|
83
|
+
restoring the prior copy if the final rename fails.
|
|
78
84
|
Persistent plugin data is not replaced by an update and is retained by removal
|
|
79
85
|
unless `--delete-data` is supplied.
|
|
80
86
|
|
|
@@ -113,6 +113,17 @@ export function mergeMarketplaceCatalogs(documents) {
|
|
|
113
113
|
message: `duplicate plugin declaration conflicts with ${first.name}; using the first declaration`,
|
|
114
114
|
});
|
|
115
115
|
}
|
|
116
|
+
else {
|
|
117
|
+
// Native catalogs often split presentation metadata. Keep precedence
|
|
118
|
+
// for declared values, but do not discard a sibling's only version.
|
|
119
|
+
const version = previous.version ?? plugin.version;
|
|
120
|
+
const description = previous.description ?? plugin.description;
|
|
121
|
+
plugins.set(plugin.name, Object.freeze({
|
|
122
|
+
...previous,
|
|
123
|
+
...(version === undefined ? {} : { version }),
|
|
124
|
+
...(description === undefined ? {} : { description }),
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
116
127
|
}
|
|
117
128
|
}
|
|
118
129
|
return Object.freeze({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { chmod, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, writeFile, lstat } from "node:fs/promises";
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
|
-
import { join, resolve } from "node:path";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { mergeMarketplaceCatalogs, readMarketplaceCatalog } from "./catalog.js";
|
|
6
6
|
import { assertNoSymlinks, assertSafeName, assertSafeRelativePath, createPluginHostPaths, resolveContainedExistingPath, resolveContainedPath, } from "./paths.js";
|
|
7
7
|
import { buildMcpConfig } from "./mcp.js";
|
|
8
8
|
import { scanInstalledPlugins } from "./runtime-discovery.js";
|
|
9
|
+
import { installedPluginVersion, readPluginMetadata } from "./plugin-metadata.js";
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
export const DEFAULT_REFRESH_TIMEOUT_MS = 10_000;
|
|
11
12
|
const CHECK_ON_OPEN_MARKER = ".check-on-open";
|
|
@@ -150,7 +151,20 @@ async function validateCatalogPaths(catalog, checkout) {
|
|
|
150
151
|
async function catalogForCheckout(checkout) {
|
|
151
152
|
const result = await readMarketplaceCatalog(checkout);
|
|
152
153
|
await validateCatalogPaths(result.catalog, checkout);
|
|
153
|
-
|
|
154
|
+
const plugins = await Promise.all(result.catalog.plugins.map(async (entry) => {
|
|
155
|
+
if (entry.source.kind !== "local")
|
|
156
|
+
return entry;
|
|
157
|
+
try {
|
|
158
|
+
const root = await resolveContainedExistingPath(checkout, entry.source.path, `${entry.name} source`);
|
|
159
|
+
const metadata = await readPluginMetadata(root);
|
|
160
|
+
return Object.freeze({ ...entry, ...metadata, ...(entry.description === undefined ? {} : { description: entry.description }) });
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Missing local bundles remain visible; installation reports the source error.
|
|
164
|
+
return entry;
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
return Object.freeze({ ...result, catalog: Object.freeze({ ...result.catalog, plugins: Object.freeze(plugins) }) });
|
|
154
168
|
}
|
|
155
169
|
function marketplaceInfo(paths, name, source) {
|
|
156
170
|
const safe = assertSafeName(name, "marketplace name");
|
|
@@ -162,8 +176,41 @@ function marketplaceInfo(paths, name, source) {
|
|
|
162
176
|
});
|
|
163
177
|
}
|
|
164
178
|
async function replaceDirectory(staged, target) {
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
// Deleting the live copy before rename loses a working install if publication
|
|
180
|
+
// fails. Retain it only for this replacement, not as a persistent rollback store.
|
|
181
|
+
const holding = await mkdtemp(join(dirname(target), ".replacing-"));
|
|
182
|
+
const previous = join(holding, "previous");
|
|
183
|
+
let movedPrevious = false;
|
|
184
|
+
let retainPrevious = false;
|
|
185
|
+
try {
|
|
186
|
+
try {
|
|
187
|
+
await rename(target, previous);
|
|
188
|
+
movedPrevious = true;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (error.code !== "ENOENT")
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
await rename(staged, target);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (movedPrevious) {
|
|
199
|
+
try {
|
|
200
|
+
await rename(previous, target);
|
|
201
|
+
}
|
|
202
|
+
catch (restoreError) {
|
|
203
|
+
retainPrevious = true;
|
|
204
|
+
throw new AggregateError([error, restoreError], `Replacement failed; previous copy retained at ${previous}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
if (!retainPrevious)
|
|
212
|
+
await removeOwnedTree(holding);
|
|
213
|
+
}
|
|
167
214
|
}
|
|
168
215
|
async function readReceipt(root) {
|
|
169
216
|
try {
|
|
@@ -229,7 +276,8 @@ async function installedInfo(paths, marketplace, plugin) {
|
|
|
229
276
|
const disabled = await hasRegularMarker(root, ".disabled");
|
|
230
277
|
const autoUpdate = await hasRegularMarker(root, ".auto-update");
|
|
231
278
|
const receipt = await readReceipt(root);
|
|
232
|
-
|
|
279
|
+
const metadata = await readPluginMetadata(root);
|
|
280
|
+
return Object.freeze({ marketplace: market, name, root, data, enabled: !disabled, autoUpdate, ...metadata, ...(receipt === undefined ? {} : { receipt }) });
|
|
233
281
|
}
|
|
234
282
|
function pluginPath(paths, marketplace, plugin) {
|
|
235
283
|
return join(paths.plugins, assertSafeName(marketplace, "marketplace name"), assertSafeName(plugin, "plugin name"));
|
|
@@ -244,7 +292,7 @@ async function findCatalogPlugin(paths, marketplace, plugin) {
|
|
|
244
292
|
throw new Error(`plugin is not in marketplace catalog: ${pluginName}@${name}`);
|
|
245
293
|
return { info, catalog: result.catalog, entry };
|
|
246
294
|
}
|
|
247
|
-
async function resolvePluginSource(paths, info, entry) {
|
|
295
|
+
async function resolvePluginSource(paths, info, entry, options = {}) {
|
|
248
296
|
if (entry.source.kind === "local") {
|
|
249
297
|
const root = await resolveContainedExistingPath(info.checkout, entry.source.path, `${entry.name} source`);
|
|
250
298
|
const stat = await lstat(root);
|
|
@@ -256,7 +304,7 @@ async function resolvePluginSource(paths, info, entry) {
|
|
|
256
304
|
const sourceStage = await mkdtemp(join(paths.plugins, ".plugin-source-"));
|
|
257
305
|
try {
|
|
258
306
|
const source = { kind: "git", value: entry.source.url, ...(entry.source.ref === undefined ? {} : { ref: entry.source.ref }) };
|
|
259
|
-
await materializeMarketplace(source, sourceStage);
|
|
307
|
+
await materializeMarketplace(source, sourceStage, options);
|
|
260
308
|
const root = entry.source.path === undefined
|
|
261
309
|
? sourceStage
|
|
262
310
|
: await resolveContainedExistingPath(sourceStage, entry.source.path, `${entry.name} Git source`);
|
|
@@ -287,11 +335,12 @@ async function copyPluginBundle(paths, source, marketplace, plugin, entry, prese
|
|
|
287
335
|
await cp(join(source, item.name), join(stage, item.name), { recursive: true, dereference: false, force: true });
|
|
288
336
|
}
|
|
289
337
|
await makeOwnerWritable(stage);
|
|
338
|
+
const metadata = await readPluginMetadata(stage);
|
|
290
339
|
await writeFile(join(stage, ".pi-plugin.json"), `${JSON.stringify({
|
|
291
340
|
marketplace: market,
|
|
292
341
|
plugin: name,
|
|
293
|
-
description: entry.description,
|
|
294
|
-
version: entry.version,
|
|
342
|
+
description: entry.description ?? metadata.description,
|
|
343
|
+
version: metadata.version ?? entry.version,
|
|
295
344
|
source: entry.source,
|
|
296
345
|
}, null, 2)}\n`, "utf8");
|
|
297
346
|
if (preserveDisabled)
|
|
@@ -300,8 +349,7 @@ async function copyPluginBundle(paths, source, marketplace, plugin, entry, prese
|
|
|
300
349
|
await writeFile(join(stage, ".auto-update"), "", "utf8");
|
|
301
350
|
await assertNoSymlinks(stage);
|
|
302
351
|
const target = pluginPath(paths, market, name);
|
|
303
|
-
await
|
|
304
|
-
await rename(stage, target);
|
|
352
|
+
await replaceDirectory(stage, target);
|
|
305
353
|
await mkdir(join(paths.data, market, name), { recursive: true });
|
|
306
354
|
return installedInfo(paths, market, name);
|
|
307
355
|
}
|
|
@@ -367,8 +415,7 @@ export function createPluginHost(agentDir) {
|
|
|
367
415
|
if (result.catalog.name !== name)
|
|
368
416
|
throw new Error(`refreshed marketplace declares ${result.catalog.name}, expected ${name}`);
|
|
369
417
|
throwIfAborted(options.signal);
|
|
370
|
-
await
|
|
371
|
-
await rename(checkout, join(root, "checkout"));
|
|
418
|
+
await replaceDirectory(checkout, join(root, "checkout"));
|
|
372
419
|
return marketplaceInfo(paths, name, source);
|
|
373
420
|
}
|
|
374
421
|
finally {
|
|
@@ -461,8 +508,9 @@ export function createPluginHost(agentDir) {
|
|
|
461
508
|
if (update && options.refresh !== false) {
|
|
462
509
|
await refreshMarketplace(marketplace, options);
|
|
463
510
|
}
|
|
511
|
+
throwIfAborted(options.signal);
|
|
464
512
|
const { info, entry } = await findCatalogPlugin(paths, marketplace, plugin);
|
|
465
|
-
const source = await resolvePluginSource(paths, info, entry);
|
|
513
|
+
const source = await resolvePluginSource(paths, info, entry, options);
|
|
466
514
|
try {
|
|
467
515
|
return await copyPluginBundle(paths, source.root, info.name, entry.name, entry, preserveDisabled, preserveAutoUpdate);
|
|
468
516
|
}
|
|
@@ -558,7 +606,7 @@ export function createPluginHost(agentDir) {
|
|
|
558
606
|
}
|
|
559
607
|
let info;
|
|
560
608
|
if (action === "install")
|
|
561
|
-
info = await mutatePlugin(marketplace, plugin, false);
|
|
609
|
+
info = await mutatePlugin(marketplace, plugin, false, options);
|
|
562
610
|
if (action === "update") {
|
|
563
611
|
const updateOptions = {
|
|
564
612
|
refresh: false,
|
|
@@ -613,21 +661,29 @@ export function createPluginHost(agentDir) {
|
|
|
613
661
|
const entry = refresh.catalog.plugins.find((candidate) => candidate.name === installed.name);
|
|
614
662
|
if (entry === undefined)
|
|
615
663
|
throw new Error(`plugin is not in marketplace catalog: ${installed.name}@${installed.marketplace}`);
|
|
616
|
-
const installedVersion =
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
664
|
+
const installedVersion = installedPluginVersion(installed);
|
|
665
|
+
const marketInfo = refresh.info ?? marketplaceInfo(paths, installed.marketplace, await readSource(join(paths.marketplaces, installed.marketplace)));
|
|
666
|
+
// Remote entries may omit or lag the bundle version too. Resolve the
|
|
667
|
+
// candidate once and copy that same source only if an update is needed.
|
|
668
|
+
const source = await resolvePluginSource(paths, marketInfo, entry, options);
|
|
669
|
+
try {
|
|
670
|
+
const metadata = await readPluginMetadata(source.root);
|
|
671
|
+
const availableVersion = metadata.version ?? entry.version;
|
|
672
|
+
if (!force && availableVersion === undefined) {
|
|
673
|
+
result = Object.freeze({ identity, ok: true, updated: false, skipped: true, reason: "bundle and catalog do not declare a version" });
|
|
674
|
+
}
|
|
675
|
+
else if (!force && installedVersion === availableVersion) {
|
|
676
|
+
result = Object.freeze({ identity, ok: true, updated: false, skipped: true, reason: "already at declared version" });
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
throwIfAborted(options.signal);
|
|
680
|
+
const current = await installedInfo(paths, installed.marketplace, installed.name);
|
|
681
|
+
const info = await copyPluginBundle(paths, source.root, installed.marketplace, installed.name, entry, !current.enabled, current.autoUpdate);
|
|
682
|
+
result = Object.freeze({ identity, ok: true, updated: true, skipped: false, info });
|
|
683
|
+
}
|
|
622
684
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
refresh: false,
|
|
626
|
-
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
627
|
-
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
628
|
-
};
|
|
629
|
-
const info = await mutatePlugin(installed.marketplace, installed.name, true, updateOptions);
|
|
630
|
-
result = Object.freeze({ identity, ok: true, updated: true, skipped: false, info });
|
|
685
|
+
finally {
|
|
686
|
+
await source.cleanup();
|
|
631
687
|
}
|
|
632
688
|
}
|
|
633
689
|
catch (error) {
|