prpm 2.1.30 → 2.1.31

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.
@@ -18,13 +18,13 @@
18
18
  "cursor": {
19
19
  "name": "Cursor",
20
20
  "supportsSkills": false,
21
- "supportsPlugins": false,
21
+ "supportsPlugins": true,
22
22
  "supportsExtensions": false,
23
23
  "supportsAgents": false,
24
24
  "supportsAgentsMd": true,
25
25
  "supportsSlashCommands": true,
26
26
  "markdownFallback": "cursor-rules.md",
27
- "notes": "Cursor supports AGENTS.md files for project instructions. Custom slash commands in .cursor/commands/*.md (introduced v1.6, Sept 2025). Built-in /summarize command. Rules are appended automatically, slash commands are explicit prompts with more weight. No native agent/skill/plugin packages."
27
+ "notes": "Cursor supports plugins (.cursor-plugin/plugin.json) that bundle rules, skills, agents, commands, hooks, and MCP servers. Custom slash commands in .cursor/commands/*.md. AGENTS.md for project instructions. Plugin marketplace at cursor.com/marketplace. No native standalone agent/skill packages."
28
28
  },
29
29
  "claude": {
30
30
  "name": "Claude Code",
package/dist/index.js CHANGED
@@ -1500,6 +1500,114 @@ var init_from_claude_plugin = __esm({
1500
1500
  }
1501
1501
  });
1502
1502
 
1503
+ // ../converters/dist/from-cursor-plugin.js
1504
+ function fromCursorPlugin(pluginJson, contents, metadata) {
1505
+ var _a, _b, _c, _d;
1506
+ const sections = [];
1507
+ const authorStr = pluginJson.author ? pluginJson.author.email ? `${pluginJson.author.name} <${pluginJson.author.email}>` : pluginJson.author.name : metadata.author;
1508
+ const mcpServers = typeof pluginJson.mcpServers === "object" && !Array.isArray(pluginJson.mcpServers) ? pluginJson.mcpServers : void 0;
1509
+ const metadataSection = {
1510
+ type: "metadata",
1511
+ data: {
1512
+ title: pluginJson.name || metadata.name,
1513
+ description: pluginJson.description || "",
1514
+ version: pluginJson.version || metadata.version || "1.0.0",
1515
+ author: authorStr,
1516
+ cursorPlugin: {
1517
+ logo: pluginJson.logo,
1518
+ mcpServers,
1519
+ contents: {
1520
+ rules: contents.rules || [],
1521
+ agents: contents.agents || [],
1522
+ skills: contents.skills || [],
1523
+ commands: contents.commands || [],
1524
+ hooks: contents.hooks
1525
+ }
1526
+ }
1527
+ }
1528
+ };
1529
+ sections.push(metadataSection);
1530
+ if (pluginJson.description) {
1531
+ sections.push({
1532
+ type: "instructions",
1533
+ title: "Overview",
1534
+ content: pluginJson.description
1535
+ });
1536
+ }
1537
+ const contentsSummary = [];
1538
+ if ((_a = contents.rules) == null ? void 0 : _a.length) {
1539
+ contentsSummary.push(`- **Rules**: ${contents.rules.length} rules`);
1540
+ }
1541
+ if ((_b = contents.agents) == null ? void 0 : _b.length) {
1542
+ contentsSummary.push(`- **Agents**: ${contents.agents.length} agents`);
1543
+ }
1544
+ if ((_c = contents.skills) == null ? void 0 : _c.length) {
1545
+ contentsSummary.push(`- **Skills**: ${contents.skills.length} skills`);
1546
+ }
1547
+ if ((_d = contents.commands) == null ? void 0 : _d.length) {
1548
+ contentsSummary.push(`- **Commands**: ${contents.commands.length} commands`);
1549
+ }
1550
+ if (contents.hooks) {
1551
+ contentsSummary.push(`- **Hooks**: configured`);
1552
+ }
1553
+ if (mcpServers && Object.keys(mcpServers).length > 0) {
1554
+ contentsSummary.push(`- **MCP Servers**: ${Object.keys(mcpServers).join(", ")}`);
1555
+ }
1556
+ if (contentsSummary.length > 0) {
1557
+ sections.push({
1558
+ type: "context",
1559
+ title: "Plugin Contents",
1560
+ content: contentsSummary.join("\n")
1561
+ });
1562
+ }
1563
+ const pkg = {
1564
+ id: metadata.id,
1565
+ version: pluginJson.version || metadata.version || "1.0.0",
1566
+ name: pluginJson.name || metadata.name,
1567
+ description: pluginJson.description || "",
1568
+ author: authorStr || "unknown",
1569
+ tags: pluginJson.keywords || metadata.tags || [],
1570
+ license: pluginJson.license || metadata.license,
1571
+ repository: pluginJson.repository || metadata.repository,
1572
+ homepage: pluginJson.homepage || metadata.homepage,
1573
+ content: {
1574
+ format: "canonical",
1575
+ version: "1.0",
1576
+ sections
1577
+ },
1578
+ sourceFormat: "cursor",
1579
+ metadata: {
1580
+ title: metadataSection.data.title,
1581
+ description: metadataSection.data.description,
1582
+ version: metadataSection.data.version,
1583
+ author: metadataSection.data.author,
1584
+ cursorPlugin: metadataSection.data.cursorPlugin
1585
+ }
1586
+ };
1587
+ setTaxonomy(pkg, "cursor", "plugin");
1588
+ return pkg;
1589
+ }
1590
+ function parseCursorPluginJson(content) {
1591
+ try {
1592
+ return JSON.parse(content);
1593
+ } catch (error) {
1594
+ throw new Error(`Failed to parse plugin.json: ${error instanceof Error ? error.message : String(error)}`);
1595
+ }
1596
+ }
1597
+ function extractCursorMCPServers(pluginJson) {
1598
+ if (typeof pluginJson.mcpServers === "object" && !Array.isArray(pluginJson.mcpServers)) {
1599
+ return pluginJson.mcpServers;
1600
+ }
1601
+ return {};
1602
+ }
1603
+ var init_from_cursor_plugin = __esm({
1604
+ "../converters/dist/from-cursor-plugin.js"() {
1605
+ "use strict";
1606
+ init_cjs_shims();
1607
+ init_taxonomy_utils();
1608
+ }
1609
+ });
1610
+
1503
1611
  // ../converters/dist/from-continue.js
