Agent skill

Adding New AI Format

by pr-pm in pr-pm/prpm

Step-by-step guide for adding support for a new AI editor format to PRPM - covers types, converters, schemas, CLI, webapp, and testing

MITAuto-check passed

Install Adding New AI Format

skills CLI
$ npx skills add pr-pm/prpm --skill adding-new-ai-format -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install pr-pm/prpm adding-new-ai-format --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/pr-pm/prpm.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/adding-new-ai-format .claude/skills/adding-new-ai-format && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
adding-new-ai-format
GitHub stars
122
Token cost
~6.6k tokens
SKILL.md length
1,061 words
Files
1
Skills in repo
35
Repo updated
First seen
Licence
MIT

At a glance

Step-by-step guide for adding support for a new AI editor format to PRPM - covers types, converters, schemas, CLI, webapp, and testing

  • Works in 12 steps: Types Package (packages/types/) → Converters Package - Schema… → Converters Package - Format… → …
  • SKILL.md covers Overview, Prerequisites, Step 1: Types Package… and Step 2: Converters Package -…, plus 10 more sections
  • Calls npm; reaches registry.prpm.dev and json-schema.org

What it does

Adding New AI Format is an agent skill from pr-pm/prpm. Step-by-step guide for adding support for a new AI editor format to PRPM - covers types, converters, schemas, CLI, webapp, and testing

Its SKILL.md is about 6.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

The repository describes itself as: The universal registry for AI coding tools. The licence is MIT.

Example prompts

  • “/adding-new-ai-format”

Workflow steps

12 steps, taken from the step headings in SKILL.md.

  1. Types Package (packages/types/)
  2. Converters Package - Schema (packages/converters/schemas/)
  3. Converters Package - Format Documentation (packages/converters/docs/)
  4. Converters Package - From Converter
  5. Converters Package - To Converter
  6. Converters Package - Exports and Validation
  7. CLI Package - Filesystem
  8. CLI Package - Format Mappings
  9. Webapp - Format Subtypes and Filter Dropdown
  10. Registry - Fastify Route Schemas
  11. Testing and Validation
  12. Additional Documentation

What it can do on your machine

Read from SKILL.md and the folder at commit 5f993e6. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • npm

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • registry.prpm.dev
    • json-schema.org

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Adding New AI Format loads about 6.6k tokens when it runs. Until then it costs about 39 tokens; SKILL.md has 1,061 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~39
When it runs · the whole SKILL.md, loaded when a task matches
~6.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from pr-pm/prpm at commit 5f993e6, republished under its MIT licence (© pr-pm). 1,061 words, ~6,618 tokens.

Download SKILL.mdSave it as .claude/skills/adding-new-ai-format/SKILL.md (or your agent's skills folder).
name
adding-new-ai-format
description
Step-by-step guide for adding support for a new AI editor format to PRPM - covers types, converters, schemas, CLI, webapp, and testing

Adding a New AI Format to PRPM

Complete process for adding support for a new AI editor format (like OpenCode, Cursor, Claude, etc.) to PRPM.

Overview

This skill documents the systematic process for adding a new AI format to PRPM, based on the OpenCode integration. Follow these steps in order to ensure complete integration across all packages.

Prerequisites

  • Format documentation (understand file structure, frontmatter, directory conventions)
  • Example files from the format
  • Understanding of format-specific features (tools, agents, commands, etc.)

Step 1: Types Package (packages/types/)

File: src/package.ts

Add the format to the Format type and FORMATS array:

typescript
export type Format =
  | 'cursor'
  | 'claude'
  | 'continue'
  | 'windsurf'
  | 'copilot'
  | 'kiro'
  | 'agents.md'
  | 'gemini.md'
  | 'claude.md'
  | 'gemini'
  | 'opencode'  // Add new format here
  | 'ruler'
  | 'generic'
  | 'mcp';

export const FORMATS: readonly Format[] = [
  'cursor',
  'claude',
  // ... other formats
  'opencode',  // Add here too
  'ruler',
  'generic',
  'mcp',
] as const;

Build and verify:

bash
npm run build --workspace=@pr-pm/types

Step 2: Converters Package - Schema (packages/converters/schemas/)

Create JSON schema file: {format}.schema.json

Example structure:

json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://registry.prpm.dev/api/v1/schemas/opencode.json",
  "title": "OpenCode Agent Format",
  "description": "JSON Schema for OpenCode Agents",
  "type": "object",
  "required": ["frontmatter", "content"],
  "properties": {
    "frontmatter": {
      "type": "object",
      "required": ["description"],
      "properties": {
        "description": { "type": "string" },
        // Format-specific fields
      },
      "additionalProperties": false
    },
    "content": {
      "type": "string",
      "description": "Body content as markdown"
    }
  }
}

