Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ PRPM supports all major AI coding assistants:
| `copilot` | [GitHub Copilot](https://github.com/features/copilot) | tool, chatmode | `.github/instructions/`, `.github/chatmodes/` | [Copilot Docs](https://docs.github.com/copilot) |
| `kiro` | [Kiro](https://kiro.ai) | rule, agent, tool, hook | `.kiro/steering/`, `.kiro/agents/`, `.kiro/hooks/` | [Kiro Docs](https://docs.kiro.ai) |
| `gemini` | [Gemini CLI](https://ai.google.dev/gemini-api/docs) | slash-command | `.gemini/commands/` | [Gemini Docs](https://ai.google.dev/gemini-api/docs) |
| `opencode` | [OpenCode](https://opencode.ai) | agent, slash-command, tool | `.opencode/agent/`, `.opencode/command/`, `.opencode/tool/` | [OpenCode Docs](https://opencode.ai/docs) |
| `opencode` | [OpenCode](https://opencode.ai) | agent, skill, slash-command, tool | `.opencode/agent/`, `.opencode/skills/`, `.opencode/command/`, `.opencode/tool/` | [OpenCode Docs](https://opencode.ai/docs) |
| `ruler` | [Ruler](https://ruler.sh) | rule, agent, tool | `.ruler/rules/` | [Ruler Docs](https://ruler.sh/docs) |
| `droid` | [Factory Droid](https://factory.ai) | skill, slash-command, hook | `.factory/skills/`, `.factory/commands/`, `.factory/hooks/` | [Factory Droid Docs](https://docs.factory.ai) |
| `agents.md` | [Agents.md](https://github.com/agentsmd) | agent, tool | `.agents/`, `AGENTS.md` | [Agents.md Docs](https://github.com/agentsmd) |
Expand Down
8 changes: 5 additions & 3 deletions packages/cli/src/__tests__/install-file-locations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ Follow TypeScript best practices.
);
});

it('installs opencode skill to native .opencode/skill/<name>/<name>.md (native skill support)', async () => {
it('installs opencode skill to native .opencode/skills/<name>/SKILL.md (native skill support)', async () => {
// OpenCode has native skill support - should NOT use progressive disclosure
const mockPackage = {
id: 'test-opencode-skill',
Expand All @@ -748,8 +748,10 @@ Follow TypeScript best practices.

await handleInstall('test-opencode-skill', { as: 'opencode' });

// OpenCode has native skill support - installs to .opencode/skill/<name>/
expect(saveFile).toHaveBeenCalledWith('.opencode/skill/test-opencode-skill/test-opencode-skill.md', expect.any(String));
// OpenCode has native skill support - installs to .opencode/skills/<name>/
expect(saveFile).toHaveBeenCalledWith('.opencode/skills/test-opencode-skill/SKILL.md', expect.any(String));
const destDir = getDestinationDir('opencode', 'skill', 'test-opencode-skill');
expect(destDir).toBe('.opencode/skills/test-opencode-skill');
// No progressive disclosure manifest update for native format
expect(addSkillToManifestMock).not.toHaveBeenCalled();
});
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/__tests__/install-multifile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ describe('install command - multi-file packages', () => {
});

it('should use native skill location for OpenCode skill install', async () => {
// OpenCode has native skill support - skills go to .opencode/skill/
// OpenCode has native skill support - skills go to .opencode/skills/
const mockPackage = {
id: 'nango-skill',
name: 'nango-skill',
Expand All @@ -429,16 +429,16 @@ describe('install command - multi-file packages', () => {

await handleInstall('nango-skill', { as: 'opencode' });

// OpenCode has native skill support - installs to .opencode/skill/<name>/
// OpenCode has native skill support - installs to .opencode/skills/<name>/
expect(saveFile).toHaveBeenCalledWith(
'.opencode/skill/nango-skill/nango-skill.md',
'.opencode/skills/nango-skill/SKILL.md',
expect.stringContaining('Builds thin wrapper actions')
);

// Verify it did NOT go to the wrong location (.opencode/agent/)
const allCalls = (saveFile as Mock).mock.calls;
const wrongLocationCalls = allCalls.filter((call: string[]) =>
call[0].includes('.opencode/agent/')
call[0].includes('.opencode/agent/') || call[0].includes('.opencode/skill/')
);
expect(wrongLocationCalls).toHaveLength(0);
});
Expand Down
13 changes: 11 additions & 2 deletions packages/cli/src/commands/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
toCodex,
toCursorHooks,
validateFormat,
getSubtypeConfig,
getNestedIndicator,
getFilePatterns,
getFileExtension,
Expand Down Expand Up @@ -1150,6 +1151,7 @@ export async function handleInstall(
}
// Strip leading dot if present, default to 'md'
const fileExtension = registryExtension?.replace(/^\./, '') || 'md';
const nestedIndicator = getNestedIndicator(effectiveFormat, effectiveSubtype);
const packageName = stripAuthorNamespace(packageId);

// For Claude skills, use SKILL.md filename in the package directory
Expand Down Expand Up @@ -1239,6 +1241,9 @@ export async function handleInstall(
} else if (effectiveFormat === 'copilot' && effectiveSubtype === 'skill') {
// GitHub Copilot skills use SKILL.md inside the skill directory
destPath = `${destDir}/SKILL.md`;
} else if (nestedIndicator) {
// Native nested package formats define their required entry file in the registry.
destPath = `${destDir}/${nestedIndicator}`;
} else {
// Check if this format/subtype needs progressive disclosure
// (format supports the subtype but doesn't have native file location for it)
Expand Down Expand Up @@ -1409,12 +1414,16 @@ export async function handleInstall(
// For Claude skills, destDir already includes package name, so use it directly
// For Cursor rules converted from Claude skills, use flat structure
const isCursorConversion = (effectiveFormat === 'cursor' && pkg.format === 'claude' && pkg.subtype === 'skill');
const nativeSubtypeConfig = getSubtypeConfig(effectiveFormat, effectiveSubtype);
const usesNativePackageSubdirectory = !needsProgressiveDisclosureMulti && Boolean(nativeSubtypeConfig?.usesPackageSubdirectory);
const packageDir = (effectiveFormat === 'claude' && effectiveSubtype === 'skill')
? destDir
: isCursorConversion
? destDir // Cursor uses flat structure
: needsProgressiveDisclosureMulti
? destDir // Progressive disclosure already includes package name
: usesNativePackageSubdirectory
? destDir // Native nested formats already include the package name
: `${destDir}/${packageName}`;
destPath = packageDir;
console.log(` 📁 Multi-file package - creating directory: ${packageDir}`);
Expand Down Expand Up @@ -1464,8 +1473,8 @@ export async function handleInstall(
// We want to strip everything up to and including the package-name directory
let relativeFileName = file.name;

// Find the skills directory index
const skillsDirIndex = pathParts.indexOf('skills');
// Find the skills directory index. OpenCode used to use singular "skill".
const skillsDirIndex = pathParts.findIndex(part => part === 'skills' || part === 'skill');
if (skillsDirIndex !== -1 && pathParts.length > skillsDirIndex + 2) {
// Skip: .claude/skills/package-name/ and keep the rest
relativeFileName = pathParts.slice(skillsDirIndex + 2).join('/');
Expand Down
6 changes: 4 additions & 2 deletions packages/converters/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Complete overview of all supported formats, their subtypes, and official documen
| | `agent` | Subagent TOML configs with MCP and skills | [developers.openai.com](https://developers.openai.com/codex/multi-agent/) |
| | `rule` | AGENTS.md project instructions | [developers.openai.com](https://developers.openai.com/codex/skills) |
| **OpenCode** | `agent` | AI agents with mode, tools, and permissions | [opencode.ai](https://opencode.ai/docs/agents/) |
| | `skill` | Agent Skills SKILL.md format | [opencode.ai](https://opencode.ai/docs/skills/) |
| | `slash-command` | User-triggered prompts with templates and placeholders | [opencode.ai](https://opencode.ai/docs/commands/) |
| **Gemini CLI** | `slash-command` | Custom slash commands in TOML format | [geminicli.com](https://geminicli.com/docs/commands/) |
| | `extension` | Extensions with MCP servers and context files | [geminicli.com](https://geminicli.com/docs/extensions/) |
Expand Down Expand Up @@ -71,7 +72,7 @@ This directory contains detailed specifications for each AI IDE/tool format that
| **Ruler** | [ruler.md](./ruler.md) | Plain markdown rules, centralized management | [okigu.com/ruler](https://okigu.com/ruler) |
| **Factory Droid** | [factory-droid.md](./factory-droid.md) | Skills, slash commands, and hooks | [docs.factory.ai](https://docs.factory.ai/) |
| **Codex** | [codex.md](./codex.md) | Skills, subagents, and AGENTS.md instructions | [developers.openai.com](https://developers.openai.com/codex/multi-agent/) |
| **OpenCode** | [opencode.md](./opencode.md) | Agents and slash commands with YAML frontmatter | [opencode.ai/docs](https://opencode.ai/docs/) |
| **OpenCode** | [opencode.md](./opencode.md) | Agents, skills, and slash commands with YAML frontmatter | [opencode.ai/docs](https://opencode.ai/docs/) |
| **Gemini CLI** | [gemini-plugin.md](./gemini-plugin.md) | Extensions with MCP servers and custom commands | [geminicli.com/docs](https://geminicli.com/docs/extensions/) |
| **agents.md** | [agents-md.md](./agents-md.md) | OpenAI format, plain markdown | [github.com/openai/agents.md](https://github.com/openai/agents.md) |
| **Trae** | [trae.md](./trae.md) | Plain markdown rules, no frontmatter | [docs.trae.ai](https://docs.trae.ai/ide/rules) |
Expand Down Expand Up @@ -134,6 +135,7 @@ Each format has a corresponding JSON Schema in `../schemas/` that defines the st
- `agent-skills.schema.json` - Agent Skills SKILL.md (shared standard)

**OpenCode Subtypes:**
- `agent-skills.schema.json` - Agent Skills SKILL.md (shared standard)
- `opencode-slash-command.schema.json` - Template-based commands

**Amp Subtypes:**
Expand Down Expand Up @@ -231,7 +233,7 @@ These specifications serve as the source of truth for:
| Kiro Hooks | `.kiro/hooks/*.json` | Multiple JSON files |
| Factory Droid | `.factory/skills/*/SKILL.md`, `.factory/commands/*.md` | Skills in subdirs, commands as files |
| Codex | `.codex/agents/*.toml`, `.agents/skills/*/SKILL.md` | Agents as TOML, skills in subdirs |
| OpenCode | `.opencode/agent/*.md`, `.opencode/command/*.md` | Agents and commands as separate files |
| OpenCode | `.opencode/agent/*.md`, `.opencode/skills/*/SKILL.md`, `.opencode/command/*.md` | Agents and commands as files, skills in subdirs |
| Gemini CLI | `.gemini/extensions/*/gemini-extension.json` | Extensions in subdirectories with JSON config |
| agents.md | `agents.md` | Single file |
| Trae | `.trae/rules/*.md` | Multiple files in directory |
Expand Down
6 changes: 3 additions & 3 deletions packages/converters/docs/opencode.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**File Locations:**
- Agents: `.opencode/agent/*.md` or `~/.config/opencode/agent/*.md`
- Skills: `.opencode/skill/${name}/SKILL.md` or `~/.opencode/skill/${name}/SKILL.md`
- Skills: `.opencode/skills/${name}/SKILL.md` or `~/.config/opencode/skills/${name}/SKILL.md`
- Slash Commands: `.opencode/command/*.md` or `~/.config/opencode/command/*.md`
- Config: `opencode.json` or `opencode.jsonc` (JSON format alternative)

Expand Down Expand Up @@ -97,7 +97,7 @@ You are an expert code reviewer with deep knowledge of software engineering prin

OpenCode skills use the **Agent Skills spec** (shared with Codex and GitHub Copilot). Skills are reusable instruction sets discovered on-demand via the native skill tool.

**Directory:** `.opencode/skill/${name}/SKILL.md`
**Directory:** `.opencode/skills/${name}/SKILL.md`

### Frontmatter Fields

Expand Down Expand Up @@ -384,7 +384,7 @@ prpm convert agent.md --from claude --to opencode

- **2025-12**: Added native skill support
- Skills use Agent Skills spec (same as Codex, Copilot)
- Directory: `.opencode/skill/${name}/SKILL.md`
- Directory: `.opencode/skills/${name}/SKILL.md`
- Required fields: `name`, `description`
- Optional fields: `license`, `compatibility`, `allowed-tools`, `metadata`
- Uses `agent-skills.schema.json` for validation
Expand Down
2 changes: 1 addition & 1 deletion packages/converters/src/format-registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
"fileExtension": ".md"
},
"skill": {
"directory": ".opencode/skill",
"directory": ".opencode/skills",
"filePatterns": ["SKILL.md"],
"nested": true,
"nestedIndicator": "SKILL.md",
Expand Down
2 changes: 1 addition & 1 deletion packages/converters/src/from-opencode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* OpenCode stores:
* - Agents in .opencode/agent/${name}.md with YAML frontmatter
* - Skills in .opencode/skill/${name}/SKILL.md with YAML frontmatter (has 'name' field, Agent Skills spec)
* - Skills in .opencode/skills/${name}/SKILL.md with YAML frontmatter (has 'name' field, Agent Skills spec)
* - Slash commands in .opencode/command/${name}.md with YAML frontmatter (has 'template' field)
*
* @see https://opencode.ai/docs/agents/
Expand Down
2 changes: 1 addition & 1 deletion packages/converters/src/to-opencode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* OpenCode stores:
* - Agents in .opencode/agent/${name}.md with YAML frontmatter
* - Skills in .opencode/skill/${name}/SKILL.md with YAML frontmatter (Agent Skills spec)
* - Skills in .opencode/skills/${name}/SKILL.md with YAML frontmatter (Agent Skills spec)
* - Slash commands in .opencode/command/${name}.md with YAML frontmatter
*
* @see https://opencode.ai/docs/agents/
Expand Down
2 changes: 1 addition & 1 deletion packages/converters/src/utils/format-capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"supportsAgentsMd": true,
"supportsSlashCommands": true,
"markdownFallback": "opencode-agent.md",
"notes": "OpenCode supports agents, skills, plugins, and slash commands. Skills use Agent Skills spec in .opencode/skill/${name}/SKILL.md. Plugins are JavaScript/TypeScript modules in .opencode/plugin with 40+ event hooks. Full agents.md support."
"notes": "OpenCode supports agents, skills, plugins, and slash commands. Skills use Agent Skills spec in .opencode/skills/${name}/SKILL.md. Plugins are JavaScript/TypeScript modules in .opencode/plugin with 40+ event hooks. Full agents.md support."
},
"ruler": {
"name": "Ruler",
Expand Down
4 changes: 2 additions & 2 deletions packages/types/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const FORMAT_SUBTYPES: Record<Format, readonly Subtype[]> = {
gemini: ["slash-command", "extension"],
"gemini-extension": ["extension", "plugin"],
"gemini.md": ["skill", "agent"],
opencode: ["agent", "slash-command", "tool", "plugin", "skill"], // skill via progressive disclosure
opencode: ["agent", "slash-command", "tool", "plugin", "skill"], // Native skills via .opencode/skills/
ruler: ["rule"],
droid: ["skill", "slash-command", "hook", "agent"], // agent via progressive disclosure
trae: ["rule"],
Expand Down Expand Up @@ -157,7 +157,7 @@ export const FORMAT_NATIVE_SUBTYPES: Partial<Record<Format, readonly Subtype[]>>
copilot: ["rule", "chatmode", "skill"], // Native skill support via .github/skills/
kiro: ["rule", "hook", "agent"], // No native skill - uses AGENTS.md
gemini: ["slash-command", "extension"], // Full native support
opencode: ["agent", "slash-command", "tool", "plugin", "skill"], // Native skill support in .opencode/skill/
opencode: ["agent", "slash-command", "tool", "plugin", "skill"], // Native skill support in .opencode/skills/
droid: ["skill", "slash-command", "hook"], // No native agent - uses AGENTS.md
zed: ["rule", "slash-command", "extension"], // No native skill/agent - uses AGENTS.md
amp: ["skill", "slash-command"], // Native skills (.agents/skills/) and commands (.agents/commands/)
Expand Down
Loading