---
name: prpm-json-best-practices
description: Best practices for structuring prpm.json package manifests with required fields, tags, organization, multi-package management, enhanced file format, eager/lazy activation, and conversion hints
---

# PRPM JSON Best Practices

You are an expert at creating and maintaining `prpm.json` package manifests for PRPM (Prompt Package Manager). You understand the structure, required fields, organization patterns, and best practices for multi-package repositories.

## When to Apply This Skill

**Use when:**

- Creating a new `prpm.json` manifest for publishing packages
- Maintaining existing `prpm.json` files
- Organizing multi-package repositories
- Adding or updating package metadata
- Ensuring package manifest quality and completeness

**Don't use for:**

- User configuration files (`.prpmrc`) - those are for users
- Lockfiles (`prpm.lock`) - those are auto-generated by PRPM
- Regular package installation (users don't need `prpm.json`)
- Dependencies already tracked in lockfiles

## Core Purpose

`prpm.json` is **only needed if you're publishing packages**. Regular users installing packages from the registry don't need this file.

Use `prpm.json` when you're:

- Publishing a package to the PRPM registry
- Creating a collection of packages
- Distributing your own prompts/rules/skills/agents
- Managing multiple related packages in a monorepo

## File Structure

### Single Package

See `examples/single-package.json` for complete structure.

**Key fields:** `name`, `version`, `description`, `author`, `license`, `format`, `subtype`, `files`

### Multi-Package Repository

See `examples/multi-package.json` for complete structure.

**Use when:** Publishing multiple related packages from one repo
**Key difference:** Top-level `packages` array with individual package definitions

### Collections Repository

See `examples/collections-repository.json` for complete structure.

**Use when:** Bundling existing published packages into curated collections
**Key points:**

- `collections` array references packages by `packageId` (not files)
- Each collection has `id`, `name`, `description`, `packages`
- Packages can be `required: true` (default) or `false` (optional)
- Use version ranges (`^1.0.0`) or `latest`
- Add `reason` to explain why package is included

### Packages + Collections (Combined)

See `examples/packages-with-collections.json` for complete structure.

**Use when:** Publishing packages AND creating collections that bundle them
**Key points:**

- Define packages in `packages` array with files
- Define collections in `collections` array referencing those packages
- Collections can reference both local packages and external ones
- Publish both individual packages and collection bundles from same repo

## Required Fields

### Top-Level (Single Package)

| Field         | Type     | Required | Description                                                                     |
| ------------- | -------- | -------- | ------------------------------------------------------------------------------- |
| `name`        | string   | **Yes**  | Package name (kebab-case, unique in registry)                                   |
| `version`     | string   | **Yes**  | Semver version (e.g., `1.0.0`)                                                  |
| `description` | string   | **Yes**  | Clear description of what the package does                                      |
| `author`      | string   | **Yes**  | Author name and optional email                                                  |
| `license`     | string   | **Yes**  | SPDX license identifier (e.g., `MIT`, `Apache-2.0`)                             |
| `format`      | string   | **Yes**  | Target format: `claude`, `cursor`, `continue`, `windsurf`, etc.                 |
| `subtype`     | string   | **Yes**  | Package type: `agent`, `skill`, `rule`, `slash-command`, `prompt`, `collection` |
| `files`       | string[] | **Yes**  | Array of files to include in package                                            |

### Optional Top-Level Fields

| Field           | Type     | Description                                                   |
| --------------- | -------- | ------------------------------------------------------------- |
| `repository`    | string   | Git repository URL                                            |
| `organization`  | string   | Organization name (for scoped packages)                       |
| `homepage`      | string   | Package homepage URL                                          |
| `documentation` | string   | Documentation URL                                             |
| `license_text`  | string   | Full text of the license file for proper attribution          |
| `license_url`   | string   | URL to the license file in the repository                     |
| `tags`          | string[] | Searchable tags (kebab-case)                                  |
| `keywords`      | string[] | Additional keywords for search                                |
| `category`      | string   | Package category                                              |
| `private`       | boolean  | If `true`, won't be published to public registry              |
| `dependencies`  | object   | Package dependencies (name: semver)                           |
| `scripts`       | object   | Lifecycle scripts (multi-package only)                        |
| `eager`         | boolean  | If `true`, skill/agent loads at session start (not on-demand) |

### Multi-Package Fields

When using `packages` array:

| Field         | Type     | Required    | Description                                |
| ------------- | -------- | ----------- | ------------------------------------------ |
| `name`        | string   | **Yes**     | Unique package name                        |
| `version`     | string   | **Yes**     | Package version                            |
| `description` | string   | **Yes**     | Package description                        |
| `format`      | string   | **Yes**     | Package format                             |
| `subtype`     | string   | **Yes**     | Package subtype                            |
| `tags`        | string[] | Recommended | Searchable tags                            |
| `files`       | string[] | **Yes**     | Files to include                           |
| `private`     | boolean  | No          | Mark as private                            |
| `eager`       | boolean  | No          | Load at session start (skills/agents only) |

### Collection Fields

When using `collections` array:

**Top-level (repository with collections):**

- `name`, `version`, `description`, `author`, `license` - **Required**
- `repository`, `organization` - Recommended
- Note: No `format`, `subtype`, or `files` required at top level

**Each collection object:**

| Field         | Type     | Required    | Description                                            |
| ------------- | -------- | ----------- | ------------------------------------------------------ |
| `id`          | string   | **Yes**     | Unique collection identifier (kebab-case, 3-100 chars) |
| `name`        | string   | **Yes**     | Display name (3-100 chars)                             |
| `description` | string   | **Yes**     | What the collection provides (10-500 chars)            |
| `packages`    | array    | **Yes**     | Array of packages to include (minimum 1)               |
| `version`     | string   | Recommended | Semantic version of collection                         |
| `category`    | string   | Recommended | Collection category (development, testing, etc.)       |
| `tags`        | string[] | Recommended | Searchable tags (kebab-case, 1-10 items)               |
| `icon`        | string   | Optional    | Emoji or icon (max 10 chars)                           |

**Each package within collection:**

| Field       | Type    | Required | Description                                   |
| ----------- | ------- | -------- | --------------------------------------------- |
| `packageId` | string  | **Yes**  | Package to include                            |
| `version`   | string  | Optional | Version range (^1.0.0, ~2.1.0, 1.0.0, latest) |
| `required`  | boolean | Optional | Whether package is required (default: true)   |
| `reason`    | string  | Optional | Why package is included (max 200 chars)       |

## Format and Subtype Values

### Format (Target AI Tool)

| Format      | Description                   |
| ----------- | ----------------------------- |
| `claude`    | Claude Code (agents, skills)  |
| `cursor`    | Cursor IDE (rules, MDC files) |
| `continue`  | Continue.dev extension        |
| `windsurf`  | Windsurf IDE                  |
| `copilot`   | GitHub Copilot                |
| `kiro`      | Kiro IDE                      |
| `agents.md` | Agents.md format              |
| `generic`   | Generic/universal format      |
| `mcp`       | Model Context Protocol        |

### Subtype (Package Type)

| Subtype         | Description              | Typical Formats       |
| --------------- | ------------------------ | --------------------- |
| `agent`         | Autonomous agents        | `claude`, `agents.md` |
| `skill`         | Specialized capabilities | `claude`              |
| `rule`          | IDE rules and guidelines | `cursor`, `windsurf`  |
| `slash-command` | Slash commands           | `cursor`, `continue`  |
| `prompt`        | Prompt templates         | `generic`             |
| `collection`    | Package collections      | Any                   |
| `chatmode`      | Chat modes               | `kiro`                |
| `tool`          | MCP tools                | `mcp`                 |

## Eager vs Lazy Activation

Skills and agents can be configured to load eagerly (at session start) or lazily (on-demand when relevant).

### When to Use Eager

**Use `eager: true` when:**

- The skill should ALWAYS be active (coding standards, style guides)
- Critical behavior that must never be skipped
- Small, foundational skills with minimal token cost

**Keep lazy (default) when:**

- Specialized skills for specific contexts
- Large skills with significant token overhead
- Skills that only apply to certain file types

### Setting Eager in prpm.json

**Package-level:**

```json
{
  "name": "code-style-enforcer",
  "version": "1.0.0",
  "format": "claude",
  "subtype": "skill",
  "eager": true,
  "files": [".claude/skills/code-style/SKILL.md"]
}
```

**File-level (enhanced files format):**

```json
{
  "files": [
    {
      "path": ".claude/skills/critical-skill/SKILL.md",
      "format": "claude",
      "subtype": "skill",
      "eager": true
    },
    {
      "path": ".claude/skills/optional-skill/SKILL.md",
      "format": "claude",
      "subtype": "skill",
      "eager": false
    }
  ]
}
```

### Precedence

When installing, the final eager setting is determined by:

1. CLI flag (`--eager`/`--lazy`) - highest priority
2. File-level `eager` setting (enhanced files)
3. Package-level `eager` setting
4. Default: lazy (false)

### Applicable Subtypes

| Subtype         | Supports Eager |
| --------------- | -------------- |
| `skill`         | Yes            |
| `agent`         | Yes            |
| `rule`          | No             |
| `slash-command` | No             |
| `hook`          | No             |

Eager loading only affects progressive disclosure formats (agents.md, gemini.md, claude.md, aider).

## Tags Best Practices

### Tag Structure

- Use **kebab-case** for all tags
- Be **specific** and **searchable**
- Include 3-8 tags per package
- Combine technology, domain, and purpose tags

### Tag Categories

**Technology Tags:**

- Languages: `typescript`, `python`, `javascript`, `rust`
- Frameworks: `react`, `nextjs`, `fastify`, `django`
- Tools: `aws`, `docker`, `kubernetes`, `postgresql`

**Domain Tags:**

- `deployment`, `testing`, `ci-cd`, `database`
- `infrastructure`, `cloud`, `monitoring`
- `documentation`, `code-review`, `security`

**Purpose Tags:**

- `troubleshooting`, `debugging`, `best-practices`
- `automation`, `quality-assurance`, `performance`
- `architecture`, `design-patterns`

**Meta Tags:**

- `meta` - For packages about creating packages
- `prpm-internal` - For internal/private packages
- `prpm-development` - For PRPM development itself

### Tag Examples

**Good Tags:**

```json
{
  "tags": ["typescript", "type-safety", "code-quality", "best-practices", "static-analysis"]
}
```

**Poor Tags:**

```json
{
  "tags": [
    "code", // Too generic
    "stuff", // Meaningless
    "TypeScript", // Wrong case
    "type_safety" // Wrong format (use kebab-case)
  ]
}
```

## Organization Best Practices

### Multi-Package Organization

**Order packages by:**

1. **Privacy** - Private packages first
2. **Format** - Group by format (claude, cursor, etc.)
3. **Subtype** - Group by subtype (agent, skill, rule)

**Example organization:**

```json
{
  "packages": [
    // Private > Claude > Agents
    { "name": "internal-agent", "private": true, "format": "claude", "subtype": "agent" },

    // Private > Claude > Skills
    { "name": "internal-skill", "private": true, "format": "claude", "subtype": "skill" },

    // Private > Cursor > Rules
    { "name": "internal-rule", "private": true, "format": "cursor", "subtype": "rule" },

    // Public > Claude > Skills
    { "name": "public-skill", "format": "claude", "subtype": "skill" },

    // Public > Cursor > Rules
    { "name": "public-rule", "format": "cursor", "subtype": "rule" }
  ]
}
```

### Naming Conventions

**Package Names:**

- Use **kebab-case**: `my-awesome-skill`
- Be **descriptive**: `typescript-type-safety` not `ts-types`
- Avoid duplicates across formats: use suffixes if needed
  - `format-conversion-agent` (Claude agent)
  - `format-conversion` (Cursor rule)

**File Paths:**

- Use **full paths from project root** (where prpm.json lives)
- Agents: `.claude/agents/name.md`
- Skills: `.claude/skills/name/SKILL.md`
- Rules: `.cursor/rules/name.mdc`
- Commands: `.claude/commands/category/name.md`

## Version Management

### Semver Guidelines

Follow semantic versioning:

- **Major (1.0.0 → 2.0.0)**: Breaking changes
- **Minor (1.0.0 → 1.1.0)**: New features, backward compatible
- **Patch (1.0.0 → 1.0.1)**: Bug fixes, backward compatible

### Version Bumping

When to bump versions:

- **Patch**: Bug fixes, typo corrections, minor improvements
- **Minor**: New sections, additional examples, new features
- **Major**: Complete rewrites, breaking changes, renamed fields

### Keep Versions in Sync

For multi-package repos, keep related packages in sync:

```json
{
  "packages": [
    { "name": "pkg-one", "version": "1.2.0" },
    { "name": "pkg-two", "version": "1.2.0" },
    { "name": "pkg-three", "version": "1.2.0" }
  ]
}
```

## File Management

### Files Array

**CRITICAL: File paths must be full paths from project root (where prpm.json lives).**

**Required:**

- List all files to include in the package
- Use **full paths from project root** - not relative to destination directories
- Paths should start with `.claude/`, `.cursor/`, etc.
- Include documentation files

**Why Full Paths?**
File paths in `prpm.json` are used for:

1. **Tarball creation** - Reads files directly from these paths
2. **Snippet extraction** - Shows file preview before install
3. **Installation** - CLI derives destination from format/subtype

**Examples:**

Claude agent (single file):

```json
{
  "format": "claude",
  "subtype": "agent",
  "files": [".claude/agents/my-agent.md"]
}
```

Claude skill (multiple files):

```json
{
  "format": "claude",
  "subtype": "skill",
  "files": [
    ".claude/skills/my-skill/SKILL.md",
    ".claude/skills/my-skill/EXAMPLES.md",
    ".claude/skills/my-skill/README.md"
  ]
}
```

Cursor rule:

```json
{
  "format": "cursor",
  "subtype": "rule",
  "files": [".cursor/rules/my-rule.mdc"]
}
```

Slash command:

```json
{
  "format": "claude",
  "subtype": "slash-command",
  "files": [".claude/commands/category/my-command.md"]
}
```

### Enhanced File Format

**Advanced:** Files can be objects with metadata instead of simple strings. Useful for packages with multiple files targeting different formats or needing per-file metadata.

**Enhanced file object structure:**

```json
{
  "files": [
    {
      "path": ".cursor/rules/typescript.mdc",
      "format": "cursor",
      "subtype": "rule",
      "name": "TypeScript Rules",
      "description": "TypeScript coding standards and best practices",
      "tags": ["typescript", "frontend"]
    },
    {
      "path": ".cursor/rules/python.mdc",
      "format": "cursor",
      "subtype": "rule",
      "name": "Python Rules",
      "description": "Python best practices for backend development",
      "tags": ["python", "backend"]
    }
  ]
}
```

**When to use enhanced format:**

- Multi-file packages with different formats/subtypes per file
- Need per-file descriptions or tags
- Want to provide display names for individual files
- Building collection packages with mixed content types

**Enhanced file fields:**

| Field         | Required | Description                                     |
| ------------- | -------- | ----------------------------------------------- |
| `path`        | **Yes**  | Relative path to file from project root         |
| `format`      | **Yes**  | File's target format (`cursor`, `claude`, etc.) |
| `subtype`     | No       | File's subtype (`rule`, `skill`, `agent`, etc.) |
| `name`        | No       | Display name for this file                      |
| `description` | No       | Description of what this file does              |
| `tags`        | No       | File-specific tags (array of strings)           |

**Note:** Cannot mix simple strings and objects in the same `files` array. Use all strings OR all objects, not both.

**Common Mistake:**

```json
{
  // ❌ WRONG - Relative paths without directory prefix
  "files": ["agents/my-agent.md"]  // Will fail to find file

  // ✅ CORRECT - Full path from project root
  "files": [".claude/agents/my-agent.md"]
}
```

### File Verification

Always verify files exist:

```bash
# Check all files in prpm.json exist
for file in $(cat prpm.json | jq -r '.packages[].files[]'); do
  if [ ! -f "$file" ]; then
    echo "Missing: $file"
  fi
done
```

## Duplicate Detection

### Check for Duplicate Names

Run this check before committing:

```bash
# Check for duplicate package names
cat prpm.json | jq -r '.packages[].name' | sort | uniq -d
```

If output is empty, no duplicates exist. If names appear, you have duplicates to resolve.

### Resolving Duplicates

**Bad:**

```json
{
  "packages": [
    { "name": "typescript-safety", "format": "claude" },
    { "name": "typescript-safety", "format": "cursor" }
  ]
}
```

**Good:**

```json
{
  "packages": [
    { "name": "typescript-safety", "format": "claude", "subtype": "skill" },
    { "name": "typescript-safety-rule", "format": "cursor", "subtype": "rule" }
  ]
}
```

## Conversion Hints (Advanced)

**Purpose:** Help improve quality when converting packages to other formats. The `conversion` field provides format-specific hints for cross-format transformations.

**Note:** This is an advanced feature primarily used by format conversion tools. Most packages don't need this.

**Structure:**

```json
{
  "name": "my-package",
  "version": "1.0.0",
  "format": "claude",
  "conversion": {
    "cursor": {
      "alwaysApply": false,
      "priority": "high",
      "globs": ["**/*.ts", "**/*.tsx"]
    },
    "kiro": {
      "inclusion": "fileMatch",
      "fileMatchPattern": "**/*.ts",
      "domain": "typescript",
      "tools": ["fs_read", "fs_write"],
      "mcpServers": {
        "database": {
          "command": "mcp-server-postgres",
          "args": [],
          "env": {
            "DATABASE_URL": "${DATABASE_URL}"
          }
        }
      }
    },
    "copilot": {
      "applyTo": ["src/**", "lib/**"],
      "excludeAgent": "code-review"
    }
  }
}
```

**Supported conversion hints:**

### Cursor Hints

```json
{
  "conversion": {
    "cursor": {
      "alwaysApply": boolean,      // Whether rule should always apply
      "priority": "high|medium|low", // Rule priority level
      "globs": ["**/*.ts"]         // File patterns to auto-attach
    }
  }
}
```

### Claude Hints

```json
{
  "conversion": {
    "claude": {
      "model": "sonnet|opus|haiku|inherit", // Preferred model
      "tools": ["Read", "Write"], // Allowed tools
      "subagentType": "format-conversion" // Subagent type if agent
    }
  }
}
```

### Kiro Hints

```json
{
  "conversion": {
    "kiro": {
      "inclusion": "always|fileMatch|manual", // When to include
      "fileMatchPattern": "**/*.ts", // Pattern for fileMatch mode
      "domain": "typescript", // Domain category
      "tools": ["fs_read", "fs_write"], // Available tools
      "mcpServers": {
        // MCP server configs
        "database": {
          "command": "mcp-server-postgres",
          "args": [],
          "env": { "DATABASE_URL": "${DATABASE_URL}" }
        }
      }
    }
  }
}
```

### Copilot Hints

```json
{
  "conversion": {
    "copilot": {
      "applyTo": "src/**", // Path patterns
      "excludeAgent": "code-review|coding-agent" // Agent to exclude
    }
  }
}
```

### Continue Hints

```json
{
  "conversion": {
    "continue": {
      "alwaysApply": boolean,           // Always apply rule
      "globs": ["**/*.ts"],             // File patterns
      "regex": ["import.*from"]         // Regex patterns
    }
  }
}
```

### Windsurf Hints

```json
{
  "conversion": {
    "windsurf": {
      "characterLimit": 12000 // Warn if exceeding limit
    }
  }
}
```

### Agents.md Hints

```json
{
  "conversion": {
    "agentsMd": {
      "project": "my-project", // Project name
      "scope": "backend" // Scope/domain
    }
  }
}
```

**When to use conversion hints:**

- Publishing cross-format packages that need specific settings per format
- Format conversion tools need guidance on how to transform content
- Package behavior should change based on target format
- Want to preserve format-specific metadata during conversions

## Common Patterns

### Private Internal Packages

```json
{
  "name": "internal-tool",
  "version": "1.0.0",
  "description": "Internal development tool",
  "private": true,
  "format": "claude",
  "subtype": "skill",
  "tags": ["prpm-internal", "development"],
  "files": [".claude/skills/internal-tool/SKILL.md"]
}
```

### Meta Packages (Creating Other Packages)

```json
{
  "name": "creating-skills",
  "version": "1.0.0",
  "description": "Guide for creating effective Claude Code skills",
  "format": "claude",
  "subtype": "skill",
  "tags": ["meta", "claude-code", "skills", "documentation", "best-practices"],
  "files": [".claude/skills/creating-skills/SKILL.md"]
}
```

### Cross-Format Packages

When you have the same content for multiple formats:

```json
{
  "packages": [
    {
      "name": "format-conversion-agent",
      "format": "claude",
      "subtype": "agent",
      "description": "Agent for converting between AI prompt formats",
      "files": [".claude/agents/format-conversion.md"]
    },
    {
      "name": "format-conversion",
      "format": "cursor",
      "subtype": "rule",
      "description": "Rule for converting between AI prompt formats",
      "files": [".cursor/rules/format-conversion.mdc"]
    }
  ]
}
```

### Collections in prpm.json

Collections CAN be defined in prpm.json alongside packages using the `collections` array. Collections bundle multiple packages together for easier installation.

**Example with both packages and collections:**

```json
{
  "name": "my-prompts-repo",
  "author": "Your Name",
  "license": "MIT",
  "packages": [
    {
      "name": "typescript-rules",
      "version": "1.0.0",
      "description": "TypeScript best practices",
      "format": "cursor",
      "subtype": "rule",
      "tags": ["typescript"],
      "files": [".cursor/rules/typescript.mdc"]
    }
  ],
  "collections": [
    {
      "id": "my-dev-setup",
      "name": "My Development Setup",
      "description": "Complete development setup with TypeScript and React",
      "version": "1.0.0",
      "category": "development",
      "tags": ["typescript", "react"],
      "packages": [
        {
          "packageId": "typescript-strict",
          "version": "^1.0.0",
          "required": true,
          "reason": "Enforces strict TypeScript type safety"
        },
        {
          "packageId": "react-best-practices",
          "version": "^2.0.0",
          "required": true
        }
      ]
    }
  ]
}
```

For more details on creating collections, see the PRPM documentation at https://docs.prpm.dev or run `prpm help collections`.

**Summary:** `prpm.json` can contain both packages (skills, agents, rules, slash-commands, etc.) and collections.

## Lifecycle Scripts

**IMPORTANT:** The `scripts` field only applies to **multi-package manifests** (prpm.json with a `packages` array). It does NOT work in single-package manifests.

Use the `scripts` field to run commands automatically during package operations, particularly for building TypeScript hooks before publishing.

### When to Use Scripts

**Primary use case: Building TypeScript Hooks**

If your packages include Claude Code hooks written in TypeScript, you MUST build them to JavaScript before publishing:

```json
{
  "name": "my-packages",
  "license": "MIT",
  "scripts": {
    "prepublishOnly": "cd packages/hooks && npm run build"
  },
  "packages": [
    {
      "name": "my-hook",
      "version": "1.0.0",
      "format": "claude",
      "subtype": "hook",
      "files": [
        ".claude/hooks/my-hook/hook.ts",
        ".claude/hooks/my-hook/hook.json",
        ".claude/hooks/my-hook/dist/hook.js"
      ]
    }
  ]
}
```

### Available Script Types

| Script           | When it Runs                      | Use Case                                       |
| ---------------- | --------------------------------- | ---------------------------------------------- |
| `prepublishOnly` | Before `prpm publish` only        | **Recommended** - Build hooks, compile assets  |
| `prepublish`     | Before publish AND on npm install | **Not recommended** - causes unexpected builds |

**Always use `prepublishOnly` instead of `prepublish`** to avoid running builds when users install your packages.

### prepublishOnly Examples

**Single hook:**

```json
{
  "scripts": {
    "prepublishOnly": "cd .claude/hooks/my-hook && npm run build"
  }
}
```

**Multiple hooks:**

```json
{
  "scripts": {
    "prepublishOnly": "cd .claude/hooks/hook-one && npm run build && cd ../hook-two && npm run build"
  }
}
```

**With tests:**

```json
{
  "scripts": {
    "prepublishOnly": "npm test && cd packages/hooks && npm run build"
  }
}
```

### What Happens During Publishing

When you run `prpm publish`:

1. PRPM checks for `scripts.prepublishOnly` in your prpm.json
2. If found, runs the script from the directory containing prpm.json
3. If script succeeds (exit code 0), publishing continues
4. If script fails (non-zero exit code), publishing is aborted

**Script execution details:**

- Working directory: Same directory as prpm.json
- Timeout: 5 minutes (300,000ms) default
- Environment: Inherits your shell's environment variables
- Output: Shown in real-time

### Best Practices for Scripts

**DO:**

- ✅ Use `prepublishOnly` for building hooks
- ✅ Chain commands with `&&` for dependencies: `npm test && npm run build`
- ✅ Keep scripts fast (under 1 minute if possible)
- ✅ Test scripts locally before publishing

**DON'T:**

- ❌ Use `prepublish` (runs on install too)
- ❌ Forget to build hooks before publishing
- ❌ Use scripts in single-package manifests (not supported)
- ❌ Put long-running operations in scripts

### Common Patterns

**Hooks in packages/ directory:**

```json
{
  "scripts": {
    "prepublishOnly": "cd packages/hooks && npm run build"
  }
}
```

**Hooks in .claude/ directory:**

```json
{
  "scripts": {
    "prepublishOnly": "cd .claude/hooks/my-hook && npm run build"
  }
}
```

**Build multiple components:**

```json
{
  "scripts": {
    "prepublishOnly": "npm run build:hooks && npm run build:assets"
  }
}
```

### Debugging Script Failures

If your prepublishOnly script fails:

1. **Check the output** - Error messages show what went wrong
2. **Run manually** - Test the exact command in your terminal
3. **Verify working directory** - Scripts run from prpm.json location
4. **Check dependencies** - Ensure npm packages are installed

**Example debugging:**

```bash
# Test your prepublishOnly script manually
cd /path/to/prpm.json/directory
cd packages/hooks && npm run build

# If it works manually but fails in PRPM, check:
# - Working directory assumptions
# - Environment variables
# - Installed dependencies
```

### Why This Matters

**Without prepublishOnly:**

- You might forget to build hooks before publishing
- Published packages contain stale/outdated JavaScript
- Users install broken hooks
- Manual builds are error-prone

**With prepublishOnly:**

- Hooks automatically build before every publish
- JavaScript always matches TypeScript source
- Prevents publishing broken code
- Consistent, reliable publishing workflow

## Validation Checklist

Before publishing, verify:

**Required Fields:**

- [ ] All packages have `name`, `version`, `description`
- [ ] All packages have `format` and `subtype`
- [ ] All packages have `files` array
- [ ] Top-level has `author` and `license`

**File Verification:**

- [ ] All files in `files` arrays exist
- [ ] File paths are relative to repo root
- [ ] No missing or broken file references

**No Duplicates:**

- [ ] No duplicate package names
- [ ] Package names are unique across entire manifest

**Tags:**

- [ ] Tags use kebab-case
- [ ] 3-8 relevant tags per package
- [ ] Tags include technology, domain, and purpose

**Organization:**

- [ ] Private packages listed first
- [ ] Packages grouped by format and subtype
- [ ] Consistent versioning across related packages

## Lockfile Management

### Understanding prpm.lock

The `prpm.lock` file is **auto-generated** and tracks installed packages. It serves as the source of truth for what's installed in your project.

**IMPORTANT:** Do NOT add packages to `prpm.json` if they already exist in `prpm.lock`:

- `prpm.lock` tracks **installed dependencies** (packages you use)
- `prpm.json` defines **published packages** (packages you create and share)

### When to Use prpm.json vs prpm.lock

**Use `prpm.json` when:**

- You're creating a package to publish to the registry
- You want to define metadata for YOUR packages
- You're setting up a multi-package repository

**Use `prpm.lock` (auto-generated) when:**

- You install packages with `prpm install`
- You want to track which packages are installed
- You want reproducible installations across environments

### Common Mistake: Duplicating Dependencies

**❌ WRONG - Don't add installed packages to prpm.json:**

```json
// prpm.json
{
  "name": "my-project",
  "packages": [
    {
      "name": "typescript-safety", // ❌ This is an INSTALLED package
      "version": "1.0.0",
      "format": "cursor",
      "subtype": "rule",
      "files": [".cursor/rules/typescript-safety.mdc"]
    }
  ]
}
```

```json
// prpm.lock (auto-generated)
{
  "packages": {
    "@prpm/typescript-safety": {
      // ✅ Already tracked here
      "version": "1.0.0",
      "format": "cursor",
      "subtype": "rule"
    }
  }
}
```

**✅ CORRECT - prpm.json only for YOUR packages:**

```json
// prpm.json - Only YOUR packages you're publishing
{
  "name": "my-project",
  "packages": [
    {
      "name": "my-custom-rule", // ✅ This is YOUR package
      "version": "1.0.0",
      "format": "cursor",
      "subtype": "rule",
      "files": [".cursor/rules/my-custom-rule.mdc"]
    }
  ]
}
```

```json
// prpm.lock - Installed dependencies (auto-generated)
{
  "packages": {
    "@prpm/typescript-safety": {
      // ✅ Installed from registry
      "version": "1.0.0",
      "format": "cursor",
      "subtype": "rule"
    }
  }
}
```

### Key Principles

1. **Lockfile is Auto-Generated** - Never manually edit `prpm.lock`
2. **Separation of Concerns**:
   - `prpm.json` = What you PUBLISH
   - `prpm.lock` = What you INSTALL
3. **Check Lockfile First** - Before adding to `prpm.json`, check if it's already in `prpm.lock`
4. **Trust the Lockfile** - It's the authoritative record of installed packages

### Workflow Example

```bash
# Install a package (updates prpm.lock automatically)
prpm install @prpm/typescript-safety

# This creates/updates prpm.lock - DO NOT add to prpm.json!

# Only create prpm.json entries for packages YOU create:
# 1. Create your custom rule/skill/agent
# 2. Add entry to prpm.json
# 3. Publish with: prpm publish
```

## Publishing Workflow

### 1. Validate Manifest

```bash
# Validate JSON syntax
cat prpm.json | jq . > /dev/null

# Check for duplicates
cat prpm.json | jq -r '.packages[].name' | sort | uniq -d

# Verify files exist
# (see File Verification section)
```

### 2. Bump Versions

Update version numbers for changed packages.

### 3. Test Locally

```bash
# Test package installation
prpm install . --dry-run
```

### 4. Publish

```bash
# Publish all packages
prpm publish

# Or publish specific package
prpm publish --package my-skill
```

## Common Mistakes to Avoid

### ❌ Missing Required Fields

```json
{
  "name": "my-skill"
  // Missing: version, description, format, subtype, files
}
```

### ❌ Wrong Tag Format

```json
{
  "tags": ["TypeScript", "Code_Quality", "bestPractices"]
  // Should be: ["typescript", "code-quality", "best-practices"]
}
```

### ❌ Duplicate Names

```json
{
  "packages": [
    { "name": "my-skill", "format": "claude" },
    { "name": "my-skill", "format": "cursor" }
    // Second should be: "my-skill-rule" or similar
  ]
}
```

### ❌ Missing Files

```json
{
  "files": [".claude/skills/my-skill/SKILL.md"]
  // But .claude/skills/my-skill/SKILL.md doesn't exist in the repo
}
```

### ❌ Absolute Paths

```json
{
  "files": ["/Users/me/project/.claude/skills/my-skill/SKILL.md"]
  // Should be: ".claude/skills/my-skill/SKILL.md" (relative to project root)
}
```

### ❌ Missing Directory Prefix

```json
{
  "files": ["agents/my-agent.md"]
  // Should be: ".claude/agents/my-agent.md" (include .claude/ prefix)
}
```

## Remember

- `prpm.json` is **only for publishing YOUR packages/collections**, not for installed dependencies
- **Never add packages from `prpm.lock` to `prpm.json`** - they serve different purposes
- `prpm.lock` tracks what you INSTALL, `prpm.json` defines what you PUBLISH
- Use `collections` array to bundle existing packages (references by packageId)
- Use `packages` array to define packages with files
- Can combine both `packages` and `collections` in same repo
- Always validate before committing
- Keep versions in sync for related packages
- Use consistent, searchable tags
- Verify all file paths exist
- Check for duplicate names
- Follow semver for version management

**Goal:** Create maintainable, well-organized package manifests and curated collections that are easy to publish and discover in the PRPM registry.