CRITICAL Schema Requirements:

  • $id must use new URL pattern: https://registry.prpm.dev/api/v1/schemas/{format}.json for base schemas
  • For subtypes: https://registry.prpm.dev/api/v1/schemas/{format}/{subtype}.json
  • Add "additionalProperties": false to frontmatter object to catch invalid fields
  • String fields requiring slugs (like name) should use pattern: "pattern": "^[a-z0-9-]+$"

If the format has subtypes (like Claude with agents/skills/commands), create separate schema files:

  • {format}-agent.schema.json
  • {format}-skill.schema.json
  • {format}-slash-command.schema.json
  • etc.

IMPORTANT: When creating subtype schemas, you MUST update the validation logic to map them.

Step 3: Converters Package - Format Documentation (packages/converters/docs/)

CRITICAL: Create comprehensive format documentation file: {format}.md

This documentation serves as the source of truth for:

  • Package authors creating packages in this format
  • PRPM contributors implementing converters
  • Users understanding format capabilities and limitations

Required sections:

markdown
# {Format Name} Format Specification

**File Locations:**
- {Type 1}: `{path}`
- {Type 2}: `{path}`

**Format:** {Markdown/JSON/etc.} with {YAML frontmatter/etc.}
**Official Docs:** {link to official documentation}

## Overview

Brief description of the format and its purpose.

## Frontmatter Fields

### Required Fields

- **`field-name`** (type): Description

### Optional Fields

- **`field-name`** (type): Description

## Content Format

Describe the body/content structure.

## Best Practices

1. Practice 1
2. Practice 2

## Conversion Notes

### From {Format} to Canonical

How the converter parses this format.

### From Canonical to {Format}

How the converter generates this format.

## Limitations

- Limitation 1
- Limitation 2

## Examples

### Example 1

```markdown
{example content}

Changelog

  • {Date}: Initial format support

**Add to README.md**:

1. **Format Matrix table**: Add row(s) with subtypes, official docs, and OpenCode docs links
2. **Available Formats table**: Add row with link to your new `.md` file
3. **Schema Validation section**: Add schema filename(s) to appropriate list
4. **Frontmatter Support table**: Add row with frontmatter requirements
5. **File Organization table**: Add row with file paths and structure

See `packages/converters/docs/README.md` for examples of how other formats are documented.

## Step 4: Converters Package - Canonical Types

**File**: `packages/converters/src/types/canonical.ts`

### 3a. Add format to CanonicalPackage.format union:

```typescript
format: 'cursor' | 'claude' | ... | 'opencode' | 'ruler' | 'generic' | 'mcp';
3b. Add format-specific metadata (if needed):
typescript
// In CanonicalPackage.metadata
metadata?: {
  // ... existing configs
  opencode?: {
    mode?: 'subagent' | 'primary' | 'all';
    model?: string;
    temperature?: number;
    permission?: Record<string, any>;
    disable?: boolean;
  };
};
3c. Add to MetadataSection.data (if storing format-specific data):
typescript
export interface MetadataSection {
  type: 'metadata';
  data: {
    title: string;
    description: string;
    // ... existing fields
    opencode?: {
      // Same structure as above
    };
  };
}
3d. Add to formatScores and sourceFormat:
typescript
formatScores?: {
  cursor?: number;
  // ... others
  opencode?: number;
};

sourceFormat?: 'cursor' | 'claude' | ... | 'opencode' | ... | 'generic';

Step 5: Converters Package - From Converter

File: packages/converters/src/from-{format}.ts

Create converter that parses format → canonical:

typescript
import type {
  CanonicalPackage,
  PackageMetadata,
  Section,
  MetadataSection,
  ToolsSection,
} from './types/canonical.js';
import { setTaxonomy } from './taxonomy-utils.js';
import yaml from 'js-yaml';  // If using YAML frontmatter

