diff --git a/README.md b/README.md index 4748b5961..5604ba17f 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,11 @@ cp -r dist/claude-code/.claude your-project/ cp -r dist/claude-code/.claude/* ~/.claude/ ``` +**OpenCode:** +```bash +cp -r dist/opencode/.opencode your-project/ +``` + **Gemini CLI:** ```bash cp -r dist/gemini/.gemini your-project/ @@ -136,6 +141,7 @@ Most commands accept an optional argument to focus on a specific area: - [Cursor](https://cursor.com) - [Claude Code](https://claude.ai/code) +- [OpenCode](https://opencode.ai) - [Gemini CLI](https://github.com/google-gemini/gemini-cli) - [Codex CLI](https://github.com/openai/codex) diff --git a/scripts/build.js b/scripts/build.js index 6e156de65..0115e6046 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -24,7 +24,8 @@ import { transformGemini, transformCodex, transformAgents, - transformKiro + transformKiro, + transformOpenCode } from './lib/transformers/index.js'; import { createAllZips } from './lib/zip.js'; import { execSync } from 'child_process'; @@ -146,6 +147,7 @@ function assembleUniversal(distDir, suffix = '') { { provider: 'codex', configDir: '.codex' }, { provider: 'agents', configDir: '.agents' }, { provider: 'kiro', configDir: '.kiro' }, + { provider: 'opencode', configDir: '.opencode' } ]; for (const { provider, configDir } of providerMappings) { @@ -171,6 +173,7 @@ This folder contains skills for all supported tools: .codex/ → Codex CLI .agents/ → VS Code Copilot, Antigravity .kiro/ → Kiro + .opencode/ → OpenCode To install, copy the relevant folder(s) into your project root. These are hidden folders (dotfiles) — press Cmd+Shift+. in Finder to see them. @@ -302,6 +305,7 @@ async function build() { transformCodex(skills, DIST_DIR, patterns); transformAgents(skills, DIST_DIR, patterns); transformKiro(skills, DIST_DIR, patterns); + transformOpenCode(skills, DIST_DIR, patterns); // Transform for each provider (prefixed with i-) const prefixOptions = { prefix: 'i-', outputSuffix: '-prefixed' }; @@ -311,6 +315,7 @@ async function build() { transformCodex(skills, DIST_DIR, patterns, prefixOptions); transformAgents(skills, DIST_DIR, patterns, prefixOptions); transformKiro(skills, DIST_DIR, patterns, prefixOptions); + transformOpenCode(skills, DIST_DIR, patterns, prefixOptions); // Assemble universal directory (unprefixed and prefixed) assembleUniversal(DIST_DIR); diff --git a/scripts/lib/transformers/index.js b/scripts/lib/transformers/index.js index 53a8034a1..6bb52d985 100644 --- a/scripts/lib/transformers/index.js +++ b/scripts/lib/transformers/index.js @@ -4,3 +4,4 @@ export { transformGemini } from './gemini.js'; export { transformCodex } from './codex.js'; export { transformAgents } from './agents.js'; export { transformKiro } from './kiro.js'; +export { transformOpenCode } from "./opencode.js"; diff --git a/scripts/lib/transformers/opencode.js b/scripts/lib/transformers/opencode.js new file mode 100644 index 000000000..838233eea --- /dev/null +++ b/scripts/lib/transformers/opencode.js @@ -0,0 +1,84 @@ +import path from 'path'; +import { + cleanDir, + ensureDir, + generateYamlFrontmatter, + prefixSkillReferences, + replacePlaceholders, + writeFile, +} from '../utils.js'; + +/** + * OpenCode Transformer (Skills Only) + * + * All skills output to .opencode/skills/{name}/SKILL.md + * User-invokable skills get args support in frontmatter. + * + * @param {Array} skills - All skills (including user-invokable ones) + * @param {string} distDir - Distribution output directory + * @param {Object} patterns - Design patterns data (unused, kept for interface consistency) + * @param {Object} options - Optional settings + * @param {string} options.prefix - Prefix to add to user-invokable skill names (e.g., 'i-') + * @param {string} options.outputSuffix - Suffix for output directory (e.g., '-prefixed') + */ +export function transformOpenCode( + skills, + distDir, + patterns = null, + options = {}, +) { + const { prefix = '', outputSuffix = '' } = options; + const opencodeDir = path.join(distDir, `opencode${outputSuffix}`); + const skillsDir = path.join(opencodeDir, '.opencode/skills'); + + cleanDir(opencodeDir); + ensureDir(skillsDir); + + const allSkillNames = skills.map((s) => s.name); + const commandNames = skills + .filter((s) => s.userInvokable) + .map((s) => `${prefix}${s.name}`); + let refCount = 0; + for (const skill of skills) { + const skillName = `${prefix}${skill.name}`; + const skillDir = path.join(skillsDir, skillName); + + const frontmatterObj = { + name: skillName, + description: skill.description, + }; + + if (skill.userInvokable) frontmatterObj['user-invokable'] = true; + if (skill.args && skill.args.length > 0) frontmatterObj.args = skill.args; + if (skill.license) frontmatterObj.license = skill.license; + if (skill.compatibility) frontmatterObj.compatibility = skill.compatibility; + if (skill.metadata) frontmatterObj.metadata = skill.metadata; + if (skill.allowedTools) + frontmatterObj['allowed-tools'] = skill.allowedTools; + + const frontmatter = generateYamlFrontmatter(frontmatterObj); + let skillBody = replacePlaceholders(skill.body, 'opencode', commandNames); + if (prefix) + skillBody = prefixSkillReferences(skillBody, prefix, allSkillNames); + const content = `${frontmatter}\n\n${skillBody}`; + const outputPath = path.join(skillDir, 'SKILL.md'); + writeFile(outputPath, content); + + // Copy reference files if they exist + if (skill.references && skill.references.length > 0) { + const refDir = path.join(skillDir, 'reference'); + ensureDir(refDir); + for (const ref of skill.references) { + const refOutputPath = path.join(refDir, `${ref.name}.md`); + const refContent = replacePlaceholders(ref.content, 'opencode'); + writeFile(refOutputPath, refContent); + refCount++; + } + } + } + + const userInvokableCount = skills.filter((s) => s.userInvokable).length; + const refInfo = refCount > 0 ? ` (${refCount} reference files)` : ''; + const prefixInfo = prefix ? ` [${prefix}prefixed]` : ''; + console.log(`✓ OpenCode${prefixInfo}: ${skills.length} skills (${userInvokableCount} user-invokable)${refInfo}`); +} diff --git a/scripts/lib/utils.js b/scripts/lib/utils.js index 9168aa2af..433e1f85b 100644 --- a/scripts/lib/utils.js +++ b/scripts/lib/utils.js @@ -293,6 +293,11 @@ export const PROVIDER_PLACEHOLDERS = { model: 'Claude', config_file: '.kiro/settings.json', ask_instruction: 'ask the user directly to clarify what you cannot infer.' + }, + opencode: { + model: 'Claude', + config_file: 'OPENCODE.md', + ask_instruction: 'STOP and call the AskUserQuestionTool to clarify.', } };