pr-pm / prpm-development
Install for your project team
Run this command in your project directory to install the skill for your entire team:
mkdir -p .claude/skills/prpm-development && curl -o .claude/skills/prpm-development/SKILL.md https://fastmcp.me/Skills/DownloadRaw?id=219
Project Skills
This skill will be saved in .claude/skills/prpm-development/ and checked into git. All team members will have access to it automatically.
Important: Please verify the skill by reviewing its instructions before using it.
Use when developing PRPM (Prompt Package Manager) - comprehensive knowledge base covering architecture, format conversion, package types, collections, quality standards, testing, and deployment
1 views
0 installs
Skill Content
---
name: prpm-development
description: Use when developing PRPM (Prompt Package Manager) - comprehensive knowledge base covering architecture, format conversion, package types, collections, quality standards, testing, and deployment
---
# PRPM Development Knowledge Base
Complete knowledge base for developing PRPM - the universal package manager for AI prompts, agents, and rules.
## Mission
Build the npm/cargo/pip equivalent for AI development artifacts. Enable developers to discover, install, share, and manage prompts across Cursor, Claude Code, Continue, Windsurf, and future AI editors.
## Core Architecture
### Universal Format Philosophy
1. **Canonical Format**: All packages stored in normalized JSON structure
2. **Smart Conversion**: Server-side format conversion with quality scoring
3. **Zero Lock-In**: Users convert between any format without data loss
4. **Format-Specific Optimization**: IDE-specific variants (e.g., Claude with MCP)
### Package Manager Best Practices
- **Semantic Versioning**: Strict semver for all packages
- **Dependency Resolution**: Smart conflict resolution like npm/cargo
- **Lock Files**: Reproducible installs (prpm-lock.json)
- **Registry-First**: All operations through central registry API
- **Caching**: Redis caching for converted packages (1-hour TTL)
### Developer Experience
- **One Command Install**: `prpm install @collection/nextjs-pro` gets everything
- **Auto-Detection**: Detect IDE from directory structure (.cursor/, .claude/)
- **Format Override**: `--as claude` to force specific format
- **Telemetry Opt-Out**: Privacy-first with easy opt-out
- **Beautiful CLI**: Clear progress indicators and colored output
### Git Workflow - CRITICAL RULES
**⚠️ NEVER PUSH DIRECTLY TO MAIN ⚠️**
PRPM uses a **branch-based workflow** with CI/CD automation. Direct pushes to main bypass all safety checks and can break production.
**ALWAYS follow this workflow:**
1. **Create a branch** for your changes:
```bash
git checkout -b feature/your-feature-name
# or
git checkout -b fix/bug-description
```
2. **Make commits** on your branch using conventional commit format:
```bash
git add [files]
git commit -m "feat: add OpenCode format support"
```
### Commit Message Format
Use conventional commit prefixes for all commits:
| Prefix | Use For | Example |
|--------|---------|---------|
| `feat:` | New features | `feat: add Kiro format support` |
| `fix:` | Bug fixes | `fix: resolve publish timeout issue` |
| `docs:` | Documentation | `docs: update API reference` |
| `chore:` | Maintenance | `chore(release): publish packages` |
| `refactor:` | Code refactoring | `refactor: simplify converter logic` |
| `test:` | Test changes | `test: add roundtrip tests for Gemini` |
| `perf:` | Performance | `perf: optimize search query` |
**Why this matters:**
- Automated changelog generation
- Clear commit history
- Easier code review
- Semantic versioning automation
**Bad commits to avoid:**
```bash
# ❌ Too vague
git commit -m "fixes"
git commit -m "updates"
git commit -m "changes"
# ✅ Clear and descriptive
git commit -m "fix: resolve schema validation for Claude agents"
git commit -m "feat: add support for Droid format subtypes"
```
3. **Push your branch**:
```bash
git push origin feature/your-feature-name
```
4. **Create a Pull Request** on GitHub:
- Automated CI tests run
- Code review happens
- Deployments are controlled
5. **After PR approval**, merge through GitHub UI:
- Triggers automated deployment
- Ensures CI passes first
- Maintains deployment history
**Why this matters:**
- Main branch is protected and deploys to production
- Direct pushes skip CI tests, linting, type checks
- Can deploy broken code to 7500+ package registry
- Breaks audit trail and rollback capability
- GitHub Actions workflows expect PRs, not direct pushes
**If you accidentally pushed to main:**
1. **DO NOT** force push to revert - breaks CI/CD
2. Create a revert commit on a branch
3. Open PR to fix the issue properly
4. Let CI/CD handle the corrective deployment
**Exception:** Only repository admins can push to main for emergency hotfixes (with explicit approval).
## Package Types
### 🎓 Skill
**Purpose**: Knowledge and guidelines for AI assistants
**Location**: `.claude/skills/`, `.cursor/rules/`
**Examples**: `@prpm/pulumi-troubleshooting`, `@typescript/best-practices`
### 🤖 Agent
**Purpose**: Autonomous AI agents for multi-step tasks
**Location**: `.claude/agents/`, `.cursor/agents/`
**Examples**: `@prpm/code-reviewer`, `@cursor/debugging-agent`
### 📋 Rule
**Purpose**: Specific instructions or constraints for AI behavior
**Location**: `.cursor/rules/`, `.cursorrules`
**Examples**: `@cursor/react-conventions`, `@cursor/test-first`
### 🔌 Plugin
**Purpose**: Extensions that add functionality
**Location**: `.cursor/plugins/`, `.claude/plugins/`
### 💬 Prompt
**Purpose**: Reusable prompt templates
**Location**: `.prompts/`, project-specific directories
### ⚡ Workflow
**Purpose**: Multi-step automation workflows
**Location**: `.workflows/`, `.github/workflows/`
### 🔧 Tool
**Purpose**: Executable utilities and scripts
**Location**: `scripts/`, `tools/`, `.bin/`
### 📄 Template
**Purpose**: Reusable file and project templates
**Location**: `templates/`, project-specific directories
### 🔗 MCP Server
**Purpose**: Model Context Protocol servers
**Location**: `.mcp/servers/`
## Format Conversion System
### Supported Formats
**Cursor (.mdc)**
- MDC frontmatter with `ruleType`, `alwaysApply`, `description`
- Markdown body
- Simple, focused on coding rules
- No structured tools/persona definitions
**Claude (agent format)**
- YAML frontmatter: `name`, `description`
- Optional: `tools` (comma-separated), `model` (sonnet/opus/haiku/inherit)
- Markdown body
- Supports persona, examples, instructions
**Continue (JSON)**
- JSON configuration
- Simple prompts, context rules
- Limited metadata support
**Windsurf**
- Similar to Cursor
- Markdown-based
- Basic structure
### Conversion Quality Scoring (0-100)
Start at 100 points, deduct for lossy conversions:
- Missing tools: -10 points
- Missing persona: -5 points
- Missing examples: -5 points
- Unsupported sections: -10 points each
- Format-specific features lost: -5 points
### Lossless vs Lossy Conversions
- **Canonical ↔ Claude**: Nearly lossless (95-100%)
- **Canonical ↔ Cursor**: Lossy on tools/persona (70-85%)
- **Canonical ↔ Continue**: Most lossy (60-75%)
## Collections System
Collections are curated bundles of packages that solve specific use cases.
### Collection Structure
```json
{
"id": "@collection/nextjs-pro",
"name": "Next.js Professional Setup",
"description": "Complete Next.js development setup",
"category": "frontend",
"packages": [
{
"packageId": "react-best-practices",
"required": true,
"reason": "Core React patterns"
},
{
"packageId": "typescript-strict",
"required": true,
"reason": "Type safety"
},
{
"packageId": "tailwind-helper",
"required": false,
"reason": "Styling utilities"
}
]
}
```
### Collection Installation Behavior
Collections can be installed using multiple identifier formats. The system intelligently resolves collections based on the format provided.
#### Installation Formats (Priority Order)
**1. Recommended Format: `collections/{slug}`**
```bash
prpm install collections/nextjs-pro
prpm install collections/nextjs-pro@2.0.0
```
- **Behavior**: Searches across ALL scopes for `name_slug = "nextjs-pro"`
- **Resolution**: Prioritizes by official → verified → downloads → created_at
- **Use Case**: User-friendly format for discovering popular collections
- **Example**: Finds `khaliqgant/nextjs-pro` even when searching `collections/nextjs-pro`
**2. Explicit Scope: `{scope}/{slug}` or `@{scope}/{slug}`**
```bash
prpm install khaliqgant/nextjs-pro
prpm install @khaliqgant/nextjs-pro
prpm install khaliqgant/nextjs-pro@2.0.0
```
- **Behavior**: Searches for specific `scope` and `name_slug` combination
- **Resolution**: Exact match only within that scope
- **Use Case**: Installing a specific author's version when multiple exist
- **Example**: Gets specifically the collection published by `khaliqgant`
**3. Name-Only Format: `{slug}`** (Legacy/Fallback)
```bash
prpm install nextjs-pro
prpm install nextjs-pro@1.0.0
```
- **Behavior**: Defaults to `scope = "collection"`, then falls back to cross-scope search
- **Resolution**: First tries scope="collection", then searches all scopes
- **Use Case**: Quick installs when collection origin doesn't matter
- **Recommendation**: Prefer `collections/{slug}` for clarity
#### Registry Resolution Logic
**Implementation Location**: `app/packages/registry/src/routes/collections.ts:485-519`
```typescript
// When scope is 'collection' (default from CLI for collections/* prefix):
if (scope === 'collection') {
// Search across ALL scopes, prioritize by:
// 1. Official collections (official = true)
// 2. Verified authors (verified = true)
// 3. Most downloads
// 4. Most recent
SELECT * FROM collections
WHERE name_slug = $1
ORDER BY official DESC, verified DESC, downloads DESC, created_at DESC
LIMIT 1
} else {
// Explicit scope: exact match only
SELECT * FROM collections
WHERE scope = $1 AND name_slug = $2
ORDER BY created_at DESC
LIMIT 1
}
```
#### CLI Resolution Logic
**Implementation Location**: `app/packages/cli/src/commands/collections.ts:487-504`
```typescript
// Parse collection spec:
// - collections/nextjs-pro → scope='collection', name_slug='nextjs-pro'
// - khaliqgant/nextjs-pro → scope='khaliqgant', name_slug='nextjs-pro'
// - @khaliqgant/nextjs-pro → scope='khaliqgant', name_slug='nextjs-pro'
// - nextjs-pro → scope='collection', name_slug='nextjs-pro'
const matchWithScope = collectionSpec.match(/^@?([^/]+)\/([^/@]+)(?:@(.+))?$/);
if (matchWithScope) {
[, scope, name_slug, version] = matchWithScope;
} else {
// No scope: default to 'collection'
[, name_slug, version] = collectionSpec.match(/^([^/@]+)(?:@(.+))?$/);
scope = 'collection';
}
```
#### Version Resolution
Collections support semantic versioning:
```bash
# Latest version (default)
prpm install collections/nextjs-pro
# Specific version
prpm install collections/nextjs-pro@2.0.4
# With scope and version
prpm install khaliqgant/nextjs-pro@2.0.4
```
**Registry Behavior**:
- Without version: Returns latest (most recent `created_at`)
- With version: Exact match required
#### Discovery Prioritization
When searching across all scopes (`collections/*` format), the system prioritizes:
1. **Official Collections** (official = true)
- Curated by PRPM maintainers
- Highest trust level
2. **Verified Authors** (verified = true)
- Known community contributors
- GitHub verified
3. **Download Count** (downloads DESC)
- Most popular collections
- Community validation
4. **Recency** (created_at DESC)
- Latest versions
- Actively maintained
#### Error Handling
**Collection Not Found**:
```bash
prpm install collections/nonexistent
# ❌ Failed to install collection: Collection not found
```
**Scope-Specific Not Found**:
```bash
prpm install wrongscope/nextjs-pro
# ❌ Failed to install collection: Collection not found
# Suggestion: Try 'collections/nextjs-pro' to search all scopes
```
### Collection Best Practices
1. **Required vs Optional**: Clearly mark essential vs nice-to-have packages
2. **Reason Documentation**: Every package explains why it's included
3. **IDE-Specific Variants**: Different packages per editor when needed
4. **Installation Order**: Consider dependencies
5. **Scope Naming**:
- Use your username/org as scope for personal collections
- Reserve "collection" scope for official PRPM collections
6. **User-Friendly IDs**: Use descriptive slugs (e.g., "nextjs-pro" not "np-setup")
7. **Version Incrementing**: Bump versions on meaningful changes (follow semver)
## Quality & Ranking System
### Multi-Factor Scoring (0-100)
**Popularity** (0-30 points):
- Total downloads (weighted by recency)
- Stars/favorites
- Trending velocity
**Quality** (0-30 points):
- User ratings (1-5 stars)
- Review sentiment
- Documentation completeness
**Trust** (0-20 points):
- Verified author badge
- Original creator vs fork
- Publisher reputation
- Security scan results
**Recency** (0-10 points):
- Last updated date (<30 days = 10 points)
- Release frequency
- Active maintenance
**Completeness** (0-10 points):
- Has README
- Has examples
- Has tags
- Complete metadata
## Technical Stack
### CLI (TypeScript + Node.js)
- **Commander.js**: CLI framework
- **Fastify Client**: HTTP client for registry
- **Tar**: Package tarball creation/extraction
- **Chalk**: Terminal colors
- **Ora**: Spinners for async operations
### Registry (TypeScript + Fastify + PostgreSQL)
- **Fastify**: High-performance web framework
- **PostgreSQL**: Primary database with GIN indexes
- **Redis**: Caching layer for converted packages
- **GitHub OAuth**: Authentication provider
- **Docker**: Containerized deployment
### Testing
- **Vitest**: Unit and integration tests
- **100% Coverage Goal**: Especially for format converters
- **Round-Trip Tests**: Ensure conversion quality
- **Fixtures**: Real-world package examples
## Testing Standards
### Test Pyramid
- **70% Unit Tests**: Format converters, parsers, utilities
- **20% Integration Tests**: API routes, database operations, CLI commands
- **10% E2E Tests**: Full workflows (install, publish, search)
### Coverage Goals
- **Format Converters**: 100% coverage (critical path)
- **CLI Commands**: 90% coverage
- **API Routes**: 85% coverage
- **Utilities**: 90% coverage
### Key Testing Patterns
```typescript
// Format converter test
describe('toCursor', () => {
it('preserves data in roundtrip', () => {
const result = toCursor(canonical);
const back = fromCursor(result.content);
expect(back).toEqual(canonical);
});
});
// CLI command test
describe('install', () => {
it('downloads and installs package', async () => {
await handleInstall('test-pkg', { as: 'cursor' });
expect(fs.existsSync('.cursor/rules/test-pkg.md')).toBe(true);
});
});
```
## Development Workflow
### Package Manager: npm (NOT pnpm)
**⚠️ CRITICAL: PRPM uses npm, not pnpm ⚠️**
- **Lock file**: `package-lock.json` (npm)
- **Install dependencies**: `npm install`
- **Run scripts**: `npm run <script>`
- **Workspace commands**: `npm run <script> --workspace=<package>`
**DO NOT use pnpm:**
- The repository has `package-lock.json`, not `pnpm-lock.yaml`
- Using pnpm will create conflicts and inconsistencies
- CI/CD uses npm for builds and deployments
- All documentation assumes npm
**Common commands:**
```bash
# Install all dependencies
npm install
# Install in specific workspace
npm install --workspace=@pr-pm/cli
# Run tests
npm test
# Build all packages
npm run build
# Run CLI locally
npm run dev --workspace=prpm
```
### When Adding Features
1. **Check Existing Patterns**: Look at similar commands/routes
2. **Update Types First**: TypeScript interfaces drive implementation
3. **Write Tests**: Create test fixtures and cases
4. **Document**: Update README and relevant docs
5. **Environment Variables**: If adding new env vars, update `.env.example` immediately
6. **Telemetry**: Add tracking for new commands (with privacy)
### When Fixing Bugs
1. **Write Failing Test**: Reproduce the bug in a test
2. **Fix Minimally**: Smallest change that fixes the issue
3. **Check Round-Trip**: Ensure conversions still work
4. **Update Fixtures**: Add bug case to test fixtures
### Dependency Management Best Practices
**AVOID Runtime Dependencies (Dynamic Imports)**
❌ **Bad**: Using dynamic imports for runtime dependencies
```typescript
// BAD - tar-stream is imported dynamically at runtime
const tarStream = await import('tar-stream');
```
**Problems with Dynamic Imports:**
1. **Module Resolution Failures**: In production (Elastic Beanstalk, Docker), dynamic imports can fail to resolve even if the package is installed as a transitive dependency
2. **Build Complexity**: TypeScript compilation doesn't validate dynamic imports
3. **Deployment Issues**: Package may not be in the module resolution path in production
4. **Harder to Debug**: Failures happen at runtime, not at build time
✅ **Good**: Declare all dependencies explicitly
```typescript
// GOOD - Import normally at the top
import * as tarStream from 'tar-stream';
```
**Dependency Guidelines:**
1. **Explicit Dependencies**: Always declare dependencies in package.json, never rely on transitive dependencies
2. **Static Imports**: Use static imports whenever possible for compile-time validation
3. **Production Dependencies**: If you import it, it should be in `dependencies`, not `devDependencies`
4. **Build-Time Validation**: Let TypeScript catch missing dependencies at build time, not runtime
### Environment Variable Management
**ALWAYS Update .env.example When Adding New Environment Variables**
Environment variables are configuration points. When adding new ones, follow this checklist:
1. **Add to `.env.example` immediately** - Don't wait until later
2. **Group logically** - Place in appropriate section (DATABASE, AUTH, etc.)
3. **Add clear comments** - Explain what it does and where to get values
4. **Include examples** - Show format (e.g., `sk-ant-api03-...` for API keys)
5. **Mark optional vs required** - Comment out optional variables with `#`
6. **Update production notes** - Add to deployment checklist if needed
**Example:**
```bash
# ==============================================================================
# NEW FEATURE SECTION
# ==============================================================================
# Description of what this variable does
# Get from: https://where-to-get-it.com
NEW_FEATURE_API_KEY=your-key-here
# Optional feature flag (default: false)
# ENABLE_NEW_FEATURE=true
```
**Why this matters:**
- New developers need to know what env vars to set
- `.env.example` is the source of truth for configuration
- Missing env vars cause runtime errors that are hard to debug
- Production deployments fail without proper env var documentation
**Finding missing env vars:**
```bash
# Search for all process.env usage
grep -rh "process.env\." packages/ --include="*.ts" --include="*.tsx" | \
grep -o "process\.env\.[A-Z_][A-Z0-9_]*" | sort -u
# Compare with .env.example to find gaps
```
### When Designing APIs
- **REST Best Practices**: Proper HTTP methods and status codes
- **Versioning**: All routes under `/api/v1/`
- **Pagination**: Limit/offset for list endpoints
- **Filtering**: Support query params for filtering
- **OpenAPI**: Document with Swagger/OpenAPI specs
## Security Standards
- **No Secrets in DB**: Never store GitHub tokens, use session IDs
- **SQL Injection**: Parameterized queries only
- **Rate Limiting**: Prevent abuse of registry API
- **Content Security**: Validate package contents before publishing
## Performance Considerations
- **Batch Operations**: Use Promise.all for independent operations
- **Database Indexes**: GIN for full-text, B-tree for lookups
- **Caching Strategy**: Cache converted packages, not raw data
- **Lazy Loading**: Don't load full package data until needed
- **Connection Pooling**: Reuse PostgreSQL connections
## Deployment
### AWS Infrastructure
#### Registry Backend (Elastic Beanstalk)
- **Environment**: Node.js 20 on 64bit Amazon Linux 2023
- **Instance**: t3.micro (cost-optimized)
- **Database**: RDS PostgreSQL
- **Cache**: ElastiCache Redis
- **DNS**: Route 53
- **SSL**: ACM certificates
#### Webapp (S3 Static Export) ⚠️ CRITICAL
**The webapp MUST be deployable as a static site via S3/CloudFront.**
**Requirements:**
- Next.js with `output: 'export'` configuration
- **NO server-side rendering (SSR)** - all pages must be static or client-side rendered
- **NO API routes** - all API calls go to the registry backend
- **Dynamic routes require `generateStaticParams()`** - return empty array `[]` for client-side only routes
- All data fetching must be client-side (useEffect, fetch, etc.)
**Common Issues:**
1. **Dynamic routes in client components** ⚠️ CANNOT USE BOTH
- Error: `Page "page" cannot use both "use client" and export function "generateStaticParams()"`
- **Solution**: Use query strings instead of path parameters
- ❌ Wrong: `/playground/shared/[token]/page.tsx` with `'use client'`
- ✅ Correct: `/playground/shared/page.tsx` with `?token=xxx` query param
- Implementation: Use `useSearchParams()` instead of `useParams()`
- **IMPORTANT**: Must wrap `useSearchParams()` in `<Suspense>` boundary
- Example:
```typescript
// ❌ Dynamic route (doesn't work with 'use client')
// /app/shared/[token]/page.tsx
const params = useParams();
const token = params.token;
// ✅ Query string with Suspense (works with 'use client')
// /app/shared/page.tsx
import { Suspense } from 'react';
function Content() {
const searchParams = useSearchParams();
const token = searchParams.get('token');
// ... component logic
}
export default function Page() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Content />
</Suspense>
);
}
```
2. **Server components in static export**
- All pages with dynamic content must use `'use client'` directive
- Shared session pages, playground interfaces, etc. are client-rendered
3. **Environment variables**
- Build-time: Embedded in static bundle (public/exposed)
- Runtime: Fetched via API from registry backend
**Why S3 Static Export?**
- Cost: $0.023/GB vs $30+/month for server hosting
- Scale: CloudFront CDN handles traffic spikes
- Reliability: No server maintenance or downtime
- Performance: Pre-rendered static pages are instant
### GitHub Actions Workflows
- **Test & Deploy**: Runs on push to main
- **NPM Publish**: Manual trigger for releases
- **Homebrew Publish**: Updates tap formula
- **Webapp Deploy**: Builds static export and deploys to S3
### Publishing PRPM to NPM
**Publishable Packages:**
- `prpm` - CLI (public)
- `@prpm/registry-client` - HTTP client (public)
- Registry and Infra are private (deployed, not published)
**Process:**
1. Go to Actions → NPM Publish
2. Select version bump (patch/minor/major)
3. Choose packages (all or specific)
4. Run workflow
**Homebrew Formula:**
- Formula repository: `khaliqgant/homebrew-prpm`
- Auto-updates on NPM publish
- Requires `HOMEBREW_TAP_TOKEN` secret
**Version Bumping:**
```bash
# CLI and client together
npm version patch --workspace=prpm --workspace=@prpm/registry-client
# Individual package
npm version minor --workspace=prpm
```
### Publish Command Troubleshooting
The publish command is a complex flow. Common issues and fixes:
| Issue | Cause | Fix |
|-------|-------|-----|
| `Schema not found` | Schemas not copied to dist | Ensure build copies schemas: check `tsup.config.ts` |
| `Cannot find module '@pr-pm/types'` | Types not built first | Run `npm run build --workspace=@pr-pm/types` first |
| `Type export missing` | tsup not exporting types | Check `dts: true` in tsup.config.ts |
| `Version mismatch` | Workspaces out of sync | Bump all related packages together |
| `Tarball too large` | Including unnecessary files | Check `.npmignore` or `files` in package.json |
| `Authentication failed` | Token expired/missing | Re-run `prpm login` or check `PRPM_TOKEN` |
**Build Order for Publishing:**
```bash
# Always build in dependency order
npm run build --workspace=@pr-pm/types
npm run build --workspace=@pr-pm/converters
npm run build --workspace=@pr-pm/registry-client
npm run build --workspace=prpm
```
**Pre-Publish Checklist:**
1. ✅ All tests pass: `npm test`
2. ✅ Types compile: `npm run typecheck`
3. ✅ Build succeeds: `npm run build`
4. ✅ Version bumped in all affected packages
5. ✅ Schemas copied to dist (for CLI)
6. ✅ No uncommitted changes
**Debugging Publish Issues:**
```bash
# Check what will be published
npm pack --workspace=prpm --dry-run
# Verify package contents
tar -tzf prpm-*.tgz
# Check for missing exports
node -e "console.log(require('./packages/cli/dist/index.js'))"
```
## Common Patterns
### CLI Command Structure
```typescript
export async function handleCommand(args: Args, options: Options) {
const startTime = Date.now();
try {
const config = await loadUserConfig();
const client = getRegistryClient(config);
const result = await client.fetchData();
console.log('✅ Success');
await telemetry.track({ command: 'name', success: true });
} catch (error) {
console.error('❌ Failed:', error.message);
await telemetry.track({ command: 'name', success: false });
process.exit(1);
}
}
```
### CLI Error Handling Pattern
**Use `CLIError` instead of `process.exit()` for testable CLI errors.**
```typescript
// ❌ BAD - Untestable, tests can't catch process.exit()
if (!packageId) {
console.error('Package ID is required');
process.exit(1);
}
// ✅ GOOD - Testable with CLIError
import { CLIError } from '../utils/cli-error.js';
if (!packageId) {
throw new CLIError('Package ID is required', { exitCode: 1 });
}
```
**Why CLIError?**
- Tests can catch and verify error conditions
- Consistent error formatting across CLI
- Exit codes are explicit and documented
- Enables reliable CI testing (PR #110 refactor)
**CLIError Usage:**
```typescript
import { CLIError, createError, createSuccess } from '../utils/cli-error.js';
// Simple error
throw new CLIError('Something went wrong');
// With exit code
throw new CLIError('Invalid format specified', { exitCode: 1 });
// With suggestions
throw new CLIError('Package not found', {
exitCode: 1,
suggestion: 'Try running: prpm search <query>'
});
// Helper functions for consistent messaging
createError('Operation failed'); // Returns formatted error
createSuccess('Package installed'); // Returns formatted success
```
**Testing CLI Errors:**
```typescript
import { describe, it, expect } from 'vitest';
import { CLIError } from '../utils/cli-error.js';
describe('install command', () => {
it('throws CLIError for missing package', async () => {
await expect(handleInstall('')).rejects.toThrow(CLIError);
});
it('provides helpful error message', async () => {
try {
await handleInstall('');
} catch (error) {
expect(error).toBeInstanceOf(CLIError);
expect(error.message).toContain('Package ID is required');
}
});
});
```
### Registry Route Structure
```typescript
server.get('/:id', {
schema: { /* OpenAPI schema */ },
}, async (request, reply) => {
const { id } = request.params;
if (!id) return reply.code(400).send({ error: 'Missing ID' });
const result = await server.pg.query('SELECT...');
return result.rows[0];
});
```
### Format Converter Structure
```typescript
export function toFormat(pkg: CanonicalPackage): ConversionResult {
const warnings: string[] = [];
let qualityScore = 100;
const content = convertSections(pkg.content.sections, warnings);
const lossyConversion = warnings.some(w => w.includes('not supported'));
if (lossyConversion) qualityScore -= 10;
return { content, format: 'target', warnings, qualityScore, lossyConversion };
}
```
## Naming Conventions
- **Files**: kebab-case (`registry-client.ts`, `to-cursor.ts`)
- **Types**: PascalCase (`CanonicalPackage`, `ConversionResult`)
- **Functions**: camelCase (`getPackage`, `convertToFormat`)
- **Constants**: UPPER_SNAKE_CASE (`DEFAULT_REGISTRY_URL`)
- **Database**: snake_case (`package_id`, `created_at`)
- **API Requests/Responses**: snake_case (`package_id`, `session_id`, `created_at`)
- **Important**: All API request and response fields use snake_case to match PostgreSQL database conventions
- Internal service methods may use camelCase, but must convert to snake_case at API boundaries
- TypeScript interfaces for API types should use snake_case fields
- Examples: `PlaygroundRunRequest.package_id`, `CreditBalance.reset_at`
## Documentation Standards
- **Inline Comments**: Explain WHY, not WHAT
- **JSDoc**: Required for public APIs
- **README**: Keep examples up-to-date
- **Markdown Docs**: Use code blocks with language tags
- **Changelog**: Follow Keep a Changelog format
- **Continuous Accuracy**: Documentation must be continuously updated and tended to for accuracy
- When adding features, update relevant docs immediately
- When fixing bugs, check if docs need corrections
- When refactoring, verify examples still work
- Review docs quarterly for outdated information
- Keep CLI docs, README, and Mintlify docs in sync
## Reference Documentation
See supporting files in this skill directory for detailed information:
- `format-conversion.md` - Complete format conversion specs
- `package-types.md` - All package types with examples
- `collections.md` - Collections system and examples
- `quality-ranking.md` - Quality and ranking algorithms
- `testing-guide.md` - Testing patterns and standards
- `deployment.md` - Deployment procedures
Remember: PRPM is infrastructure. It must be rock-solid, fast, and trustworthy like npm or cargo.