// Define format-specific interfaces
interface FormatFrontmatter {
  // Format-specific frontmatter structure
}

// Parse frontmatter if needed
function parseFrontmatter(content: string): {
  frontmatter: Record<string, any>;
  body: string
} {
  const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
  if (!match) {
    return { frontmatter: {}, body: content };
  }

  const frontmatter = yaml.load(match[1]) as Record<string, any>;
  const body = match[2];

  return { frontmatter, body };
}

export function fromFormat(
  content: string,
  metadata: Partial<PackageMetadata> & Pick<PackageMetadata, 'id' | 'name' | 'version' | 'author'>
): CanonicalPackage {
  const { frontmatter, body } = parseFrontmatter(content);
  const fm = frontmatter as FormatFrontmatter;

  const sections: Section[] = [];

  // 1. Create metadata section
  const metadataSection: MetadataSection = {
    type: 'metadata',
    data: {
      title: metadata.name || metadata.id,
      description: fm.description || metadata.description || '',
      version: metadata.version || '1.0.0',
      author: metadata.author,
    },
  };

  // Store format-specific data for roundtrip
  if (/* has format-specific fields */) {
    metadataSection.data.formatName = {
      // Format-specific data
    };
  }

  sections.push(metadataSection);

  // 2. Extract tools (if applicable)
  if (fm.tools) {
    const enabledTools = Object.entries(fm.tools)
      .filter(([_, enabled]) => enabled === true)
      .map(([tool, _]) => {
        // Normalize tool names to canonical format
        return normalizeToolName(tool);
      });

    if (enabledTools.length > 0) {
      sections.push({
        type: 'tools',
        tools: enabledTools,
      });
    }
  }

  // 3. Add body as instructions
  if (body.trim()) {
    sections.push({
      type: 'instructions',
      title: 'Instructions',
      content: body.trim(),
    });
  }

  // 4. Build canonical package
  const canonicalContent: CanonicalPackage['content'] = {
    format: 'canonical',
    version: '1.0',
    sections
  };

  const pkg: CanonicalPackage = {
    ...metadata,
    id: metadata.id,
    name: metadata.name || metadata.id,
    version: metadata.version,
    author: metadata.author,
    description: metadata.description || fm.description || '',
    tags: metadata.tags || [],
    format: 'formatname',
    subtype: 'agent', // Or detect from content
    content: canonicalContent,
  };

  setTaxonomy(pkg, 'formatname', 'agent');
  return pkg;
}

Key points:

  • Import yaml if format uses YAML frontmatter
  • Extract all format-specific metadata for roundtrip conversion
  • Normalize tool names to canonical format (Write, Edit, Bash, etc.)
  • Always include format: 'canonical' and version: '1.0' in content
  • InstructionsSection requires title field
  • Call setTaxonomy() before returning

Step 6: Converters Package - To Converter

File: packages/converters/src/to-{format}.ts

Create converter that converts canonical → format:

typescript
import type {
  CanonicalPackage,
  ConversionResult,
} from './types/canonical.js';
import yaml from 'js-yaml';

export function toFormat(pkg: CanonicalPackage): ConversionResult {
  const warnings: string[] = [];
  let qualityScore = 100;

  try {
    const content = convertContent(pkg, warnings);

    const lossyConversion = warnings.some(w =>
      w.includes('not supported') || w.includes('skipped')
    );

    if (lossyConversion) {
      qualityScore -= 10;
    }

    return {
      content,
      format: 'formatname',
      warnings: warnings.length > 0 ? warnings : undefined,
      lossyConversion,
      qualityScore,
    };
  } catch (error) {
    warnings.push(`Conversion error: ${error instanceof Error ? error.message : String(error)}`);
    return {
      content: '',
      format: 'formatname',
      warnings,
      lossyConversion: true,
      qualityScore: 0,
    };
  }
}

