@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,289 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { promises as fs } from "fs"
3
+ import path from "path"
4
+ import os from "os"
5
+
6
+ async function exists(filePath: string): Promise<boolean> {
7
+ try {
8
+ await fs.access(filePath)
9
+ return true
10
+ } catch {
11
+ return false
12
+ }
13
+ }
14
+
15
+ async function runGit(args: string[], cwd: string, env?: NodeJS.ProcessEnv): Promise<void> {
16
+ const proc = Bun.spawn(["git", ...args], {
17
+ cwd,
18
+ stdout: "pipe",
19
+ stderr: "pipe",
20
+ env: env ?? process.env,
21
+ })
22
+ const exitCode = await proc.exited
23
+ const stderr = await new Response(proc.stderr).text()
24
+ if (exitCode !== 0) {
25
+ throw new Error(`git ${args.join(" ")} failed (exit ${exitCode}).\nstderr: ${stderr}`)
26
+ }
27
+ }
28
+
29
+ describe("CLI", () => {
30
+ test("install converts fixture plugin to OpenCode output", async () => {
31
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-opencode-"))
32
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
33
+
34
+ const proc = Bun.spawn([
35
+ "bun",
36
+ "run",
37
+ "src/index.ts",
38
+ "install",
39
+ fixtureRoot,
40
+ "--to",
41
+ "opencode",
42
+ "--output",
43
+ tempRoot,
44
+ ], {
45
+ cwd: path.join(import.meta.dir, ".."),
46
+ stdout: "pipe",
47
+ stderr: "pipe",
48
+ })
49
+
50
+ const exitCode = await proc.exited
51
+ const stdout = await new Response(proc.stdout).text()
52
+ const stderr = await new Response(proc.stderr).text()
53
+
54
+ if (exitCode !== 0) {
55
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
56
+ }
57
+
58
+ expect(stdout).toContain("Installed compound-engineering")
59
+ expect(await exists(path.join(tempRoot, "opencode.json"))).toBe(true)
60
+ expect(await exists(path.join(tempRoot, ".opencode", "agents", "repo-research-analyst.md"))).toBe(true)
61
+ expect(await exists(path.join(tempRoot, ".opencode", "agents", "security-sentinel.md"))).toBe(true)
62
+ expect(await exists(path.join(tempRoot, ".opencode", "skills", "skill-one", "SKILL.md"))).toBe(true)
63
+ expect(await exists(path.join(tempRoot, ".opencode", "plugins", "converted-hooks.ts"))).toBe(true)
64
+ })
65
+
66
+ test("install defaults output to ~/.opencode", async () => {
67
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-local-default-"))
68
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
69
+
70
+ const repoRoot = path.join(import.meta.dir, "..")
71
+ const proc = Bun.spawn([
72
+ "bun",
73
+ "run",
74
+ path.join(repoRoot, "src", "index.ts"),
75
+ "install",
76
+ fixtureRoot,
77
+ "--to",
78
+ "opencode",
79
+ ], {
80
+ cwd: tempRoot,
81
+ stdout: "pipe",
82
+ stderr: "pipe",
83
+ env: {
84
+ ...process.env,
85
+ HOME: tempRoot,
86
+ },
87
+ })
88
+
89
+ const exitCode = await proc.exited
90
+ const stdout = await new Response(proc.stdout).text()
91
+ const stderr = await new Response(proc.stderr).text()
92
+
93
+ if (exitCode !== 0) {
94
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
95
+ }
96
+
97
+ expect(stdout).toContain("Installed compound-engineering")
98
+ expect(await exists(path.join(tempRoot, ".opencode", "opencode.json"))).toBe(true)
99
+ expect(await exists(path.join(tempRoot, ".opencode", "agents", "repo-research-analyst.md"))).toBe(true)
100
+ })
101
+
102
+ test("list returns plugins in a temp workspace", async () => {
103
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-list-"))
104
+ const pluginsRoot = path.join(tempRoot, "plugins", "demo-plugin", ".claude-plugin")
105
+ await fs.mkdir(pluginsRoot, { recursive: true })
106
+ await fs.writeFile(path.join(pluginsRoot, "plugin.json"), "{\n \"name\": \"demo-plugin\",\n \"version\": \"1.0.0\"\n}\n")
107
+
108
+ const repoRoot = path.join(import.meta.dir, "..")
109
+ const proc = Bun.spawn(["bun", "run", path.join(repoRoot, "src", "index.ts"), "list"], {
110
+ cwd: tempRoot,
111
+ stdout: "pipe",
112
+ stderr: "pipe",
113
+ })
114
+
115
+ const exitCode = await proc.exited
116
+ const stdout = await new Response(proc.stdout).text()
117
+ const stderr = await new Response(proc.stderr).text()
118
+
119
+ if (exitCode !== 0) {
120
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
121
+ }
122
+
123
+ expect(stdout).toContain("demo-plugin")
124
+ })
125
+
126
+ test("install pulls from GitHub when local path is missing", async () => {
127
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-github-install-"))
128
+ const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-github-workspace-"))
129
+ const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-github-repo-"))
130
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
131
+ const pluginRoot = path.join(repoRoot, "plugins", "compound-engineering")
132
+
133
+ await fs.mkdir(path.dirname(pluginRoot), { recursive: true })
134
+ await fs.cp(fixtureRoot, pluginRoot, { recursive: true })
135
+
136
+ const gitEnv = {
137
+ ...process.env,
138
+ GIT_AUTHOR_NAME: "Test",
139
+ GIT_AUTHOR_EMAIL: "test@example.com",
140
+ GIT_COMMITTER_NAME: "Test",
141
+ GIT_COMMITTER_EMAIL: "test@example.com",
142
+ }
143
+
144
+ await runGit(["init"], repoRoot, gitEnv)
145
+ await runGit(["add", "."], repoRoot, gitEnv)
146
+ await runGit(["commit", "-m", "fixture"], repoRoot, gitEnv)
147
+
148
+ const projectRoot = path.join(import.meta.dir, "..")
149
+ const proc = Bun.spawn([
150
+ "bun",
151
+ "run",
152
+ path.join(projectRoot, "src", "index.ts"),
153
+ "install",
154
+ "compound-engineering",
155
+ "--to",
156
+ "opencode",
157
+ ], {
158
+ cwd: workspaceRoot,
159
+ stdout: "pipe",
160
+ stderr: "pipe",
161
+ env: {
162
+ ...process.env,
163
+ HOME: tempRoot,
164
+ COMPOUND_PLUGIN_GITHUB_SOURCE: repoRoot,
165
+ },
166
+ })
167
+
168
+ const exitCode = await proc.exited
169
+ const stdout = await new Response(proc.stdout).text()
170
+ const stderr = await new Response(proc.stderr).text()
171
+
172
+ if (exitCode !== 0) {
173
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
174
+ }
175
+
176
+ expect(stdout).toContain("Installed compound-engineering")
177
+ expect(await exists(path.join(tempRoot, ".opencode", "opencode.json"))).toBe(true)
178
+ expect(await exists(path.join(tempRoot, ".opencode", "agents", "repo-research-analyst.md"))).toBe(true)
179
+ })
180
+
181
+ test("convert writes OpenCode output", async () => {
182
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-convert-"))
183
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
184
+
185
+ const proc = Bun.spawn([
186
+ "bun",
187
+ "run",
188
+ "src/index.ts",
189
+ "convert",
190
+ fixtureRoot,
191
+ "--to",
192
+ "opencode",
193
+ "--output",
194
+ tempRoot,
195
+ ], {
196
+ cwd: path.join(import.meta.dir, ".."),
197
+ stdout: "pipe",
198
+ stderr: "pipe",
199
+ })
200
+
201
+ const exitCode = await proc.exited
202
+ const stdout = await new Response(proc.stdout).text()
203
+ const stderr = await new Response(proc.stderr).text()
204
+
205
+ if (exitCode !== 0) {
206
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
207
+ }
208
+
209
+ expect(stdout).toContain("Converted compound-engineering")
210
+ expect(await exists(path.join(tempRoot, "opencode.json"))).toBe(true)
211
+ })
212
+
213
+ test("convert supports --codex-home for codex output", async () => {
214
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-codex-home-"))
215
+ const codexRoot = path.join(tempRoot, ".codex")
216
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
217
+
218
+ const proc = Bun.spawn([
219
+ "bun",
220
+ "run",
221
+ "src/index.ts",
222
+ "convert",
223
+ fixtureRoot,
224
+ "--to",
225
+ "codex",
226
+ "--codex-home",
227
+ codexRoot,
228
+ ], {
229
+ cwd: path.join(import.meta.dir, ".."),
230
+ stdout: "pipe",
231
+ stderr: "pipe",
232
+ })
233
+
234
+ const exitCode = await proc.exited
235
+ const stdout = await new Response(proc.stdout).text()
236
+ const stderr = await new Response(proc.stderr).text()
237
+
238
+ if (exitCode !== 0) {
239
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
240
+ }
241
+
242
+ expect(stdout).toContain("Converted compound-engineering")
243
+ expect(stdout).toContain(codexRoot)
244
+ expect(await exists(path.join(codexRoot, "prompts", "workflows-review.md"))).toBe(true)
245
+ expect(await exists(path.join(codexRoot, "skills", "workflows-review", "SKILL.md"))).toBe(true)
246
+ expect(await exists(path.join(codexRoot, "AGENTS.md"))).toBe(true)
247
+ })
248
+
249
+ test("install supports --also with codex output", async () => {
250
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "cli-also-"))
251
+ const fixtureRoot = path.join(import.meta.dir, "fixtures", "sample-plugin")
252
+ const codexRoot = path.join(tempRoot, ".codex")
253
+
254
+ const proc = Bun.spawn([
255
+ "bun",
256
+ "run",
257
+ "src/index.ts",
258
+ "install",
259
+ fixtureRoot,
260
+ "--to",
261
+ "opencode",
262
+ "--also",
263
+ "codex",
264
+ "--codex-home",
265
+ codexRoot,
266
+ "--output",
267
+ tempRoot,
268
+ ], {
269
+ cwd: path.join(import.meta.dir, ".."),
270
+ stdout: "pipe",
271
+ stderr: "pipe",
272
+ })
273
+
274
+ const exitCode = await proc.exited
275
+ const stdout = await new Response(proc.stdout).text()
276
+ const stderr = await new Response(proc.stderr).text()
277
+
278
+ if (exitCode !== 0) {
279
+ throw new Error(`CLI failed (exit ${exitCode}).\nstdout: ${stdout}\nstderr: ${stderr}`)
280
+ }
281
+
282
+ expect(stdout).toContain("Installed compound-engineering")
283
+ expect(stdout).toContain(codexRoot)
284
+ expect(await exists(path.join(codexRoot, "prompts", "workflows-review.md"))).toBe(true)
285
+ expect(await exists(path.join(codexRoot, "skills", "workflows-review", "SKILL.md"))).toBe(true)
286
+ expect(await exists(path.join(codexRoot, "skills", "skill-one", "SKILL.md"))).toBe(true)
287
+ expect(await exists(path.join(codexRoot, "AGENTS.md"))).toBe(true)
288
+ })
289
+ })
@@ -0,0 +1,62 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { promises as fs } from "fs"
3
+ import path from "path"
4
+ import os from "os"
5
+ import {
6
+ CODEX_AGENTS_BLOCK_END,
7
+ CODEX_AGENTS_BLOCK_START,
8
+ ensureCodexAgentsFile,
9
+ } from "../src/utils/codex-agents"
10
+
11
+ async function readFile(filePath: string): Promise<string> {
12
+ return fs.readFile(filePath, "utf8")
13
+ }
14
+
15
+ describe("ensureCodexAgentsFile", () => {
16
+ test("creates AGENTS.md with managed block when missing", async () => {
17
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-agents-"))
18
+ await ensureCodexAgentsFile(tempRoot)
19
+
20
+ const agentsPath = path.join(tempRoot, "AGENTS.md")
21
+ const content = await readFile(agentsPath)
22
+ expect(content).toContain(CODEX_AGENTS_BLOCK_START)
23
+ expect(content).toContain("Tool mapping")
24
+ expect(content).toContain(CODEX_AGENTS_BLOCK_END)
25
+ })
26
+
27
+ test("appends block without touching existing content", async () => {
28
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-agents-existing-"))
29
+ const agentsPath = path.join(tempRoot, "AGENTS.md")
30
+ await fs.writeFile(agentsPath, "# My Rules\n\nKeep this.")
31
+
32
+ await ensureCodexAgentsFile(tempRoot)
33
+
34
+ const content = await readFile(agentsPath)
35
+ expect(content).toContain("# My Rules")
36
+ expect(content).toContain("Keep this.")
37
+ expect(content).toContain(CODEX_AGENTS_BLOCK_START)
38
+ expect(content).toContain(CODEX_AGENTS_BLOCK_END)
39
+ })
40
+
41
+ test("replaces only the managed block when present", async () => {
42
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-agents-update-"))
43
+ const agentsPath = path.join(tempRoot, "AGENTS.md")
44
+ const seed = [
45
+ "Intro text",
46
+ CODEX_AGENTS_BLOCK_START,
47
+ "old content",
48
+ CODEX_AGENTS_BLOCK_END,
49
+ "Footer text",
50
+ ].join("\n")
51
+ await fs.writeFile(agentsPath, seed)
52
+
53
+ await ensureCodexAgentsFile(tempRoot)
54
+
55
+ const content = await readFile(agentsPath)
56
+ expect(content).toContain("Intro text")
57
+ expect(content).toContain("Footer text")
58
+ expect(content).not.toContain("old content")
59
+ expect(content).toContain(CODEX_AGENTS_BLOCK_START)
60
+ expect(content).toContain(CODEX_AGENTS_BLOCK_END)
61
+ })
62
+ })
@@ -0,0 +1,121 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { convertClaudeToCodex } from "../src/converters/claude-to-codex"
3
+ import { parseFrontmatter } from "../src/utils/frontmatter"
4
+ import type { ClaudePlugin } from "../src/types/claude"
5
+
6
+ const fixturePlugin: ClaudePlugin = {
7
+ root: "/tmp/plugin",
8
+ manifest: { name: "fixture", version: "1.0.0" },
9
+ agents: [
10
+ {
11
+ name: "Security Reviewer",
12
+ description: "Security-focused agent",
13
+ capabilities: ["Threat modeling", "OWASP"],
14
+ model: "claude-sonnet-4-20250514",
15
+ body: "Focus on vulnerabilities.",
16
+ sourcePath: "/tmp/plugin/agents/security-reviewer.md",
17
+ },
18
+ ],
19
+ commands: [
20
+ {
21
+ name: "workflows:plan",
22
+ description: "Planning command",
23
+ argumentHint: "[FOCUS]",
24
+ model: "inherit",
25
+ allowedTools: ["Read"],
26
+ body: "Plan the work.",
27
+ sourcePath: "/tmp/plugin/commands/workflows/plan.md",
28
+ },
29
+ ],
30
+ skills: [
31
+ {
32
+ name: "existing-skill",
33
+ description: "Existing skill",
34
+ sourceDir: "/tmp/plugin/skills/existing-skill",
35
+ skillPath: "/tmp/plugin/skills/existing-skill/SKILL.md",
36
+ },
37
+ ],
38
+ hooks: undefined,
39
+ mcpServers: {
40
+ local: { command: "echo", args: ["hello"] },
41
+ },
42
+ }
43
+
44
+ describe("convertClaudeToCodex", () => {
45
+ test("converts commands to prompts and agents to skills", () => {
46
+ const bundle = convertClaudeToCodex(fixturePlugin, {
47
+ agentMode: "subagent",
48
+ inferTemperature: false,
49
+ permissions: "none",
50
+ })
51
+
52
+ expect(bundle.prompts).toHaveLength(1)
53
+ const prompt = bundle.prompts[0]
54
+ expect(prompt.name).toBe("workflows-plan")
55
+
56
+ const parsedPrompt = parseFrontmatter(prompt.content)
57
+ expect(parsedPrompt.data.description).toBe("Planning command")
58
+ expect(parsedPrompt.data["argument-hint"]).toBe("[FOCUS]")
59
+ expect(parsedPrompt.body).toContain("$workflows-plan")
60
+ expect(parsedPrompt.body).toContain("Plan the work.")
61
+
62
+ expect(bundle.skillDirs[0]?.name).toBe("existing-skill")
63
+ expect(bundle.generatedSkills).toHaveLength(2)
64
+
65
+ const commandSkill = bundle.generatedSkills.find((skill) => skill.name === "workflows-plan")
66
+ expect(commandSkill).toBeDefined()
67
+ const parsedCommandSkill = parseFrontmatter(commandSkill!.content)
68
+ expect(parsedCommandSkill.data.name).toBe("workflows-plan")
69
+ expect(parsedCommandSkill.data.description).toBe("Planning command")
70
+ expect(parsedCommandSkill.body).toContain("Allowed tools")
71
+
72
+ const agentSkill = bundle.generatedSkills.find((skill) => skill.name === "security-reviewer")
73
+ expect(agentSkill).toBeDefined()
74
+ const parsedSkill = parseFrontmatter(agentSkill!.content)
75
+ expect(parsedSkill.data.name).toBe("security-reviewer")
76
+ expect(parsedSkill.data.description).toBe("Security-focused agent")
77
+ expect(parsedSkill.body).toContain("Capabilities")
78
+ expect(parsedSkill.body).toContain("Threat modeling")
79
+ })
80
+
81
+ test("passes through MCP servers", () => {
82
+ const bundle = convertClaudeToCodex(fixturePlugin, {
83
+ agentMode: "subagent",
84
+ inferTemperature: false,
85
+ permissions: "none",
86
+ })
87
+
88
+ expect(bundle.mcpServers?.local?.command).toBe("echo")
89
+ expect(bundle.mcpServers?.local?.args).toEqual(["hello"])
90
+ })
91
+
92
+ test("truncates generated skill descriptions to Codex limits and single line", () => {
93
+ const longDescription = `Line one\nLine two ${"a".repeat(2000)}`
94
+ const plugin: ClaudePlugin = {
95
+ ...fixturePlugin,
96
+ agents: [
97
+ {
98
+ name: "Long Description Agent",
99
+ description: longDescription,
100
+ body: "Body",
101
+ sourcePath: "/tmp/plugin/agents/long.md",
102
+ },
103
+ ],
104
+ commands: [],
105
+ skills: [],
106
+ }
107
+
108
+ const bundle = convertClaudeToCodex(plugin, {
109
+ agentMode: "subagent",
110
+ inferTemperature: false,
111
+ permissions: "none",
112
+ })
113
+
114
+ const generated = bundle.generatedSkills[0]
115
+ const parsed = parseFrontmatter(generated.content)
116
+ const description = String(parsed.data.description ?? "")
117
+ expect(description.length).toBeLessThanOrEqual(1024)
118
+ expect(description).not.toContain("\n")
119
+ expect(description.endsWith("...")).toBe(true)
120
+ })
121
+ })
@@ -0,0 +1,76 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import { promises as fs } from "fs"
3
+ import path from "path"
4
+ import os from "os"
5
+ import { writeCodexBundle } from "../src/targets/codex"
6
+ import type { CodexBundle } from "../src/types/codex"
7
+
8
+ async function exists(filePath: string): Promise<boolean> {
9
+ try {
10
+ await fs.access(filePath)
11
+ return true
12
+ } catch {
13
+ return false
14
+ }
15
+ }
16
+
17
+ describe("writeCodexBundle", () => {
18
+ test("writes prompts, skills, and config", async () => {
19
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-test-"))
20
+ const bundle: CodexBundle = {
21
+ prompts: [{ name: "command-one", content: "Prompt content" }],
22
+ skillDirs: [
23
+ {
24
+ name: "skill-one",
25
+ sourceDir: path.join(import.meta.dir, "fixtures", "sample-plugin", "skills", "skill-one"),
26
+ },
27
+ ],
28
+ generatedSkills: [{ name: "agent-skill", content: "Skill content" }],
29
+ mcpServers: {
30
+ local: { command: "echo", args: ["hello"], env: { KEY: "VALUE" } },
31
+ remote: {
32
+ url: "https://example.com/mcp",
33
+ headers: { Authorization: "Bearer token" },
34
+ },
35
+ },
36
+ }
37
+
38
+ await writeCodexBundle(tempRoot, bundle)
39
+
40
+ expect(await exists(path.join(tempRoot, ".codex", "prompts", "command-one.md"))).toBe(true)
41
+ expect(await exists(path.join(tempRoot, ".codex", "skills", "skill-one", "SKILL.md"))).toBe(true)
42
+ expect(await exists(path.join(tempRoot, ".codex", "skills", "agent-skill", "SKILL.md"))).toBe(true)
43
+ const configPath = path.join(tempRoot, ".codex", "config.toml")
44
+ expect(await exists(configPath)).toBe(true)
45
+
46
+ const config = await fs.readFile(configPath, "utf8")
47
+ expect(config).toContain("[mcp_servers.local]")
48
+ expect(config).toContain("command = \"echo\"")
49
+ expect(config).toContain("args = [\"hello\"]")
50
+ expect(config).toContain("[mcp_servers.local.env]")
51
+ expect(config).toContain("KEY = \"VALUE\"")
52
+ expect(config).toContain("[mcp_servers.remote]")
53
+ expect(config).toContain("url = \"https://example.com/mcp\"")
54
+ expect(config).toContain("http_headers")
55
+ })
56
+
57
+ test("writes directly into a .codex output root", async () => {
58
+ const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "codex-home-"))
59
+ const codexRoot = path.join(tempRoot, ".codex")
60
+ const bundle: CodexBundle = {
61
+ prompts: [{ name: "command-one", content: "Prompt content" }],
62
+ skillDirs: [
63
+ {
64
+ name: "skill-one",
65
+ sourceDir: path.join(import.meta.dir, "fixtures", "sample-plugin", "skills", "skill-one"),
66
+ },
67
+ ],
68
+ generatedSkills: [],
69
+ }
70
+
71
+ await writeCodexBundle(codexRoot, bundle)
72
+
73
+ expect(await exists(path.join(codexRoot, "prompts", "command-one.md"))).toBe(true)
74
+ expect(await exists(path.join(codexRoot, "skills", "skill-one", "SKILL.md"))).toBe(true)
75
+ })
76
+ })