@every-env/compound-plugin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +37 -0
- package/.github/workflows/deploy-docs.yml +39 -0
- package/AGENTS.md +48 -0
- package/CLAUDE.md +380 -0
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/bun.lock +30 -0
- package/docs/css/docs.css +675 -0
- package/docs/css/style.css +2886 -0
- package/docs/index.html +1046 -0
- package/docs/js/main.js +225 -0
- package/docs/pages/agents.html +649 -0
- package/docs/pages/changelog.html +495 -0
- package/docs/pages/commands.html +523 -0
- package/docs/pages/getting-started.html +582 -0
- package/docs/pages/mcp-servers.html +409 -0
- package/docs/pages/skills.html +611 -0
- package/docs/solutions/plugin-versioning-requirements.md +77 -0
- package/docs/specs/claude-code.md +67 -0
- package/docs/specs/codex.md +59 -0
- package/docs/specs/opencode.md +57 -0
- package/package.json +26 -0
- package/plans/grow-your-own-garden-plugin-architecture.md +102 -0
- package/plans/landing-page-launchkit-refresh.md +279 -0
- package/plugins/coding-tutor/.claude-plugin/plugin.json +9 -0
- package/plugins/coding-tutor/README.md +37 -0
- package/plugins/coding-tutor/commands/quiz-me.md +1 -0
- package/plugins/coding-tutor/commands/sync-tutorials.md +25 -0
- package/plugins/coding-tutor/commands/teach-me.md +1 -0
- package/plugins/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +207 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +193 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
- package/plugins/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +118 -0
- package/plugins/compound-engineering/.claude-plugin/plugin.json +33 -0
- package/plugins/compound-engineering/CHANGELOG.md +393 -0
- package/plugins/compound-engineering/CLAUDE.md +90 -0
- package/plugins/compound-engineering/LICENSE +21 -0
- package/plugins/compound-engineering/README.md +219 -0
- package/plugins/compound-engineering/agents/design/design-implementation-reviewer.md +94 -0
- package/plugins/compound-engineering/agents/design/design-iterator.md +197 -0
- package/plugins/compound-engineering/agents/design/figma-design-sync.md +172 -0
- package/plugins/compound-engineering/agents/docs/ankane-readme-writer.md +50 -0
- package/plugins/compound-engineering/agents/research/best-practices-researcher.md +100 -0
- package/plugins/compound-engineering/agents/research/framework-docs-researcher.md +83 -0
- package/plugins/compound-engineering/agents/research/git-history-analyzer.md +42 -0
- package/plugins/compound-engineering/agents/research/repo-research-analyst.md +113 -0
- package/plugins/compound-engineering/agents/review/agent-native-reviewer.md +246 -0
- package/plugins/compound-engineering/agents/review/architecture-strategist.md +52 -0
- package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +85 -0
- package/plugins/compound-engineering/agents/review/data-integrity-guardian.md +70 -0
- package/plugins/compound-engineering/agents/review/data-migration-expert.md +97 -0
- package/plugins/compound-engineering/agents/review/deployment-verification-agent.md +159 -0
- package/plugins/compound-engineering/agents/review/dhh-rails-reviewer.md +45 -0
- package/plugins/compound-engineering/agents/review/julik-frontend-races-reviewer.md +222 -0
- package/plugins/compound-engineering/agents/review/kieran-python-reviewer.md +104 -0
- package/plugins/compound-engineering/agents/review/kieran-rails-reviewer.md +86 -0
- package/plugins/compound-engineering/agents/review/kieran-typescript-reviewer.md +95 -0
- package/plugins/compound-engineering/agents/review/pattern-recognition-specialist.md +57 -0
- package/plugins/compound-engineering/agents/review/performance-oracle.md +110 -0
- package/plugins/compound-engineering/agents/review/security-sentinel.md +93 -0
- package/plugins/compound-engineering/agents/workflow/bug-reproduction-validator.md +67 -0
- package/plugins/compound-engineering/agents/workflow/every-style-editor.md +64 -0
- package/plugins/compound-engineering/agents/workflow/lint.md +16 -0
- package/plugins/compound-engineering/agents/workflow/pr-comment-resolver.md +69 -0
- package/plugins/compound-engineering/agents/workflow/spec-flow-analyzer.md +113 -0
- package/plugins/compound-engineering/commands/agent-native-audit.md +277 -0
- package/plugins/compound-engineering/commands/changelog.md +137 -0
- package/plugins/compound-engineering/commands/create-agent-skill.md +8 -0
- package/plugins/compound-engineering/commands/deepen-plan.md +546 -0
- package/plugins/compound-engineering/commands/deploy-docs.md +112 -0
- package/plugins/compound-engineering/commands/feature-video.md +342 -0
- package/plugins/compound-engineering/commands/generate_command.md +162 -0
- package/plugins/compound-engineering/commands/heal-skill.md +142 -0
- package/plugins/compound-engineering/commands/lfg.md +19 -0
- package/plugins/compound-engineering/commands/plan_review.md +7 -0
- package/plugins/compound-engineering/commands/release-docs.md +211 -0
- package/plugins/compound-engineering/commands/report-bug.md +150 -0
- package/plugins/compound-engineering/commands/reproduce-bug.md +99 -0
- package/plugins/compound-engineering/commands/resolve_parallel.md +34 -0
- package/plugins/compound-engineering/commands/resolve_pr_parallel.md +49 -0
- package/plugins/compound-engineering/commands/resolve_todo_parallel.md +35 -0
- package/plugins/compound-engineering/commands/test-browser.md +339 -0
- package/plugins/compound-engineering/commands/triage.md +310 -0
- package/plugins/compound-engineering/commands/workflows/compound.md +202 -0
- package/plugins/compound-engineering/commands/workflows/plan.md +466 -0
- package/plugins/compound-engineering/commands/workflows/review.md +514 -0
- package/plugins/compound-engineering/commands/workflows/work.md +363 -0
- package/plugins/compound-engineering/commands/xcode-test.md +331 -0
- package/plugins/compound-engineering/skills/agent-browser/SKILL.md +223 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/SKILL.md +435 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/product-implications.md +443 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
- package/plugins/compound-engineering/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/SKILL.md +184 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/resources.md +119 -0
- package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/plugins/compound-engineering/skills/compound-docs/SKILL.md +510 -0
- package/plugins/compound-engineering/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/plugins/compound-engineering/skills/compound-docs/assets/resolution-template.md +93 -0
- package/plugins/compound-engineering/skills/compound-docs/references/yaml-schema.md +65 -0
- package/plugins/compound-engineering/skills/compound-docs/schema.yaml +176 -0
- package/plugins/compound-engineering/skills/create-agent-skills/SKILL.md +299 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/api-security.md +226 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/best-practices.md +404 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/common-patterns.md +595 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/core-principles.md +437 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/executable-code.md +175 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/official-spec.md +185 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/skill-structure.md +372 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/using-templates.md +112 -0
- package/plugins/compound-engineering/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
- package/plugins/compound-engineering/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/plugins/compound-engineering/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-reference.md +96 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-script.md +93 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-template.md +74 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-workflow.md +120 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/audit-skill.md +138 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/get-guidance.md +121 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
- package/plugins/compound-engineering/skills/create-agent-skills/workflows/verify-skill.md +204 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/SKILL.md +185 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/architecture.md +653 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/controllers.md +303 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/frontend.md +510 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/gems.md +266 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/models.md +359 -0
- package/plugins/compound-engineering/skills/dhh-rails-style/references/testing.md +338 -0
- package/plugins/compound-engineering/skills/dspy-ruby/SKILL.md +594 -0
- package/plugins/compound-engineering/skills/dspy-ruby/assets/config-template.rb +359 -0
- package/plugins/compound-engineering/skills/dspy-ruby/assets/module-template.rb +326 -0
- package/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb +143 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/core-concepts.md +265 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/optimization.md +623 -0
- package/plugins/compound-engineering/skills/dspy-ruby/references/providers.md +338 -0
- package/plugins/compound-engineering/skills/every-style-editor/SKILL.md +134 -0
- package/plugins/compound-engineering/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/plugins/compound-engineering/skills/file-todos/SKILL.md +251 -0
- package/plugins/compound-engineering/skills/file-todos/assets/todo-template.md +155 -0
- package/plugins/compound-engineering/skills/frontend-design/SKILL.md +42 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/SKILL.md +237 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/requirements.txt +2 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/compose_images.py +157 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/edit_image.py +144 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/gemini_images.py +263 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/generate_image.py +133 -0
- package/plugins/compound-engineering/skills/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
- package/plugins/compound-engineering/skills/git-worktree/SKILL.md +302 -0
- package/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh +345 -0
- package/plugins/compound-engineering/skills/rclone/SKILL.md +150 -0
- package/plugins/compound-engineering/skills/rclone/scripts/check_setup.sh +60 -0
- package/plugins/compound-engineering/skills/skill-creator/SKILL.md +209 -0
- package/plugins/compound-engineering/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/compound-engineering/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/compound-engineering/skills/skill-creator/scripts/quick_validate.py +65 -0
- package/src/commands/convert.ts +156 -0
- package/src/commands/install.ts +221 -0
- package/src/commands/list.ts +37 -0
- package/src/converters/claude-to-codex.ts +124 -0
- package/src/converters/claude-to-opencode.ts +392 -0
- package/src/index.ts +20 -0
- package/src/parsers/claude.ts +248 -0
- package/src/targets/codex.ts +91 -0
- package/src/targets/index.ts +29 -0
- package/src/targets/opencode.ts +48 -0
- package/src/types/claude.ts +88 -0
- package/src/types/codex.ts +23 -0
- package/src/types/opencode.ts +54 -0
- package/src/utils/codex-agents.ts +64 -0
- package/src/utils/files.ts +64 -0
- package/src/utils/frontmatter.ts +65 -0
- package/tests/claude-parser.test.ts +89 -0
- package/tests/cli.test.ts +289 -0
- package/tests/codex-agents.test.ts +62 -0
- package/tests/codex-converter.test.ts +121 -0
- package/tests/codex-writer.test.ts +76 -0
- package/tests/converter.test.ts +171 -0
- package/tests/fixtures/custom-paths/.claude-plugin/plugin.json +8 -0
- package/tests/fixtures/custom-paths/agents/default-agent.md +5 -0
- package/tests/fixtures/custom-paths/commands/default-command.md +5 -0
- package/tests/fixtures/custom-paths/custom-agents/custom-agent.md +5 -0
- package/tests/fixtures/custom-paths/custom-commands/custom-command.md +5 -0
- package/tests/fixtures/custom-paths/custom-hooks/hooks.json +7 -0
- package/tests/fixtures/custom-paths/custom-skills/custom-skill/SKILL.md +5 -0
- package/tests/fixtures/custom-paths/hooks/hooks.json +7 -0
- package/tests/fixtures/custom-paths/skills/default-skill/SKILL.md +5 -0
- package/tests/fixtures/invalid-command-path/.claude-plugin/plugin.json +5 -0
- package/tests/fixtures/invalid-hooks-path/.claude-plugin/plugin.json +5 -0
- package/tests/fixtures/invalid-mcp-path/.claude-plugin/plugin.json +5 -0
- package/tests/fixtures/mcp-file/.claude-plugin/plugin.json +5 -0
- package/tests/fixtures/mcp-file/.mcp.json +6 -0
- package/tests/fixtures/sample-plugin/.claude-plugin/plugin.json +30 -0
- package/tests/fixtures/sample-plugin/agents/agent-one.md +10 -0
- package/tests/fixtures/sample-plugin/agents/security-reviewer.md +7 -0
- package/tests/fixtures/sample-plugin/commands/command-one.md +7 -0
- package/tests/fixtures/sample-plugin/commands/model-command.md +8 -0
- package/tests/fixtures/sample-plugin/commands/nested/command-two.md +9 -0
- package/tests/fixtures/sample-plugin/commands/pattern-command.md +7 -0
- package/tests/fixtures/sample-plugin/commands/skill-command.md +7 -0
- package/tests/fixtures/sample-plugin/commands/todo-command.md +7 -0
- package/tests/fixtures/sample-plugin/hooks/hooks.json +156 -0
- package/tests/fixtures/sample-plugin/skills/skill-one/SKILL.md +6 -0
- package/tests/frontmatter.test.ts +20 -0
- package/tests/opencode-writer.test.ts +62 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { defineCommand } from "citty"
|
|
2
|
+
import os from "os"
|
|
3
|
+
import path from "path"
|
|
4
|
+
import { loadClaudePlugin } from "../parsers/claude"
|
|
5
|
+
import { targets } from "../targets"
|
|
6
|
+
import type { PermissionMode } from "../converters/claude-to-opencode"
|
|
7
|
+
import { ensureCodexAgentsFile } from "../utils/codex-agents"
|
|
8
|
+
|
|
9
|
+
const permissionModes: PermissionMode[] = ["none", "broad", "from-commands"]
|
|
10
|
+
|
|
11
|
+
export default defineCommand({
|
|
12
|
+
meta: {
|
|
13
|
+
name: "convert",
|
|
14
|
+
description: "Convert a Claude Code plugin into another format",
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
source: {
|
|
18
|
+
type: "positional",
|
|
19
|
+
required: true,
|
|
20
|
+
description: "Path to the Claude plugin directory",
|
|
21
|
+
},
|
|
22
|
+
to: {
|
|
23
|
+
type: "string",
|
|
24
|
+
default: "opencode",
|
|
25
|
+
description: "Target format (opencode | codex)",
|
|
26
|
+
},
|
|
27
|
+
output: {
|
|
28
|
+
type: "string",
|
|
29
|
+
alias: "o",
|
|
30
|
+
description: "Output directory (project root)",
|
|
31
|
+
},
|
|
32
|
+
codexHome: {
|
|
33
|
+
type: "string",
|
|
34
|
+
alias: "codex-home",
|
|
35
|
+
description: "Write Codex output to this .codex root (ex: ~/.codex)",
|
|
36
|
+
},
|
|
37
|
+
also: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "Comma-separated extra targets to generate (ex: codex)",
|
|
40
|
+
},
|
|
41
|
+
permissions: {
|
|
42
|
+
type: "string",
|
|
43
|
+
default: "broad",
|
|
44
|
+
description: "Permission mapping: none | broad | from-commands",
|
|
45
|
+
},
|
|
46
|
+
agentMode: {
|
|
47
|
+
type: "string",
|
|
48
|
+
default: "subagent",
|
|
49
|
+
description: "Default agent mode: primary | subagent",
|
|
50
|
+
},
|
|
51
|
+
inferTemperature: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
default: true,
|
|
54
|
+
description: "Infer agent temperature from name/description",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
async run({ args }) {
|
|
58
|
+
const targetName = String(args.to)
|
|
59
|
+
const target = targets[targetName]
|
|
60
|
+
if (!target) {
|
|
61
|
+
throw new Error(`Unknown target: ${targetName}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!target.implemented) {
|
|
65
|
+
throw new Error(`Target ${targetName} is registered but not implemented yet.`)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const permissions = String(args.permissions)
|
|
69
|
+
if (!permissionModes.includes(permissions as PermissionMode)) {
|
|
70
|
+
throw new Error(`Unknown permissions mode: ${permissions}`)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const plugin = await loadClaudePlugin(String(args.source))
|
|
74
|
+
const outputRoot = resolveOutputRoot(args.output)
|
|
75
|
+
const codexHome = resolveCodexRoot(args.codexHome)
|
|
76
|
+
|
|
77
|
+
const options = {
|
|
78
|
+
agentMode: String(args.agentMode) === "primary" ? "primary" : "subagent",
|
|
79
|
+
inferTemperature: Boolean(args.inferTemperature),
|
|
80
|
+
permissions: permissions as PermissionMode,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const primaryOutputRoot = targetName === "codex" && codexHome ? codexHome : outputRoot
|
|
84
|
+
const bundle = target.convert(plugin, options)
|
|
85
|
+
if (!bundle) {
|
|
86
|
+
throw new Error(`Target ${targetName} did not return a bundle.`)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await target.write(primaryOutputRoot, bundle)
|
|
90
|
+
console.log(`Converted ${plugin.manifest.name} to ${targetName} at ${primaryOutputRoot}`)
|
|
91
|
+
|
|
92
|
+
const extraTargets = parseExtraTargets(args.also)
|
|
93
|
+
const allTargets = [targetName, ...extraTargets]
|
|
94
|
+
for (const extra of extraTargets) {
|
|
95
|
+
const handler = targets[extra]
|
|
96
|
+
if (!handler) {
|
|
97
|
+
console.warn(`Skipping unknown target: ${extra}`)
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
if (!handler.implemented) {
|
|
101
|
+
console.warn(`Skipping ${extra}: not implemented yet.`)
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
const extraBundle = handler.convert(plugin, options)
|
|
105
|
+
if (!extraBundle) {
|
|
106
|
+
console.warn(`Skipping ${extra}: no output returned.`)
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
const extraRoot = extra === "codex" && codexHome
|
|
110
|
+
? codexHome
|
|
111
|
+
: path.join(outputRoot, extra)
|
|
112
|
+
await handler.write(extraRoot, extraBundle)
|
|
113
|
+
console.log(`Converted ${plugin.manifest.name} to ${extra} at ${extraRoot}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (allTargets.includes("codex")) {
|
|
117
|
+
await ensureCodexAgentsFile(codexHome)
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
function parseExtraTargets(value: unknown): string[] {
|
|
123
|
+
if (!value) return []
|
|
124
|
+
return String(value)
|
|
125
|
+
.split(",")
|
|
126
|
+
.map((entry) => entry.trim())
|
|
127
|
+
.filter(Boolean)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function resolveCodexHome(value: unknown): string | null {
|
|
131
|
+
if (!value) return null
|
|
132
|
+
const raw = String(value).trim()
|
|
133
|
+
if (!raw) return null
|
|
134
|
+
const expanded = expandHome(raw)
|
|
135
|
+
return path.resolve(expanded)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function resolveCodexRoot(value: unknown): string {
|
|
139
|
+
return resolveCodexHome(value) ?? path.join(os.homedir(), ".codex")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function expandHome(value: string): string {
|
|
143
|
+
if (value === "~") return os.homedir()
|
|
144
|
+
if (value.startsWith(`~${path.sep}`)) {
|
|
145
|
+
return path.join(os.homedir(), value.slice(2))
|
|
146
|
+
}
|
|
147
|
+
return value
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveOutputRoot(value: unknown): string {
|
|
151
|
+
if (value && String(value).trim()) {
|
|
152
|
+
const expanded = expandHome(String(value).trim())
|
|
153
|
+
return path.resolve(expanded)
|
|
154
|
+
}
|
|
155
|
+
return process.cwd()
|
|
156
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { defineCommand } from "citty"
|
|
2
|
+
import { promises as fs } from "fs"
|
|
3
|
+
import os from "os"
|
|
4
|
+
import path from "path"
|
|
5
|
+
import { loadClaudePlugin } from "../parsers/claude"
|
|
6
|
+
import { targets } from "../targets"
|
|
7
|
+
import { pathExists } from "../utils/files"
|
|
8
|
+
import type { PermissionMode } from "../converters/claude-to-opencode"
|
|
9
|
+
import { ensureCodexAgentsFile } from "../utils/codex-agents"
|
|
10
|
+
|
|
11
|
+
const permissionModes: PermissionMode[] = ["none", "broad", "from-commands"]
|
|
12
|
+
|
|
13
|
+
export default defineCommand({
|
|
14
|
+
meta: {
|
|
15
|
+
name: "install",
|
|
16
|
+
description: "Install and convert a Claude plugin",
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
plugin: {
|
|
20
|
+
type: "positional",
|
|
21
|
+
required: true,
|
|
22
|
+
description: "Plugin name or path",
|
|
23
|
+
},
|
|
24
|
+
to: {
|
|
25
|
+
type: "string",
|
|
26
|
+
default: "opencode",
|
|
27
|
+
description: "Target format (opencode | codex)",
|
|
28
|
+
},
|
|
29
|
+
output: {
|
|
30
|
+
type: "string",
|
|
31
|
+
alias: "o",
|
|
32
|
+
description: "Output directory (project root)",
|
|
33
|
+
},
|
|
34
|
+
codexHome: {
|
|
35
|
+
type: "string",
|
|
36
|
+
alias: "codex-home",
|
|
37
|
+
description: "Write Codex output to this .codex root (ex: ~/.codex)",
|
|
38
|
+
},
|
|
39
|
+
also: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Comma-separated extra targets to generate (ex: codex)",
|
|
42
|
+
},
|
|
43
|
+
permissions: {
|
|
44
|
+
type: "string",
|
|
45
|
+
default: "broad",
|
|
46
|
+
description: "Permission mapping: none | broad | from-commands",
|
|
47
|
+
},
|
|
48
|
+
agentMode: {
|
|
49
|
+
type: "string",
|
|
50
|
+
default: "subagent",
|
|
51
|
+
description: "Default agent mode: primary | subagent",
|
|
52
|
+
},
|
|
53
|
+
inferTemperature: {
|
|
54
|
+
type: "boolean",
|
|
55
|
+
default: true,
|
|
56
|
+
description: "Infer agent temperature from name/description",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
async run({ args }) {
|
|
60
|
+
const targetName = String(args.to)
|
|
61
|
+
const target = targets[targetName]
|
|
62
|
+
if (!target) {
|
|
63
|
+
throw new Error(`Unknown target: ${targetName}`)
|
|
64
|
+
}
|
|
65
|
+
if (!target.implemented) {
|
|
66
|
+
throw new Error(`Target ${targetName} is registered but not implemented yet.`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const permissions = String(args.permissions)
|
|
70
|
+
if (!permissionModes.includes(permissions as PermissionMode)) {
|
|
71
|
+
throw new Error(`Unknown permissions mode: ${permissions}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const resolvedPlugin = await resolvePluginPath(String(args.plugin))
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const plugin = await loadClaudePlugin(resolvedPlugin.path)
|
|
78
|
+
const outputRoot = resolveOutputRoot(args.output)
|
|
79
|
+
const codexHome = resolveCodexRoot(args.codexHome)
|
|
80
|
+
|
|
81
|
+
const options = {
|
|
82
|
+
agentMode: String(args.agentMode) === "primary" ? "primary" : "subagent",
|
|
83
|
+
inferTemperature: Boolean(args.inferTemperature),
|
|
84
|
+
permissions: permissions as PermissionMode,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const bundle = target.convert(plugin, options)
|
|
88
|
+
if (!bundle) {
|
|
89
|
+
throw new Error(`Target ${targetName} did not return a bundle.`)
|
|
90
|
+
}
|
|
91
|
+
const primaryOutputRoot = targetName === "codex" && codexHome ? codexHome : outputRoot
|
|
92
|
+
await target.write(primaryOutputRoot, bundle)
|
|
93
|
+
console.log(`Installed ${plugin.manifest.name} to ${primaryOutputRoot}`)
|
|
94
|
+
|
|
95
|
+
const extraTargets = parseExtraTargets(args.also)
|
|
96
|
+
const allTargets = [targetName, ...extraTargets]
|
|
97
|
+
for (const extra of extraTargets) {
|
|
98
|
+
const handler = targets[extra]
|
|
99
|
+
if (!handler) {
|
|
100
|
+
console.warn(`Skipping unknown target: ${extra}`)
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
if (!handler.implemented) {
|
|
104
|
+
console.warn(`Skipping ${extra}: not implemented yet.`)
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
const extraBundle = handler.convert(plugin, options)
|
|
108
|
+
if (!extraBundle) {
|
|
109
|
+
console.warn(`Skipping ${extra}: no output returned.`)
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
const extraRoot = extra === "codex" && codexHome
|
|
113
|
+
? codexHome
|
|
114
|
+
: path.join(outputRoot, extra)
|
|
115
|
+
await handler.write(extraRoot, extraBundle)
|
|
116
|
+
console.log(`Installed ${plugin.manifest.name} to ${extraRoot}`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (allTargets.includes("codex")) {
|
|
120
|
+
await ensureCodexAgentsFile(codexHome)
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
if (resolvedPlugin.cleanup) {
|
|
124
|
+
await resolvedPlugin.cleanup()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
type ResolvedPluginPath = {
|
|
131
|
+
path: string
|
|
132
|
+
cleanup?: () => Promise<void>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function resolvePluginPath(input: string): Promise<ResolvedPluginPath> {
|
|
136
|
+
const directPath = path.resolve(input)
|
|
137
|
+
if (await pathExists(directPath)) return { path: directPath }
|
|
138
|
+
|
|
139
|
+
const pluginsPath = path.join(process.cwd(), "plugins", input)
|
|
140
|
+
if (await pathExists(pluginsPath)) return { path: pluginsPath }
|
|
141
|
+
|
|
142
|
+
return await resolveGitHubPluginPath(input)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function parseExtraTargets(value: unknown): string[] {
|
|
146
|
+
if (!value) return []
|
|
147
|
+
return String(value)
|
|
148
|
+
.split(",")
|
|
149
|
+
.map((entry) => entry.trim())
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function resolveCodexHome(value: unknown): string | null {
|
|
154
|
+
if (!value) return null
|
|
155
|
+
const raw = String(value).trim()
|
|
156
|
+
if (!raw) return null
|
|
157
|
+
const expanded = expandHome(raw)
|
|
158
|
+
return path.resolve(expanded)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function resolveCodexRoot(value: unknown): string {
|
|
162
|
+
return resolveCodexHome(value) ?? path.join(os.homedir(), ".codex")
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function expandHome(value: string): string {
|
|
166
|
+
if (value === "~") return os.homedir()
|
|
167
|
+
if (value.startsWith(`~${path.sep}`)) {
|
|
168
|
+
return path.join(os.homedir(), value.slice(2))
|
|
169
|
+
}
|
|
170
|
+
return value
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function resolveOutputRoot(value: unknown): string {
|
|
174
|
+
if (value && String(value).trim()) {
|
|
175
|
+
const expanded = expandHome(String(value).trim())
|
|
176
|
+
return path.resolve(expanded)
|
|
177
|
+
}
|
|
178
|
+
return path.join(os.homedir(), ".opencode")
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function resolveGitHubPluginPath(pluginName: string): Promise<ResolvedPluginPath> {
|
|
182
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "compound-plugin-"))
|
|
183
|
+
const source = resolveGitHubSource()
|
|
184
|
+
try {
|
|
185
|
+
await cloneGitHubRepo(source, tempRoot)
|
|
186
|
+
} catch (error) {
|
|
187
|
+
await fs.rm(tempRoot, { recursive: true, force: true })
|
|
188
|
+
throw error
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const pluginPath = path.join(tempRoot, "plugins", pluginName)
|
|
192
|
+
if (!(await pathExists(pluginPath))) {
|
|
193
|
+
await fs.rm(tempRoot, { recursive: true, force: true })
|
|
194
|
+
throw new Error(`Could not find plugin ${pluginName} in ${source}.`)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
path: pluginPath,
|
|
199
|
+
cleanup: async () => {
|
|
200
|
+
await fs.rm(tempRoot, { recursive: true, force: true })
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function resolveGitHubSource(): string {
|
|
206
|
+
const override = process.env.COMPOUND_PLUGIN_GITHUB_SOURCE
|
|
207
|
+
if (override && override.trim()) return override.trim()
|
|
208
|
+
return "https://github.com/EveryInc/compound-engineering-plugin"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function cloneGitHubRepo(source: string, destination: string): Promise<void> {
|
|
212
|
+
const proc = Bun.spawn(["git", "clone", "--depth", "1", source, destination], {
|
|
213
|
+
stdout: "pipe",
|
|
214
|
+
stderr: "pipe",
|
|
215
|
+
})
|
|
216
|
+
const exitCode = await proc.exited
|
|
217
|
+
const stderr = await new Response(proc.stderr).text()
|
|
218
|
+
if (exitCode !== 0) {
|
|
219
|
+
throw new Error(`Failed to clone ${source}. ${stderr.trim()}`)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import { promises as fs } from "fs"
|
|
3
|
+
import { defineCommand } from "citty"
|
|
4
|
+
import { pathExists } from "../utils/files"
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
meta: {
|
|
8
|
+
name: "list",
|
|
9
|
+
description: "List available Claude plugins under plugins/",
|
|
10
|
+
},
|
|
11
|
+
async run() {
|
|
12
|
+
const root = process.cwd()
|
|
13
|
+
const pluginsDir = path.join(root, "plugins")
|
|
14
|
+
if (!(await pathExists(pluginsDir))) {
|
|
15
|
+
console.log("No plugins directory found.")
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const entries = await fs.readdir(pluginsDir, { withFileTypes: true })
|
|
20
|
+
const plugins: string[] = []
|
|
21
|
+
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (!entry.isDirectory()) continue
|
|
24
|
+
const manifestPath = path.join(pluginsDir, entry.name, ".claude-plugin", "plugin.json")
|
|
25
|
+
if (await pathExists(manifestPath)) {
|
|
26
|
+
plugins.push(entry.name)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (plugins.length === 0) {
|
|
31
|
+
console.log("No Claude plugins found under plugins/.")
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(plugins.sort().join("\n"))
|
|
36
|
+
},
|
|
37
|
+
})
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { formatFrontmatter } from "../utils/frontmatter"
|
|
2
|
+
import type { ClaudeAgent, ClaudeCommand, ClaudePlugin } from "../types/claude"
|
|
3
|
+
import type { CodexBundle, CodexGeneratedSkill } from "../types/codex"
|
|
4
|
+
import type { ClaudeToOpenCodeOptions } from "./claude-to-opencode"
|
|
5
|
+
|
|
6
|
+
export type ClaudeToCodexOptions = ClaudeToOpenCodeOptions
|
|
7
|
+
|
|
8
|
+
const CODEX_DESCRIPTION_MAX_LENGTH = 1024
|
|
9
|
+
|
|
10
|
+
export function convertClaudeToCodex(
|
|
11
|
+
plugin: ClaudePlugin,
|
|
12
|
+
_options: ClaudeToCodexOptions,
|
|
13
|
+
): CodexBundle {
|
|
14
|
+
const promptNames = new Set<string>()
|
|
15
|
+
const skillDirs = plugin.skills.map((skill) => ({
|
|
16
|
+
name: skill.name,
|
|
17
|
+
sourceDir: skill.sourceDir,
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
const usedSkillNames = new Set<string>(skillDirs.map((skill) => normalizeName(skill.name)))
|
|
21
|
+
const commandSkills: CodexGeneratedSkill[] = []
|
|
22
|
+
const prompts = plugin.commands.map((command) => {
|
|
23
|
+
const promptName = uniqueName(normalizeName(command.name), promptNames)
|
|
24
|
+
const commandSkill = convertCommandSkill(command, usedSkillNames)
|
|
25
|
+
commandSkills.push(commandSkill)
|
|
26
|
+
const content = renderPrompt(command, commandSkill.name)
|
|
27
|
+
return { name: promptName, content }
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const agentSkills = plugin.agents.map((agent) => convertAgent(agent, usedSkillNames))
|
|
31
|
+
const generatedSkills = [...commandSkills, ...agentSkills]
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
prompts,
|
|
35
|
+
skillDirs,
|
|
36
|
+
generatedSkills,
|
|
37
|
+
mcpServers: plugin.mcpServers,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function convertAgent(agent: ClaudeAgent, usedNames: Set<string>): CodexGeneratedSkill {
|
|
42
|
+
const name = uniqueName(normalizeName(agent.name), usedNames)
|
|
43
|
+
const description = sanitizeDescription(
|
|
44
|
+
agent.description ?? `Converted from Claude agent ${agent.name}`,
|
|
45
|
+
)
|
|
46
|
+
const frontmatter: Record<string, unknown> = { name, description }
|
|
47
|
+
|
|
48
|
+
let body = agent.body.trim()
|
|
49
|
+
if (agent.capabilities && agent.capabilities.length > 0) {
|
|
50
|
+
const capabilities = agent.capabilities.map((capability) => `- ${capability}`).join("\n")
|
|
51
|
+
body = `## Capabilities\n${capabilities}\n\n${body}`.trim()
|
|
52
|
+
}
|
|
53
|
+
if (body.length === 0) {
|
|
54
|
+
body = `Instructions converted from the ${agent.name} agent.`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const content = formatFrontmatter(frontmatter, body)
|
|
58
|
+
return { name, content }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function convertCommandSkill(command: ClaudeCommand, usedNames: Set<string>): CodexGeneratedSkill {
|
|
62
|
+
const name = uniqueName(normalizeName(command.name), usedNames)
|
|
63
|
+
const frontmatter: Record<string, unknown> = {
|
|
64
|
+
name,
|
|
65
|
+
description: sanitizeDescription(
|
|
66
|
+
command.description ?? `Converted from Claude command ${command.name}`,
|
|
67
|
+
),
|
|
68
|
+
}
|
|
69
|
+
const sections: string[] = []
|
|
70
|
+
if (command.argumentHint) {
|
|
71
|
+
sections.push(`## Arguments\n${command.argumentHint}`)
|
|
72
|
+
}
|
|
73
|
+
if (command.allowedTools && command.allowedTools.length > 0) {
|
|
74
|
+
sections.push(`## Allowed tools\n${command.allowedTools.map((tool) => `- ${tool}`).join("\n")}`)
|
|
75
|
+
}
|
|
76
|
+
sections.push(command.body.trim())
|
|
77
|
+
const body = sections.filter(Boolean).join("\n\n").trim()
|
|
78
|
+
const content = formatFrontmatter(frontmatter, body.length > 0 ? body : command.body)
|
|
79
|
+
return { name, content }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderPrompt(command: ClaudeCommand, skillName: string): string {
|
|
83
|
+
const frontmatter: Record<string, unknown> = {
|
|
84
|
+
description: command.description,
|
|
85
|
+
"argument-hint": command.argumentHint,
|
|
86
|
+
}
|
|
87
|
+
const instructions = `Use the $${skillName} skill for this command and follow its instructions.`
|
|
88
|
+
const body = [instructions, "", command.body].join("\n").trim()
|
|
89
|
+
return formatFrontmatter(frontmatter, body)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeName(value: string): string {
|
|
93
|
+
const trimmed = value.trim()
|
|
94
|
+
if (!trimmed) return "item"
|
|
95
|
+
const normalized = trimmed
|
|
96
|
+
.toLowerCase()
|
|
97
|
+
.replace(/[\\/]+/g, "-")
|
|
98
|
+
.replace(/[:\s]+/g, "-")
|
|
99
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
100
|
+
.replace(/-+/g, "-")
|
|
101
|
+
.replace(/^-+|-+$/g, "")
|
|
102
|
+
return normalized || "item"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function sanitizeDescription(value: string, maxLength = CODEX_DESCRIPTION_MAX_LENGTH): string {
|
|
106
|
+
const normalized = value.replace(/\s+/g, " ").trim()
|
|
107
|
+
if (normalized.length <= maxLength) return normalized
|
|
108
|
+
const ellipsis = "..."
|
|
109
|
+
return normalized.slice(0, Math.max(0, maxLength - ellipsis.length)).trimEnd() + ellipsis
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function uniqueName(base: string, used: Set<string>): string {
|
|
113
|
+
if (!used.has(base)) {
|
|
114
|
+
used.add(base)
|
|
115
|
+
return base
|
|
116
|
+
}
|
|
117
|
+
let index = 2
|
|
118
|
+
while (used.has(`${base}-${index}`)) {
|
|
119
|
+
index += 1
|
|
120
|
+
}
|
|
121
|
+
const name = `${base}-${index}`
|
|
122
|
+
used.add(name)
|
|
123
|
+
return name
|
|
124
|
+
}
|