@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.
Files changed (226) hide show
  1. package/.claude-plugin/marketplace.json +37 -0
  2. package/.github/workflows/deploy-docs.yml +39 -0
  3. package/AGENTS.md +48 -0
  4. package/CLAUDE.md +380 -0
  5. package/LICENSE +21 -0
  6. package/README.md +65 -0
  7. package/bun.lock +30 -0
  8. package/docs/css/docs.css +675 -0
  9. package/docs/css/style.css +2886 -0
  10. package/docs/index.html +1046 -0
  11. package/docs/js/main.js +225 -0
  12. package/docs/pages/agents.html +649 -0
  13. package/docs/pages/changelog.html +495 -0
  14. package/docs/pages/commands.html +523 -0
  15. package/docs/pages/getting-started.html +582 -0
  16. package/docs/pages/mcp-servers.html +409 -0
  17. package/docs/pages/skills.html +611 -0
  18. package/docs/solutions/plugin-versioning-requirements.md +77 -0
  19. package/docs/specs/claude-code.md +67 -0
  20. package/docs/specs/codex.md +59 -0
  21. package/docs/specs/opencode.md +57 -0
  22. package/package.json +26 -0
  23. package/plans/grow-your-own-garden-plugin-architecture.md +102 -0
  24. package/plans/landing-page-launchkit-refresh.md +279 -0
  25. package/plugins/coding-tutor/.claude-plugin/plugin.json +9 -0
  26. package/plugins/coding-tutor/README.md +37 -0
  27. package/plugins/coding-tutor/commands/quiz-me.md +1 -0
  28. package/plugins/coding-tutor/commands/sync-tutorials.md +25 -0
  29. package/plugins/coding-tutor/commands/teach-me.md +1 -0
  30. package/plugins/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
  31. package/plugins/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +207 -0
  32. package/plugins/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +193 -0
  33. package/plugins/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
  34. package/plugins/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +118 -0
  35. package/plugins/compound-engineering/.claude-plugin/plugin.json +33 -0
  36. package/plugins/compound-engineering/CHANGELOG.md +393 -0
  37. package/plugins/compound-engineering/CLAUDE.md +90 -0
  38. package/plugins/compound-engineering/LICENSE +21 -0
  39. package/plugins/compound-engineering/README.md +219 -0
  40. package/plugins/compound-engineering/agents/design/design-implementation-reviewer.md +94 -0
  41. package/plugins/compound-engineering/agents/design/design-iterator.md +197 -0
  42. package/plugins/compound-engineering/agents/design/figma-design-sync.md +172 -0
  43. package/plugins/compound-engineering/agents/docs/ankane-readme-writer.md +50 -0
  44. package/plugins/compound-engineering/agents/research/best-practices-researcher.md +100 -0
  45. package/plugins/compound-engineering/agents/research/framework-docs-researcher.md +83 -0
  46. package/plugins/compound-engineering/agents/research/git-history-analyzer.md +42 -0
  47. package/plugins/compound-engineering/agents/research/repo-research-analyst.md +113 -0
  48. package/plugins/compound-engineering/agents/review/agent-native-reviewer.md +246 -0
  49. package/plugins/compound-engineering/agents/review/architecture-strategist.md +52 -0
  50. package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +85 -0
  51. package/plugins/compound-engineering/agents/review/data-integrity-guardian.md +70 -0
  52. package/plugins/compound-engineering/agents/review/data-migration-expert.md +97 -0
  53. package/plugins/compound-engineering/agents/review/deployment-verification-agent.md +159 -0
  54. package/plugins/compound-engineering/agents/review/dhh-rails-reviewer.md +45 -0
  55. package/plugins/compound-engineering/agents/review/julik-frontend-races-reviewer.md +222 -0
  56. package/plugins/compound-engineering/agents/review/kieran-python-reviewer.md +104 -0
  57. package/plugins/compound-engineering/agents/review/kieran-rails-reviewer.md +86 -0
  58. package/plugins/compound-engineering/agents/review/kieran-typescript-reviewer.md +95 -0
  59. package/plugins/compound-engineering/agents/review/pattern-recognition-specialist.md +57 -0
  60. package/plugins/compound-engineering/agents/review/performance-oracle.md +110 -0
  61. package/plugins/compound-engineering/agents/review/security-sentinel.md +93 -0
  62. package/plugins/compound-engineering/agents/workflow/bug-reproduction-validator.md +67 -0
  63. package/plugins/compound-engineering/agents/workflow/every-style-editor.md +64 -0
  64. package/plugins/compound-engineering/agents/workflow/lint.md +16 -0
  65. package/plugins/compound-engineering/agents/workflow/pr-comment-resolver.md +69 -0
  66. package/plugins/compound-engineering/agents/workflow/spec-flow-analyzer.md +113 -0
  67. package/plugins/compound-engineering/commands/agent-native-audit.md +277 -0
  68. package/plugins/compound-engineering/commands/changelog.md +137 -0
  69. package/plugins/compound-engineering/commands/create-agent-skill.md +8 -0
  70. package/plugins/compound-engineering/commands/deepen-plan.md +546 -0
  71. package/plugins/compound-engineering/commands/deploy-docs.md +112 -0
  72. package/plugins/compound-engineering/commands/feature-video.md +342 -0
  73. package/plugins/compound-engineering/commands/generate_command.md +162 -0
  74. package/plugins/compound-engineering/commands/heal-skill.md +142 -0
  75. package/plugins/compound-engineering/commands/lfg.md +19 -0
  76. package/plugins/compound-engineering/commands/plan_review.md +7 -0
  77. package/plugins/compound-engineering/commands/release-docs.md +211 -0
  78. package/plugins/compound-engineering/commands/report-bug.md +150 -0
  79. package/plugins/compound-engineering/commands/reproduce-bug.md +99 -0
  80. package/plugins/compound-engineering/commands/resolve_parallel.md +34 -0
  81. package/plugins/compound-engineering/commands/resolve_pr_parallel.md +49 -0
  82. package/plugins/compound-engineering/commands/resolve_todo_parallel.md +35 -0
  83. package/plugins/compound-engineering/commands/test-browser.md +339 -0
  84. package/plugins/compound-engineering/commands/triage.md +310 -0
  85. package/plugins/compound-engineering/commands/workflows/compound.md +202 -0
  86. package/plugins/compound-engineering/commands/workflows/plan.md +466 -0
  87. package/plugins/compound-engineering/commands/workflows/review.md +514 -0
  88. package/plugins/compound-engineering/commands/workflows/work.md +363 -0
  89. package/plugins/compound-engineering/commands/xcode-test.md +331 -0
  90. package/plugins/compound-engineering/skills/agent-browser/SKILL.md +223 -0
  91. package/plugins/compound-engineering/skills/agent-native-architecture/SKILL.md +435 -0
  92. package/plugins/compound-engineering/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
  93. package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
  94. package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
  95. package/plugins/compound-engineering/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
  96. package/plugins/compound-engineering/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
  97. package/plugins/compound-engineering/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
  98. package/plugins/compound-engineering/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
  99. package/plugins/compound-engineering/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
  100. package/plugins/compound-engineering/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
  101. package/plugins/compound-engineering/skills/agent-native-architecture/references/product-implications.md +443 -0
  102. package/plugins/compound-engineering/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
  103. package/plugins/compound-engineering/skills/agent-native-architecture/references/self-modification.md +269 -0
  104. package/plugins/compound-engineering/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
  105. package/plugins/compound-engineering/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
  106. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/SKILL.md +184 -0
  107. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  108. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  109. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  110. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/resources.md +119 -0
  111. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  112. package/plugins/compound-engineering/skills/compound-docs/SKILL.md +510 -0
  113. package/plugins/compound-engineering/skills/compound-docs/assets/critical-pattern-template.md +34 -0
  114. package/plugins/compound-engineering/skills/compound-docs/assets/resolution-template.md +93 -0
  115. package/plugins/compound-engineering/skills/compound-docs/references/yaml-schema.md +65 -0
  116. package/plugins/compound-engineering/skills/compound-docs/schema.yaml +176 -0
  117. package/plugins/compound-engineering/skills/create-agent-skills/SKILL.md +299 -0
  118. package/plugins/compound-engineering/skills/create-agent-skills/references/api-security.md +226 -0
  119. package/plugins/compound-engineering/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
  120. package/plugins/compound-engineering/skills/create-agent-skills/references/best-practices.md +404 -0
  121. package/plugins/compound-engineering/skills/create-agent-skills/references/common-patterns.md +595 -0
  122. package/plugins/compound-engineering/skills/create-agent-skills/references/core-principles.md +437 -0
  123. package/plugins/compound-engineering/skills/create-agent-skills/references/executable-code.md +175 -0
  124. package/plugins/compound-engineering/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
  125. package/plugins/compound-engineering/skills/create-agent-skills/references/official-spec.md +185 -0
  126. package/plugins/compound-engineering/skills/create-agent-skills/references/recommended-structure.md +168 -0
  127. package/plugins/compound-engineering/skills/create-agent-skills/references/skill-structure.md +372 -0
  128. package/plugins/compound-engineering/skills/create-agent-skills/references/using-scripts.md +113 -0
  129. package/plugins/compound-engineering/skills/create-agent-skills/references/using-templates.md +112 -0
  130. package/plugins/compound-engineering/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
  131. package/plugins/compound-engineering/skills/create-agent-skills/templates/router-skill.md +73 -0
  132. package/plugins/compound-engineering/skills/create-agent-skills/templates/simple-skill.md +33 -0
  133. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-reference.md +96 -0
  134. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-script.md +93 -0
  135. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-template.md +74 -0
  136. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-workflow.md +120 -0
  137. package/plugins/compound-engineering/skills/create-agent-skills/workflows/audit-skill.md +138 -0
  138. package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
  139. package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
  140. package/plugins/compound-engineering/skills/create-agent-skills/workflows/get-guidance.md +121 -0
  141. package/plugins/compound-engineering/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
  142. package/plugins/compound-engineering/skills/create-agent-skills/workflows/verify-skill.md +204 -0
  143. package/plugins/compound-engineering/skills/dhh-rails-style/SKILL.md +185 -0
  144. package/plugins/compound-engineering/skills/dhh-rails-style/references/architecture.md +653 -0
  145. package/plugins/compound-engineering/skills/dhh-rails-style/references/controllers.md +303 -0
  146. package/plugins/compound-engineering/skills/dhh-rails-style/references/frontend.md +510 -0
  147. package/plugins/compound-engineering/skills/dhh-rails-style/references/gems.md +266 -0
  148. package/plugins/compound-engineering/skills/dhh-rails-style/references/models.md +359 -0
  149. package/plugins/compound-engineering/skills/dhh-rails-style/references/testing.md +338 -0
  150. package/plugins/compound-engineering/skills/dspy-ruby/SKILL.md +594 -0
  151. package/plugins/compound-engineering/skills/dspy-ruby/assets/config-template.rb +359 -0
  152. package/plugins/compound-engineering/skills/dspy-ruby/assets/module-template.rb +326 -0
  153. package/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb +143 -0
  154. package/plugins/compound-engineering/skills/dspy-ruby/references/core-concepts.md +265 -0
  155. package/plugins/compound-engineering/skills/dspy-ruby/references/optimization.md +623 -0
  156. package/plugins/compound-engineering/skills/dspy-ruby/references/providers.md +338 -0
  157. package/plugins/compound-engineering/skills/every-style-editor/SKILL.md +134 -0
  158. package/plugins/compound-engineering/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  159. package/plugins/compound-engineering/skills/file-todos/SKILL.md +251 -0
  160. package/plugins/compound-engineering/skills/file-todos/assets/todo-template.md +155 -0
  161. package/plugins/compound-engineering/skills/frontend-design/SKILL.md +42 -0
  162. package/plugins/compound-engineering/skills/gemini-imagegen/SKILL.md +237 -0
  163. package/plugins/compound-engineering/skills/gemini-imagegen/requirements.txt +2 -0
  164. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/compose_images.py +157 -0
  165. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/edit_image.py +144 -0
  166. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/gemini_images.py +263 -0
  167. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/generate_image.py +133 -0
  168. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
  169. package/plugins/compound-engineering/skills/git-worktree/SKILL.md +302 -0
  170. package/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh +345 -0
  171. package/plugins/compound-engineering/skills/rclone/SKILL.md +150 -0
  172. package/plugins/compound-engineering/skills/rclone/scripts/check_setup.sh +60 -0
  173. package/plugins/compound-engineering/skills/skill-creator/SKILL.md +209 -0
  174. package/plugins/compound-engineering/skills/skill-creator/scripts/init_skill.py +303 -0
  175. package/plugins/compound-engineering/skills/skill-creator/scripts/package_skill.py +110 -0
  176. package/plugins/compound-engineering/skills/skill-creator/scripts/quick_validate.py +65 -0
  177. package/src/commands/convert.ts +156 -0
  178. package/src/commands/install.ts +221 -0
  179. package/src/commands/list.ts +37 -0
  180. package/src/converters/claude-to-codex.ts +124 -0
  181. package/src/converters/claude-to-opencode.ts +392 -0
  182. package/src/index.ts +20 -0
  183. package/src/parsers/claude.ts +248 -0
  184. package/src/targets/codex.ts +91 -0
  185. package/src/targets/index.ts +29 -0
  186. package/src/targets/opencode.ts +48 -0
  187. package/src/types/claude.ts +88 -0
  188. package/src/types/codex.ts +23 -0
  189. package/src/types/opencode.ts +54 -0
  190. package/src/utils/codex-agents.ts +64 -0
  191. package/src/utils/files.ts +64 -0
  192. package/src/utils/frontmatter.ts +65 -0
  193. package/tests/claude-parser.test.ts +89 -0
  194. package/tests/cli.test.ts +289 -0
  195. package/tests/codex-agents.test.ts +62 -0
  196. package/tests/codex-converter.test.ts +121 -0
  197. package/tests/codex-writer.test.ts +76 -0
  198. package/tests/converter.test.ts +171 -0
  199. package/tests/fixtures/custom-paths/.claude-plugin/plugin.json +8 -0
  200. package/tests/fixtures/custom-paths/agents/default-agent.md +5 -0
  201. package/tests/fixtures/custom-paths/commands/default-command.md +5 -0
  202. package/tests/fixtures/custom-paths/custom-agents/custom-agent.md +5 -0
  203. package/tests/fixtures/custom-paths/custom-commands/custom-command.md +5 -0
  204. package/tests/fixtures/custom-paths/custom-hooks/hooks.json +7 -0
  205. package/tests/fixtures/custom-paths/custom-skills/custom-skill/SKILL.md +5 -0
  206. package/tests/fixtures/custom-paths/hooks/hooks.json +7 -0
  207. package/tests/fixtures/custom-paths/skills/default-skill/SKILL.md +5 -0
  208. package/tests/fixtures/invalid-command-path/.claude-plugin/plugin.json +5 -0
  209. package/tests/fixtures/invalid-hooks-path/.claude-plugin/plugin.json +5 -0
  210. package/tests/fixtures/invalid-mcp-path/.claude-plugin/plugin.json +5 -0
  211. package/tests/fixtures/mcp-file/.claude-plugin/plugin.json +5 -0
  212. package/tests/fixtures/mcp-file/.mcp.json +6 -0
  213. package/tests/fixtures/sample-plugin/.claude-plugin/plugin.json +30 -0
  214. package/tests/fixtures/sample-plugin/agents/agent-one.md +10 -0
  215. package/tests/fixtures/sample-plugin/agents/security-reviewer.md +7 -0
  216. package/tests/fixtures/sample-plugin/commands/command-one.md +7 -0
  217. package/tests/fixtures/sample-plugin/commands/model-command.md +8 -0
  218. package/tests/fixtures/sample-plugin/commands/nested/command-two.md +9 -0
  219. package/tests/fixtures/sample-plugin/commands/pattern-command.md +7 -0
  220. package/tests/fixtures/sample-plugin/commands/skill-command.md +7 -0
  221. package/tests/fixtures/sample-plugin/commands/todo-command.md +7 -0
  222. package/tests/fixtures/sample-plugin/hooks/hooks.json +156 -0
  223. package/tests/fixtures/sample-plugin/skills/skill-one/SKILL.md +6 -0
  224. package/tests/frontmatter.test.ts +20 -0
  225. package/tests/opencode-writer.test.ts +62 -0
  226. 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
+ }