@danieldeusing/seedr 0.1.129 → 0.1.130

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/dist/cli.js CHANGED
@@ -148,7 +148,7 @@ function isTelemetryDisabled(env = process.env) {
148
148
  return env[TELEMETRY_OPT_OUT_VARIABLE] !== void 0;
149
149
  }
150
150
  function cliVersion() {
151
- return true ? "0.1.129" : "dev";
151
+ return true ? "0.1.130" : "dev";
152
152
  }
153
153
  async function sendEvent(event) {
154
154
  try {
@@ -168,6 +168,36 @@ function trackInstalls(slug, type, results, scope) {
168
168
  return Promise.all(sends).then(() => void 0);
169
169
  }
170
170
 
171
+ // src/utils/ledger.ts
172
+ import { homedir } from "os";
173
+ import { join } from "path";
174
+ var LEDGER_PATH = join(homedir(), ".seedr", "installed.json");
175
+ function ledgerKey(type, slug) {
176
+ return `${type}:${slug}`;
177
+ }
178
+ async function readLedger() {
179
+ const ledger = await readJson(LEDGER_PATH);
180
+ return { version: 1, items: ledger.items ?? {} };
181
+ }
182
+ async function recordInstall(entry) {
183
+ const ledger = await readLedger();
184
+ const key = ledgerKey(entry.type, entry.slug);
185
+ const previous = ledger.items[key];
186
+ const agents = previous && previous.version === entry.version ? [.../* @__PURE__ */ new Set([...previous.agents, ...entry.agents])].sort() : [...entry.agents].sort();
187
+ ledger.items[key] = { ...entry, agents };
188
+ await writeJson(LEDGER_PATH, ledger);
189
+ }
190
+ async function recordRemoval(type, slug, agents) {
191
+ const ledger = await readLedger();
192
+ const key = ledgerKey(type, slug);
193
+ const entry = ledger.items[key];
194
+ if (!entry) return;
195
+ const left = entry.agents.filter((agent) => !agents.includes(agent));
196
+ if (left.length === 0) delete ledger.items[key];
197
+ else ledger.items[key] = { ...entry, agents: left };
198
+ await writeJson(LEDGER_PATH, ledger);
199
+ }
200
+
171
201
  // src/config/compatibility.ts
172
202
  function filterCompatibleAgents(type, agents) {
173
203
  const compatible = AGENT_COMPATIBILITY[type];
@@ -185,8 +215,8 @@ function describeIncompatibility(type, agent) {
185
215
  }
186
216
 
187
217
  // src/handlers/agent.ts
188
- import { join, relative } from "path";
189
- import { homedir } from "os";
218
+ import { join as join2, relative } from "path";
219
+ import { homedir as homedir2 } from "os";
190
220
  import { lstat, readdir, unlink } from "fs/promises";
191
221
  import chalk2 from "chalk";
192
222
  import ora from "ora";
@@ -197,7 +227,7 @@ function subagentFileName(agent, slug) {
197
227
  async function resolveAgentFilePath(agent, slug, scope, cwd) {
198
228
  const destDir = getContentPath(agent, "agent", scope, cwd);
199
229
  if (!destDir) return null;
200
- const scopeRoot = scope === "user" ? homedir() : cwd;
230
+ const scopeRoot = scope === "user" ? homedir2() : cwd;
201
231
  return resolveContained(scopeRoot, relative(scopeRoot, destDir), subagentFileName(agent, slug));
202
232
  }
203
233
  async function installAgentForCodingAgent(item, agent, scope, method, force, cwd) {
@@ -212,7 +242,7 @@ async function installAgentForCodingAgent(item, agent, scope, method, force, cwd
212
242
  }
213
243
  await assertOverwritable(destPath, force);
214
244
  const sourcePath = getItemSourcePath(item);
215
- const sourceFile = sourcePath ? join(sourcePath, mainFileName("agent")) : null;
245
+ const sourceFile = sourcePath ? join2(sourcePath, mainFileName("agent")) : null;
216
246
  if (method === "symlink" && sourceFile && await exists(sourceFile)) {
217
247
  await installFile(sourceFile, destPath, "symlink");
218
248
  } else {
@@ -273,12 +303,12 @@ async function planAgent(item, agents, scope, method, cwd) {
273
303
  const destPath = await resolveAgentFilePath(agent, item.slug, scope, cwd);
274
304
  if (!destPath) throw new Error(`${CODING_AGENTS[agent].name} does not support agents`);
275
305
  const sourcePath = getItemSourcePath(item);
276
- const linked = method === "symlink" && sourcePath !== null && await exists(join(sourcePath, mainFileName("agent")));
306
+ const linked = method === "symlink" && sourcePath !== null && await exists(join2(sourcePath, mainFileName("agent")));
277
307
  changes.push({
278
308
  agent,
279
309
  kind: await exists(destPath) ? "modify" : "create",
280
310
  path: destPath,
281
- detail: linked ? `symlink \u2192 ${join(sourcePath, mainFileName("agent"))}` : "agent definition file"
311
+ detail: linked ? `symlink \u2192 ${join2(sourcePath, mainFileName("agent"))}` : "agent definition file"
282
312
  });
283
313
  }
284
314
  return changes;
@@ -298,8 +328,8 @@ var agentHandler = {
298
328
  };
299
329
 
300
330
  // src/handlers/command.ts
301
- import { join as join2, relative as relative2 } from "path";
302
- import { homedir as homedir2 } from "os";
331
+ import { join as join3, relative as relative2 } from "path";
332
+ import { homedir as homedir3 } from "os";
303
333
  import { lstat as lstat2, readdir as readdir2, unlink as unlink2 } from "fs/promises";
304
334
  import chalk3 from "chalk";
305
335
  import ora2 from "ora";
@@ -307,7 +337,7 @@ var SLUG_LABEL2 = "command slug";
307
337
  async function resolveCommandFilePath(agent, slug, scope, cwd) {
308
338
  const destDir = getContentPath(agent, "command", scope, cwd);
309
339
  if (!destDir) return null;
310
- const scopeRoot = scope === "user" ? homedir2() : cwd;
340
+ const scopeRoot = scope === "user" ? homedir3() : cwd;
311
341
  return resolveContained(scopeRoot, relative2(scopeRoot, destDir), `${slug}.md`);
312
342
  }
313
343
  async function installCommandForCodingAgent(item, agent, scope, method, force, cwd) {
@@ -322,7 +352,7 @@ async function installCommandForCodingAgent(item, agent, scope, method, force, c
322
352
  }
323
353
  await assertOverwritable(destPath, force);
324
354
  const sourcePath = getItemSourcePath(item);
325
- const sourceFile = sourcePath ? join2(sourcePath, mainFileName("command")) : null;
355
+ const sourceFile = sourcePath ? join3(sourcePath, mainFileName("command")) : null;
326
356
  if (method === "symlink" && sourceFile && await exists(sourceFile)) {
327
357
  await installFile(sourceFile, destPath, "symlink");
328
358
  } else {
@@ -382,12 +412,12 @@ async function planCommand(item, agents, scope, method, cwd) {
382
412
  const destPath = await resolveCommandFilePath(agent, item.slug, scope, cwd);
383
413
  if (!destPath) throw new Error(`${CODING_AGENTS[agent].name} does not support commands`);
384
414
  const sourcePath = getItemSourcePath(item);
385
- const linked = method === "symlink" && sourcePath !== null && await exists(join2(sourcePath, mainFileName("command")));
415
+ const linked = method === "symlink" && sourcePath !== null && await exists(join3(sourcePath, mainFileName("command")));
386
416
  changes.push({
387
417
  agent,
388
418
  kind: await exists(destPath) ? "modify" : "create",
389
419
  path: destPath,
390
- detail: linked ? `symlink \u2192 ${join2(sourcePath, mainFileName("command"))}` : "slash command file"
420
+ detail: linked ? `symlink \u2192 ${join3(sourcePath, mainFileName("command"))}` : "slash command file"
391
421
  });
392
422
  }
393
423
  return changes;
@@ -407,8 +437,8 @@ var commandHandler = {
407
437
  };
408
438
 
409
439
  // src/handlers/hook.ts
410
- import { join as join3, basename, dirname } from "path";
411
- import { homedir as homedir3 } from "os";
440
+ import { join as join4, basename, dirname } from "path";
441
+ import { homedir as homedir4 } from "os";
412
442
  import { lstat as lstat3, mkdir, mkdtemp, readFile, realpath, rm, unlink as unlink3 } from "fs/promises";
413
443
  import chalk4 from "chalk";
414
444
  import ora3 from "ora";
@@ -416,10 +446,10 @@ var CLAUDE_ONLY_ERROR = "Hooks are only supported for Claude Code";
416
446
  var SCRIPT_NAME_LABEL = "hook script name";
417
447
  var SCRIPT_MODE = 493;
418
448
  function getScopeRoot(scope, cwd) {
419
- return scope === "user" ? homedir3() : cwd;
449
+ return scope === "user" ? homedir4() : cwd;
420
450
  }
421
451
  function getHooksDir(scope, cwd) {
422
- return scope === "user" ? join3(claudeUserRoot(), "hooks") : join3(getScopeRoot(scope, cwd), ".claude", "hooks");
452
+ return scope === "user" ? join4(claudeUserRoot(), "hooks") : join4(getScopeRoot(scope, cwd), ".claude", "hooks");
423
453
  }
424
454
  function getScriptPath(scope, scriptName) {
425
455
  switch (scope) {
@@ -465,13 +495,13 @@ async function assertRegularFileOrMissing(path) {
465
495
  async function loadScriptBytes(item, scriptFile, hooksDir) {
466
496
  const sourcePath = getItemSourcePath(item);
467
497
  if (sourcePath && await exists(sourcePath)) {
468
- return readFile(join3(sourcePath, scriptFile));
498
+ return readFile(join4(sourcePath, scriptFile));
469
499
  }
470
- const fetchDir = await mkdtemp(join3(dirname(hooksDir), ".seedr-hook-"));
500
+ const fetchDir = await mkdtemp(join4(dirname(hooksDir), ".seedr-hook-"));
471
501
  try {
472
- const contentDir = join3(fetchDir, "content");
502
+ const contentDir = join4(fetchDir, "content");
473
503
  await fetchItemToDestination(item, contentDir);
474
- return await readFile(join3(contentDir, scriptFile));
504
+ return await readFile(join4(contentDir, scriptFile));
475
505
  } finally {
476
506
  await rm(fetchDir, { recursive: true, force: true });
477
507
  }
@@ -1219,18 +1249,18 @@ var settingsHandler = {
1219
1249
  };
1220
1250
 
1221
1251
  // src/handlers/plugin.ts
1222
- import { join as join5, relative as relative3 } from "path";
1252
+ import { join as join6, relative as relative3 } from "path";
1223
1253
  import { mkdir as mkdir3, mkdtemp as mkdtemp2, rm as rm2, stat } from "fs/promises";
1224
1254
  import chalk7 from "chalk";
1225
1255
  import ora6 from "ora";
1226
1256
 
1227
1257
  // src/handlers/pluginStores.ts
1228
- import { homedir as homedir4 } from "os";
1229
- import { basename as basename2, dirname as dirname3, isAbsolute, join as join4 } from "path";
1258
+ import { homedir as homedir5 } from "os";
1259
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join5 } from "path";
1230
1260
  import { mkdir as mkdir2, readFile as readFile3, writeFile } from "fs/promises";
1231
1261
  import { execFile } from "child_process";
1232
1262
  import { promisify } from "util";
1233
- var home = homedir4();
1263
+ var home = homedir5();
1234
1264
  var CLAUDE_MANIFEST = ".claude-plugin/plugin.json";
1235
1265
  async function readTextOrEmpty2(path) {
1236
1266
  try {
@@ -1240,7 +1270,7 @@ async function readTextOrEmpty2(path) {
1240
1270
  }
1241
1271
  }
1242
1272
  async function writeText(path, text) {
1243
- await mkdir2(join4(path, ".."), { recursive: true });
1273
+ await mkdir2(join5(path, ".."), { recursive: true });
1244
1274
  await writeFile(path, text, "utf-8");
1245
1275
  }
1246
1276
  function getPluginId(name, marketplace) {
@@ -1255,8 +1285,8 @@ function manifestsFor(ownManifest) {
1255
1285
  return ownManifest ? [ownManifest, CLAUDE_MANIFEST] : [CLAUDE_MANIFEST];
1256
1286
  }
1257
1287
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1258
- var CLAUDE_SELF_MARKETPLACE = join4(".claude-plugin", "marketplace.json");
1259
- var COPILOT_SELF_MARKETPLACE = join4(".github", "plugin", "marketplace.json");
1288
+ var CLAUDE_SELF_MARKETPLACE = join5(".claude-plugin", "marketplace.json");
1289
+ var COPILOT_SELF_MARKETPLACE = join5(".github", "plugin", "marketplace.json");
1260
1290
  async function alignSelfMarketplace(path, marketplace, name, item) {
1261
1291
  const existing = await exists(path) ? await readJson(path) : {};
1262
1292
  const plugins = Array.isArray(existing.plugins) ? existing.plugins : [];
@@ -1272,17 +1302,17 @@ async function alignSelfMarketplace(path, marketplace, name, item) {
1272
1302
  });
1273
1303
  }
1274
1304
  async function ensureSelfMarketplace(contentPath, marketplace, name, item, alsoAlign = []) {
1275
- await alignSelfMarketplace(join4(contentPath, CLAUDE_SELF_MARKETPLACE), marketplace, name, item);
1305
+ await alignSelfMarketplace(join5(contentPath, CLAUDE_SELF_MARKETPLACE), marketplace, name, item);
1276
1306
  for (const relative4 of alsoAlign) {
1277
- const path = join4(contentPath, relative4);
1307
+ const path = join5(contentPath, relative4);
1278
1308
  if (await exists(path)) await alignSelfMarketplace(path, marketplace, name, item);
1279
1309
  }
1280
1310
  }
1281
- var CLAUDE_PLUGINS_DIR = join4(claudeUserRoot(), "plugins");
1282
- var CLAUDE_CACHE_DIR = join4(CLAUDE_PLUGINS_DIR, "cache");
1283
- var CLAUDE_INSTALLED_PATH = join4(CLAUDE_PLUGINS_DIR, "installed_plugins.json");
1284
- var CLAUDE_KNOWN_MARKETPLACES_PATH = join4(CLAUDE_PLUGINS_DIR, "known_marketplaces.json");
1285
- var CLAUDE_MARKETPLACES_DIR = join4(CLAUDE_PLUGINS_DIR, "marketplaces");
1311
+ var CLAUDE_PLUGINS_DIR = join5(claudeUserRoot(), "plugins");
1312
+ var CLAUDE_CACHE_DIR = join5(CLAUDE_PLUGINS_DIR, "cache");
1313
+ var CLAUDE_INSTALLED_PATH = join5(CLAUDE_PLUGINS_DIR, "installed_plugins.json");
1314
+ var CLAUDE_KNOWN_MARKETPLACES_PATH = join5(CLAUDE_PLUGINS_DIR, "known_marketplaces.json");
1315
+ var CLAUDE_MARKETPLACES_DIR = join5(CLAUDE_PLUGINS_DIR, "marketplaces");
1286
1316
  function isScopeEntry(entry, scope, cwd) {
1287
1317
  return entry.scope === scope && (scope === "user" || entry.projectPath === cwd);
1288
1318
  }
@@ -1446,8 +1476,21 @@ var claudeStore = {
1446
1476
  }
1447
1477
  };
1448
1478
  var COPILOT_DIR = copilotUserRoot();
1449
- var COPILOT_SETTINGS_PATH = join4(COPILOT_DIR, "settings.json");
1450
- var COPILOT_PLUGINS_DIR = join4(COPILOT_DIR, "installed-plugins");
1479
+ var COPILOT_SETTINGS_PATH = join5(COPILOT_DIR, "settings.json");
1480
+ var COPILOT_PLUGINS_DIR = join5(COPILOT_DIR, "installed-plugins");
1481
+ var COPILOT_HOOKS_DIR = join5(COPILOT_DIR, "hooks");
1482
+ var COPILOT_PLUGIN_HOOKS = join5("hooks", "hooks-copilot.json");
1483
+ function copilotHooksPath(name) {
1484
+ return resolveContained(COPILOT_HOOKS_DIR, name, "hooks.json");
1485
+ }
1486
+ async function writeCopilotHooks(cachePath, name) {
1487
+ const source = join5(cachePath, COPILOT_PLUGIN_HOOKS);
1488
+ if (!await exists(source)) return;
1489
+ const destination = await copilotHooksPath(name);
1490
+ const hooks = (await readFile3(source, "utf8")).replaceAll("${PLUGIN_ROOT}", cachePath);
1491
+ await mkdir2(dirname3(destination), { recursive: true });
1492
+ await writeFile(destination, hooks);
1493
+ }
1451
1494
  var copilotStore = {
1452
1495
  manifestPaths: manifestsFor(),
1453
1496
  userGlobal: true,
@@ -1515,7 +1558,14 @@ var copilotStore = {
1515
1558
  path: COPILOT_SETTINGS_PATH,
1516
1559
  detail: `enabledPlugins["${context.pluginId}"] = true`,
1517
1560
  apply: () => enableInJsonSettings(COPILOT_SETTINGS_PATH, context.pluginId)
1518
- }
1561
+ },
1562
+ ...context.cachePath ? [
1563
+ {
1564
+ path: join5(COPILOT_HOOKS_DIR, context.name, "hooks.json"),
1565
+ detail: `${COPILOT_PLUGIN_HOOKS} with \${PLUGIN_ROOT} resolved \u2014 only when the plugin ships one`,
1566
+ apply: () => writeCopilotHooks(context.cachePath, context.name)
1567
+ }
1568
+ ] : []
1519
1569
  ],
1520
1570
  async listInstalled() {
1521
1571
  const settings = await readJson(COPILOT_SETTINGS_PATH);
@@ -1526,6 +1576,7 @@ var copilotStore = {
1526
1576
  if (!disabled) return false;
1527
1577
  const split = splitPluginId(pluginId);
1528
1578
  if (split) {
1579
+ await removePathEntry(dirname3(await copilotHooksPath(split.name)));
1529
1580
  let tree;
1530
1581
  try {
1531
1582
  tree = await resolveContained(COPILOT_PLUGINS_DIR, split.marketplace, split.name);
@@ -1544,8 +1595,8 @@ var copilotStore = {
1544
1595
  }
1545
1596
  };
1546
1597
  var CODEX_DIR = codexUserRoot();
1547
- var CODEX_CONFIG_PATH = join4(CODEX_DIR, "config.toml");
1548
- var CODEX_CACHE_DIR = join4(CODEX_DIR, "plugins", "cache");
1598
+ var CODEX_CONFIG_PATH = join5(CODEX_DIR, "config.toml");
1599
+ var CODEX_CACHE_DIR = join5(CODEX_DIR, "plugins", "cache");
1549
1600
  function toCodexMarketplaceTable(marketplace, url, revision) {
1550
1601
  return {
1551
1602
  keyPath: ["marketplaces", marketplace],
@@ -1617,9 +1668,9 @@ var codexStore = {
1617
1668
  }
1618
1669
  };
1619
1670
  function openCodeConfigPath(scope, cwd) {
1620
- return scope === "user" ? join4(openCodeUserConfigDir(), "opencode.json") : join4(cwd, "opencode.json");
1671
+ return scope === "user" ? join5(openCodeUserConfigDir(), "opencode.json") : join5(cwd, "opencode.json");
1621
1672
  }
1622
- var OPENCODE_PLUGINS_DIR = join4(openCodeUserConfigDir(), "plugins");
1673
+ var OPENCODE_PLUGINS_DIR = join5(openCodeUserConfigDir(), "plugins");
1623
1674
  function toOpenCodePluginSpec(item, name, cachePath) {
1624
1675
  if (isFirstParty(item.sourceType)) {
1625
1676
  if (!cachePath) throw new Error(`OpenCode cannot install "${name}": a first-party plugin needs a directory to load from`);
@@ -1691,9 +1742,9 @@ var openCodeStore = {
1691
1742
  return true;
1692
1743
  }
1693
1744
  };
1694
- var GEMINI_CONFIG_DIR = join4(home, ".gemini", "config");
1695
- var GEMINI_PLUGINS_DIR = join4(GEMINI_CONFIG_DIR, "plugins");
1696
- var GEMINI_IMPORT_MANIFEST_PATH = join4(GEMINI_CONFIG_DIR, "import_manifest.json");
1745
+ var GEMINI_CONFIG_DIR = join5(home, ".gemini", "config");
1746
+ var GEMINI_PLUGINS_DIR = join5(GEMINI_CONFIG_DIR, "plugins");
1747
+ var GEMINI_IMPORT_MANIFEST_PATH = join5(GEMINI_CONFIG_DIR, "import_manifest.json");
1697
1748
  var antigravityStore = {
1698
1749
  manifestPaths: manifestsFor(),
1699
1750
  userGlobal: true,
@@ -1715,7 +1766,7 @@ var antigravityStore = {
1715
1766
  * (compound-engineering does) is left exactly as it is.
1716
1767
  */
1717
1768
  async prepareTree(contentPath, { name, version, item }) {
1718
- const manifestPath = join4(contentPath, "plugin.json");
1769
+ const manifestPath = join5(contentPath, "plugin.json");
1719
1770
  if (await exists(manifestPath)) return;
1720
1771
  await writeJson(manifestPath, {
1721
1772
  name,
@@ -1814,7 +1865,7 @@ function resolvePluginIdentity(item, pluginJson) {
1814
1865
  }
1815
1866
  async function readManifestFromDir(dir, manifestPaths) {
1816
1867
  for (const manifest of manifestPaths) {
1817
- const path = join5(dir, ...manifest.split("/"));
1868
+ const path = join6(dir, ...manifest.split("/"));
1818
1869
  if (await exists(path)) return readJson(path);
1819
1870
  }
1820
1871
  return {};
@@ -1833,7 +1884,7 @@ async function readManifestForPlan(item, manifestPaths) {
1833
1884
  async function createStagingDir(cacheRoot, slug) {
1834
1885
  const tmpRoot = await resolveContained(cacheRoot, ".tmp");
1835
1886
  await mkdir3(tmpRoot, { recursive: true });
1836
- const staging = await mkdtemp2(join5(tmpRoot, `${slug}-`));
1887
+ const staging = await mkdtemp2(join6(tmpRoot, `${slug}-`));
1837
1888
  return resolveContained(cacheRoot, relative3(cacheRoot, staging));
1838
1889
  }
1839
1890
  async function stageContent(item, contentPath) {
@@ -1901,7 +1952,7 @@ async function installPluginForAgent(item, agent, scope, _method, force, cwd) {
1901
1952
  const cacheRoot = store.cacheRoot ?? (isFirstParty(item.sourceType) ? store.firstPartyCacheRoot ?? null : null);
1902
1953
  const staging = cacheRoot ? await createStagingDir(cacheRoot, item.slug) : null;
1903
1954
  try {
1904
- const contentPath = staging ? join5(staging, "content") : null;
1955
+ const contentPath = staging ? join6(staging, "content") : null;
1905
1956
  const fetched = contentPath ? await stageContent(item, contentPath) : null;
1906
1957
  const pluginJson = contentPath ? await readManifestFromDir(contentPath, store.manifestPaths) : await readManifestForPlan(item, store.manifestPaths);
1907
1958
  const { name, version } = resolvePluginIdentity(item, pluginJson);
@@ -1929,7 +1980,7 @@ async function installPluginForAgent(item, agent, scope, _method, force, cwd) {
1929
1980
  ...store.mutations(context)
1930
1981
  ]);
1931
1982
  spinner.succeed(brand(`Installed ${item.name} for ${CODING_AGENTS[agent].name}`));
1932
- return { agent, success: true, path: cachePath ?? "" };
1983
+ return { agent, success: true, path: cachePath ?? "", version };
1933
1984
  } finally {
1934
1985
  if (staging) await rm2(staging, { recursive: true, force: true });
1935
1986
  }
@@ -2056,35 +2107,35 @@ import chalk8 from "chalk";
2056
2107
  import ora7 from "ora";
2057
2108
 
2058
2109
  // src/handlers/ruleTargets.ts
2059
- import { homedir as homedir5 } from "os";
2060
- import { join as join6 } from "path";
2061
- var home2 = homedir5();
2110
+ import { homedir as homedir6 } from "os";
2111
+ import { join as join7 } from "path";
2112
+ var home2 = homedir6();
2062
2113
  var markdownFile = (slug) => `${slug}.md`;
2063
2114
  var claudeTarget = {
2064
2115
  kind: "file",
2065
- dir: (scope, cwd) => join6(scope === "user" ? claudeUserRoot() : join6(cwd, ".claude"), "rules"),
2116
+ dir: (scope, cwd) => join7(scope === "user" ? claudeUserRoot() : join7(cwd, ".claude"), "rules"),
2066
2117
  fileName: markdownFile,
2067
2118
  keepsFrontmatter: true
2068
2119
  };
2069
2120
  var antigravityTarget = {
2070
2121
  kind: "file",
2071
- dir: (scope, cwd) => join6(scope === "user" ? join6(home2, ".gemini", "config") : join6(cwd, ".agents"), "rules"),
2122
+ dir: (scope, cwd) => join7(scope === "user" ? join7(home2, ".gemini", "config") : join7(cwd, ".agents"), "rules"),
2072
2123
  fileName: markdownFile,
2073
2124
  keepsFrontmatter: true
2074
2125
  };
2075
2126
  var copilotTarget = {
2076
2127
  kind: "file",
2077
- dir: (scope, cwd) => join6(scope === "user" ? copilotUserRoot() : join6(cwd, ".github"), "instructions"),
2128
+ dir: (scope, cwd) => join7(scope === "user" ? copilotUserRoot() : join7(cwd, ".github"), "instructions"),
2078
2129
  fileName: (slug) => `${slug}.instructions.md`,
2079
2130
  keepsFrontmatter: true
2080
2131
  };
2081
2132
  var codexTarget = {
2082
2133
  kind: "section",
2083
- file: (scope, cwd) => scope === "user" ? join6(codexUserRoot(), "AGENTS.md") : join6(cwd, "AGENTS.md")
2134
+ file: (scope, cwd) => scope === "user" ? join7(codexUserRoot(), "AGENTS.md") : join7(cwd, "AGENTS.md")
2084
2135
  };
2085
2136
  var openCodeTarget = {
2086
2137
  kind: "section",
2087
- file: (scope, cwd) => scope === "user" ? join6(openCodeUserConfigDir(), "AGENTS.md") : join6(cwd, "AGENTS.md")
2138
+ file: (scope, cwd) => scope === "user" ? join7(openCodeUserConfigDir(), "AGENTS.md") : join7(cwd, "AGENTS.md")
2088
2139
  };
2089
2140
  var TARGETS = {
2090
2141
  claude: claudeTarget,
@@ -2354,6 +2405,24 @@ function formatPlan(changes) {
2354
2405
  }
2355
2406
  return lines;
2356
2407
  }
2408
+ async function noteInstall(item, results, scope) {
2409
+ const succeeded = results.filter((r) => r.success);
2410
+ const installed = succeeded.map((r) => r.agent);
2411
+ if (installed.length === 0) return;
2412
+ try {
2413
+ await recordInstall({
2414
+ type: item.type,
2415
+ slug: item.slug,
2416
+ version: succeeded.find((r) => r.version)?.version ?? item.version,
2417
+ contentDigest: item.contentDigest,
2418
+ agents: installed,
2419
+ scope,
2420
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
2421
+ });
2422
+ } catch (error2) {
2423
+ warn(`Installed, but the install record could not be written: ${error2.message}`);
2424
+ }
2425
+ }
2357
2426
  function summarizeResults(results) {
2358
2427
  const successful = results.filter((r) => r.success);
2359
2428
  const failed = results.filter((r) => !r.success);
@@ -2526,6 +2595,7 @@ async function runAdd(name, options, cwd = process.cwd()) {
2526
2595
  console.log();
2527
2596
  const results = await handler.install(item, agents, scope, method, decideForce(options), cwd);
2528
2597
  void trackInstalls(item.slug, item.type, results, scope);
2598
+ await noteInstall(item, results, scope);
2529
2599
  const exitCode = printInstallSummary(results);
2530
2600
  if (exitCode === 0) outro("Installation complete");
2531
2601
  return exitCode;
@@ -2645,11 +2715,37 @@ ${itemType.toUpperCase()}S`));
2645
2715
  );
2646
2716
  return 0;
2647
2717
  }
2718
+ async function installedVersionAnnotator() {
2719
+ let ledger;
2720
+ try {
2721
+ ledger = await readLedger();
2722
+ } catch {
2723
+ return () => "";
2724
+ }
2725
+ let current;
2726
+ try {
2727
+ const items = await listItems();
2728
+ current = new Map(
2729
+ items.filter((item) => item.contentDigest).map((item) => [ledgerKey(item.type, item.slug), item.contentDigest])
2730
+ );
2731
+ } catch {
2732
+ current = /* @__PURE__ */ new Map();
2733
+ }
2734
+ return (type, slug) => {
2735
+ const key = ledgerKey(type, slug);
2736
+ const entry = ledger.items[key];
2737
+ if (!entry?.version) return "";
2738
+ const available = current.get(key);
2739
+ const stale = entry.contentDigest && available && available !== entry.contentDigest;
2740
+ return chalk10.gray(` ${entry.version}`) + (stale ? chalk10.yellow(" \u2192 update available") : "");
2741
+ };
2742
+ }
2648
2743
  async function listInstalled(params) {
2649
2744
  console.log(brand(`
2650
2745
  Installed items (${params.scope} scope):
2651
2746
  `));
2652
2747
  const groups = await collectInstalledItems(params);
2748
+ const annotate = await installedVersionAnnotator();
2653
2749
  let total = 0;
2654
2750
  let currentType = null;
2655
2751
  for (const group of groups) {
@@ -2660,7 +2756,7 @@ Installed items (${params.scope} scope):
2660
2756
  }
2661
2757
  console.log(chalk10.blue(` ${CODING_AGENTS[group.agent].name}`));
2662
2758
  for (const slug of group.slugs) {
2663
- console.log(` ${chalk10.white(slug)}`);
2759
+ console.log(` ${chalk10.white(slug)}${annotate(group.type, slug)}`);
2664
2760
  total++;
2665
2761
  }
2666
2762
  console.log("");
@@ -2707,6 +2803,7 @@ async function removeFromAgents(slug, type, agents, scope, cwd) {
2707
2803
  const handler = getHandler(type);
2708
2804
  if (!handler) return 0;
2709
2805
  let successCount = 0;
2806
+ const removedFrom = [];
2710
2807
  for (const agent of agents) {
2711
2808
  if (!isTypeSupported(type, agent)) {
2712
2809
  ora8().start().info(
@@ -2718,11 +2815,18 @@ async function removeFromAgents(slug, type, agents, scope, cwd) {
2718
2815
  const removed = await handler.uninstall(slug, agent, scope, cwd);
2719
2816
  if (removed) {
2720
2817
  spinner.succeed(brand(`Removed from ${CODING_AGENTS[agent].name}`));
2818
+ removedFrom.push(agent);
2721
2819
  successCount++;
2722
2820
  } else {
2723
2821
  spinner.info(chalk11.gray(`Not found in ${CODING_AGENTS[agent].name}`));
2724
2822
  }
2725
2823
  }
2824
+ if (removedFrom.length > 0) {
2825
+ try {
2826
+ await recordRemoval(type, slug, removedFrom);
2827
+ } catch {
2828
+ }
2829
+ }
2726
2830
  return successCount;
2727
2831
  }
2728
2832
  function validateRemoveRequest(name, options) {
@@ -2815,7 +2919,7 @@ var removeCommand = new Command3("remove").alias("rm").description("Remove an in
2815
2919
  import { Command as Command4 } from "commander";
2816
2920
  import chalk12 from "chalk";
2817
2921
  import ora9 from "ora";
2818
- import { join as join7 } from "path";
2922
+ import { join as join8 } from "path";
2819
2923
  function readmeFor(agent) {
2820
2924
  return `# ${CODING_AGENTS[agent].name} Configuration
2821
2925
 
@@ -2833,7 +2937,7 @@ async function initializeAgent(agent, cwd) {
2833
2937
  const path = getAgentPath(agent, "project", cwd);
2834
2938
  if (await exists(path)) return false;
2835
2939
  await ensureDir(path);
2836
- await writeTextFile(join7(path, "README.md"), readmeFor(agent));
2940
+ await writeTextFile(join8(path, "README.md"), readmeFor(agent));
2837
2941
  return true;
2838
2942
  }
2839
2943
  async function runInit(options, cwd = process.cwd()) {
@@ -2894,7 +2998,7 @@ var initCommand = new Command4("init").description("Initialize coding agent conf
2894
2998
  // package.json
2895
2999
  var package_default = {
2896
3000
  name: "@danieldeusing/seedr",
2897
- version: "0.1.129",
3001
+ version: "0.1.130",
2898
3002
  description: "Seed your projects with AI configurations",
2899
3003
  type: "module",
2900
3004
  bin: {
package/dist/index.d.ts CHANGED
@@ -72,6 +72,13 @@ interface InstallResult {
72
72
  success: boolean;
73
73
  path: string;
74
74
  error?: string;
75
+ /**
76
+ * The version that landed, when it differs from the registry item's own.
77
+ * A plugin carries its version in its manifest and the registry item carries
78
+ * a separate one for the catalogue entry — recording the item's would say
79
+ * "1.0.20" for a plugin every agent knows as 0.16.2.
80
+ */
81
+ version?: string;
75
82
  }
76
83
 
77
84
  declare function installSkill(item: RegistryItem, agents: CodingAgent[], scope: InstallScope, method: InstallMethod, force: boolean, cwd?: string): Promise<InstallResult[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danieldeusing/seedr",
3
- "version": "0.1.129",
3
+ "version": "0.1.130",
4
4
  "description": "Seed your projects with AI configurations",
5
5
  "type": "module",
6
6
  "bin": {