function convertContent(pkg: CanonicalPackage, warnings: string[]): string {
  const lines: string[] = [];

  // Extract sections
  const metadata = pkg.content.sections.find(s => s.type === 'metadata');
  const tools = pkg.content.sections.find(s => s.type === 'tools');
  const instructions = pkg.content.sections.find(s => s.type === 'instructions');

  // Build frontmatter
  const frontmatter: Record<string, any> = {};

  if (metadata?.type === 'metadata') {
    frontmatter.description = metadata.data.description;
  }

  // Restore format-specific metadata (for roundtrip)
  const formatData = metadata?.type === 'metadata' ? metadata.data.formatName : undefined;
  if (formatData) {
    Object.assign(frontmatter, formatData);
  }

  // Convert tools
  if (tools?.type === 'tools' && tools.tools.length > 0) {
    frontmatter.tools = convertToolsToFormatStructure(tools.tools);
  }

  // Generate YAML frontmatter (if applicable)
  lines.push('---');
  lines.push(yaml.dump(frontmatter, { indent: 2, lineWidth: -1 }).trim());
  lines.push('---');
  lines.push('');

  // Add body content
  if (instructions?.type === 'instructions') {
    lines.push(instructions.content);
  }

  return lines.join('\n').trim() + '\n';
}

Section type handling:

  • PersonaSection: section.data.role (NOT section.content)
  • RulesSection: section.items (NOT section.rules), each item has rule.content
  • InstructionsSection: section.content and section.title
  • ExamplesSection: section.examples array with description and code

Step 7: Converters Package - Exports and Validation

File: packages/converters/src/index.ts

Add to exports:

typescript
// From converters
export { fromFormat } from './from-format.js';

// To converters
export { toFormat } from './to-format.js';

File: packages/converters/src/validation.ts

7a. Add to FormatType:
typescript
export type FormatType =
  | 'cursor'
  | 'claude'
  // ... others
  | 'opencode'
  | 'canonical';
7b. Add to base schema map:
typescript
const schemaMap: Record<FormatType, string> = {
  'cursor': 'cursor.schema.json',
  // ... others
  'opencode': 'opencode.schema.json',
  'canonical': 'canonical.schema.json',
};
7c. CRITICAL: Add subtype schemas to subtypeSchemaMap:
typescript
const subtypeSchemaMap: Record<string, string> = {
  'claude:agent': 'claude-agent.schema.json',
  'claude:skill': 'claude-skill.schema.json',
  'claude:slash-command': 'claude-slash-command.schema.json',
  'claude:hook': 'claude-hook.schema.json',
  'cursor:slash-command': 'cursor-command.schema.json',
  'kiro:hook': 'kiro-hooks.schema.json',
  'kiro:agent': 'kiro-agent.schema.json',
  'droid:skill': 'droid-skill.schema.json',
  'droid:slash-command': 'droid-slash-command.schema.json',
  'droid:hook': 'droid-hook.schema.json',
  'opencode:slash-command': 'opencode-slash-command.schema.json',  // Add your subtypes here
};

Why this matters: Without adding subtypes to subtypeSchemaMap, validation will fall back to the base format schema and won't validate subtype-specific fields. This causes validation to fail or pass incorrectly.

File: packages/converters/src/taxonomy-utils.ts

Add to Format type:

typescript
export type Format = 'cursor' | 'claude' | ... | 'opencode' | ... | 'mcp';

Add to normalizeFormat:

typescript
export function normalizeFormat(sourceFormat: string): Format {
  const normalized = sourceFormat.toLowerCase();

  if (normalized.includes('cursor')) return 'cursor';
  // ... others
  if (normalized.includes('opencode')) return 'opencode';

  return 'generic';
}

Build converters:

bash
npm run build --workspace=@pr-pm/converters

Step 8: CLI Package - Filesystem

File: packages/cli/src/core/filesystem.ts

7a. Add to getDestinationDir:
typescript
export function getDestinationDir(format: Format, subtype: Subtype, name?: string): string {
  const packageName = stripAuthorNamespace(name);

  switch (format) {
    // ... existing cases

    case 'opencode':
      // OpenCode supports agents, slash commands, and custom tools
      // Agents: .opencode/agent/*.md
      // Commands: .opencode/command/*.md
      // Tools: .opencode/tool/*.ts or *.js
      if (subtype === 'agent') return '.opencode/agent';
      if (subtype === 'slash-command') return '.opencode/command';
      if (subtype === 'tool') return '.opencode/tool';
      return '.opencode/agent';  // Default

    // ... rest
  }
}
7b. Add to autoDetectFormat:
typescript
const formatDirs: Array<{ format: Format; dir: string }> = [
  { format: 'cursor', dir: '.cursor' },
  // ... others
  { format: 'opencode', dir: '.opencode' },
  { format: 'agents.md', dir: '.agents' },
];

