plugins 1.3.1-snapshot.0 → 1.3.1

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 (2) hide show
  1. package/dist/index.js +132 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -540,7 +540,9 @@ async function multiSelect(title, options, maxVisible = 8) {
540
540
  if (state === "active") {
541
541
  const blockCursor = isColorSupported ? `\x1B[7m \x1B[0m` : "_";
542
542
  lines.push(`${c.gray(S.bar)} ${c.dim("Search:")} ${query}${blockCursor}`);
543
- lines.push(`${c.gray(S.bar)} ${c.dim("\u2191\u2193 move, space toggle, a all, n none, enter confirm")}`);
543
+ lines.push(
544
+ `${c.gray(S.bar)} ${c.dim("\u2191\u2193 move, space toggle, a all, n none, enter confirm")}`
545
+ );
544
546
  lines.push(`${c.gray(S.bar)}`);
545
547
  const visibleStart = Math.max(
546
548
  0,
@@ -689,6 +691,15 @@ var cachePopulated = false;
689
691
  async function installPlugins(plugins, target, scope, repoPath, source) {
690
692
  switch (target.id) {
691
693
  case "claude-code": {
694
+ const officialRef = getOfficialPluginRef(source);
695
+ if (officialRef) {
696
+ const ok = await installViaClaudeCli(officialRef, scope);
697
+ if (ok) {
698
+ cachePopulated = true;
699
+ break;
700
+ }
701
+ barDebug(c.dim("Falling back to direct file-based install"));
702
+ }
692
703
  const workspace = await stageInstallWorkspace(plugins, repoPath, target.id);
693
704
  await installToClaudeCode(workspace.plugins, scope, workspace.repoPath, source);
694
705
  break;
@@ -727,6 +738,47 @@ async function stageInstallWorkspace(plugins, repoPath, targetId, stagingBaseDir
727
738
  plugins: stagedPlugins
728
739
  };
729
740
  }
741
+ var OFFICIAL_MARKETPLACE_SOURCE = "anthropics/claude-plugins-official";
742
+ function getOfficialPluginRef(source) {
743
+ let repo = null;
744
+ const shorthand = source.match(/^([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
745
+ if (shorthand) repo = shorthand[1].toLowerCase();
746
+ if (!repo) {
747
+ const https = source.match(/^https?:\/\/github\.com\/([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
748
+ if (https) repo = https[1].toLowerCase();
749
+ }
750
+ if (!repo) {
751
+ const ssh = source.match(/^git@github\.com:([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
752
+ if (ssh) repo = ssh[1].toLowerCase();
753
+ }
754
+ if (repo === "vercel/vercel-plugin") {
755
+ return "vercel@claude-plugins-official";
756
+ }
757
+ return null;
758
+ }
759
+ async function installViaClaudeCli(pluginRef, scope) {
760
+ const claude = findClaudeOrNull();
761
+ if (!claude) return false;
762
+ try {
763
+ step("Registering official Claude marketplace");
764
+ execSync2(`${claude} plugin marketplace add ${OFFICIAL_MARKETPLACE_SOURCE}`, {
765
+ stdio: "pipe",
766
+ timeout: 12e4
767
+ });
768
+ stepDone("Official marketplace registered");
769
+ step(`Installing ${c.cyan(pluginRef)} via Claude CLI`);
770
+ execSync2(`${claude} plugin install "${pluginRef}" --scope ${scope}`, {
771
+ stdio: "pipe",
772
+ timeout: 12e4
773
+ });
774
+ stepDone(`Installed ${c.cyan(pluginRef)} via Claude CLI`);
775
+ return true;
776
+ } catch (err) {
777
+ const msg = err instanceof Error ? err.message : String(err);
778
+ barDebug(c.dim(`Claude CLI install failed: ${msg}`));
779
+ return false;
780
+ }
781
+ }
730
782
  async function installToClaudeCode(plugins, scope, repoPath, source) {
731
783
  await installToPluginCache(plugins, scope, repoPath, source);
732
784
  }
@@ -796,7 +848,9 @@ async function installToPluginCache(plugins, scope, repoPath, source) {
796
848
  };
797
849
  if (existsSync2(installedPath)) {
798
850
  try {
799
- installedData = JSON.parse(await readFile2(installedPath, "utf-8"));
851
+ const parsed = JSON.parse(await readFile2(installedPath, "utf-8"));
852
+ installedData.version = parsed.version ?? 2;
853
+ installedData.plugins = parsed.plugins ?? {};
800
854
  } catch {
801
855
  }
802
856
  }
@@ -1099,6 +1153,23 @@ async function prepareForClaudeCode(plugins, repoPath, marketplaceName) {
1099
1153
  await preparePluginDirForVendor(plugin, ".claude-plugin", "CLAUDE_PLUGIN_ROOT");
1100
1154
  }
1101
1155
  }
1156
+ function findClaudeOrNull() {
1157
+ try {
1158
+ const path = execSync2("which claude", { encoding: "utf-8", stdio: "pipe" }).trim();
1159
+ if (path) return path;
1160
+ } catch {
1161
+ }
1162
+ const home = homedir2();
1163
+ const candidates = [
1164
+ join3(home, ".local", "bin", "claude"),
1165
+ join3(home, ".bun", "bin", "claude"),
1166
+ "/usr/local/bin/claude"
1167
+ ];
1168
+ for (const candidate of candidates) {
1169
+ if (existsSync2(candidate)) return candidate;
1170
+ }
1171
+ return null;
1172
+ }
1102
1173
  async function preparePluginDirForVendor(plugin, vendorDir, envVar) {
1103
1174
  const pluginPath = plugin.path;
1104
1175
  const openPluginDir = join3(pluginPath, ".plugin");
@@ -1208,6 +1279,29 @@ function normalizeGitUrl(source) {
1208
1279
  }
1209
1280
  return source;
1210
1281
  }
1282
+ async function isMarketplaceNew(marketplaceName) {
1283
+ const knownPath = join3(homedir2(), ".claude", "plugins", "known_marketplaces.json");
1284
+ if (!existsSync2(knownPath)) return true;
1285
+ try {
1286
+ const data = JSON.parse(await readFile2(knownPath, "utf-8"));
1287
+ return !data[marketplaceName];
1288
+ } catch {
1289
+ return true;
1290
+ }
1291
+ }
1292
+ async function setAutoUpdate(marketplaceName, enabled) {
1293
+ const knownPath = join3(homedir2(), ".claude", "plugins", "known_marketplaces.json");
1294
+ if (!existsSync2(knownPath)) return;
1295
+ let data = {};
1296
+ try {
1297
+ data = JSON.parse(await readFile2(knownPath, "utf-8"));
1298
+ } catch {
1299
+ return;
1300
+ }
1301
+ if (!data[marketplaceName]) return;
1302
+ data[marketplaceName].autoUpdate = enabled;
1303
+ await writeFile(knownPath, JSON.stringify(data, null, 2));
1304
+ }
1211
1305
 
1212
1306
  // lib/telemetry.ts
1213
1307
  var TELEMETRY_URL = "https://plugins-telemetry.labs.vercel.dev/t";
@@ -1319,14 +1413,14 @@ async function cmdDiscover(source) {
1319
1413
  if (remotePlugins.length > 0) {
1320
1414
  if (values.remote) {
1321
1415
  barEmpty();
1322
- step(`${c.bold(String(remotePlugins.length))} remote plugin(s) ${c.dim("(hosted in external repos)")}`);
1416
+ step(
1417
+ `${c.bold(String(remotePlugins.length))} remote plugin(s) ${c.dim("(hosted in external repos)")}`
1418
+ );
1323
1419
  barEmpty();
1324
1420
  printRemotePluginTable(remotePlugins);
1325
1421
  } else {
1326
1422
  barEmpty();
1327
- barLine(
1328
- c.dim(`${remotePlugins.length} remote plugin(s) not shown. Run:`)
1329
- );
1423
+ barLine(c.dim(`${remotePlugins.length} remote plugin(s) not shown. Run:`));
1330
1424
  barLine(` ${c.cyan(`npx plugins discover ${source} --remote`)}`);
1331
1425
  }
1332
1426
  printMissingPaths(missingPaths);
@@ -1368,9 +1462,7 @@ async function cmdInstall(source, opts) {
1368
1462
  barEmpty();
1369
1463
  step("No plugins found.");
1370
1464
  if (remotePlugins.length > 0) {
1371
- barLine(
1372
- c.dim(`${remotePlugins.length} remote plugin(s) not shown. Run:`)
1373
- );
1465
+ barLine(c.dim(`${remotePlugins.length} remote plugin(s) not shown. Run:`));
1374
1466
  barLine(` ${c.cyan(`npx plugins discover ${source} --remote`)}`);
1375
1467
  printMissingPaths(missingPaths);
1376
1468
  }
@@ -1412,9 +1504,7 @@ async function cmdInstall(source, opts) {
1412
1504
  printPluginTable(plugins);
1413
1505
  if (remotePlugins.length > 0) {
1414
1506
  barEmpty();
1415
- barLine(
1416
- c.dim(`+ ${remotePlugins.length} remote plugin(s) not included. Run:`)
1417
- );
1507
+ barLine(c.dim(`+ ${remotePlugins.length} remote plugin(s) not included. Run:`));
1418
1508
  barLine(` ${c.cyan(`npx plugins discover ${source} --remote`)}`);
1419
1509
  printMissingPaths(missingPaths);
1420
1510
  }
@@ -1447,9 +1537,7 @@ async function cmdInstall(source, opts) {
1447
1537
  }
1448
1538
  selectedPlugins = plugins.filter((p) => selected.includes(p.name));
1449
1539
  if (remotePlugins.length > 0) {
1450
- barLine(
1451
- c.dim(`+ ${remotePlugins.length} remote plugin(s) not included. Run:`)
1452
- );
1540
+ barLine(c.dim(`+ ${remotePlugins.length} remote plugin(s) not included. Run:`));
1453
1541
  barLine(` ${c.cyan(`npx plugins discover ${source} --remote`)}`);
1454
1542
  printMissingPaths(missingPaths);
1455
1543
  }
@@ -1459,9 +1547,29 @@ async function cmdInstall(source, opts) {
1459
1547
  barEmpty();
1460
1548
  }
1461
1549
  const scope = opts.scope ?? "user";
1550
+ const marketplaceName = selectedPlugins[0]?.marketplace ?? deriveMarketplaceName(source);
1551
+ const willInstallToClaude = installTargets.some(
1552
+ (t) => t.id === "claude-code" || t.id === "cursor" && process.platform !== "win32"
1553
+ );
1554
+ const isNew = willInstallToClaude && await isMarketplaceNew(marketplaceName);
1462
1555
  for (const target of installTargets) {
1463
1556
  await installPlugins(selectedPlugins, target, scope, repoPath, source);
1464
1557
  }
1558
+ const installedViaOfficialCli = !!getOfficialPluginRef(source);
1559
+ if (isNew && willInstallToClaude && !installedViaOfficialCli) {
1560
+ let enableAutoUpdate = true;
1561
+ if (!opts.yes && process.stdin.isTTY) {
1562
+ barEmpty();
1563
+ const response = await readLine(
1564
+ `${c.cyan(S.stepActive)} Enable auto-updates? ${c.dim("[Y/n]")} `
1565
+ );
1566
+ enableAutoUpdate = response.trim().toLowerCase() !== "n";
1567
+ }
1568
+ if (enableAutoUpdate) {
1569
+ await setAutoUpdate(marketplaceName, true);
1570
+ stepDone("Auto-updates enabled");
1571
+ }
1572
+ }
1465
1573
  track({
1466
1574
  event: "install",
1467
1575
  source,
@@ -1476,9 +1584,12 @@ async function cmdInstall(source, opts) {
1476
1584
  }
1477
1585
  function pluginComponents(p) {
1478
1586
  const parts = [];
1479
- if (p.skills.length) parts.push(`${p.skills.length} ${p.skills.length === 1 ? "skill" : "skills"}`);
1480
- if (p.commands.length) parts.push(`${p.commands.length} ${p.commands.length === 1 ? "cmd" : "cmds"}`);
1481
- if (p.agents.length) parts.push(`${p.agents.length} ${p.agents.length === 1 ? "agent" : "agents"}`);
1587
+ if (p.skills.length)
1588
+ parts.push(`${p.skills.length} ${p.skills.length === 1 ? "skill" : "skills"}`);
1589
+ if (p.commands.length)
1590
+ parts.push(`${p.commands.length} ${p.commands.length === 1 ? "cmd" : "cmds"}`);
1591
+ if (p.agents.length)
1592
+ parts.push(`${p.agents.length} ${p.agents.length === 1 ? "agent" : "agents"}`);
1482
1593
  if (p.rules.length) parts.push(`${p.rules.length} ${p.rules.length === 1 ? "rule" : "rules"}`);
1483
1594
  if (p.hasHooks) parts.push("hooks");
1484
1595
  if (p.hasMcp) parts.push("mcp");
@@ -1496,7 +1607,9 @@ function printPluginTable(plugins) {
1496
1607
  const name = p.name.padEnd(nameWidth);
1497
1608
  const comp = compStrs[i];
1498
1609
  const desc = truncate(p.description ?? "", descWidth);
1499
- barLine(`${c.bold(name)} ${comp ? c.cyan(comp.padEnd(compWidth)) : " ".repeat(compWidth)} ${c.dim(desc)}`);
1610
+ barLine(
1611
+ `${c.bold(name)} ${comp ? c.cyan(comp.padEnd(compWidth)) : " ".repeat(compWidth)} ${c.dim(desc)}`
1612
+ );
1500
1613
  }
1501
1614
  }
1502
1615
  function printRemotePluginTable(plugins) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugins",
3
- "version": "1.3.1-snapshot.0",
3
+ "version": "1.3.1",
4
4
  "description": "Install open-plugin format plugins into agent tools",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.28.1",