@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +1 -0
  3. package/node_modules/@nklisch/pi-astral-pocket/LICENSE +3 -0
  4. package/node_modules/@nklisch/pi-astral-pocket/README.md +138 -0
  5. package/node_modules/@nklisch/pi-astral-pocket/package.json +54 -0
  6. package/node_modules/@nklisch/pi-astral-pocket/src/activation.ts +44 -0
  7. package/node_modules/@nklisch/pi-astral-pocket/src/config.ts +95 -0
  8. package/node_modules/@nklisch/pi-astral-pocket/src/controller.ts +78 -0
  9. package/node_modules/@nklisch/pi-astral-pocket/src/distiller.ts +271 -0
  10. package/node_modules/@nklisch/pi-astral-pocket/src/guidance.ts +66 -0
  11. package/node_modules/@nklisch/pi-astral-pocket/src/index.ts +222 -0
  12. package/node_modules/@nklisch/pi-astral-pocket/src/provider.ts +128 -0
  13. package/node_modules/@nklisch/pi-astral-pocket/src/scope.ts +33 -0
  14. package/node_modules/@nklisch/pi-astral-pocket/src/sessions.ts +237 -0
  15. package/node_modules/@nklisch/pi-astral-pocket/src/store.ts +427 -0
  16. package/node_modules/@nklisch/pi-astral-pocket/src/tools.ts +142 -0
  17. package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-arm64.node +0 -0
  18. package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-x64.node +0 -0
  19. package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-arm64-gnu.node +0 -0
  20. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
  21. package/node_modules/@nklisch/pi-conveniences/extensions/agents-context.ts +4 -6
  22. package/node_modules/@nklisch/pi-conveniences/extensions/context-window-footer.ts +38 -22
  23. package/node_modules/@nklisch/pi-conveniences/package.json +1 -1
  24. package/node_modules/@nklisch/pi-plugins/README.md +12 -6
  25. package/node_modules/@nklisch/pi-plugins/dist/catalog.js +11 -0
  26. package/node_modules/@nklisch/pi-plugins/dist/catalog.js.map +1 -1
  27. package/node_modules/@nklisch/pi-plugins/dist/host.js +85 -29
  28. package/node_modules/@nklisch/pi-plugins/dist/host.js.map +1 -1
  29. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-layout.d.ts +4 -0
  30. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-layout.js +24 -0
  31. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager-layout.js.map +1 -0
  32. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.d.ts +6 -0
  33. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.js +130 -47
  34. package/node_modules/@nklisch/pi-plugins/dist/pi/plugin-manager.js.map +1 -1
  35. package/node_modules/@nklisch/pi-plugins/dist/plugin-metadata.d.ts +9 -0
  36. package/node_modules/@nklisch/pi-plugins/dist/plugin-metadata.js +37 -0
  37. package/node_modules/@nklisch/pi-plugins/dist/plugin-metadata.js.map +1 -0
  38. package/node_modules/@nklisch/pi-plugins/dist/runtime-discovery.js +2 -0
  39. package/node_modules/@nklisch/pi-plugins/dist/runtime-discovery.js.map +1 -1
  40. package/node_modules/@nklisch/pi-plugins/dist/types.d.ts +4 -1
  41. package/node_modules/@nklisch/pi-plugins/dist/types.js.map +1 -1
  42. package/node_modules/@nklisch/pi-plugins/package.json +1 -1
  43. 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
+ }
@@ -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
- const content = readAgentsContext(
85
- event?.systemPromptOptions?.cwd ?? process.cwd(),
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-usage";
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 === CODEX_STATUS_ID) return 0;
231
- if (MODE_STATUS_IDS.has(key)) return 1;
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
- const model = formatModelName(ctx.model?.id);
275
- const modelLabel = formatModelLabel(theme, model, pi.getThinkingLevel());
276
- const branch = footerData.getGitBranch();
277
- const branchLabel = branch ? `git ${branch}` : "no git";
278
- const folderLabel = formatFolderLabel(theme, ctx.cwd);
279
- const snapshot = getContextSnapshot(ctx);
280
- const extensionStatuses = footerData.getExtensionStatuses();
281
- const statuses = formatExtensionStatuses(theme, extensionStatuses);
282
- const primaryStatus = formatExtensionStatuses(theme, extensionStatuses, 1);
283
- const codexStatus = formatExtensionStatusByKey(extensionStatuses, CODEX_STATUS_ID);
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), statuses]
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), statuses]
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 rightCompactWithPrimaryStatus = [rightCompact, primaryStatus]
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, rightCompactWithPrimaryStatus)) {
324
- return [formatFooterLine(width, leftCompact, rightCompactWithPrimaryStatus)];
339
+ if (footerFits(width, leftCompact, rightCompactWithCodex)) {
340
+ return [formatFooterLine(width, leftCompact, rightCompactWithCodex)];
325
341
  }
326
342
 
327
- if (codexStatus && footerFits(width, leftCompact, codexStatus)) {
328
- return [formatFooterLine(width, leftCompact, codexStatus)];
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.2",
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 catalog declares a
53
- version different from its installed receipt. Missing versions, offline
54
- sources, and item failures leave the installed copy unchanged and do not block
55
- startup.
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
- return result;
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
- await removeOwnedTree(target);
166
- await rename(staged, target);
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
- return Object.freeze({ marketplace: market, name, root, data, enabled: !disabled, autoUpdate, ...(receipt === undefined ? {} : { receipt }) });
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 removeOwnedTree(target);
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 removeOwnedTree(join(root, "checkout"));
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 = typeof installed.receipt?.version === "string" ? installed.receipt.version : undefined;
617
- if (!force && entry.version === undefined) {
618
- result = Object.freeze({ identity, ok: true, updated: false, skipped: true, reason: "catalog version is not declared" });
619
- }
620
- else if (!force && installedVersion === entry.version) {
621
- result = Object.freeze({ identity, ok: true, updated: false, skipped: true, reason: "already at declared catalog version" });
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
- else {
624
- const updateOptions = {
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) {