1504
1612
  function fromContinue(content, metadata, explicitSubtype) {
1505
1613
  const pkg = fromClaude(content, metadata, "continue", explicitSubtype);
@@ -8771,7 +8879,7 @@ function fromCodex(content, metadata) {
8771
8879
  return pkg;
8772
8880
  }
8773
8881
  function fromCodexAgentRole(content, metadata) {
8774
- var _a;
8882
+ var _a, _b;
8775
8883
  let role = {};
8776
8884
  try {
8777
8885
  role = import_toml.default.parse(content);
@@ -8781,8 +8889,8 @@ function fromCodexAgentRole(content, metadata) {
8781
8889
  const metadataSection = {
8782
8890
  type: "metadata",
8783
8891
  data: {
8784
- title: metadata.name || metadata.id,
8785
- description: metadata.description || "",
8892
+ title: role.name || metadata.name || metadata.id,
8893
+ description: role.description || metadata.description || "",
8786
8894
  version: metadata.version || "1.0.0",
8787
8895
  author: metadata.author
8788
8896
  }
@@ -8790,10 +8898,15 @@ function fromCodexAgentRole(content, metadata) {
8790
8898
  metadataSection.data.codexAgent = {
8791
8899
  model: role.model,
8792
8900
  modelReasoningEffort: role.model_reasoning_effort,
8793
- sandboxMode: role.sandbox_mode
8901
+ sandboxMode: role.sandbox_mode,
8902
+ name: role.name,
8903
+ description: role.description,
8904
+ nicknameCandidates: role.nickname_candidates,
8905
+ mcpServers: role.mcp_servers,
8906
+ skillsConfig: (_a = role.skills) == null ? void 0 : _a.config
8794
8907
  };
8795
8908
  sections.push(metadataSection);
8796
- if ((_a = role.developer_instructions) == null ? void 0 : _a.trim()) {
8909
+ if ((_b = role.developer_instructions) == null ? void 0 : _b.trim()) {
8797
8910
  sections.push({
8798
8911
  type: "instructions",
8799
8912
  title: "Instructions",
@@ -8808,10 +8921,10 @@ function fromCodexAgentRole(content, metadata) {
8808
8921
  const pkg = {
8809
8922
  ...metadata,
8810
8923
  id: metadata.id,
8811
- name: metadata.name || metadata.id,
8924
+ name: role.name || metadata.name || metadata.id,
8812
8925
  version: metadata.version,
8813
8926
  author: metadata.author,
8814
- description: metadata.description || "",
8927
+ description: role.description || metadata.description || "",
8815
8928
  tags: metadata.tags || [],
8816
8929
  format: "codex",
8817
8930
  subtype: "agent",
@@ -9033,6 +9146,7 @@ function loadSchema(format, subtype) {
9033
9146
  "cursor:slash-command": "cursor-command.schema.json",
9034
9147
  "cursor:hook": "cursor-hooks.schema.json",
9035
9148
  // cursor + hook subtype uses cursor-hooks schema
9149
+ "cursor:plugin": "cursor-plugin.schema.json",
9036
9150
  "kiro:hook": "kiro-hook.schema.json",
9037
9151
  "kiro:agent": "kiro-agent.schema.json",
9038
9152
  "droid:skill": "droid-skill.schema.json",
@@ -9942,6 +10056,110 @@ var init_to_claude_plugin = __esm({
9942
10056
  }
9943
10057
  });
9944
10058
 
10059
+ // ../converters/dist/to-cursor-plugin.js
10060
+ function toCursorPlugin(pkg) {
10061
+ var _a, _b, _c, _d;
10062
+ const warnings = [];
10063
+ let qualityScore = 100;
10064
+ try {
10065
+ const metadata = pkg.content.sections.find((s) => s.type === "metadata");
10066
+ const storedPluginData = (metadata == null ? void 0 : metadata.type) === "metadata" ? metadata.data.cursorPlugin : void 0;
10067
+ const pluginJson = {
10068
+ name: pkg.name || pkg.id
10069
+ };
10070
+ if (pkg.description || (metadata == null ? void 0 : metadata.type) === "metadata" && metadata.data.description) {
10071
+ pluginJson.description = pkg.description || ((metadata == null ? void 0 : metadata.type) === "metadata" ? metadata.data.description : "");
10072
+ }
10073
+ if (pkg.version) {
10074
+ pluginJson.version = pkg.version;
10075
+ }
10076
+ if (pkg.author) {
10077
+ const emailMatch = pkg.author.match(/^(.+?)\s*<(.+?)>$/);
10078
+ if (emailMatch) {
10079
+ pluginJson.author = { name: emailMatch[1].trim(), email: emailMatch[2] };
10080
+ } else {
10081
+ pluginJson.author = { name: pkg.author };
10082
+ }
10083
+ }
10084
+ if (pkg.homepage) {
10085
+ pluginJson.homepage = pkg.homepage;
10086
+ }
10087
+ if (pkg.repository) {
10088
+ pluginJson.repository = pkg.repository;
10089
+ }
10090
+ if (pkg.license) {
10091
+ pluginJson.license = pkg.license;
10092
+ }
10093
+ if (pkg.tags && pkg.tags.length > 0) {
10094
+ pluginJson.keywords = pkg.tags;
10095
+ }
10096
+ if (storedPluginData == null ? void 0 : storedPluginData.logo) {
10097
+ pluginJson.logo = storedPluginData.logo;
10098
+ }
10099
+ if ((storedPluginData == null ? void 0 : storedPluginData.mcpServers) && Object.keys(storedPluginData.mcpServers).length > 0) {
10100
+ pluginJson.mcpServers = storedPluginData.mcpServers;
10101
+ }
10102
+ const pluginContents = (storedPluginData == null ? void 0 : storedPluginData.contents) || {
10103
+ rules: [],
10104
+ agents: [],
10105
+ skills: [],
10106
+ commands: []
10107
+ };
10108
+ const content = JSON.stringify(pluginJson, null, 2);
10109
+ if (!pluginJson.description) {
10110
+ warnings.push("Missing description");
10111
+ qualityScore -= 5;
10112
+ }
10113
+ const mcpServerCount = (storedPluginData == null ? void 0 : storedPluginData.mcpServers) ? Object.keys(storedPluginData.mcpServers).length : 0;
10114
+ if (mcpServerCount === 0) {
10115
+ warnings.push("No MCP servers configured");
10116
+ }
10117
+ const totalFiles = (((_a = pluginContents.rules) == null ? void 0 : _a.length) || 0) + (((_b = pluginContents.agents) == null ? void 0 : _b.length) || 0) + (((_c = pluginContents.skills) == null ? void 0 : _c.length) || 0) + (((_d = pluginContents.commands) == null ? void 0 : _d.length) || 0) + (pluginContents.hooks ? 1 : 0);
10118
+ if (totalFiles === 0 && mcpServerCount === 0) {
10119
+ warnings.push("Plugin has no rules, agents, skills, commands, hooks, or MCP servers");
10120
+ qualityScore -= 20;
10121
+ }
10122
+ return {
10123
+ content,
10124
+ format: "cursor",
10125
+ warnings: warnings.length > 0 ? warnings : void 0,
10126
+ lossyConversion: false,
10127
+ qualityScore: Math.max(0, qualityScore),
10128
+ pluginJson,
10129
+ pluginContents
10130
+ };
10131
+ } catch (error) {
10132
+ warnings.push(`Conversion error: ${error instanceof Error ? error.message : String(error)}`);
10133
+ return {
10134
+ content: "{}",
10135
+ format: "cursor",
10136
+ warnings,
10137
+ lossyConversion: true,
10138
+ qualityScore: 0,
10139
+ pluginJson: { name: pkg.name || pkg.id },
10140
+ pluginContents: {}
10141
+ };
10142
+ }
10143
+ }
10144
+ function generateCursorPluginJson(pluginJson) {
10145
+ return JSON.stringify(pluginJson, null, 2);
10146
+ }
10147
+ function createMinimalCursorPluginJson(name, options) {
10148
+ return {
10149
+ name,
10150
+ version: (options == null ? void 0 : options.version) || "1.0.0",
10151
+ description: options == null ? void 0 : options.description,
10152
+ author: options == null ? void 0 : options.author,
10153
+ mcpServers: options == null ? void 0 : options.mcpServers
10154
+ };
10155
+ }
10156
+ var init_to_cursor_plugin = __esm({
10157
+ "../converters/dist/to-cursor-plugin.js"() {
10158
+ "use strict";
10159
+ init_cjs_shims();
10160
+ }
10161
+ });
10162
+
9945
10163
  // ../converters/dist/to-continue.js
9946
10164
  function toContinue(pkg) {
9947
10165
  var _a, _b;
@@ -12635,7 +12853,7 @@ function toCodex(pkg, options = {}) {
12635
12853
  const isSlashCommand = pkg.subtype === "slash-command";
12636
12854
  let content;
12637
12855
  if (isSkill && !config.forceAgentsMd) {
12638
- content = convertToSkillMd(pkg, warnings);
12856
+ content = convertToSkillMd(pkg, warnings, config);
12639
12857
  } else if (isAgent) {
12640
12858
  content = convertToAgentRoleToml(pkg, warnings);
12641
12859
  } else if (isSlashCommand) {
@@ -12673,6 +12891,18 @@ function convertToAgentRoleToml(pkg, warnings) {
12673
12891
  const instructionsSection = pkg.content.sections.find((s) => s.type === "instructions");
12674
12892
  const role = {};
12675
12893
  const codexAgent = (metadataSection == null ? void 0 : metadataSection.type) === "metadata" ? metadataSection.data.codexAgent : void 0;
12894
+ const agentName = (codexAgent == null ? void 0 : codexAgent.name) || pkg.name;
12895
+ role["name"] = agentName;
12896
+ const agentDescription = (codexAgent == null ? void 0 : codexAgent.description) || ((metadataSection == null ? void 0 : metadataSection.type) === "metadata" ? metadataSection.data.description : "") || pkg.description || "";
12897
+ role["description"] = agentDescription;
12898
+ if ((instructionsSection == null ? void 0 : instructionsSection.type) === "instructions") {
12899
+ role["developer_instructions"] = instructionsSection.content;
12900
+ } else if (pkg.description) {
12901
+ role["developer_instructions"] = pkg.description;
12902
+ }
12903
+ if ((codexAgent == null ? void 0 : codexAgent.nicknameCandidates) && codexAgent.nicknameCandidates.length > 0) {
12904
+ role["nickname_candidates"] = codexAgent.nicknameCandidates;
12905
+ }
12676
12906
  if (codexAgent == null ? void 0 : codexAgent.model) {
12677
12907
  role["model"] = codexAgent.model;
12678
12908
  }
@@ -12682,10 +12912,11 @@ function convertToAgentRoleToml(pkg, warnings) {
12682
12912
  if (codexAgent == null ? void 0 : codexAgent.sandboxMode) {
12683
12913
  role["sandbox_mode"] = codexAgent.sandboxMode;
12684
12914
  }
12685
- if ((instructionsSection == null ? void 0 : instructionsSection.type) === "instructions") {
12686
- role["developer_instructions"] = instructionsSection.content;
12687
- } else if (pkg.description) {
12688
- role["developer_instructions"] = pkg.description;
12915
+ if ((codexAgent == null ? void 0 : codexAgent.mcpServers) && Object.keys(codexAgent.mcpServers).length > 0) {
12916
+ role["mcp_servers"] = codexAgent.mcpServers;
12917
+ }
12918
+ if ((codexAgent == null ? void 0 : codexAgent.skillsConfig) && Object.keys(codexAgent.skillsConfig).length > 0) {
12919
+ role["skills"] = { config: codexAgent.skillsConfig };
12689
12920
  }
12690
12921
  for (const section of pkg.content.sections) {
12691
12922
  if (section.type === "tools") {
@@ -12698,7 +12929,7 @@ function convertToAgentRoleToml(pkg, warnings) {
12698
12929
  }
12699
12930
  return import_toml2.default.stringify(role);
12700
12931
  }
12701
- function convertToSkillMd(pkg, warnings) {
12932
+ function convertToSkillMd(pkg, warnings, config) {
12702
12933
  const lines = [];
12703
12934
  const metadataSection = pkg.content.sections.find((s) => s.type === "metadata");
12704
12935
  const toolsSection = pkg.content.sections.find((s) => s.type === "tools");
@@ -12731,6 +12962,9 @@ function convertToSkillMd(pkg, warnings) {
12731
12962
  if ((toolsSection == null ? void 0 : toolsSection.type) === "tools" && toolsSection.tools.length > 0 && !frontmatter["allowed-tools"]) {
12732
12963
  frontmatter["allowed-tools"] = toolsSection.tools.join(" ");
12733
12964
  }
12965
+ if (config == null ? void 0 : config.allowedTools) {
12966
+ frontmatter["allowed-tools"] = normalizeAllowedTools(config.allowedTools);
12967
+ }
12734
12968
  lines.push("---");
12735
12969
  lines.push(jsYaml.dump(frontmatter, { indent: 2, lineWidth: -1 }).trim());
12736
12970
  lines.push("---");
@@ -12757,6 +12991,9 @@ function truncateDescription(desc, maxLength) {
12757
12991
  return desc;
12758
12992
  return desc.substring(0, maxLength - 3) + "...";
12759
12993
  }
12994
+ function normalizeAllowedTools(tools) {
12995
+ return tools.split(/[,\s]+/).map((tool) => tool.trim()).filter(Boolean).join(" ");
12996
+ }
12760
12997
  function stripNamespace(name) {
12761
12998
  return name.replace(/^@[^/]+\//, "");
12762
12999
  }
@@ -13291,6 +13528,11 @@ var init_format_registry = __esm({
13291
13528
  directory: ".cursor/commands",
13292
13529
  filePatterns: ["*.md"],
13293
13530
  fileExtension: ".md"
13531
+ },
13532
+ plugin: {
13533
+ directory: ".cursor-plugin",
13534
+ filePatterns: ["plugin.json"],
13535
+ fileExtension: ".json"
13294
13536
  }
13295
13537
  }
13296
13538
  },
@@ -13912,8 +14154,9 @@ function formatSupportsSubtype2(format, subtype) {
13912
14154
  case "skill":
13913
14155
  return capabilities.supportsSkills || false;
13914
14156
  case "plugin":
14157
+ return capabilities.supportsPlugins || false;
13915
14158
  case "extension":
13916
- return capabilities.supportsPlugins || capabilities.supportsExtensions || false;
14159
+ return capabilities.supportsExtensions || false;
13917
14160
  case "agent":
13918
14161
  return capabilities.supportsAgents || false;
13919
14162
  default:
@@ -14357,6 +14600,7 @@ __export(dist_exports, {
14357
14600
  continueSchema: () => continueSchema,
14358
14601
  copilotSchema: () => copilotSchema,
14359
14602
  copilotSkillSchema: () => copilotSkillSchema,
14603
+ createMinimalCursorPluginJson: () => createMinimalCursorPluginJson,
14360
14604
  createMinimalPluginJson: () => createMinimalPluginJson,
14361
14605
  cursorCommandSchema: () => cursorCommandSchema,
14362
14606
  cursorHooksSchema: () => cursorHooksSchema,
@@ -14366,6 +14610,7 @@ __export(dist_exports, {
14366
14610
  droidSchema: () => droidSchema,
14367
14611
  droidSkillSchema: () => droidSkillSchema,
14368
14612
  droidSlashCommandSchema: () => droidSlashCommandSchema,
14613
+ extractCursorMCPServers: () => extractCursorMCPServers,
14369
14614
  extractMCPServers: () => extractMCPServers,
14370
14615
  extractMCPServersFromCanonical: () => extractMCPServers2,
14371
14616
  findFormatByRootFile: () => findFormatByRootFile,
@@ -14384,6 +14629,7 @@ __export(dist_exports, {
14384
14629
  fromCopilot: () => fromCopilot,
14385
14630
  fromCursor: () => fromCursor,
14386
14631
  fromCursorHooks: () => fromCursorHooks,
14632
+ fromCursorPlugin: () => fromCursorPlugin,
14387
14633
  fromDroid: () => fromDroid,
14388
14634
  fromGemini: () => fromGemini,
14389
14635
  fromGeminiPlugin: () => fromGeminiPlugin,
@@ -14402,6 +14648,7 @@ __export(dist_exports, {
14402
14648
  geminiToClaudeMCP: () => geminiToClaudeMCP,
14403
14649
  geminiToKiroMCP: () => geminiToKiroMCP,
14404
14650
  generateCodexFilename: () => generateFilename2,
14651
+ generateCursorPluginJson: () => generateCursorPluginJson,
14405
14652
  generateMCPServerPackage: () => generateMCPServerPackage,
14406
14653
  generatePluginJson: () => generatePluginJson,
14407
14654
  generateZedFilename: () => generateFilename,
@@ -14457,6 +14704,7 @@ __export(dist_exports, {
14457
14704
  normalizeFormat: () => normalizeFormat,
14458
14705
  opencodeSchema: () => opencodeSchema,
14459
14706
  opencodeSlashCommandSchema: () => opencodeSlashCommandSchema,
14707
+ parseCursorPluginJson: () => parseCursorPluginJson,
14460
14708
  parseMCPServerJson: () => parseMCPServerJson,
14461
14709
  parsePluginJson: () => parsePluginJson,
14462
14710
  replitSchema: () => replitSchema,
@@ -14476,6 +14724,7 @@ __export(dist_exports, {
14476
14724
  toCopilot: () => toCopilot,
14477
14725
  toCursor: () => toCursor,
14478
14726
  toCursorHooks: () => toCursorHooks,
14727
+ toCursorPlugin: () => toCursorPlugin,
14479
14728
  toDroid: () => toDroid,
14480
14729
  toGemini: () => toGemini,
14481
14730
  toGeminiPlugin: () => toGeminiPlugin,
@@ -14511,6 +14760,7 @@ var init_dist = __esm({
14511
14760
  init_from_cursor_hooks();
14512
14761
  init_from_claude();
14513
14762
  init_from_claude_plugin();
14763
+ init_from_cursor_plugin();
14514
14764
  init_from_continue();
14515
14765
  init_from_copilot();
14516
14766
  init_from_kiro();
@@ -14534,6 +14784,7 @@ var init_dist = __esm({
14534
14784
  init_to_cursor_hooks();
14535
14785
  init_to_claude();
14536
14786
  init_to_claude_plugin();
14787
+ init_to_cursor_plugin();
14537
14788
  init_to_continue();
14538
14789
  init_to_copilot();
14539
14790
  init_to_kiro();
@@ -17350,6 +17601,43 @@ function getPackageIcon2(format, subtype) {
17350
17601
  };
17351
17602
  return subtypeIcons[subtype] || formatIcons[format] || "\u{1F4E6}";
17352
17603
  }
17604
+ function hasConfigValues(config) {
17605
+ return Boolean(config.tools || config.model);
17606
+ }
17607
+ function normalizeAllowedTools2(tools) {
17608
+ return tools.split(/[,\s]+/).map((tool) => tool.trim()).filter(Boolean).join(" ");
17609
+ }
17610
+ function normalizeToolsForClaude(tools) {
17611
+ if (tools.includes(",")) {
17612
+ return tools.split(",").map((t) => t.trim()).filter(Boolean).join(", ");
17613
+ }
17614
+ const parsed = tools.match(/[^\s,()]+(?:\([^)]*\))?/g) || [];
17615
+ return parsed.map((t) => t.trim()).filter(Boolean).join(", ");
17616
+ }
17617
+ function applyAgentSkillsTools(content, tools) {
17618
+ if (!content.startsWith("---\n")) {
17619
+ return content;
17620
+ }
17621
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
17622
+ if (!match) {
17623
+ return content;
17624
+ }
17625
+ const [, frontmatterText, body] = match;
17626
+ const lines = frontmatterText.split("\n");
17627
+ const normalizedTools = normalizeAllowedTools2(tools);
17628
+ const allowedToolsIndex = lines.findIndex((line) => line.startsWith("allowed-tools:"));
17629
+ if (allowedToolsIndex >= 0) {
17630
+ lines[allowedToolsIndex] = `allowed-tools: ${normalizedTools}`;
17631
+ } else {
17632
+ const descriptionIndex = lines.findIndex((line) => line.startsWith("description:"));
17633
+ const insertAt = descriptionIndex >= 0 ? descriptionIndex + 1 : lines.length;
17634
+ lines.splice(insertAt, 0, `allowed-tools: ${normalizedTools}`);
17635
+ }
17636
+ return `---
17637
+ ${lines.join("\n")}
17638
+ ---
17639
+ ${body}`;
17640
+ }
17353
17641
  function getPackageLabel2(format, subtype) {
17354
17642
  const formatLabels = {
17355
17643
  "claude": "Claude",
@@ -17716,10 +18004,10 @@ This could indicate:
17716
18004
  try {
17717
18005
  switch (sourceFormat) {
17718
18006
  case "cursor":
17719
- canonicalPkg = fromCursor(sourceContent, metadata);
18007
+ canonicalPkg = fromCursor(sourceContent, metadata, pkg.subtype);
17720
18008
  break;
17721
18009
  case "claude":
17722
- canonicalPkg = fromClaude(sourceContent, metadata);
18010
+ canonicalPkg = fromClaude(sourceContent, metadata, "claude", pkg.subtype);
17723
18011
  break;
17724
18012
  case "windsurf":
17725
18013
  canonicalPkg = fromWindsurf(sourceContent, metadata);
@@ -17751,6 +18039,10 @@ This could indicate:
17751
18039
  }
17752
18040
  let convertedContent;
17753
18041
  const targetFormat2 = format == null ? void 0 : format.toLowerCase();
18042
+ const effectiveClaudeConfig = {
18043
+ ...config.claude,
18044
+ ...options.tools ? { tools: normalizeToolsForClaude(options.tools) } : {}
18045
+ };
17754
18046
  try {
17755
18047
  switch (targetFormat2) {
17756
18048
  case "cursor":
@@ -17766,7 +18058,7 @@ This could indicate:
17766
18058
  break;
17767
18059
  case "claude":
17768
18060
  case "claude.md":
17769
- const claudeResult = toClaude(canonicalPkg);
18061
+ const claudeResult = toClaude(canonicalPkg, hasConfigValues(effectiveClaudeConfig) ? { claudeConfig: effectiveClaudeConfig } : {});
17770
18062
  convertedContent = claudeResult.content;
17771
18063
  break;
17772
18064
  case "continue":
@@ -17826,7 +18118,7 @@ This could indicate:
17826
18118
  convertedContent = toReplit(canonicalPkg).content;
17827
18119
  break;
17828
18120
  case "codex":
17829
- convertedContent = toCodex(canonicalPkg).content;
18121
+ convertedContent = toCodex(canonicalPkg, options.tools ? { codexConfig: { allowedTools: options.tools } } : {}).content;
17830
18122
  break;
17831
18123
  case "generic":
17832
18124
  convertedContent = toCursor(canonicalPkg).content;
@@ -18158,11 +18450,19 @@ This could indicate:
18158
18450
  }
18159
18451
  }
18160
18452
  if (format === "claude" && hasClaudeHeader(mainFile)) {
18161
- if (config.claude) {
18453
+ const effectiveClaudeConfig = {
18454
+ ...config.claude,
18455
+ ...options.tools ? { tools: normalizeToolsForClaude(options.tools) } : {}
18456
+ };
18457
+ if (hasConfigValues(effectiveClaudeConfig)) {
18162
18458
  console.log(` \u2699\uFE0F Applying Claude agent config...`);
18163
- mainFile = applyClaudeConfig(mainFile, config.claude);
18459
+ mainFile = applyClaudeConfig(mainFile, effectiveClaudeConfig);
18164
18460
  }
18165
18461
  }
18462
+ if (effectiveFormat === "codex" && effectiveSubtype === "skill" && options.tools) {
18463
+ console.log(` \u2699\uFE0F Applying Codex skill tools override...`);
18464
+ mainFile = applyAgentSkillsTools(mainFile, options.tools);
18465
+ }
18166
18466
  if (effectiveFormat === "claude" && effectiveSubtype === "hook") {
18167
18467
  destPath = destPath || `${destDir}/settings.json`;
18168
18468
  let hookConfig;
@@ -18294,6 +18594,10 @@ This could indicate:
18294
18594
  fileName = fileName.split("/").pop() || fileName;
18295
18595
  }
18296
18596
  }
18597
+ if (effectiveFormat === "codex" && effectiveSubtype === "skill" && options.tools && (fileName === "SKILL.md" || fileName.endsWith("/SKILL.md"))) {
18598
+ console.log(` \u2699\uFE0F Applying Codex skill tools override (multi-file)...`);
18599
+ fileContent = applyAgentSkillsTools(fileContent, options.tools);
18600
+ }
18297
18601
  const filePath = `${packageDir}/${fileName}`;
18298
18602
  await saveFile(filePath, fileContent);
18299
18603
  fileCount++;
@@ -18647,7 +18951,7 @@ async function installFromLockfile(options) {
18647
18951
  }
18648
18952
  function createInstallCommand() {
18649
18953
  const command = new import_commander12.Command("install");
18650
- command.description("Install a package from the registry, or install all packages from prpm.lock if no package specified").argument("[package]", "Package to install (e.g., react-rules or react-rules@1.2.0). If omitted, installs all packages from prpm.lock").option("--version <version>", "Specific version to install").option("--as <format>", `Convert and install in specific format (${import_types.FORMATS.join(", ")})`).option("--format <format>", "Alias for --as").option("--location <path>", "Custom location for installed files (Agents.md or nested Cursor rules)").option("--subtype <subtype>", "Specify subtype when converting (skill, agent, rule, etc.)").option("--hook-mapping <strategy>", "Hook mapping strategy: auto (default), strict, skip", "auto").option("--frozen-lockfile", "Fail if lock file needs to be updated (for CI)").option("-y, --yes", "Auto-confirm prompts (overwrite files without asking)").option("--no-append", "Skip adding skill to manifest file (skill files only)").option("--manifest-file <filename>", "Custom manifest filename for progressive disclosure").option("--eager", "Force skill/agent to always activate (not on-demand)").option("--lazy", "Use default on-demand activation (overrides package eager setting)").option("--global", "Install MCP servers to global config (e.g., ~/.claude/settings.json, ~/.codex/config.toml, ~/.cursor/mcp.json, ~/.kiro/settings/mcp.json)").option("--editor <editor>", "[Deprecated: use --as] Target editor for MCP server installation").action(async (packageSpec, options) => {
18954
+ command.description("Install a package from the registry, or install all packages from prpm.lock if no package specified").argument("[package]", "Package to install (e.g., react-rules or react-rules@1.2.0). If omitted, installs all packages from prpm.lock").option("--version <version>", "Specific version to install").option("--as <format>", `Convert and install in specific format (${import_types.FORMATS.join(", ")})`).option("--format <format>", "Alias for --as").option("--location <path>", "Custom location for installed files (Agents.md or nested Cursor rules)").option("--subtype <subtype>", "Specify subtype when converting (skill, agent, rule, etc.)").option("--hook-mapping <strategy>", "Hook mapping strategy: auto (default), strict, skip", "auto").option("--frozen-lockfile", "Fail if lock file needs to be updated (for CI)").option("-y, --yes", "Auto-confirm prompts (overwrite files without asking)").option("--no-append", "Skip adding skill to manifest file (skill files only)").option("--manifest-file <filename>", "Custom manifest filename for progressive disclosure").option("--eager", "Force skill/agent to always activate (not on-demand)").option("--lazy", "Use default on-demand activation (overrides package eager setting)").option("--tools <tools>", "Override Claude/Codex tool list for this install (comma- or space-separated)").option("--global", "Install MCP servers to global config (e.g., ~/.claude/settings.json, ~/.codex/config.toml, ~/.cursor/mcp.json, ~/.kiro/settings/mcp.json)").option("--editor <editor>", "[Deprecated: use --as] Target editor for MCP server installation").action(async (packageSpec, options) => {
18651
18955
  const rawAs = options.format || options.as;
18652
18956
  const validFormats = import_types.FORMATS;
18653
18957
  const isMCPEditorOnly = rawAs && !validFormats.includes(rawAs) && MCP_EDITORS.includes(rawAs);
@@ -18694,6 +18998,9 @@ Valid strategies: ${VALID_HOOK_MAPPING_STRATEGIES.join(", ")}`
18694
18998
  );
18695
18999
  }
18696
19000
  if (!packageSpec) {
19001
+ if (options.tools) {
19002
+ console.warn("\u26A0\uFE0F --tools is ignored when installing from prpm.lock (no package specified)");
19003
+ }
18697
19004
  await installFromLockfile({
18698
19005
  as: convertTo,
18699
19006
  subtype: options.subtype,
@@ -18715,6 +19022,7 @@ Valid strategies: ${VALID_HOOK_MAPPING_STRATEGIES.join(", ")}`
18715
19022
  manifestFile: options.manifestFile,
18716
19023
  hookMapping: options.hookMapping,
18717
19024
  eager,
19025
+ tools: options.tools,
18718
19026
  global: options.global,
18719
19027
  editor: mcpEditor
18720
19028
  });
@@ -0,0 +1,353 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://registry.prpm.dev/api/v1/schemas/claude/hook-config.json",
4
+ "$comment": "https://code.claude.com/docs/en/hooks",
5
+ "title": "Claude Hook Configuration",
6
+ "description": "JSON Schema for Claude Code hook.json configuration files. Defines hooks that execute on specific events.",
7
+ "type": "object",
8
+ "properties": {
9
+ "description": {
10
+ "type": "string",
11
+ "description": "Optional description of this hook configuration"
12
+ },
13
+ "hooks": {
14
+ "type": "object",
15
+ "description": "Hook event configurations",
16
+ "properties": {
17
+ "PreToolUse": {
18
+ "$ref": "#/$defs/hookEventConfig",
19
+ "description": "Fires after Claude creates tool parameters, before processing. Exit 2 blocks the tool call."
20
+ },
21
+ "PermissionRequest": {
22
+ "$ref": "#/$defs/hookEventConfig",
23
+ "description": "Fires when user is shown a permission dialog. Exit 2 denies permission."
24
+ },
25
+ "PostToolUse": {
26
+ "$ref": "#/$defs/hookEventConfig",
27
+ "description": "Fires immediately after a tool completes successfully. Cannot block (tool already ran)."
28
+ },
29
+ "Notification": {
30
+ "$ref": "#/$defs/hookEventConfig",
31
+ "description": "Fires when Claude Code sends notifications. Matcher matches notification_type."
32
+ },
33
+ "UserPromptSubmit": {
34
+ "$ref": "#/$defs/hookEventConfig",
35
+ "description": "Fires when user submits a prompt, before Claude processes it. Exit 2 blocks and erases prompt."
36
+ },
37
+ "Stop": {
38
+ "$ref": "#/$defs/hookEventConfig",
39
+ "description": "Fires when main Claude Code agent finishes responding. Exit 2 blocks stoppage."
40
+ },
41
+ "SubagentStop": {
42
+ "$ref": "#/$defs/hookEventConfig",
43
+ "description": "Fires when a Claude Code subagent (Task tool) finishes. Exit 2 blocks stoppage."
44
+ },
45
+ "PreCompact": {
46
+ "$ref": "#/$defs/hookEventConfig",
47
+ "description": "Fires before Claude Code runs a compact operation."
48
+ },
49
+ "SessionStart": {
50
+ "$ref": "#/$defs/hookEventConfig",
51
+ "description": "Fires when Claude Code starts or resumes a session."
52
+ },
53
+ "SessionEnd": {
54
+ "$ref": "#/$defs/hookEventConfig",
55
+ "description": "Fires when a Claude Code session ends."
56
+ }
57
+ },
58
+ "additionalProperties": false
59
+ }
60
+ },
61
+ "required": ["hooks"],
62
+ "$defs": {
63
+ "hookEventConfig": {
64
+ "type": "array",
65
+ "items": {
66
+ "type": "object",
67
+ "properties": {
68
+ "matcher": {
69
+ "type": "string",
70
+ "description": "Pattern to match tool names (PreToolUse/PostToolUse), notification types (Notification), etc. Use regex patterns like 'Write|Edit' or wildcards like '*'. Empty string matches all."
71
+ },
72
+ "hooks": {
73
+ "type": "array",
74
+ "items": {
75
+ "$ref": "#/$defs/hookDefinition"
76
+ },
77
+ "description": "Array of hooks to execute when matcher matches"
78
+ }
79
+ },
80
+ "required": ["hooks"]
81
+ }
82
+ },
83
+ "hookDefinition": {
84
+ "type": "object",
85
+ "oneOf": [
86
+ {
87
+ "properties": {
88
+ "type": {
89
+ "const": "command",
90
+ "description": "Command hook - executes a bash command"
91
+ },
92
+ "command": {
93
+ "type": "string",
94
+ "description": "Bash command to execute. Use ${CLAUDE_PLUGIN_ROOT} for plugin-relative paths."
95
+ },
96
+ "timeout": {
97
+ "type": "number",
98
+ "default": 60,
99
+ "description": "Timeout in seconds (default: 60)"
100
+ }
101
+ },
102
+ "required": ["type", "command"]
103
+ },
104
+ {
105
+ "properties": {
106
+ "type": {
107
+ "const": "prompt",
108
+ "description": "Prompt hook - uses LLM reasoning (Stop/SubagentStop events only)"
109
+ },
110
+ "prompt": {
111
+ "type": "string",
112
+ "description": "LLM prompt text. Use $ARGUMENTS placeholder for context. Response must be { ok: boolean, reason: string }."
113
+ },
114
+ "timeout": {
115
+ "type": "number",
116
+ "default": 30,
117
+ "description": "Timeout in seconds (default: 30)"
118
+ }
119
+ },
120
+ "required": ["type", "prompt"]
121
+ }
122
+ ],
123
+ "properties": {
124
+ "type": {
125
+ "type": "string",
126
+ "enum": ["command", "prompt"],
127
+ "description": "Hook type"
128
+ },
129
+ "timeout": {
130
+ "type": "number",
131
+ "minimum": 1,
132
+ "maximum": 300,
133
+ "description": "Timeout in seconds"
134
+ },
135
+ "once": {
136
+ "type": "boolean",
137
+ "default": false,
138
+ "description": "Run only once per session (skills/slash commands only)"
139
+ },
140
+ "systemMessage": {
141
+ "type": "string",
142
+ "description": "Message shown to user when hook executes"
143
+ }
144
+ }
145
+ },
146
+ "hookInput": {
147
+ "type": "object",
148
+ "description": "JSON structure sent to hooks via stdin",
149
+ "properties": {
150
+ "session_id": {
151
+ "type": "string",
152
+ "description": "Current session identifier"
153
+ },
154
+ "transcript_path": {
155
+ "type": "string",
156
+ "description": "Path to session transcript file"
157
+ },
158
+ "cwd": {
159
+ "type": "string",
160
+ "description": "Current working directory"
161
+ },
162
+ "permission_mode": {
163
+ "type": "string",
164
+ "enum": ["default", "plan", "acceptEdits", "dontAsk", "bypassPermissions"],
165
+ "description": "Current permission mode"
166
+ },
167
+ "hook_event_name": {
168
+ "type": "string",
169
+ "enum": ["PreToolUse", "PermissionRequest", "PostToolUse", "Notification", "UserPromptSubmit", "Stop", "SubagentStop", "PreCompact", "SessionStart", "SessionEnd"],
170
+ "description": "Name of the hook event"
171
+ },
172
+ "tool_name": {
173
+ "type": "string",
174
+ "description": "Name of the tool (PreToolUse/PostToolUse only)"
175
+ },
176
+ "tool_input": {
177
+ "type": "object",
178
+ "description": "Input parameters for the tool (PreToolUse/PostToolUse only)"
179
+ },
180
+ "tool_use_id": {
181
+ "type": "string",
182
+ "description": "Unique identifier for this tool use (PreToolUse/PostToolUse only)"
183
+ },
184
+ "tool_response": {
185
+ "type": "object",
186
+ "description": "Response from the tool (PostToolUse only)"
187
+ },
188
+ "message": {
189
+ "type": "string",
190
+ "description": "Notification message (Notification only)"
191
+ },
192
+ "notification_type": {
193
+ "type": "string",
194
+ "enum": ["permission_prompt", "idle_prompt", "auth_success", "elicitation_dialog"],
195
+ "description": "Type of notification (Notification only)"
196
+ },
197
+ "prompt": {
198
+ "type": "string",
199
+ "description": "User's prompt text (UserPromptSubmit only)"
200
+ },
201
+ "stop_hook_active": {
202
+ "type": "boolean",
203
+ "description": "Whether stop hook is active (Stop/SubagentStop only)"
204
+ },
205
+ "trigger": {
206
+ "type": "string",
207
+ "enum": ["manual", "auto"],
208
+ "description": "What triggered the compact (PreCompact only)"
209
+ },
210
+ "custom_instructions": {
211
+ "type": "string",
212
+ "description": "Custom instructions for compact (PreCompact only)"
213
+ },
214
+ "source": {
215
+ "type": "string",
216
+ "enum": ["startup", "resume", "clear", "compact"],
217
+ "description": "How the session started (SessionStart only)"
218
+ },
219
+ "reason": {
220
+ "type": "string",
221
+ "enum": ["clear", "logout", "prompt_input_exit", "other"],
222
+ "description": "Why the session ended (SessionEnd only)"
223
+ }
224
+ }
225
+ },
226
+ "hookOutput": {
227
+ "type": "object",
228
+ "description": "JSON structure hooks can output via stdout (exit code 0 only)",
229
+ "properties": {
230
+ "continue": {
231
+ "type": "boolean",
232
+ "default": true,
233
+ "description": "Whether Claude should continue after hook"
234
+ },
235
+ "stopReason": {
236
+ "type": "string",
237
+ "description": "Message when continue is false"
238
+ },
239
+ "suppressOutput": {
240
+ "type": "boolean",
241
+ "default": false,
242
+ "description": "Hide stdout from transcript"
243
+ },
244
+ "systemMessage": {
245
+ "type": "string",
246
+ "description": "Warning message to show user"
247
+ },
248
+ "decision": {
249
+ "type": "string",
250
+ "enum": ["block"],
251
+ "description": "Set to 'block' to block the operation"
252
+ },
253
+ "reason": {
254
+ "type": "string",
255
+ "description": "Reason for the decision"
256
+ },
257
+ "hookSpecificOutput": {
258
+ "type": "object",
259
+ "description": "Event-specific output fields",
260
+ "properties": {
261
+ "hookEventName": {
262
+ "type": "string"
263
+ },
264
+ "permissionDecision": {
265
+ "type": "string",
266
+ "enum": ["allow", "deny", "ask"],
267
+ "description": "PreToolUse: permission decision"
268
+ },
269
+ "permissionDecisionReason": {
270
+ "type": "string",
271
+ "description": "PreToolUse: reason for decision"
272
+ },
273
+ "updatedInput": {
274
+ "type": "object",
275
+ "description": "PreToolUse/PermissionRequest: modified tool input"
276
+ },
277
+ "additionalContext": {
278
+ "type": "string",
279
+ "description": "Additional context to add"
280
+ }
281
+ }
282
+ }
283
+ }
284
+ },
285
+ "promptHookResponse": {
286
+ "type": "object",
287
+ "description": "Response format for prompt-based hooks",
288
+ "properties": {
289
+ "ok": {
290
+ "type": "boolean",
291
+ "description": "Whether the check passed"
292
+ },
293
+ "reason": {
294
+ "type": "string",
295
+ "description": "Explanation (required when ok is false)"
296
+ }
297
+ },
298
+ "required": ["ok"]
299
+ }
300
+ },
301
+ "examples": [
302
+ {
303
+ "description": "Format files on save",
304
+ "hooks": {
305
+ "PostToolUse": [
306
+ {
307
+ "matcher": "Write|Edit",
308
+ "hooks": [
309
+ {
310
+ "type": "command",
311
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
312
+ "timeout": 5
313
+ }
314
+ ]
315
+ }
316
+ ]
317
+ }
318
+ },
319
+ {
320
+ "description": "Block sensitive file access",
321
+ "hooks": {
322
+ "PreToolUse": [
323
+ {
324
+ "matcher": "Write|Edit|Read",
325
+ "hooks": [
326
+ {
327
+ "type": "command",
328
+ "command": "${CLAUDE_PLUGIN_ROOT}/scripts/block-sensitive.sh"
329
+ }
330
+ ]
331
+ }
332
+ ]
333
+ }
334
+ },
335
+ {
336
+ "description": "Verify task completion",
337
+ "hooks": {
338
+ "Stop": [
339
+ {
340
+ "matcher": "*",
341
+ "hooks": [
342
+ {
343
+ "type": "prompt",
344
+ "prompt": "Verify all requested tasks were completed. $ARGUMENTS",
345
+ "timeout": 30
346
+ }
347
+ ]
348
+ }
349
+ ]
350
+ }
351
+ }
352
+ ]
353
+ }
@@ -1,10 +1,28 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://registry.prpm.dev/api/v1/schemas/codex/agent-role.json",
4
- "title": "Codex Agent Role Format",
5
- "description": "JSON Schema for OpenAI Codex CLI agent role configuration (TOML format, installed to ~/.codex/agents/)",
4
+ "title": "Codex Agent Role / Subagent Format",
5
+ "description": "JSON Schema for OpenAI Codex CLI agent role and subagent configuration (TOML format, installed to ~/.codex/agents/ or .codex/agents/)",
6
6
  "type": "object",
7
+ "required": ["name", "description", "developer_instructions"],
7
8
  "properties": {
9
+ "name": {
10
+ "type": "string",
11
+ "description": "Display name of the agent/subagent"
12
+ },
13
+ "description": {
14
+ "type": "string",
15
+ "description": "Short description of what this agent does and when to use it"
16
+ },
17
+ "developer_instructions": {
18
+ "type": "string",
19
+ "description": "System prompt / developer instructions for this agent role"
20
+ },
21
+ "nickname_candidates": {
22
+ "type": "array",
23
+ "items": { "type": "string" },
24
+ "description": "Alternative names the agent can be referred to as"
25
+ },
8
26
  "model": {
9
27
  "type": "string",
10
28
  "description": "Model identifier to use for this agent role"
@@ -14,14 +32,40 @@
14
32
  "enum": ["low", "medium", "high"],
15
33
  "description": "Reasoning effort level for the model"
16
34
  },
17
- "developer_instructions": {
18
- "type": "string",
19
- "description": "System prompt / developer instructions for this agent role"
20
- },
21
35
  "sandbox_mode": {
22
36
  "type": "string",
23
37
  "enum": ["read-only", "workspace-write", "danger-full-access"],
24
38
  "description": "Filesystem/network sandbox policy: read-only (default), workspace-write, or danger-full-access (no sandbox)"
39
+ },
40
+ "mcp_servers": {
41
+ "type": "object",
42
+ "description": "MCP server configurations available to this agent",
43
+ "additionalProperties": {
44
+ "type": "object",
45
+ "properties": {
46
+ "command": { "type": "string" },
47
+ "args": {
48
+ "type": "array",
49
+ "items": { "type": "string" }
50
+ },
51
+ "env": {
52
+ "type": "object",
53
+ "additionalProperties": { "type": "string" }
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "skills": {
59
+ "type": "object",
60
+ "description": "Skills configuration for this agent",
61
+ "properties": {
62
+ "config": {
63
+ "type": "object",
64
+ "description": "Skill-specific configuration key-value pairs",
65
+ "additionalProperties": true
66
+ }
67
+ },
68
+ "additionalProperties": true
25
69
  }
26
70
  },
27
71
  "additionalProperties": false
@@ -0,0 +1,169 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://registry.prpm.dev/api/v1/schemas/cursor-plugin.json",
4
+ "$comment": "https://cursor.com/docs/plugins/building",
5
+ "title": "Cursor Plugin Format",
6
+ "description": "JSON Schema for Cursor plugins - bundles of rules, skills, agents, commands, hooks, and MCP server configurations",
7
+ "type": "object",
8
+ "required": ["name"],
9
+ "properties": {
10
+ "name": {
11
+ "type": "string",
12
+ "description": "Plugin identifier (lowercase, kebab-case with alphanumerics, hyphens, and periods)",
13
+ "pattern": "^[a-z0-9][a-z0-9.-]*[a-z0-9]$"
14
+ },
15
+ "description": {
16
+ "type": "string",
17
+ "description": "Brief explanation of plugin functionality"
18
+ },
19
+ "version": {
20
+ "type": "string",
21
+ "description": "Semantic version (e.g., '1.0.0')",
22
+ "pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?(\\+[a-zA-Z0-9.]+)?$"
23
+ },
24
+ "author": {
25
+ "type": "object",
26
+ "description": "Plugin author information",
27
+ "required": ["name"],
28
+ "properties": {
29
+ "name": {
30
+ "type": "string",
31
+ "description": "Author name"
32
+ },
33
+ "email": {
34
+ "type": "string",
35
+ "format": "email",
36
+ "description": "Author email address"
37
+ }
38
+ },
39
+ "additionalProperties": false
40
+ },
41
+ "homepage": {
42
+ "type": "string",
43
+ "format": "uri",
44
+ "description": "Plugin homepage URL"
45
+ },
46
+ "repository": {
47
+ "type": "string",
48
+ "format": "uri",
49
+ "description": "Repository URL"
50
+ },
51
+ "license": {
52
+ "type": "string",
53
+ "description": "SPDX license identifier (e.g., 'MIT', 'Apache-2.0')"
54
+ },
55
+ "keywords": {
56
+ "type": "array",
57
+ "items": {
58
+ "type": "string"
59
+ },
60
+ "description": "Keywords for discovery and categorization"
61
+ },
62
+ "logo": {
63
+ "type": "string",
64
+ "description": "Relative path to SVG logo or absolute URL"
65
+ },
66
+ "rules": {
67
+ "oneOf": [
68
+ { "type": "string" },
69
+ { "type": "array", "items": { "type": "string" } }
70
+ ],
71
+ "description": "Path(s) to rule files or directories"
72
+ },
73
+ "agents": {
74
+ "oneOf": [
75
+ { "type": "string" },
76
+ { "type": "array", "items": { "type": "string" } }
77
+ ],
78
+ "description": "Path(s) to agent files or directories"
79
+ },
80
+ "skills": {
81
+ "oneOf": [
82
+ { "type": "string" },
83
+ { "type": "array", "items": { "type": "string" } }
84
+ ],
85
+ "description": "Path(s) to skill directories"
86
+ },
87
+ "commands": {
88
+ "oneOf": [
89
+ { "type": "string" },
90
+ { "type": "array", "items": { "type": "string" } }
91
+ ],
92
+ "description": "Path(s) to command files or directories"
93
+ },
94
+ "hooks": {
95
+ "oneOf": [
96
+ { "type": "string" },
97
+ {
98
+ "type": "object",
99
+ "properties": {
100
+ "hooks": {
101
+ "type": "object",
102
+ "additionalProperties": {
103
+ "type": "array",
104
+ "items": {
105
+ "type": "object",
106
+ "required": ["command"],
107
+ "properties": {
108
+ "command": { "type": "string" },
109
+ "matcher": { "type": "string" }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ],
117
+ "description": "Path to hooks.json or inline hooks configuration"
118
+ },
119
+ "mcpServers": {
120
+ "oneOf": [
121
+ { "type": "string" },
122
+ {
123
+ "type": "object",
124
+ "description": "MCP server configurations",
125
+ "additionalProperties": {
126
+ "type": "object",
127
+ "required": ["command"],
128
+ "properties": {
129
+ "command": {
130
+ "type": "string",
131
+ "description": "Command to run the MCP server"
132
+ },
133
+ "args": {
134
+ "type": "array",
135
+ "items": { "type": "string" },
136
+ "description": "Arguments for the command"
137
+ },
138
+ "env": {
139
+ "type": "object",
140
+ "additionalProperties": { "type": "string" },
141
+ "description": "Environment variables"
142
+ }
143
+ }
144
+ }
145
+ },
146
+ {
147
+ "type": "array",
148
+ "items": { "type": "string" }
149
+ }
150
+ ],
151
+ "description": "Path to .mcp.json, inline MCP config, or array of paths"
152
+ }
153
+ },
154
+ "examples": [
155
+ {
156
+ "name": "my-cursor-plugin",
157
+ "description": "A plugin with rules, agents, and MCP servers",
158
+ "version": "1.0.0",
159
+ "author": { "name": "Jane Doe", "email": "jane@example.com" },
160
+ "keywords": ["typescript", "testing"],
161
+ "mcpServers": {
162
+ "playwright": {
163
+ "command": "npx",
164
+ "args": ["-y", "@playwright/mcp@latest"]
165
+ }
166
+ }
167
+ }
168
+ ]
169
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm",
3
- "version": "2.1.30",
3
+ "version": "2.1.31",
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.31",
49
- "@pr-pm/registry-client": "^2.3.30",
50
- "@pr-pm/types": "^2.1.31",
48
+ "@pr-pm/converters": "^2.1.32",
49
+ "@pr-pm/registry-client": "^2.3.31",
50
+ "@pr-pm/types": "^2.1.32",
51
51
  "ajv": "^8.17.1",
52
52
  "ajv-formats": "^3.0.1",
53
53
  "chalk": "^5.6.2",