Step 9: CLI Package - Format Mappings

Files: packages/cli/src/commands/search.ts and packages/cli/src/commands/install.ts

Add to both files:

8a. formatIcons:
typescript
const formatIcons: Record<Format, string> = {
  'claude': '🤖',
  'cursor': '📋',
  // ... others
  'opencode': '⚡',  // Choose appropriate emoji
  'gemini.md': '✨',  // Don't forget format aliases
  'claude.md': '🤖',
  'ruler': '📏',
  'generic': '📦',
};
8b. formatLabels:
typescript
const formatLabels: Record<Format, string> = {
  'claude': 'Claude',
  'cursor': 'Cursor',
  // ... others
  'opencode': 'OpenCode',
  'gemini.md': 'Gemini',  // Format aliases
  'claude.md': 'Claude',
  'ruler': 'Ruler',
  'generic': '',
};

Step 10: Webapp - Format Subtypes and Filter Dropdown

File: packages/webapp/src/app/(app)/search/SearchClient.tsx

9a. Add to FORMAT_SUBTYPES:
typescript
const FORMAT_SUBTYPES: Record<Format, Subtype[]> = {
  'cursor': ['rule', 'agent', 'slash-command', 'tool'],
  'claude': ['skill', 'agent', 'slash-command', 'tool', 'hook'],
  'claude.md': ['agent'],  // Format aliases
  'gemini.md': ['slash-command'],
  // ... others
  'opencode': ['agent', 'slash-command', 'tool'],  // List all supported subtypes
  'ruler': ['rule', 'agent', 'tool'],
  'generic': ['rule', 'agent', 'skill', 'slash-command', 'tool', 'chatmode', 'hook'],
};
9b. Add to format filter dropdown (around line 1195):
typescript
<select
  value={selectedFormat}
  onChange={(e) => setSelectedFormat(e.target.value as Format | '')}
  className="w-full px-3 py-2 bg-prpm-dark border border-prpm-border rounded text-white focus:outline-none focus:border-prpm-accent"
>
  <option value="">All Formats</option>
  <option value="cursor">Cursor</option>
  <option value="claude">Claude</option>
  <option value="continue">Continue</option>
  <option value="windsurf">Windsurf</option>
  <option value="copilot">GitHub Copilot</option>
  <option value="kiro">Kiro</option>
  <option value="gemini">Gemini CLI</option>
  <option value="droid">Droid</option>
  <option value="opencode">OpenCode</option>  {/* Add your format here */}
  <option value="mcp">MCP</option>
  <option value="agents.md">Agents.md</option>
  <option value="generic">Generic</option>
</select>
9c. Add compatibility info section (after the dropdown):
typescript
{selectedFormat === 'opencode' && (
  <div className="mt-3 p-3 bg-gray-500/10 border border-gray-500/30 rounded-lg">
    <p className="text-xs text-gray-400">
      Tool-specific format for <strong>OpenCode AI</strong>
    </p>
  </div>
)}

Step 11: Registry - Fastify Route Schemas

CRITICAL: Add the format to all Fastify route validation schemas to prevent 400 errors.

10a. File: packages/registry/src/routes/download.ts

Add to format enum in schema (2-3 places):

typescript
// Download route schema (line ~46)
format: {
  type: 'string',
  enum: ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'ruler', 'agents.md', 'gemini', 'droid', 'opencode', 'generic'],
  description: 'Target format for conversion (optional)',
},

// Compatibility check route schema (lines ~201, 205)
from: {
  type: 'string',
  enum: ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'ruler', 'agents.md', 'gemini', 'droid', 'opencode', 'generic'],
},
to: {
  type: 'string',
  enum: ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'ruler', 'agents.md', 'gemini', 'droid', 'opencode', 'generic'],
},
10b. File: packages/registry/src/routes/search.ts

Add to FORMAT_ENUM constant (line ~12):

typescript
const FORMAT_ENUM = ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'gemini', 'ruler', 'droid', 'opencode', 'generic', 'mcp'] as const;
10c. File: packages/registry/src/routes/analytics.ts

Add to both Zod schema and Fastify schema:

typescript
// Zod schema (line ~15)
const TrackDownloadSchema = z.object({
  packageId: z.string(),
  version: z.string().optional(),
  format: z.enum(['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'gemini', 'ruler', 'droid', 'opencode', 'generic', 'mcp']).optional(),
  client: z.enum(['cli', 'web', 'api']).optional(),
});

// Fastify schema (line ~45)
format: {
  type: 'string',
  enum: ['cursor', 'claude', 'continue', 'windsurf', 'copilot', 'kiro', 'agents.md', 'gemini', 'ruler', 'droid', 'opencode', 'generic', 'mcp'],
  description: 'Download format'
},

Why this matters: Without these additions, the registry will reject API requests with 400 validation errors when users try to download or filter by the new format.

Step 12: Testing and Validation

11a. Build types package first:
bash
npm run build --workspace=@pr-pm/types

This is critical because other packages depend on the updated Format type.

11b. Build registry and webapp:
bash
npm run build --workspace=@pr-pm/registry
npm run build --workspace=@pr-pm/webapp
11c. Run typecheck:
bash
npm run typecheck

Fix any TypeScript errors:

  • Missing format in type unions
  • Format aliases ('gemini.md', 'claude.md')
  • Section structure (use correct field names)
11d. Build all packages:
bash
npm run build
11e. Run converter tests:
bash
npm test --workspace=@pr-pm/converters
Show full SKILL.md (439 more words)Show less
typescript
// packages/converters/src/__tests__/to-opencode.test.ts
import { describe, it, expect } from 'vitest';
import { toOpencode } from '../to-opencode.js';
import { validateMarkdown } from '../validation.js';
import type { CanonicalPackage } from '../types/canonical.js';

describe('OpenCode Format', () => {
  it('should convert from OpenCode to canonical', () => {
    const opencodeContent = `---
description: Test agent
mode: subagent
---
Test instructions`;

    const result = fromOpencode(opencodeContent, {
      id: 'test',
      name: 'test',
      version: '1.0.0',
      author: 'test',
    });

    expect(result.format).toBe('opencode');
    expect(result.subtype).toBe('agent');
  });

  it('should convert canonical to OpenCode', () => {
    const canonical: CanonicalPackage = {
      // ... build test package
    };

    const result = toOpencode(canonical);
    expect(result.format).toBe('opencode');
    expect(result.content).toContain('---');
  });

  // CRITICAL: Add schema validation tests!
  describe('JSON Schema Validation', () => {
    it('should generate schema-compliant agent output', () => {
      const agentPackage: CanonicalPackage = {
        // ... build agent test package with subtype: 'agent'
      };

      const result = toOpencode(agentPackage);
      const validation = validateMarkdown('opencode', result.content, 'agent');

      if (!validation.valid) {
        console.error('Validation errors:', validation.errors);
      }

      expect(validation.valid).toBe(true);
      expect(validation.errors).toHaveLength(0);
    });
  });
});

Why Schema Validation Tests Matter:

  • Catch mismatches between converter implementation and schema
  • Ensure converters generate compliant output
  • Reveal missing required fields or incorrect field names
  • Example: We discovered Claude agent schema was missing required mode field via validation tests

Step 13: Additional Documentation

Beyond the format documentation created in Step 3:

  • User-facing: Add to Mintlify docs if the format needs special installation instructions
  • Internal: Add notes to docs/development/ if there are special considerations
  • Decision logs: Document any architectural decisions in docs/decisions/

Common Pitfalls

1. Missing Format Aliases

Formats like 'gemini.md' and 'claude.md' are aliases that MUST be included in all format mappings.

2. Incorrect Section Structure
  • PersonaSection uses data.role, not content
  • RulesSection uses items, not rules
  • InstructionsSection requires title field
  • Each Rule has content, not description
3. CanonicalContent Requirements

Must always include:

typescript
{
  format: 'canonical',
  version: '1.0',
  sections: [...]
}
4. setTaxonomy Signature
typescript
setTaxonomy(pkg, 'formatname', 'subtype');  // Returns void
return pkg;  // Return the package separately
5. Tool Name Normalization

Map format-specific tool names to canonical:

  • write → Write
  • edit → Edit
  • bash → Bash
6. YAML Import

If using YAML frontmatter:

typescript
import yaml from 'js-yaml';  // Top-level import
// NOT: const yaml = await import('js-yaml');

Checklist

Before submitting:

Types Package:

  • Added format to types/src/package.ts (Format type and FORMATS array)
  • Built types package

Converters Package:

  • Created schema file(s) in converters/schemas/
  • If format has subtypes, created separate schema files for each subtype (e.g., {format}-agent.schema.json, {format}-slash-command.schema.json)
  • Created format documentation in converters/docs/{format}.md
  • Updated converters/docs/README.md (Format Matrix, Available Formats, Schema Validation, Frontmatter Support, File Organization tables)
  • Updated converters/src/types/canonical.ts (all 4 places: format union, metadata, MetadataSection.data, formatScores, sourceFormat)
  • Created from-{format}.ts converter
  • Created to-{format}.ts converter
  • Updated converters/src/index.ts exports
  • Updated converters/src/validation.ts (FormatType, schemaMap, and CRITICAL: subtypeSchemaMap for each subtype)
  • Updated converters/src/taxonomy-utils.ts (Format type and normalizeFormat)
  • Copied all schemas to packages/cli/dist/schemas/ for runtime use

CLI Package:

  • Updated cli/src/core/filesystem.ts (getDestinationDir and autoDetectFormat)
  • Updated cli/src/commands/search.ts (formatIcons and formatLabels, including aliases)
  • Updated cli/src/commands/install.ts (formatIcons and formatLabels, including aliases)

Webapp Package:

  • Updated webapp SearchClient.tsx (FORMAT_SUBTYPES, including aliases)
  • Added to format filter dropdown
  • Added compatibility info section

Registry Package:

  • Updated registry/src/routes/download.ts (format enum in 2-3 places)
  • Updated registry/src/routes/search.ts (FORMAT_ENUM constant)
  • Updated registry/src/routes/analytics.ts (Zod schema and Fastify schema)
  • Built registry package

Testing:

  • Ran typecheck successfully
  • Built all packages successfully
  • Wrote tests for converters
  • Documented the integration

Example: OpenCode Integration

See the following files for reference:

  • packages/converters/src/from-opencode.ts
  • packages/converters/src/to-opencode.ts
  • packages/converters/schemas/opencode.schema.json
  • Git commit history for the OpenCode integration PR

Summary

Adding a new format requires changes across 6 packages:

  1. types - Add to Format type (build first!)
  2. converters - Schema, from/to converters, canonical types, validation, taxonomy
  3. cli - Filesystem and format mappings
  4. webapp - Format subtypes, filter dropdown, compatibility info
  5. registry - Fastify route schemas (download, search, analytics)
  6. tests - Verify everything works

Build order matters: types → converters → cli → webapp → registry

Follow the steps systematically, use existing format implementations as reference, and always run typecheck and tests before submitting.

© pr-pm, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/adding-new-ai-format of pr-pm/prpm.

Open the folder on GitHubat commit 5f993e6

Compare with similar skills

Adding New AI Format next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Adding New AI Format compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Adding New AI Format this skillpr-pm/prpm122—~6.6kAutomated safety check: PassMIT
EditorQinghongLin/data2story-skill156—~3.9kAutomated safety check: PassMIT
EditorQinghongLin/data2story-skill156—~1.4kAutomated safety check: PassMIT
Editoralecs5am/ralphy136—~3.6kAutomated safety check: PassApache-2.0
Handsontable Cell Editor Developerhandsontable/handsontable22k—~3.8kAutomated safety check: PassCustom licence
Editor Test Harvesterudecode/plate17k—~7.6kAutomated safety check: PassCustom licence

Similar skills

  • Editor

    QinghongLin/data2story-skill

    Read analyst.json and detective.json, make all editorial decisions — what the blog argues, which findings matter, narrative arc and section structure.

    156 GitHub stars~3.9k tokensUpdated 3 mo ago
    Frontend & DesignAuto-check passed
  • Editor

    QinghongLin/data2story-skill

    Read analyst.json and detective.json, make all editorial decisions — what the blog argues, which findings matter, narrative arc and section structure.

    156 GitHub stars~1.4k tokensUpdated 3 mo ago
    Frontend & DesignAuto-check passed
  • Editor

    alecs5am/ralphy

    Composition and render craft — assembles scenario.json plus asset-manifest.json into a HyperFrames HTML composition and renders the mp4.

    136 GitHub stars~3.6k tokensUpdated 15 days ago
    Media & CreativeAuto-check passed
  • Handsontable Cell Editor Developer

    handsontable/handsontable

    Guides building a new Handsontable cell editor through its four-state lifecycle, DOM setup, viewport-aware positioning and async validation, matching the grid's existing editor classes.

    22k GitHub stars~3.8k tokensUpdated today
    Frontend & DesignAuto-check passed
  • Mine external editor repositories for portable editor-behavior tests with ClawSweeper-style discipline: multi-pass exhaustive inventory, confidence scoring, framework-specific skip reasons…

    17k GitHub stars~7.6k tokensUpdated today
    Auto-check passed
  • Editor Harvest Plan

    udecode/plate

    Turn an editor-test-harvester report into one lane-specific execution plan, e.g.

    17k GitHub stars~3.5k tokensUpdated today
    Auto-check passed

More from pr-pm/prpm

All 35 skills in this repo
  • Reference for writing Claude Code agent files: location, frontmatter fields, validation limits, tool and model choices, and the required content format.

    122 GitHub starsUsed in 3 repos~4k tokens
    Auto-check passed
  • Covers how to build, configure and publish Claude Code hooks: event types, exit codes, JSON I/O, and PRPM packaging.

    122 GitHub starsUsed in 2 repos~3.9k tokens
    Auto-check: notes
  • Shows how to write .claude/rules/ files correctly: paths frontmatter instead of globs, quoted glob patterns, global rules and conversion of Cursor rules.

    122 GitHub starsUsed in 2 repos~551 tokens
    Auto-check passed
  • Reference for writing portable Agent Skills packages, covering SKILL.md frontmatter limits, name rules, directory layout and where Codex CLI, GitHub Copilot and Amp look for skills.

    122 GitHub starsUsed in 1 repo~1.5k tokens
    Auto-check passed
  • A skill your agent uses when building custom Kiro AI agents or when user asks for agent configurations - provides JSON structure, tool configuration, prompt patterns, and security best practices for…

    122 GitHub starsUsed in 1 repo~1.9k tokens
    Auto-check passed
  • A skill your agent uses when creating OpenCode agents - provides markdown format with YAML frontmatter, mode/tools/permission configuration, and best practices for specialized AI assistants

    122 GitHub starsUsed in 1 repo~2k tokens
    Auto-check passed

Questions about Adding New AI Format

What does Adding New AI Format do?

Step-by-step guide for adding support for a new AI editor format to PRPM - covers types, converters, schemas, CLI, webapp, and testing. Adding New AI Format is an agent skill from pr-pm/prpm.

How do I install Adding New AI Format in Claude Code?

Run `npx skills add pr-pm/prpm --skill adding-new-ai-format -a claude-code`. Or copy the skill folder (.claude/skills/adding-new-ai-format in pr-pm/prpm) into .claude/skills/adding-new-ai-format in your project. Claude Code loads it when a task matches its description.

How do I install Adding New AI Format in Codex?

Run `npx skills add pr-pm/prpm --skill adding-new-ai-format -a codex`. Or copy the skill folder (.claude/skills/adding-new-ai-format in pr-pm/prpm) into .agents/skills/adding-new-ai-format in your project. Codex loads it when a task matches its description.

Can I use Adding New AI Format in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add pr-pm/prpm --skill adding-new-ai-format -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/adding-new-ai-format, .gemini/skills/adding-new-ai-format, .github/skills/adding-new-ai-format and .opencode/skills/adding-new-ai-format in your project.

What does Adding New AI Format need to run?

Going by SKILL.md and its folder, Adding New AI Format needs the command-line tools its instructions call (npm).

Does Adding New AI Format access the network?

SKILL.md names 2 domains. In commands or code: registry.prpm.dev and json-schema.org; the agent is likely to contact these when it follows the instructions. This is read from the text; nothing was executed.

Is Adding New AI Format safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Adding New AI Format use?

Adding New AI Format is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Adding New AI Format use?

About 6.6k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Adding New AI Format?

Skills that share tags, products or a category with Adding New AI Format: Editor (QinghongLin/data2story-skill, 156 stars), Editor (QinghongLin/data2story-skill, 156 stars), Editor (alecs5am/ralphy, 136 stars) and Handsontable Cell Editor Developer (handsontable/handsontable, 22k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Adding New AI Format?

pr-pm (a GitHub organization) maintains it in pr-pm/prpm, which has 122 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on October 6, 2026.

Source: pr-pm/prpm on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.