prpm 2.1.3 → 2.1.5

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 +223 -51
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -191,6 +191,19 @@ function addCollectionToLockfile(lockfile, collectionKey, collectionInfo) {
191
191
  };
192
192
  lockfile.generated = (/* @__PURE__ */ new Date()).toISOString();
193
193
  }
194
+ function getCollectionFromLockfile(lockfile, collectionKey) {
195
+ if (!lockfile || !lockfile.collections) {
196
+ return null;
197
+ }
198
+ return lockfile.collections[collectionKey] || null;
199
+ }
200
+ function removeCollectionFromLockfile(lockfile, collectionKey) {
201
+ if (!lockfile.collections) {
202
+ return;
203
+ }
204
+ delete lockfile.collections[collectionKey];
205
+ lockfile.generated = (/* @__PURE__ */ new Date()).toISOString();
206
+ }
194
207
  var import_fs, import_path, import_crypto, LOCKFILE_NAME, LOCKFILE_VERSION;
195
208
  var init_lockfile = __esm({
196
209
  "src/core/lockfile.ts"() {
@@ -884,7 +897,8 @@ function fromClaude(content, metadata, sourceFormat = "claude", explicitSubtype)
884
897
  const pkg = {
885
898
  id: metadata.id,
886
899
  version: metadata.version || "1.0.0",
887
- name: frontmatter.name || metadata.id,
900
+ name: metadata.name || frontmatter.name || metadata.id,
901
+ // Package name for registry
888
902
  description: frontmatter.description || "",
889
903
  author: metadata.author || "unknown",
890
904
  tags: metadata.tags || [],
@@ -930,20 +944,27 @@ function parseMarkdownBody(body) {
930
944
  let preamble = [];
931
945
  let h1Title;
932
946
  let h1Icon;
947
+ let inCodeBlock = false;
933
948
  for (let i = 0; i < lines.length; i++) {
934
949
  const line = lines[i];
935
- if (line.startsWith("# ")) {
950
+ if (line.startsWith("```")) {
951
+ inCodeBlock = !inCodeBlock;
952
+ }
953
+ if (line.startsWith("# ") && !inCodeBlock && h1Title === void 0) {
936
954
  const titleText = line.substring(2).trim();
937
- const iconMatch = titleText.match(new RegExp("^(\\p{Emoji})\\s+(.+)$", "u"));
938
- if (iconMatch) {
939
- h1Icon = iconMatch[1];
940
- h1Title = iconMatch[2];
955
+ if (titleText.includes("{") || titleText.includes("[") || titleText.startsWith("Output")) {
941
956
  } else {
942
- h1Title = titleText;
957
+ const iconMatch = titleText.match(new RegExp("^(\\p{Emoji})\\s+(.+)$", "u"));
958
+ if (iconMatch) {
959
+ h1Icon = iconMatch[1];
960
+ h1Title = iconMatch[2];
961
+ } else {
962
+ h1Title = titleText;
963
+ }
964
+ continue;
943
965
  }
944
- continue;
945
966
  }
946
- if (line.startsWith("## ")) {
967
+ if (line.startsWith("## ") && !inCodeBlock) {
947
968
  if (currentSection) {
948
969
  sections.push(createSectionFromBlock(currentSection.title, currentSection.lines.join("\n")));
949
970
  }
@@ -8712,16 +8733,19 @@ function loadSchema(format, subtype) {
8712
8733
  "claude:skill": "claude-skill.schema.json",
8713
8734
  "claude:slash-command": "claude-slash-command.schema.json",
8714
8735
  "claude:hook": "claude-hook.schema.json",
8736
+ "claude:plugin": "claude-plugin.schema.json",
8715
8737
  "cursor:slash-command": "cursor-command.schema.json",
8716
8738
  "cursor:hook": "cursor-hooks.schema.json",
8717
8739
  // cursor + hook subtype uses cursor-hooks schema
8718
- "kiro:hook": "kiro-hooks.schema.json",
8740
+ "kiro:hook": "kiro-hook.schema.json",
8719
8741
  "kiro:agent": "kiro-agent.schema.json",
8720
8742
  "droid:skill": "droid-skill.schema.json",
8721
8743
  "droid:slash-command": "droid-slash-command.schema.json",
8722
8744
  "droid:hook": "droid-hook.schema.json",
8723
8745
  "opencode:slash-command": "opencode-slash-command.schema.json",
8724
- "gemini:extension": "gemini-extension.schema.json"
8746
+ "opencode:plugin": "opencode-plugin.schema.json",
8747
+ "gemini:extension": "gemini-extension.schema.json",
8748
+ "mcp:server": "mcp-server.schema.json"
8725
8749
  };
8726
8750
  schemaFilename = subtypeSchemaMap[cacheKey];
8727
8751
  }
@@ -12715,7 +12739,7 @@ var init_format_registry = __esm({
12715
12739
  },
12716
12740
  opencode: {
12717
12741
  name: "OpenCode",
12718
- description: "OpenCode agents, commands, and tools",
12742
+ description: "OpenCode agents, commands, tools, and plugins",
12719
12743
  documentationUrl: "https://opencode.ai",
12720
12744
  subtypes: {
12721
12745
  agent: {
@@ -12732,6 +12756,11 @@ var init_format_registry = __esm({
12732
12756
  directory: ".opencode/tool",
12733
12757
  filePatterns: ["*.md", "*.json"],
12734
12758
  fileExtension: ".md"
12759
+ },
12760
+ plugin: {
12761
+ directory: ".opencode/plugin",
12762
+ filePatterns: ["*.js", "*.ts"],
12763
+ fileExtension: ".ts"
12735
12764
  }
12736
12765
  }
12737
12766
  },
@@ -16503,7 +16532,7 @@ function findMainFile(files, format, subtype) {
16503
16532
  return null;
16504
16533
  }
16505
16534
  async function handleInstall(packageSpec, options) {
16506
- var _a, _b;
16535
+ var _a;
16507
16536
  const startTime = Date.now();
16508
16537
  let success = false;
16509
16538
  let error;
@@ -16868,7 +16897,7 @@ This could indicate:
16868
16897
  console.log(` \u26A0\uFE0F --location option currently applies to Cursor or Agents.md installs. Ignoring provided value for ${effectiveFormat}.`);
16869
16898
  locationOverride = void 0;
16870
16899
  }
16871
- let destPath;
16900
+ let destPath = "";
16872
16901
  let destDir = "";
16873
16902
  let fileCount = 0;
16874
16903
  let hookMetadata = void 0;
@@ -17049,7 +17078,7 @@ This could indicate:
17049
17078
  }
17050
17079
  }
17051
17080
  }
17052
- } else if (effectiveFormat === "copilot") {
17081
+ } else if (effectiveFormat === "copilot" && (effectiveSubtype === "chatmode" || effectiveSubtype === "rule")) {
17053
17082
  if (effectiveSubtype === "chatmode") {
17054
17083
  destPath = `${destDir}/${packageName}.chatmode.md`;
17055
17084
  } else {
@@ -17068,7 +17097,25 @@ This could indicate:
17068
17097
  } else if (effectiveFormat === "droid" && effectiveSubtype === "skill") {
17069
17098
  destPath = `${destDir}/SKILL.md`;
17070
17099
  } else {
17071
- destPath = `${destDir}/${packageName}.${fileExtension}`;
17100
+ const nativeSubtypes2 = import_types.FORMAT_NATIVE_SUBTYPES[effectiveFormat];
17101
+ const needsProgressiveDisclosureHere = nativeSubtypes2 && !nativeSubtypes2.includes(effectiveSubtype) && (effectiveSubtype === "skill" || effectiveSubtype === "agent" || effectiveSubtype === "slash-command");
17102
+ if (needsProgressiveDisclosureHere) {
17103
+ if (effectiveSubtype === "skill") {
17104
+ destDir = `.openskills/${packageName}`;
17105
+ destPath = `${destDir}/SKILL.md`;
17106
+ console.log(` \u{1F4E6} Installing skill to ${destDir}/ for progressive disclosure`);
17107
+ } else if (effectiveSubtype === "agent") {
17108
+ destDir = `.openagents/${packageName}`;
17109
+ destPath = `${destDir}/AGENT.md`;
17110
+ console.log(` \u{1F916} Installing agent to ${destDir}/ for progressive disclosure`);
17111
+ } else if (effectiveSubtype === "slash-command") {
17112
+ destDir = ".opencommands";
17113
+ destPath = `${destDir}/${packageName}.md`;
17114
+ console.log(` \u26A1 Installing command to ${destDir}/ for progressive disclosure`);
17115
+ }
17116
+ } else {
17117
+ destPath = `${destDir}/${packageName}.${fileExtension}`;
17118
+ }
17072
17119
  }
17073
17120
  if (format === "cursor" && effectiveFormat === "cursor") {
17074
17121
  if (!hasMDCHeader(mainFile)) {
@@ -17087,6 +17134,7 @@ This could indicate:
17087
17134
  }
17088
17135
  }
17089
17136
  if (effectiveFormat === "claude" && effectiveSubtype === "hook") {
17137
+ destPath = destPath || `${destDir}/settings.json`;
17090
17138
  let hookConfig;
17091
17139
  try {
17092
17140
  hookConfig = JSON.parse(mainFile);
@@ -17223,41 +17271,23 @@ ${afterFrontmatter}`;
17223
17271
  }
17224
17272
  }
17225
17273
  let progressiveDisclosureMetadata;
17226
- const progressiveDisclosureFormats = [
17227
- "agents.md",
17228
- // Universal AGENTS.md format
17229
- "gemini.md",
17230
- // Uses GEMINI.md
17231
- "claude.md",
17232
- // Uses CLAUDE.md
17233
- "aider",
17234
- // Uses CONVENTIONS.md
17235
- "codex",
17236
- // Uses AGENTS.md (no native skills/agents)
17237
- "copilot",
17238
- // Uses AGENTS.md (no native skills/agents)
17239
- "kiro",
17240
- // Uses AGENTS.md (no native skills)
17241
- "opencode",
17242
- // Uses AGENTS.md (no native skills)
17243
- "replit",
17244
- // Uses AGENTS.md (no native skills)
17245
- "zed",
17246
- // Uses AGENTS.md (no native skills)
17247
- "generic"
17248
- // Uses AGENTS.md (fallback format)
17249
- ];
17250
- const partialNativeSupport = {
17251
- "cursor": ["agent"]
17252
- // Cursor has native rules/commands but no native agents
17253
- };
17254
- const needsProgressiveDisclosure = progressiveDisclosureFormats.includes(effectiveFormat) && (effectiveSubtype === "skill" || effectiveSubtype === "agent" || effectiveSubtype === "slash-command") || ((_b = partialNativeSupport[effectiveFormat]) == null ? void 0 : _b.includes(effectiveSubtype));
17274
+ const nativeSubtypes = import_types.FORMAT_NATIVE_SUBTYPES[effectiveFormat];
17275
+ const isProgressiveDisclosureSubtype = effectiveSubtype === "skill" || effectiveSubtype === "agent" || effectiveSubtype === "slash-command";
17276
+ const hasNativeSupport = (nativeSubtypes == null ? void 0 : nativeSubtypes.includes(effectiveSubtype)) ?? false;
17277
+ const needsProgressiveDisclosure = isProgressiveDisclosureSubtype && !hasNativeSupport;
17255
17278
  if (needsProgressiveDisclosure && !options.noAppend) {
17279
+ const resourceName = stripAuthorNamespace2(packageId);
17280
+ if (effectiveSubtype === "skill") {
17281
+ destDir = `.openskills/${resourceName}`;
17282
+ } else if (effectiveSubtype === "agent") {
17283
+ destDir = `.openagents/${resourceName}`;
17284
+ } else if (effectiveSubtype === "slash-command") {
17285
+ destDir = ".opencommands";
17286
+ }
17256
17287
  if (!destDir) {
17257
17288
  throw new Error("Internal error: destDir not set for progressive disclosure installation");
17258
17289
  }
17259
17290
  const manifestPath = options.manifestFile || getManifestFilename(effectiveFormat);
17260
- const resourceName = stripAuthorNamespace2(packageId);
17261
17291
  const resourceType = effectiveSubtype === "slash-command" ? "command" : effectiveSubtype;
17262
17292
  let mainFile;
17263
17293
  if (resourceType === "command") {
@@ -19398,6 +19428,137 @@ async function promptForFormat(packageId, formats) {
19398
19428
  });
19399
19429
  });
19400
19430
  }
19431
+ async function handleCollectionUninstall(collectionKey, collection, lockfile, requestedFormat) {
19432
+ var _a;
19433
+ if (!collection) {
19434
+ throw new CLIError(`\u274C Collection "collections/${collectionKey}" not found in lockfile`, 1);
19435
+ }
19436
+ console.log(`
19437
+ \u{1F4E6} Uninstalling collection: ${collectionKey}`);
19438
+ console.log(` ${collection.packages.length} packages to uninstall
19439
+ `);
19440
+ let uninstalledCount = 0;
19441
+ let failedCount = 0;
19442
+ for (const packageId of collection.packages) {
19443
+ try {
19444
+ const matchingKeys = [];
19445
+ for (const key of Object.keys(lockfile.packages)) {
19446
+ const parsed = parseLockfileKey(key);
19447
+ if (parsed.packageId === packageId) {
19448
+ const pkg = lockfile.packages[key];
19449
+ if (((_a = pkg.fromCollection) == null ? void 0 : _a.name_slug) === collectionKey) {
19450
+ if (requestedFormat) {
19451
+ if (pkg.format === requestedFormat || parsed.format === requestedFormat) {
19452
+ matchingKeys.push(key);
19453
+ }
19454
+ } else {
19455
+ matchingKeys.push(key);
19456
+ }
19457
+ }
19458
+ }
19459
+ }
19460
+ if (matchingKeys.length === 0) {
19461
+ console.log(` \u26A0\uFE0F ${packageId}: not found (may have been uninstalled manually)`);
19462
+ continue;
19463
+ }
19464
+ for (const lockfileKey of matchingKeys) {
19465
+ const parsed = parseLockfileKey(lockfileKey);
19466
+ const formatDisplay = parsed.format ? ` (${parsed.format})` : "";
19467
+ await uninstallSinglePackage(lockfileKey, packageId, lockfile);
19468
+ console.log(` \u2713 ${packageId}${formatDisplay}`);
19469
+ uninstalledCount++;
19470
+ }
19471
+ } catch (error) {
19472
+ const errorMessage = error instanceof Error ? error.message : String(error);
19473
+ console.error(` \u2717 ${packageId}: ${errorMessage}`);
19474
+ failedCount++;
19475
+ }
19476
+ }
19477
+ const freshLockfile = await readLockfile();
19478
+ if (freshLockfile) {
19479
+ removeCollectionFromLockfile(freshLockfile, collectionKey);
19480
+ await writeLockfile(freshLockfile);
19481
+ }
19482
+ console.log(`
19483
+ \u2705 Collection uninstalled`);
19484
+ console.log(` ${uninstalledCount} packages removed`);
19485
+ if (failedCount > 0) {
19486
+ console.log(` ${failedCount} packages failed to uninstall`);
19487
+ }
19488
+ console.log(` \u{1F512} Collection removed from lock file`);
19489
+ }
19490
+ async function uninstallSinglePackage(lockfileKey, packageId, lockfile) {
19491
+ const parsed = parseLockfileKey(lockfileKey);
19492
+ const pkg = await removePackage(lockfileKey);
19493
+ if (!pkg) {
19494
+ throw new Error(`Package not found in lockfile`);
19495
+ }
19496
+ if (pkg.progressiveDisclosure) {
19497
+ const { manifestPath, resourceName, skillName } = pkg.progressiveDisclosure;
19498
+ const name = resourceName || skillName;
19499
+ if (name) {
19500
+ try {
19501
+ await removeSkillFromManifest(name, manifestPath);
19502
+ } catch (error) {
19503
+ }
19504
+ }
19505
+ }
19506
+ if (pkg.pluginMetadata) {
19507
+ const { files, mcpServers, mcpGlobal } = pkg.pluginMetadata;
19508
+ for (const filePath of files) {
19509
+ try {
19510
+ await import_fs11.promises.unlink(filePath);
19511
+ } catch (error) {
19512
+ const err = error;
19513
+ if (err.code !== "ENOENT") {
19514
+ }
19515
+ }
19516
+ }
19517
+ if (mcpServers && Object.keys(mcpServers).length > 0) {
19518
+ removeMCPServers(mcpServers, mcpGlobal || false);
19519
+ }
19520
+ return;
19521
+ }
19522
+ if (pkg.format === "claude" && pkg.subtype === "hook" && pkg.hookMetadata) {
19523
+ const settingsPath = pkg.installedPath || ".claude/settings.json";
19524
+ try {
19525
+ const settingsContent = await import_fs11.promises.readFile(settingsPath, "utf-8");
19526
+ const settings = JSON.parse(settingsContent);
19527
+ if (settings.hooks) {
19528
+ for (const event of pkg.hookMetadata.events) {
19529
+ if (settings.hooks[event]) {
19530
+ settings.hooks[event] = settings.hooks[event].filter(
19531
+ (hook) => hook.__prpm_hook_id !== pkg.hookMetadata.hookId
19532
+ );
19533
+ if (settings.hooks[event].length === 0) {
19534
+ delete settings.hooks[event];
19535
+ }
19536
+ }
19537
+ }
19538
+ await import_fs11.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
19539
+ }
19540
+ } catch (error) {
19541
+ }
19542
+ return;
19543
+ }
19544
+ const targetPath = pkg.installedPath;
19545
+ if (!targetPath) {
19546
+ throw new Error("Installation path unknown");
19547
+ }
19548
+ try {
19549
+ const stats = await import_fs11.promises.stat(targetPath);
19550
+ if (stats.isDirectory()) {
19551
+ await import_fs11.promises.rm(targetPath, { recursive: true, force: true });
19552
+ } else if (stats.isFile()) {
19553
+ await import_fs11.promises.unlink(targetPath);
19554
+ }
19555
+ } catch (error) {
19556
+ const err = error;
19557
+ if (err.code !== "ENOENT") {
19558
+ throw err;
19559
+ }
19560
+ }
19561
+ }
19401
19562
  async function handleUninstall(name, options = {}) {
19402
19563
  try {
19403
19564
  const requestedFormat = options.format || options.as;
@@ -19408,6 +19569,13 @@ async function handleUninstall(name, options = {}) {
19408
19569
  if (!lockfile) {
19409
19570
  throw new CLIError("\u274C No prpm.lock file found", 1);
19410
19571
  }
19572
+ const isCollectionPrefix = name.startsWith("collections/");
19573
+ const collectionKey = isCollectionPrefix ? name.replace("collections/", "") : name;
19574
+ const collection = getCollectionFromLockfile(lockfile, collectionKey);
19575
+ if (collection || isCollectionPrefix) {
19576
+ await handleCollectionUninstall(collectionKey, collection, lockfile, requestedFormat);
19577
+ return;
19578
+ }
19411
19579
  const matchingKeys = [];
19412
19580
  for (const key of Object.keys(lockfile.packages)) {
19413
19581
  const parsed = parseLockfileKey(key);
@@ -19530,8 +19698,8 @@ async function handleUninstall(name, options = {}) {
19530
19698
  throw new Error(`Failed to remove hooks from settings: ${error}`);
19531
19699
  }
19532
19700
  }
19533
- console.log(`\u2705 Successfully uninstalled ${name}`);
19534
- return;
19701
+ console.log(`\u2705 Successfully uninstalled ${name}${formatDisplay}`);
19702
+ continue;
19535
19703
  }
19536
19704
  const packageName = stripAuthorNamespace2(name);
19537
19705
  let targetPath;
@@ -19571,7 +19739,7 @@ async function handleUninstall(name, options = {}) {
19571
19739
  }
19572
19740
  function createUninstallCommand() {
19573
19741
  const command = new import_commander2.Command("uninstall");
19574
- command.description("Uninstall a prompt package").argument("<id>", "Package ID to uninstall").option("--format <format>", "Specific format to uninstall (if multiple formats installed)").option("--as <format>", "Alias for --format (use when multiple formats are installed)").alias("remove").action(handleUninstall);
19742
+ command.description("Uninstall a prompt package or collection").argument("<id>", "Package ID or collection name (e.g., collections/my-collection)").option("--format <format>", "Specific format to uninstall (if multiple formats installed)").option("--as <format>", "Alias for --format (use when multiple formats are installed)").alias("remove").action(handleUninstall);
19575
19743
  return command;
19576
19744
  }
19577
19745
 
@@ -20844,7 +21012,7 @@ async function validatePackageFiles(manifest) {
20844
21012
  warnings.push(...result.warnings);
20845
21013
  } else if (formatType === "kiro") {
20846
21014
  if (filePath.endsWith(".json")) {
20847
- const result = await validateStructuredFile(filePath, formatType, "hook");
21015
+ const result = await validateStructuredFile(filePath, formatType, manifest.subtype);
20848
21016
  errors.push(...result.errors);
20849
21017
  warnings.push(...result.warnings);
20850
21018
  } else {
@@ -21473,9 +21641,13 @@ async function handlePublish(options) {
21473
21641
  let version;
21474
21642
  try {
21475
21643
  const config = await getConfig();
21476
- if (!config.token) {
21644
+ const isCIMode = process.env.CI_MODE === "true";
21645
+ if (!config.token && !isCIMode) {
21477
21646
  throw new CLIError('\u274C Not logged in. Run "prpm login" first.', 1);
21478
21647
  }
21648
+ if (isCIMode && !config.token) {
21649
+ console.log("\u{1F916} CI Mode: Publishing without authentication\n");
21650
+ }
21479
21651
  console.log("\u{1F4E6} Publishing package...\n");
21480
21652
  const prpmJsonPath = (0, import_path22.join)(process.cwd(), "prpm.json");
21481
21653
  const marketplaceJsonPath = (0, import_path22.join)(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "2.1.3",
3
+ "version": "2.1.5",
4
4
  "description": "Prompt Package Manager CLI - Install and manage prompt-based files",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -45,9 +45,9 @@
45
45
  "license": "MIT",
46
46
  "dependencies": {
47
47
  "@octokit/rest": "^22.0.0",
48
- "@pr-pm/converters": "^2.1.3",
49
- "@pr-pm/registry-client": "^2.3.3",
50
- "@pr-pm/types": "^2.1.3",
48
+ "@pr-pm/converters": "^2.1.5",
49
+ "@pr-pm/registry-client": "^2.3.5",
50
+ "@pr-pm/types": "^2.1.5",
51
51
  "ajv": "^8.17.1",
52
52
  "ajv-formats": "^3.0.1",
53
53
  "commander": "^11.1.0",