@danieldeusing/seedr 0.1.108 → 0.1.109

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.
@@ -1362,6 +1362,7 @@ export {
1362
1362
  canonicalAgents,
1363
1363
  AGENT_COMPATIBILITY,
1364
1364
  isTypeSupported,
1365
+ isFirstParty,
1365
1366
  SLUG_PATTERN2 as SLUG_PATTERN,
1366
1367
  MAX_SLUG_LENGTH2 as MAX_SLUG_LENGTH,
1367
1368
  isValidSlug2 as isValidSlug,
package/dist/cli.js CHANGED
@@ -37,6 +37,7 @@ import {
37
37
  info,
38
38
  installFile,
39
39
  intro,
40
+ isFirstParty,
40
41
  isInteractive,
41
42
  isLegacyAgent,
42
43
  isTypeSupported,
@@ -67,7 +68,7 @@ import {
67
68
  writeFileAtomic,
68
69
  writeJson,
69
70
  writeTextFile
70
- } from "./chunk-3KPB5CCX.js";
71
+ } from "./chunk-USS5HTCG.js";
71
72
 
72
73
  // src/cli.ts
73
74
  import { Command as Command5 } from "commander";
@@ -147,7 +148,7 @@ function isTelemetryDisabled(env = process.env) {
147
148
  return env[TELEMETRY_OPT_OUT_VARIABLE] !== void 0;
148
149
  }
149
150
  function cliVersion() {
150
- return true ? "0.1.108" : "dev";
151
+ return true ? "0.1.109" : "dev";
151
152
  }
152
153
  async function sendEvent(event) {
153
154
  try {
@@ -1225,7 +1226,7 @@ import ora6 from "ora";
1225
1226
 
1226
1227
  // src/handlers/pluginStores.ts
1227
1228
  import { homedir as homedir4 } from "os";
1228
- import { join as join4 } from "path";
1229
+ import { basename as basename2, isAbsolute, join as join4 } from "path";
1229
1230
  import { mkdir as mkdir2, readFile as readFile3, writeFile } from "fs/promises";
1230
1231
  import { execFile } from "child_process";
1231
1232
  import { promisify } from "util";
@@ -1253,6 +1254,23 @@ function splitPluginId(pluginId) {
1253
1254
  function manifestsFor(ownManifest) {
1254
1255
  return ownManifest ? [ownManifest, CLAUDE_MANIFEST] : [CLAUDE_MANIFEST];
1255
1256
  }
1257
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1258
+ async function ensureSelfMarketplace(contentPath, marketplace, name, item) {
1259
+ const dir = join4(contentPath, ".claude-plugin");
1260
+ const path = join4(dir, "marketplace.json");
1261
+ const existing = await exists(path) ? await readJson(path) : {};
1262
+ const plugins = Array.isArray(existing.plugins) ? existing.plugins : [];
1263
+ const listed = plugins.some((entry) => isRecord(entry) && entry.name === name);
1264
+ const description = item.description ? { description: item.description } : {};
1265
+ await mkdir2(dir, { recursive: true });
1266
+ await writeJson(path, {
1267
+ ...existing,
1268
+ name: marketplace,
1269
+ owner: existing.owner ?? { name: item.author?.name ?? "seedr" },
1270
+ ...existing.description === void 0 ? description : {},
1271
+ plugins: listed ? plugins : [...plugins, { name, source: "./", ...description }]
1272
+ });
1273
+ }
1256
1274
  var CLAUDE_PLUGINS_DIR = join4(claudeUserRoot(), "plugins");
1257
1275
  var CLAUDE_CACHE_DIR = join4(CLAUDE_PLUGINS_DIR, "cache");
1258
1276
  var CLAUDE_INSTALLED_PATH = join4(CLAUDE_PLUGINS_DIR, "installed_plugins.json");
@@ -1277,14 +1295,41 @@ async function disableInJsonSettings(path, pluginId) {
1277
1295
  var claudeStore = {
1278
1296
  manifestPaths: manifestsFor(),
1279
1297
  userGlobal: false,
1298
+ firstParty: true,
1280
1299
  cacheRoot: CLAUDE_CACHE_DIR,
1281
1300
  cachePath: (marketplace, name, version) => resolveContained(CLAUDE_CACHE_DIR, marketplace, name, version),
1301
+ async prepareTree(contentPath, { name, marketplace, item }) {
1302
+ if (isFirstParty(item.sourceType)) await ensureSelfMarketplace(contentPath, marketplace, name, item);
1303
+ },
1282
1304
  /**
1283
1305
  * Claude Code reports a plugin as "orphaned" unless its marketplace is both
1284
1306
  * recorded and cloned, so this is the one store that fetches a second
1285
1307
  * repository. The other agents only record the source.
1308
+ *
1309
+ * A first-party plugin has no repository: its installed tree is the
1310
+ * marketplace (see `ensureSelfMarketplace`), recorded as the `directory`
1311
+ * source Claude writes for `claude plugin marketplace add <dir>`. Always
1312
+ * upserted, because the path carries the version and moves with it.
1286
1313
  */
1287
- async ensureMarketplace(marketplace, item) {
1314
+ async ensureMarketplace(marketplace, item, cachePath) {
1315
+ if (isFirstParty(item.sourceType)) {
1316
+ if (!cachePath) return [];
1317
+ return [
1318
+ {
1319
+ path: CLAUDE_KNOWN_MARKETPLACES_PATH,
1320
+ detail: `marketplace "${marketplace}" from directory ${cachePath}`,
1321
+ apply: async () => {
1322
+ const current = await readJson(CLAUDE_KNOWN_MARKETPLACES_PATH);
1323
+ current[marketplace] = {
1324
+ source: { source: "directory", path: cachePath },
1325
+ installLocation: cachePath,
1326
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
1327
+ };
1328
+ await writeJson(CLAUDE_KNOWN_MARKETPLACES_PATH, current);
1329
+ }
1330
+ }
1331
+ ];
1332
+ }
1288
1333
  const known = await readJson(CLAUDE_KNOWN_MARKETPLACES_PATH);
1289
1334
  if (known[marketplace]) return [];
1290
1335
  const repo = marketplaceRepo(item);
@@ -1341,7 +1386,7 @@ var claudeStore = {
1341
1386
  version: context.version,
1342
1387
  installedAt: now,
1343
1388
  lastUpdated: now,
1344
- gitCommitSha: context.gitCommitSha
1389
+ ...context.gitCommitSha !== void 0 ? { gitCommitSha: context.gitCommitSha } : {}
1345
1390
  };
1346
1391
  const existing = registry.plugins[context.pluginId] || [];
1347
1392
  registry.plugins[context.pluginId] = [
@@ -1381,6 +1426,13 @@ var claudeStore = {
1381
1426
  try {
1382
1427
  await removePathEntry(await resolveContained(CLAUDE_CACHE_DIR, installPath));
1383
1428
  } catch {
1429
+ continue;
1430
+ }
1431
+ const marketplace = splitPluginId(pluginId)?.marketplace;
1432
+ const known = await readJson(CLAUDE_KNOWN_MARKETPLACES_PATH);
1433
+ if (marketplace && known[marketplace]?.source.source === "directory" && known[marketplace].source.path === installPath) {
1434
+ delete known[marketplace];
1435
+ await writeJson(CLAUDE_KNOWN_MARKETPLACES_PATH, known);
1384
1436
  }
1385
1437
  }
1386
1438
  return true;
@@ -1392,9 +1444,34 @@ var COPILOT_PLUGINS_DIR = join4(COPILOT_DIR, "installed-plugins");
1392
1444
  var copilotStore = {
1393
1445
  manifestPaths: manifestsFor(),
1394
1446
  userGlobal: true,
1447
+ firstParty: true,
1395
1448
  cacheRoot: COPILOT_PLUGINS_DIR,
1396
1449
  cachePath: (marketplace, name) => resolveContained(COPILOT_PLUGINS_DIR, marketplace, name),
1397
- async ensureMarketplace(marketplace, item) {
1450
+ async prepareTree(contentPath, { name, marketplace, item }) {
1451
+ if (isFirstParty(item.sourceType)) await ensureSelfMarketplace(contentPath, marketplace, name, item);
1452
+ },
1453
+ /**
1454
+ * A first-party plugin's installed tree is its marketplace, recorded the way
1455
+ * `copilot plugin marketplace add <dir>` records one: a `directory` source.
1456
+ * Direct installs from a path exist too, but Copilot 1.0.80 announces them
1457
+ * as deprecated in favour of `plugin@marketplace`, which this is.
1458
+ */
1459
+ async ensureMarketplace(marketplace, item, cachePath) {
1460
+ if (isFirstParty(item.sourceType)) {
1461
+ if (!cachePath) return [];
1462
+ return [
1463
+ {
1464
+ path: COPILOT_SETTINGS_PATH,
1465
+ detail: `extraKnownMarketplaces["${marketplace}"] = directory ${cachePath}`,
1466
+ apply: async () => {
1467
+ const current = await readJson(COPILOT_SETTINGS_PATH);
1468
+ current.extraKnownMarketplaces = current.extraKnownMarketplaces || {};
1469
+ current.extraKnownMarketplaces[marketplace] = { source: { source: "directory", path: cachePath } };
1470
+ await writeJson(COPILOT_SETTINGS_PATH, current);
1471
+ }
1472
+ }
1473
+ ];
1474
+ }
1398
1475
  const repo = marketplaceRepo(item);
1399
1476
  if (!repo) return [];
1400
1477
  const settings = await readJson(COPILOT_SETTINGS_PATH);
@@ -1428,9 +1505,18 @@ var copilotStore = {
1428
1505
  if (!disabled) return false;
1429
1506
  const split = splitPluginId(pluginId);
1430
1507
  if (split) {
1508
+ let tree;
1431
1509
  try {
1432
- await removePathEntry(await resolveContained(COPILOT_PLUGINS_DIR, split.marketplace, split.name));
1510
+ tree = await resolveContained(COPILOT_PLUGINS_DIR, split.marketplace, split.name);
1511
+ await removePathEntry(tree);
1433
1512
  } catch {
1513
+ return true;
1514
+ }
1515
+ const settings = await readJson(COPILOT_SETTINGS_PATH);
1516
+ const marketplace = settings.extraKnownMarketplaces?.[split.marketplace];
1517
+ if (marketplace?.source.source === "directory" && marketplace.source.path === tree) {
1518
+ delete settings.extraKnownMarketplaces[split.marketplace];
1519
+ await writeJson(COPILOT_SETTINGS_PATH, settings);
1434
1520
  }
1435
1521
  }
1436
1522
  return true;
@@ -1460,6 +1546,8 @@ async function upsertCodex(table) {
1460
1546
  var codexStore = {
1461
1547
  manifestPaths: manifestsFor(".codex-plugin/plugin.json"),
1462
1548
  userGlobal: true,
1549
+ // `[marketplaces.<name>]` is git-sourced and nothing else has been observed.
1550
+ firstParty: false,
1463
1551
  cacheRoot: CODEX_CACHE_DIR,
1464
1552
  // Real cache entries on disk are <marketplace>/<name>/<version> — e.g.
1465
1553
  // claude-plugins-official/claude-code-setup/1.0.0. Dropping the version
@@ -1510,7 +1598,12 @@ var codexStore = {
1510
1598
  function openCodeConfigPath(scope, cwd) {
1511
1599
  return scope === "user" ? join4(openCodeUserConfigDir(), "opencode.json") : join4(cwd, "opencode.json");
1512
1600
  }
1513
- function toOpenCodePluginSpec(item, name) {
1601
+ var OPENCODE_PLUGINS_DIR = join4(openCodeUserConfigDir(), "plugins");
1602
+ function toOpenCodePluginSpec(item, name, cachePath) {
1603
+ if (isFirstParty(item.sourceType)) {
1604
+ if (!cachePath) throw new Error(`OpenCode cannot install "${name}": a first-party plugin needs a directory to load from`);
1605
+ return cachePath;
1606
+ }
1514
1607
  const source = item.pluginSource;
1515
1608
  if (source?.path) {
1516
1609
  throw new Error(
@@ -1522,19 +1615,25 @@ function toOpenCodePluginSpec(item, name) {
1522
1615
  const pinned = source?.sha ?? item.sourceRevision;
1523
1616
  return `${name}@git+https://github.com/${repo}.git${pinned ? `#${pinned}` : ""}`;
1524
1617
  }
1618
+ var isPathSpec = (spec) => isAbsolute(spec) || spec.startsWith("~") || spec.startsWith(".");
1525
1619
  function specName(spec) {
1620
+ if (isPathSpec(spec)) return basename2(spec);
1526
1621
  const at = spec.indexOf("@", 1);
1527
1622
  return at > 0 ? spec.slice(0, at) : spec;
1528
1623
  }
1529
1624
  var openCodeStore = {
1530
1625
  manifestPaths: manifestsFor(),
1531
1626
  userGlobal: false,
1627
+ firstParty: true,
1532
1628
  cacheRoot: null,
1533
- // OpenCode installs the module from the spec; seedr writes no tree.
1534
- cachePath: async () => null,
1629
+ firstPartyCacheRoot: OPENCODE_PLUGINS_DIR,
1630
+ // OpenCode installs a remote module from its spec, so seedr writes no tree;
1631
+ // only a first-party plugin, which has no spec, is copied to a directory
1632
+ // OpenCode can load by path.
1633
+ cachePath: (_marketplace, name, _version, item) => isFirstParty(item.sourceType) ? resolveContained(OPENCODE_PLUGINS_DIR, name) : Promise.resolve(null),
1535
1634
  mutations: (context) => {
1536
1635
  const path = openCodeConfigPath(context.scope, context.cwd);
1537
- const spec = toOpenCodePluginSpec(context.item, context.name);
1636
+ const spec = toOpenCodePluginSpec(context.item, context.name, context.cachePath);
1538
1637
  return [
1539
1638
  {
1540
1639
  path,
@@ -1562,6 +1661,12 @@ var openCodeStore = {
1562
1661
  if (remaining.length === plugins.length) return false;
1563
1662
  config.plugin = remaining;
1564
1663
  await writeJson(path, config);
1664
+ if (plugins.some((entry) => specName(entry) === name && isPathSpec(entry))) {
1665
+ try {
1666
+ await removePathEntry(await resolveContained(OPENCODE_PLUGINS_DIR, name));
1667
+ } catch {
1668
+ }
1669
+ }
1565
1670
  return true;
1566
1671
  }
1567
1672
  };
@@ -1571,6 +1676,8 @@ var GEMINI_IMPORT_MANIFEST_PATH = join4(GEMINI_CONFIG_DIR, "import_manifest.json
1571
1676
  var antigravityStore = {
1572
1677
  manifestPaths: manifestsFor(),
1573
1678
  userGlobal: true,
1679
+ // Filed by name from a tree, so a first-party copy is as good as any other.
1680
+ firstParty: true,
1574
1681
  cacheRoot: GEMINI_PLUGINS_DIR,
1575
1682
  // Antigravity files plugins by name only — it has no marketplace dimension.
1576
1683
  cachePath: (_marketplace, name) => resolveContained(GEMINI_PLUGINS_DIR, name),
@@ -1665,10 +1772,18 @@ function pluginStoreFor(agent) {
1665
1772
  // src/handlers/plugin.ts
1666
1773
  var SLUG_LABEL3 = "plugin slug";
1667
1774
  function resolveMarketplaceName(item) {
1668
- const marketplace = item.marketplace || item.author?.name || "seedr";
1775
+ const fallback = isFirstParty(item.sourceType) ? item.slug : item.author?.name;
1776
+ const marketplace = item.marketplace || fallback || "seedr";
1669
1777
  assertSafePathSegment(marketplace, "plugin marketplace");
1670
1778
  return marketplace;
1671
1779
  }
1780
+ function assertStoreAccepts(store, item, agent) {
1781
+ if (isFirstParty(item.sourceType) && !store.firstParty) {
1782
+ throw new Error(
1783
+ `${CODING_AGENTS[agent].name} installs plugins only from a git marketplace, and a first-party plugin has none \u2014 it lives in the registry itself`
1784
+ );
1785
+ }
1786
+ }
1672
1787
  function resolvePluginIdentity(item, pluginJson) {
1673
1788
  const name = pluginJson.name || item.slug;
1674
1789
  const version = pluginJson.version || "1.0.0";
@@ -1710,12 +1825,11 @@ async function stageContent(item, contentPath) {
1710
1825
  }
1711
1826
  function resolveGitCommitSha(item, fetched) {
1712
1827
  const sha = fetched?.sourceRevision ?? getEffectiveSourceRevision(item);
1713
- if (!sha) {
1714
- throw new Error(
1715
- `Cannot record a source revision for "${item.slug}": the registry entry has neither sourceRevision nor pluginSource.sha`
1716
- );
1717
- }
1718
- return sha;
1828
+ if (sha) return sha;
1829
+ if (isFirstParty(item.sourceType)) return void 0;
1830
+ throw new Error(
1831
+ `Cannot record a source revision for "${item.slug}": the registry entry has neither sourceRevision nor pluginSource.sha`
1832
+ );
1719
1833
  }
1720
1834
  async function rollback(undo, error2) {
1721
1835
  const failures = [];
@@ -1754,8 +1868,9 @@ async function installPluginForAgent(item, agent, scope, _method, force, cwd) {
1754
1868
  try {
1755
1869
  const store = pluginStoreFor(agent);
1756
1870
  assertValidSlug(item.slug, SLUG_LABEL3);
1871
+ assertStoreAccepts(store, item, agent);
1757
1872
  const marketplace = resolveMarketplaceName(item);
1758
- const cacheRoot = store.cacheRoot;
1873
+ const cacheRoot = store.cacheRoot ?? (isFirstParty(item.sourceType) ? store.firstPartyCacheRoot ?? null : null);
1759
1874
  const staging = cacheRoot ? await createStagingDir(cacheRoot, item.slug) : null;
1760
1875
  try {
1761
1876
  const contentPath = staging ? join5(staging, "content") : null;
@@ -1763,12 +1878,12 @@ async function installPluginForAgent(item, agent, scope, _method, force, cwd) {
1763
1878
  const pluginJson = contentPath ? await readManifestFromDir(contentPath, store.manifestPaths) : await readManifestForPlan(item, store.manifestPaths);
1764
1879
  const { name, version } = resolvePluginIdentity(item, pluginJson);
1765
1880
  if (contentPath && store.prepareTree) {
1766
- await store.prepareTree(contentPath, { name, version, item });
1881
+ await store.prepareTree(contentPath, { name, version, marketplace, item });
1767
1882
  }
1768
1883
  const gitCommitSha = resolveGitCommitSha(item, fetched);
1769
1884
  const pluginId = getPluginId(name, marketplace);
1770
- const cachePath = await store.cachePath(marketplace, name, version);
1771
- const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item) : [];
1885
+ const cachePath = await store.cachePath(marketplace, name, version, item);
1886
+ const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item, cachePath) : [];
1772
1887
  const context = {
1773
1888
  item,
1774
1889
  marketplace,
@@ -1845,28 +1960,27 @@ async function getInstalledPlugins(agent, scope, cwd = process.cwd()) {
1845
1960
  async function kindFor(path) {
1846
1961
  return await exists(path) ? "modify" : "create";
1847
1962
  }
1963
+ function treeDetail(item) {
1964
+ return isFirstParty(item.sourceType) ? "plugin files (copied from the registry, listed as their own marketplace)" : "plugin files (digest-verified download)";
1965
+ }
1848
1966
  async function planPlugin(item, agents, scope, _method, cwd) {
1849
1967
  const changes = [];
1850
1968
  for (const agent of agents) {
1851
1969
  const store = pluginStoreFor(agent);
1852
1970
  assertValidSlug(item.slug, SLUG_LABEL3);
1971
+ assertStoreAccepts(store, item, agent);
1853
1972
  const marketplace = resolveMarketplaceName(item);
1854
1973
  const { name, version } = resolvePluginIdentity(
1855
1974
  item,
1856
1975
  await readManifestForPlan(item, store.manifestPaths)
1857
1976
  );
1858
1977
  const pluginId = getPluginId(name, marketplace);
1859
- const cachePath = await store.cachePath(marketplace, name, version);
1978
+ const cachePath = await store.cachePath(marketplace, name, version, item);
1860
1979
  resolveGitCommitSha(item, null);
1861
1980
  if (cachePath) {
1862
- changes.push({
1863
- agent,
1864
- kind: await kindFor(cachePath),
1865
- path: cachePath,
1866
- detail: "plugin files (digest-verified download)"
1867
- });
1981
+ changes.push({ agent, kind: await kindFor(cachePath), path: cachePath, detail: treeDetail(item) });
1868
1982
  }
1869
- const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item) : [];
1983
+ const marketplaceMutations = store.ensureMarketplace ? await store.ensureMarketplace(marketplace, item, cachePath) : [];
1870
1984
  const context = {
1871
1985
  item,
1872
1986
  marketplace,
@@ -2752,7 +2866,7 @@ var initCommand = new Command4("init").description("Initialize coding agent conf
2752
2866
  // package.json
2753
2867
  var package_default = {
2754
2868
  name: "@danieldeusing/seedr",
2755
- version: "0.1.108",
2869
+ version: "0.1.109",
2756
2870
  description: "Seed your projects with AI configurations",
2757
2871
  type: "module",
2758
2872
  bin: {
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  loadManifest,
16
16
  searchItems,
17
17
  uninstallSkill
18
- } from "./chunk-3KPB5CCX.js";
18
+ } from "./chunk-USS5HTCG.js";
19
19
  export {
20
20
  ALL_AGENTS,
21
21
  CODING_AGENTS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danieldeusing/seedr",
3
- "version": "0.1.108",
3
+ "version": "0.1.109",
4
4
  "description": "Seed your projects with AI configurations",
5
5
  "type": "module",
6
6
  "bin": {