@polygraph/codex-plugin 0.4.40 → 0.4.41

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygraph",
3
- "version": "0.4.40",
3
+ "version": "0.4.41",
4
4
  "description": "AI agent skills and subagents for Polygraph sessions, repository context, and coordination",
5
5
  "author": {
6
6
  "name": "Narwhal Technologies Inc",
package/README.md CHANGED
@@ -46,6 +46,7 @@ It detects your AI agent — Claude Code, Codex, OpenCode, and more — and inst
46
46
 
47
47
  - **polygraph-init-subagent** — Discovers candidate repositories and initializes a Polygraph session
48
48
  - **polygraph-delegate-subagent** — Delegates work to a child agent in another repository, polls for completion
49
+ - **session-debrief** — Analyzes the raw logs of past Polygraph sessions and returns a structured, rank-ordered debrief for the current task
49
50
 
50
51
  ## Development
51
52
 
@@ -0,0 +1,24 @@
1
+ name = "session-debrief"
2
+ description = "Analyze the raw logs of one or more past Polygraph sessions and return a structured, rank-ordered debrief for the current task. Launch as a background agent with a ranked list of relevant Polygraph session IDs/lines and a one-paragraph statement of the current task; it invokes the session-debrief skill, pulls parent and child transcripts via the polygraph CLI, and returns one consolidated debrief. Read-only with respect to the inspected sessions."
3
+ developer_instructions = '''
4
+ # Session Debrief Subagent
5
+
6
+ You produce debriefs of PAST Polygraph sessions so a parent agent working on a NEW task can decide what context is relevant. You run in the background and speed matters — the parent keeps working while it waits and folds your debrief in whenever it lands.
7
+
8
+ You are READ-ONLY with respect to the inspected sessions: never resume them, never spawn agents into them, never push branches, create PRs, or update their descriptions.
9
+
10
+ ## Input Parameters (from Main Agent)
11
+
12
+ The main agent provides these in the prompt:
13
+
14
+ | Parameter | Description |
15
+ | ------------- | ------------------------------------------------------------------------------------------------ |
16
+ | `sessions` | A ranked list of relevant Polygraph sessions (IDs/lines, most relevant first, with optional titles/URLs) |
17
+ | `currentTask` | A one-paragraph statement of the task the parent is currently working on |
18
+
19
+ ## What to do
20
+
21
+ Invoke the `session-debrief` skill and follow its procedure and output template exactly. Pass through the ranked session list and the current-task statement you received. The skill is the single source of truth for how to pull transcripts via the polygraph CLI, how to fan out across multiple sessions, and how to format each debrief section — do not reinvent or duplicate that procedure here.
22
+
23
+ Return the consolidated, rank-ordered debrief as your final message.
24
+ '''
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // source/codex/lib/installer.mjs
4
+ import { spawnSync } from "node:child_process";
4
5
  import {
5
6
  cpSync,
6
7
  existsSync,
@@ -35,10 +36,19 @@ function resolveUserHome(env = process.env) {
35
36
  const userHome = env.HOME?.trim() || homedir();
36
37
  return resolve(expandHome(userHome, env));
37
38
  }
38
- function getMarketplacePath(userHome) {
39
+ function getMarketplaceRoot(userHome) {
40
+ return join(userHome, ".polygraph", "codex-marketplace");
41
+ }
42
+ function getMarketplacePath(marketplaceRoot) {
43
+ return join(marketplaceRoot, ".agents", "plugins", "marketplace.json");
44
+ }
45
+ function getPluginInstallPath(marketplaceRoot) {
46
+ return join(marketplaceRoot, "plugins", PLUGIN_NAME);
47
+ }
48
+ function getLegacyMarketplacePath(userHome) {
39
49
  return join(userHome, ".agents", "plugins", "marketplace.json");
40
50
  }
41
- function getPluginInstallPath(userHome) {
51
+ function getLegacyPluginInstallPath(userHome) {
42
52
  return join(userHome, ".agents", "plugins", PLUGIN_NAME);
43
53
  }
44
54
  function loadPackageMetadata(packageRoot) {
@@ -74,7 +84,9 @@ function loadPackageMetadata(packageRoot) {
74
84
  function installPlugin({
75
85
  packageRoot,
76
86
  env = process.env,
77
- force = false
87
+ force = false,
88
+ // Injectable so tests do not have to shell out to a real codex binary.
89
+ register = registerMarketplace
78
90
  } = {}) {
79
91
  if (!packageRoot) {
80
92
  throw new Error("packageRoot is required");
@@ -83,8 +95,9 @@ function installPlugin({
83
95
  const codexHome = resolveCodexHome(env);
84
96
  const userHome = resolveUserHome(env);
85
97
  const agentsPath = getAgentsPath(codexHome);
86
- const marketplacePath = getMarketplacePath(userHome);
87
- const pluginPath = getPluginInstallPath(userHome);
98
+ const marketplaceRoot = getMarketplaceRoot(userHome);
99
+ const marketplacePath = getMarketplacePath(marketplaceRoot);
100
+ const pluginPath = getPluginInstallPath(marketplaceRoot);
88
101
  const installAlreadyPresent = existsSync(pluginPath);
89
102
  let previousVersion = null;
90
103
  if (installAlreadyPresent) {
@@ -117,15 +130,20 @@ function installPlugin({
117
130
  copied = true;
118
131
  }
119
132
  const agentsChanged = installCodexAgents({ packageRoot, agentsPath });
120
- const marketplaceChanged = enablePluginInMarketplace({
133
+ const marketplaceChanged = writeMarketplaceManifest({
121
134
  marketplacePath,
122
- pluginPath,
123
- userHome
135
+ marketplaceRoot,
136
+ pluginPath
124
137
  });
138
+ const legacyCleanup = cleanLegacyMarketplaceEntry({ userHome });
139
+ const registration = register({ marketplaceRoot, env });
125
140
  return {
126
141
  ok: true,
127
142
  action: "install",
128
143
  plugin: PLUGIN_ID,
144
+ marketplaceName: MARKETPLACE_NAME,
145
+ marketplaceRoot,
146
+ marketplaceRegistered: registration.registered,
129
147
  version,
130
148
  codexHome,
131
149
  agentsPath,
@@ -136,9 +154,68 @@ function installPlugin({
136
154
  pluginUpdated: installAlreadyPresent && versionMismatch && !force,
137
155
  previousVersion,
138
156
  agentsChanged,
139
- marketplaceChanged
157
+ marketplaceChanged,
158
+ legacyEntryRemoved: legacyCleanup.entryRemoved,
159
+ legacyMarketplaceRemoved: legacyCleanup.fileRemoved,
160
+ ...registration.error ? { marketplaceRegistrationError: registration.error } : {}
140
161
  };
141
162
  }
163
+ function registerMarketplace({ marketplaceRoot, env = process.env }) {
164
+ const result = spawnSync(
165
+ "codex",
166
+ ["plugin", "marketplace", "add", marketplaceRoot, "--json"],
167
+ { encoding: "utf8", env }
168
+ );
169
+ if (result.error) {
170
+ return {
171
+ registered: false,
172
+ error: `Could not run \`codex\`: ${result.error.message}. Run \`codex plugin marketplace add ${marketplaceRoot}\` once codex is available.`
173
+ };
174
+ }
175
+ if (result.status !== 0) {
176
+ return {
177
+ registered: false,
178
+ error: `\`codex plugin marketplace add\` failed: ${(result.stderr || "").trim()}`
179
+ };
180
+ }
181
+ return { registered: true };
182
+ }
183
+ function cleanLegacyMarketplaceEntry({ userHome }) {
184
+ const legacyPath = getLegacyMarketplacePath(userHome);
185
+ const unchanged = { entryRemoved: false, fileRemoved: false };
186
+ if (!existsSync(legacyPath)) {
187
+ return unchanged;
188
+ }
189
+ let marketplace;
190
+ try {
191
+ marketplace = readJsonFile(legacyPath);
192
+ } catch {
193
+ return unchanged;
194
+ }
195
+ if (!isPlainObject(marketplace) || !Array.isArray(marketplace.plugins)) {
196
+ return unchanged;
197
+ }
198
+ const remaining = marketplace.plugins.filter(
199
+ (plugin) => plugin?.name !== PLUGIN_NAME
200
+ );
201
+ if (remaining.length === marketplace.plugins.length) {
202
+ return unchanged;
203
+ }
204
+ if (remaining.length === 0 && marketplace.name === MARKETPLACE_NAME) {
205
+ rmSync(legacyPath, { force: true });
206
+ rmSync(getLegacyPluginInstallPath(userHome), {
207
+ recursive: true,
208
+ force: true
209
+ });
210
+ return { entryRemoved: true, fileRemoved: true };
211
+ }
212
+ writeJsonFile(legacyPath, { ...marketplace, plugins: remaining });
213
+ rmSync(getLegacyPluginInstallPath(userHome), {
214
+ recursive: true,
215
+ force: true
216
+ });
217
+ return { entryRemoved: true, fileRemoved: false };
218
+ }
142
219
  function checkInstall({ packageRoot, env = process.env } = {}) {
143
220
  let version = null;
144
221
  if (packageRoot) {
@@ -147,29 +224,46 @@ function checkInstall({ packageRoot, env = process.env } = {}) {
147
224
  const codexHome = resolveCodexHome(env);
148
225
  const userHome = resolveUserHome(env);
149
226
  const agentsPath = getAgentsPath(codexHome);
150
- const marketplacePath = getMarketplacePath(userHome);
151
- const pluginPath = getPluginInstallPath(userHome);
227
+ const marketplaceRoot = getMarketplaceRoot(userHome);
228
+ const marketplacePath = getMarketplacePath(marketplaceRoot);
229
+ const pluginPath = getPluginInstallPath(marketplaceRoot);
152
230
  const pluginInstalled = isValidInstalledPluginDir(pluginPath);
153
231
  const agentsInstalled = packageRoot ? areCodexAgentsInstalled({ packageRoot, agentsPath }) : hasDefaultCodexAgents(agentsPath);
154
232
  const marketplaceConfigured = isPluginConfiguredInMarketplace({
155
233
  marketplacePath,
156
- userHome,
234
+ marketplaceRoot,
157
235
  pluginPath
158
236
  });
159
- const ok = pluginInstalled && agentsInstalled && marketplaceConfigured;
237
+ const legacyMarketplacePresent = hasLegacyPluginEntry({ userHome });
238
+ const ok = pluginInstalled && agentsInstalled && marketplaceConfigured && !legacyMarketplacePresent;
160
239
  return {
161
240
  ok,
162
241
  action: "check",
163
242
  plugin: PLUGIN_ID,
243
+ marketplaceName: MARKETPLACE_NAME,
244
+ marketplaceRoot,
164
245
  codexHome,
165
246
  agentsPath,
166
247
  pluginPath,
167
248
  marketplacePath,
168
249
  pluginInstalled,
169
250
  agentsInstalled,
170
- marketplaceConfigured
251
+ marketplaceConfigured,
252
+ legacyMarketplacePresent
171
253
  };
172
254
  }
255
+ function hasLegacyPluginEntry({ userHome }) {
256
+ const legacyPath = getLegacyMarketplacePath(userHome);
257
+ if (!existsSync(legacyPath)) {
258
+ return false;
259
+ }
260
+ try {
261
+ const marketplace = readJsonFile(legacyPath);
262
+ return Array.isArray(marketplace?.plugins) && marketplace.plugins.some((plugin) => plugin?.name === PLUGIN_NAME);
263
+ } catch {
264
+ return false;
265
+ }
266
+ }
173
267
  function getPackagePayloadPaths(packageRoot, packageJson) {
174
268
  const relativePaths = new Set(packageJson.files ?? []);
175
269
  relativePaths.add("package.json");
@@ -234,44 +328,30 @@ function hasDefaultCodexAgents(agentsPath) {
234
328
  (agentFile) => existsSync(join(agentsPath, agentFile))
235
329
  );
236
330
  }
237
- function enablePluginInMarketplace({
331
+ function writeMarketplaceManifest({
238
332
  marketplacePath,
239
- pluginPath,
240
- userHome
333
+ marketplaceRoot,
334
+ pluginPath
241
335
  }) {
242
- const marketplace = readJsonFile(marketplacePath, {});
243
- if (marketplace.plugins !== void 0 && !Array.isArray(marketplace.plugins)) {
244
- throw new Error(`Expected plugins array in ${marketplacePath}`);
245
- }
246
- const marketplacePluginPath = toMarketplaceSourcePath(userHome, pluginPath);
247
- const nextPluginEntry = {
248
- name: PLUGIN_NAME,
249
- source: {
250
- source: "local",
251
- path: marketplacePluginPath
252
- },
253
- policy: {
254
- installation: "AVAILABLE",
255
- authentication: "ON_INSTALL"
256
- },
257
- category: "Productivity"
258
- };
259
- const plugins = marketplace.plugins ?? [];
260
- const existingIndex = plugins.findIndex(
261
- (plugin) => plugin?.name === PLUGIN_NAME
262
- );
263
- const nextPlugins = existingIndex === -1 ? [...plugins, nextPluginEntry] : plugins.map(
264
- (plugin, index) => index === existingIndex ? nextPluginEntry : plugin
265
- );
266
336
  const nextMarketplace = {
267
- ...marketplace,
268
- name: marketplace.name ?? MARKETPLACE_NAME,
269
- interface: isPlainObject(marketplace.interface) ? {
270
- ...marketplace.interface,
271
- displayName: marketplace.interface.displayName ?? MARKETPLACE_DISPLAY_NAME
272
- } : { displayName: MARKETPLACE_DISPLAY_NAME },
273
- plugins: nextPlugins
337
+ name: MARKETPLACE_NAME,
338
+ interface: { displayName: MARKETPLACE_DISPLAY_NAME },
339
+ plugins: [
340
+ {
341
+ name: PLUGIN_NAME,
342
+ source: {
343
+ source: "local",
344
+ path: toMarketplaceSourcePath(marketplaceRoot, pluginPath)
345
+ },
346
+ policy: {
347
+ installation: "AVAILABLE",
348
+ authentication: "ON_INSTALL"
349
+ },
350
+ category: "Productivity"
351
+ }
352
+ ]
274
353
  };
354
+ const marketplace = readJsonFile(marketplacePath, null);
275
355
  const changed = JSON.stringify(nextMarketplace) !== JSON.stringify(marketplace);
276
356
  if (changed) {
277
357
  writeJsonFile(marketplacePath, nextMarketplace);
@@ -280,13 +360,21 @@ function enablePluginInMarketplace({
280
360
  }
281
361
  function isPluginConfiguredInMarketplace({
282
362
  marketplacePath,
283
- userHome,
363
+ marketplaceRoot,
284
364
  pluginPath
285
365
  }) {
286
366
  if (!existsSync(marketplacePath)) {
287
367
  return false;
288
368
  }
289
- const marketplace = readJsonFile(marketplacePath);
369
+ let marketplace;
370
+ try {
371
+ marketplace = readJsonFile(marketplacePath);
372
+ } catch {
373
+ return false;
374
+ }
375
+ if (marketplace?.name !== MARKETPLACE_NAME) {
376
+ return false;
377
+ }
290
378
  if (!Array.isArray(marketplace.plugins)) {
291
379
  return false;
292
380
  }
@@ -296,7 +384,7 @@ function isPluginConfiguredInMarketplace({
296
384
  if (!isPlainObject(pluginEntry?.source) || pluginEntry.source.source !== "local") {
297
385
  return false;
298
386
  }
299
- const configuredPath = resolve(userHome, pluginEntry.source.path);
387
+ const configuredPath = resolve(marketplaceRoot, pluginEntry.source.path);
300
388
  return configuredPath === resolve(pluginPath);
301
389
  }
302
390
  function readJsonFile(path, fallbackValue) {
@@ -341,11 +429,11 @@ function expandHome(inputPath, env) {
341
429
  }
342
430
  return inputPath;
343
431
  }
344
- function toMarketplaceSourcePath(userHome, targetPath) {
345
- const relativePath = relative(userHome, targetPath);
432
+ function toMarketplaceSourcePath(marketplaceRoot, targetPath) {
433
+ const relativePath = relative(marketplaceRoot, targetPath);
346
434
  if (relativePath === "" || relativePath === "." || relativePath.startsWith(`..${sep}`) || relativePath === "..") {
347
435
  throw new Error(
348
- `Expected plugin install path ${targetPath} to be inside ${userHome} so it can be referenced from the personal marketplace`
436
+ `Expected plugin install path ${targetPath} to be inside ${marketplaceRoot} so it can be referenced from the marketplace manifest`
349
437
  );
350
438
  }
351
439
  return `./${relativePath.split(sep).join("/")}`;
@@ -360,8 +448,10 @@ var usage = `Usage:
360
448
  npx @polygraph/codex-plugin install [--force] [--json]
361
449
  npx @polygraph/codex-plugin check [--json]
362
450
 
363
- The install command materializes the plugin payload so that codex's official
364
- plugin system can pick it up. After running install, run:
451
+ The install command materializes the plugin payload into the marketplace Polygraph
452
+ owns (~/.polygraph/codex-marketplace) and registers that marketplace with codex.
453
+ It does not write to the shared ~/.agents/plugins/marketplace.json. After running
454
+ install, run:
365
455
 
366
456
  codex plugin add polygraph@polygraph-plugins
367
457
 
@@ -408,17 +498,30 @@ ${usage}`);
408
498
  } else {
409
499
  const pluginState = result.pluginInstalled ? "plugin files present" : "plugin files not present";
410
500
  const agentsState = result.agentsInstalled ? "agents installed" : "agents not installed";
411
- const marketplaceState = result.marketplaceConfigured ? "plugin present in marketplace" : "plugin not present in marketplace";
501
+ const marketplaceState = result.marketplaceConfigured ? `plugin published from the ${result.marketplaceName} marketplace` : `plugin not published from the ${result.marketplaceName} marketplace`;
502
+ const legacyState = result.legacyMarketplacePresent ? "; a stale entry remains in ~/.agents/plugins/marketplace.json (re-run install to clear it)" : "";
412
503
  console.error(
413
- `Polygraph Codex plugin check failed: ${pluginState}; ${agentsState}; ${marketplaceState}.`
504
+ `Polygraph Codex plugin check failed: ${pluginState}; ${agentsState}; ${marketplaceState}${legacyState}.`
414
505
  );
415
506
  }
416
507
  } else {
417
508
  console.log(`Materialized Polygraph Codex plugin ${result.version}.`);
418
509
  console.log(`Plugin path: ${result.pluginPath}`);
419
510
  console.log(`Agents: ${result.agentsPath}`);
420
- console.log(`Marketplace: ${result.marketplacePath}`);
421
- console.log(`Next step: codex plugin add ${result.plugin}`);
511
+ console.log(`Marketplace: ${result.marketplaceName} (${result.marketplaceRoot})`);
512
+ if (result.legacyEntryRemoved) {
513
+ console.log(
514
+ result.legacyMarketplaceRemoved ? "Removed the obsolete ~/.agents/plugins/marketplace.json written by an earlier version." : "Removed the obsolete polygraph entry from ~/.agents/plugins/marketplace.json; your other plugins were left untouched."
515
+ );
516
+ }
517
+ if (result.marketplaceRegistered) {
518
+ console.log(`Next step: codex plugin add ${result.plugin}`);
519
+ } else {
520
+ console.warn(`Warning: ${result.marketplaceRegistrationError}`);
521
+ console.warn(
522
+ `Then run: codex plugin add ${result.plugin}`
523
+ );
524
+ }
422
525
  }
423
526
  if (command === "check" && !result.ok) {
424
527
  process.exitCode = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polygraph/codex-plugin",
3
- "version": "0.4.40",
3
+ "version": "0.4.41",
4
4
  "description": "AI agent skills and subagents for Polygraph sessions, repository context, and coordination",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,