compound-engineering-pi 0.2.3

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 (332) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/bin/compound-engineering-pi +12 -0
  4. package/bin/compound-plugin +12 -0
  5. package/compound-engineering-pi +12 -0
  6. package/compound-plugin +5 -0
  7. package/docs/pi.md +152 -0
  8. package/extensions/compound-engineering-compat.ts +452 -0
  9. package/package.json +84 -0
  10. package/pi-resources/compound-engineering/mcporter.json +7 -0
  11. package/plugins/coding-tutor/.claude-plugin/plugin.json +9 -0
  12. package/plugins/coding-tutor/README.md +37 -0
  13. package/plugins/coding-tutor/commands/quiz-me.md +1 -0
  14. package/plugins/coding-tutor/commands/sync-tutorials.md +25 -0
  15. package/plugins/coding-tutor/commands/teach-me.md +1 -0
  16. package/plugins/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
  17. package/plugins/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +207 -0
  18. package/plugins/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +193 -0
  19. package/plugins/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
  20. package/plugins/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +118 -0
  21. package/plugins/compound-engineering/.claude-plugin/plugin.json +33 -0
  22. package/plugins/compound-engineering/CHANGELOG.md +457 -0
  23. package/plugins/compound-engineering/CLAUDE.md +89 -0
  24. package/plugins/compound-engineering/LICENSE +21 -0
  25. package/plugins/compound-engineering/README.md +232 -0
  26. package/plugins/compound-engineering/agents/design/design-implementation-reviewer.md +109 -0
  27. package/plugins/compound-engineering/agents/design/design-iterator.md +224 -0
  28. package/plugins/compound-engineering/agents/design/figma-design-sync.md +190 -0
  29. package/plugins/compound-engineering/agents/docs/ankane-readme-writer.md +65 -0
  30. package/plugins/compound-engineering/agents/research/best-practices-researcher.md +126 -0
  31. package/plugins/compound-engineering/agents/research/framework-docs-researcher.md +106 -0
  32. package/plugins/compound-engineering/agents/research/git-history-analyzer.md +59 -0
  33. package/plugins/compound-engineering/agents/research/learnings-researcher.md +264 -0
  34. package/plugins/compound-engineering/agents/research/repo-research-analyst.md +135 -0
  35. package/plugins/compound-engineering/agents/review/agent-native-reviewer.md +261 -0
  36. package/plugins/compound-engineering/agents/review/architecture-strategist.md +67 -0
  37. package/plugins/compound-engineering/agents/review/code-simplicity-reviewer.md +101 -0
  38. package/plugins/compound-engineering/agents/review/data-integrity-guardian.md +85 -0
  39. package/plugins/compound-engineering/agents/review/data-migration-expert.md +112 -0
  40. package/plugins/compound-engineering/agents/review/deployment-verification-agent.md +174 -0
  41. package/plugins/compound-engineering/agents/review/dhh-rails-reviewer.md +66 -0
  42. package/plugins/compound-engineering/agents/review/julik-frontend-races-reviewer.md +221 -0
  43. package/plugins/compound-engineering/agents/review/kieran-python-reviewer.md +133 -0
  44. package/plugins/compound-engineering/agents/review/kieran-rails-reviewer.md +115 -0
  45. package/plugins/compound-engineering/agents/review/kieran-typescript-reviewer.md +124 -0
  46. package/plugins/compound-engineering/agents/review/pattern-recognition-specialist.md +72 -0
  47. package/plugins/compound-engineering/agents/review/performance-oracle.md +137 -0
  48. package/plugins/compound-engineering/agents/review/schema-drift-detector.md +154 -0
  49. package/plugins/compound-engineering/agents/review/security-sentinel.md +114 -0
  50. package/plugins/compound-engineering/agents/workflow/bug-reproduction-validator.md +82 -0
  51. package/plugins/compound-engineering/agents/workflow/every-style-editor.md +64 -0
  52. package/plugins/compound-engineering/agents/workflow/lint.md +16 -0
  53. package/plugins/compound-engineering/agents/workflow/pr-comment-resolver.md +84 -0
  54. package/plugins/compound-engineering/agents/workflow/spec-flow-analyzer.md +134 -0
  55. package/plugins/compound-engineering/commands/agent-native-audit.md +278 -0
  56. package/plugins/compound-engineering/commands/changelog.md +138 -0
  57. package/plugins/compound-engineering/commands/create-agent-skill.md +9 -0
  58. package/plugins/compound-engineering/commands/deepen-plan.md +546 -0
  59. package/plugins/compound-engineering/commands/deploy-docs.md +113 -0
  60. package/plugins/compound-engineering/commands/feature-video.md +342 -0
  61. package/plugins/compound-engineering/commands/generate_command.md +163 -0
  62. package/plugins/compound-engineering/commands/heal-skill.md +143 -0
  63. package/plugins/compound-engineering/commands/lfg.md +20 -0
  64. package/plugins/compound-engineering/commands/release-docs.md +212 -0
  65. package/plugins/compound-engineering/commands/report-bug.md +151 -0
  66. package/plugins/compound-engineering/commands/reproduce-bug.md +100 -0
  67. package/plugins/compound-engineering/commands/resolve_parallel.md +35 -0
  68. package/plugins/compound-engineering/commands/resolve_todo_parallel.md +37 -0
  69. package/plugins/compound-engineering/commands/slfg.md +32 -0
  70. package/plugins/compound-engineering/commands/technical_review.md +8 -0
  71. package/plugins/compound-engineering/commands/test-browser.md +339 -0
  72. package/plugins/compound-engineering/commands/test-xcode.md +332 -0
  73. package/plugins/compound-engineering/commands/triage.md +311 -0
  74. package/plugins/compound-engineering/commands/workflows/brainstorm.md +124 -0
  75. package/plugins/compound-engineering/commands/workflows/compound.md +239 -0
  76. package/plugins/compound-engineering/commands/workflows/plan.md +551 -0
  77. package/plugins/compound-engineering/commands/workflows/review.md +526 -0
  78. package/plugins/compound-engineering/commands/workflows/work.md +433 -0
  79. package/plugins/compound-engineering/skills/agent-browser/SKILL.md +223 -0
  80. package/plugins/compound-engineering/skills/agent-native-architecture/SKILL.md +435 -0
  81. package/plugins/compound-engineering/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
  82. package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
  83. package/plugins/compound-engineering/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
  84. package/plugins/compound-engineering/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
  85. package/plugins/compound-engineering/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
  86. package/plugins/compound-engineering/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
  87. package/plugins/compound-engineering/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
  88. package/plugins/compound-engineering/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
  89. package/plugins/compound-engineering/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
  90. package/plugins/compound-engineering/skills/agent-native-architecture/references/product-implications.md +443 -0
  91. package/plugins/compound-engineering/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
  92. package/plugins/compound-engineering/skills/agent-native-architecture/references/self-modification.md +269 -0
  93. package/plugins/compound-engineering/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
  94. package/plugins/compound-engineering/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
  95. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/SKILL.md +184 -0
  96. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  97. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  98. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  99. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/resources.md +119 -0
  100. package/plugins/compound-engineering/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  101. package/plugins/compound-engineering/skills/brainstorming/SKILL.md +190 -0
  102. package/plugins/compound-engineering/skills/compound-docs/SKILL.md +511 -0
  103. package/plugins/compound-engineering/skills/compound-docs/assets/critical-pattern-template.md +34 -0
  104. package/plugins/compound-engineering/skills/compound-docs/assets/resolution-template.md +93 -0
  105. package/plugins/compound-engineering/skills/compound-docs/references/yaml-schema.md +65 -0
  106. package/plugins/compound-engineering/skills/compound-docs/schema.yaml +176 -0
  107. package/plugins/compound-engineering/skills/create-agent-skills/SKILL.md +275 -0
  108. package/plugins/compound-engineering/skills/create-agent-skills/references/api-security.md +226 -0
  109. package/plugins/compound-engineering/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
  110. package/plugins/compound-engineering/skills/create-agent-skills/references/best-practices.md +404 -0
  111. package/plugins/compound-engineering/skills/create-agent-skills/references/common-patterns.md +595 -0
  112. package/plugins/compound-engineering/skills/create-agent-skills/references/core-principles.md +437 -0
  113. package/plugins/compound-engineering/skills/create-agent-skills/references/executable-code.md +175 -0
  114. package/plugins/compound-engineering/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
  115. package/plugins/compound-engineering/skills/create-agent-skills/references/official-spec.md +134 -0
  116. package/plugins/compound-engineering/skills/create-agent-skills/references/recommended-structure.md +168 -0
  117. package/plugins/compound-engineering/skills/create-agent-skills/references/skill-structure.md +152 -0
  118. package/plugins/compound-engineering/skills/create-agent-skills/references/using-scripts.md +113 -0
  119. package/plugins/compound-engineering/skills/create-agent-skills/references/using-templates.md +112 -0
  120. package/plugins/compound-engineering/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
  121. package/plugins/compound-engineering/skills/create-agent-skills/templates/router-skill.md +73 -0
  122. package/plugins/compound-engineering/skills/create-agent-skills/templates/simple-skill.md +33 -0
  123. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-reference.md +96 -0
  124. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-script.md +93 -0
  125. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-template.md +74 -0
  126. package/plugins/compound-engineering/skills/create-agent-skills/workflows/add-workflow.md +120 -0
  127. package/plugins/compound-engineering/skills/create-agent-skills/workflows/audit-skill.md +138 -0
  128. package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
  129. package/plugins/compound-engineering/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
  130. package/plugins/compound-engineering/skills/create-agent-skills/workflows/get-guidance.md +121 -0
  131. package/plugins/compound-engineering/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
  132. package/plugins/compound-engineering/skills/create-agent-skills/workflows/verify-skill.md +204 -0
  133. package/plugins/compound-engineering/skills/dhh-rails-style/SKILL.md +185 -0
  134. package/plugins/compound-engineering/skills/dhh-rails-style/references/architecture.md +653 -0
  135. package/plugins/compound-engineering/skills/dhh-rails-style/references/controllers.md +303 -0
  136. package/plugins/compound-engineering/skills/dhh-rails-style/references/frontend.md +510 -0
  137. package/plugins/compound-engineering/skills/dhh-rails-style/references/gems.md +266 -0
  138. package/plugins/compound-engineering/skills/dhh-rails-style/references/models.md +359 -0
  139. package/plugins/compound-engineering/skills/dhh-rails-style/references/testing.md +338 -0
  140. package/plugins/compound-engineering/skills/document-review/SKILL.md +87 -0
  141. package/plugins/compound-engineering/skills/dspy-ruby/SKILL.md +737 -0
  142. package/plugins/compound-engineering/skills/dspy-ruby/assets/config-template.rb +187 -0
  143. package/plugins/compound-engineering/skills/dspy-ruby/assets/module-template.rb +300 -0
  144. package/plugins/compound-engineering/skills/dspy-ruby/assets/signature-template.rb +221 -0
  145. package/plugins/compound-engineering/skills/dspy-ruby/references/core-concepts.md +674 -0
  146. package/plugins/compound-engineering/skills/dspy-ruby/references/observability.md +366 -0
  147. package/plugins/compound-engineering/skills/dspy-ruby/references/optimization.md +603 -0
  148. package/plugins/compound-engineering/skills/dspy-ruby/references/providers.md +418 -0
  149. package/plugins/compound-engineering/skills/dspy-ruby/references/toolsets.md +502 -0
  150. package/plugins/compound-engineering/skills/every-style-editor/SKILL.md +134 -0
  151. package/plugins/compound-engineering/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  152. package/plugins/compound-engineering/skills/file-todos/SKILL.md +252 -0
  153. package/plugins/compound-engineering/skills/file-todos/assets/todo-template.md +155 -0
  154. package/plugins/compound-engineering/skills/frontend-design/SKILL.md +42 -0
  155. package/plugins/compound-engineering/skills/gemini-imagegen/SKILL.md +237 -0
  156. package/plugins/compound-engineering/skills/gemini-imagegen/requirements.txt +2 -0
  157. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/compose_images.py +157 -0
  158. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/edit_image.py +144 -0
  159. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/gemini_images.py +263 -0
  160. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/generate_image.py +133 -0
  161. package/plugins/compound-engineering/skills/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
  162. package/plugins/compound-engineering/skills/git-worktree/SKILL.md +302 -0
  163. package/plugins/compound-engineering/skills/git-worktree/scripts/worktree-manager.sh +337 -0
  164. package/plugins/compound-engineering/skills/orchestrating-swarms/SKILL.md +1718 -0
  165. package/plugins/compound-engineering/skills/rclone/SKILL.md +150 -0
  166. package/plugins/compound-engineering/skills/rclone/scripts/check_setup.sh +60 -0
  167. package/plugins/compound-engineering/skills/resolve-pr-parallel/SKILL.md +89 -0
  168. package/plugins/compound-engineering/skills/resolve-pr-parallel/scripts/get-pr-comments +68 -0
  169. package/plugins/compound-engineering/skills/resolve-pr-parallel/scripts/resolve-pr-thread +23 -0
  170. package/plugins/compound-engineering/skills/skill-creator/SKILL.md +210 -0
  171. package/plugins/compound-engineering/skills/skill-creator/scripts/init_skill.py +303 -0
  172. package/plugins/compound-engineering/skills/skill-creator/scripts/package_skill.py +110 -0
  173. package/plugins/compound-engineering/skills/skill-creator/scripts/quick_validate.py +65 -0
  174. package/prompts/deepen-plan.md +549 -0
  175. package/prompts/feature-video.md +341 -0
  176. package/prompts/resolve_todo_parallel.md +36 -0
  177. package/prompts/test-browser.md +342 -0
  178. package/prompts/workflows-brainstorm.md +123 -0
  179. package/prompts/workflows-compound.md +238 -0
  180. package/prompts/workflows-plan.md +550 -0
  181. package/prompts/workflows-review.md +529 -0
  182. package/prompts/workflows-work.md +432 -0
  183. package/skills/agent-browser/SKILL.md +223 -0
  184. package/skills/agent-native-architecture/SKILL.md +435 -0
  185. package/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
  186. package/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
  187. package/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
  188. package/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
  189. package/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
  190. package/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
  191. package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
  192. package/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
  193. package/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
  194. package/skills/agent-native-architecture/references/product-implications.md +443 -0
  195. package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
  196. package/skills/agent-native-architecture/references/self-modification.md +269 -0
  197. package/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
  198. package/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
  199. package/skills/agent-native-reviewer/SKILL.md +260 -0
  200. package/skills/andrew-kane-gem-writer/SKILL.md +184 -0
  201. package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  202. package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  203. package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  204. package/skills/andrew-kane-gem-writer/references/resources.md +119 -0
  205. package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  206. package/skills/ankane-readme-writer/SKILL.md +63 -0
  207. package/skills/architecture-strategist/SKILL.md +66 -0
  208. package/skills/best-practices-researcher/SKILL.md +125 -0
  209. package/skills/brainstorming/SKILL.md +190 -0
  210. package/skills/bug-reproduction-validator/SKILL.md +81 -0
  211. package/skills/code-simplicity-reviewer/SKILL.md +100 -0
  212. package/skills/compound-docs/SKILL.md +511 -0
  213. package/skills/compound-docs/assets/critical-pattern-template.md +34 -0
  214. package/skills/compound-docs/assets/resolution-template.md +93 -0
  215. package/skills/compound-docs/references/yaml-schema.md +65 -0
  216. package/skills/compound-docs/schema.yaml +176 -0
  217. package/skills/create-agent-skills/SKILL.md +275 -0
  218. package/skills/create-agent-skills/references/api-security.md +226 -0
  219. package/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
  220. package/skills/create-agent-skills/references/best-practices.md +404 -0
  221. package/skills/create-agent-skills/references/common-patterns.md +595 -0
  222. package/skills/create-agent-skills/references/core-principles.md +437 -0
  223. package/skills/create-agent-skills/references/executable-code.md +175 -0
  224. package/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
  225. package/skills/create-agent-skills/references/official-spec.md +134 -0
  226. package/skills/create-agent-skills/references/recommended-structure.md +168 -0
  227. package/skills/create-agent-skills/references/skill-structure.md +152 -0
  228. package/skills/create-agent-skills/references/using-scripts.md +113 -0
  229. package/skills/create-agent-skills/references/using-templates.md +112 -0
  230. package/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
  231. package/skills/create-agent-skills/templates/router-skill.md +73 -0
  232. package/skills/create-agent-skills/templates/simple-skill.md +33 -0
  233. package/skills/create-agent-skills/workflows/add-reference.md +96 -0
  234. package/skills/create-agent-skills/workflows/add-script.md +93 -0
  235. package/skills/create-agent-skills/workflows/add-template.md +74 -0
  236. package/skills/create-agent-skills/workflows/add-workflow.md +120 -0
  237. package/skills/create-agent-skills/workflows/audit-skill.md +138 -0
  238. package/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
  239. package/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
  240. package/skills/create-agent-skills/workflows/get-guidance.md +121 -0
  241. package/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
  242. package/skills/create-agent-skills/workflows/verify-skill.md +204 -0
  243. package/skills/data-integrity-guardian/SKILL.md +84 -0
  244. package/skills/data-migration-expert/SKILL.md +111 -0
  245. package/skills/deployment-verification-agent/SKILL.md +173 -0
  246. package/skills/design-implementation-reviewer/SKILL.md +107 -0
  247. package/skills/design-iterator/SKILL.md +222 -0
  248. package/skills/dhh-rails-reviewer/SKILL.md +65 -0
  249. package/skills/dhh-rails-style/SKILL.md +185 -0
  250. package/skills/dhh-rails-style/references/architecture.md +653 -0
  251. package/skills/dhh-rails-style/references/controllers.md +303 -0
  252. package/skills/dhh-rails-style/references/frontend.md +510 -0
  253. package/skills/dhh-rails-style/references/gems.md +266 -0
  254. package/skills/dhh-rails-style/references/models.md +359 -0
  255. package/skills/dhh-rails-style/references/testing.md +338 -0
  256. package/skills/document-review/SKILL.md +87 -0
  257. package/skills/dspy-ruby/SKILL.md +737 -0
  258. package/skills/dspy-ruby/assets/config-template.rb +187 -0
  259. package/skills/dspy-ruby/assets/module-template.rb +300 -0
  260. package/skills/dspy-ruby/assets/signature-template.rb +221 -0
  261. package/skills/dspy-ruby/references/core-concepts.md +674 -0
  262. package/skills/dspy-ruby/references/observability.md +366 -0
  263. package/skills/dspy-ruby/references/optimization.md +603 -0
  264. package/skills/dspy-ruby/references/providers.md +418 -0
  265. package/skills/dspy-ruby/references/toolsets.md +502 -0
  266. package/skills/every-style-editor/SKILL.md +134 -0
  267. package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  268. package/skills/every-style-editor-2/SKILL.md +62 -0
  269. package/skills/figma-design-sync/SKILL.md +188 -0
  270. package/skills/file-todos/SKILL.md +252 -0
  271. package/skills/file-todos/assets/todo-template.md +155 -0
  272. package/skills/framework-docs-researcher/SKILL.md +105 -0
  273. package/skills/frontend-design/SKILL.md +42 -0
  274. package/skills/gemini-imagegen/SKILL.md +237 -0
  275. package/skills/gemini-imagegen/requirements.txt +2 -0
  276. package/skills/gemini-imagegen/scripts/compose_images.py +157 -0
  277. package/skills/gemini-imagegen/scripts/edit_image.py +144 -0
  278. package/skills/gemini-imagegen/scripts/gemini_images.py +263 -0
  279. package/skills/gemini-imagegen/scripts/generate_image.py +133 -0
  280. package/skills/gemini-imagegen/scripts/multi_turn_chat.py +216 -0
  281. package/skills/git-history-analyzer/SKILL.md +58 -0
  282. package/skills/git-worktree/SKILL.md +302 -0
  283. package/skills/git-worktree/scripts/worktree-manager.sh +337 -0
  284. package/skills/julik-frontend-races-reviewer/SKILL.md +220 -0
  285. package/skills/kieran-python-reviewer/SKILL.md +132 -0
  286. package/skills/kieran-rails-reviewer/SKILL.md +114 -0
  287. package/skills/kieran-typescript-reviewer/SKILL.md +123 -0
  288. package/skills/learnings-researcher/SKILL.md +263 -0
  289. package/skills/lint/SKILL.md +14 -0
  290. package/skills/orchestrating-swarms/SKILL.md +1718 -0
  291. package/skills/pattern-recognition-specialist/SKILL.md +71 -0
  292. package/skills/performance-oracle/SKILL.md +136 -0
  293. package/skills/pr-comment-resolver/SKILL.md +82 -0
  294. package/skills/rclone/SKILL.md +150 -0
  295. package/skills/rclone/scripts/check_setup.sh +60 -0
  296. package/skills/repo-research-analyst/SKILL.md +134 -0
  297. package/skills/resolve_pr_parallel/SKILL.md +89 -0
  298. package/skills/resolve_pr_parallel/scripts/get-pr-comments +68 -0
  299. package/skills/resolve_pr_parallel/scripts/resolve-pr-thread +23 -0
  300. package/skills/schema-drift-detector/SKILL.md +153 -0
  301. package/skills/security-sentinel/SKILL.md +113 -0
  302. package/skills/skill-creator/SKILL.md +210 -0
  303. package/skills/skill-creator/scripts/init_skill.py +303 -0
  304. package/skills/skill-creator/scripts/package_skill.py +110 -0
  305. package/skills/skill-creator/scripts/quick_validate.py +65 -0
  306. package/skills/spec-flow-analyzer/SKILL.md +133 -0
  307. package/src/commands/convert.ts +183 -0
  308. package/src/commands/install.ts +273 -0
  309. package/src/commands/list.ts +37 -0
  310. package/src/commands/sync.ts +89 -0
  311. package/src/converters/claude-to-codex.ts +182 -0
  312. package/src/converters/claude-to-opencode.ts +395 -0
  313. package/src/converters/claude-to-pi.ts +205 -0
  314. package/src/index.ts +22 -0
  315. package/src/parsers/claude-home.ts +65 -0
  316. package/src/parsers/claude.ts +252 -0
  317. package/src/sync/codex.ts +92 -0
  318. package/src/sync/opencode.ts +75 -0
  319. package/src/sync/pi.ts +88 -0
  320. package/src/targets/codex.ts +96 -0
  321. package/src/targets/index.ts +38 -0
  322. package/src/targets/opencode.ts +57 -0
  323. package/src/targets/pi.ts +131 -0
  324. package/src/templates/pi/compat-extension.ts +452 -0
  325. package/src/types/claude.ts +90 -0
  326. package/src/types/codex.ts +23 -0
  327. package/src/types/opencode.ts +54 -0
  328. package/src/types/pi.ts +40 -0
  329. package/src/utils/codex-agents.ts +64 -0
  330. package/src/utils/files.ts +77 -0
  331. package/src/utils/frontmatter.ts +65 -0
  332. package/src/utils/symlink.ts +43 -0
@@ -0,0 +1,205 @@
1
+ import { formatFrontmatter } from "../utils/frontmatter"
2
+ import type { ClaudeAgent, ClaudeCommand, ClaudeMcpServer, ClaudePlugin } from "../types/claude"
3
+ import type {
4
+ PiBundle,
5
+ PiGeneratedSkill,
6
+ PiMcporterConfig,
7
+ PiMcporterServer,
8
+ } from "../types/pi"
9
+ import type { ClaudeToOpenCodeOptions } from "./claude-to-opencode"
10
+ import { PI_COMPAT_EXTENSION_SOURCE } from "../templates/pi/compat-extension"
11
+
12
+ export type ClaudeToPiOptions = ClaudeToOpenCodeOptions
13
+
14
+ const PI_DESCRIPTION_MAX_LENGTH = 1024
15
+
16
+ export function convertClaudeToPi(
17
+ plugin: ClaudePlugin,
18
+ _options: ClaudeToPiOptions,
19
+ ): PiBundle {
20
+ const promptNames = new Set<string>()
21
+ const usedSkillNames = new Set<string>(plugin.skills.map((skill) => normalizeName(skill.name)))
22
+
23
+ const prompts = plugin.commands
24
+ .filter((command) => !command.disableModelInvocation)
25
+ .map((command) => convertPrompt(command, promptNames))
26
+
27
+ const generatedSkills = plugin.agents.map((agent) => convertAgent(agent, usedSkillNames))
28
+
29
+ const extensions = [
30
+ {
31
+ name: "compound-engineering-compat.ts",
32
+ content: PI_COMPAT_EXTENSION_SOURCE,
33
+ },
34
+ ]
35
+
36
+ return {
37
+ prompts,
38
+ skillDirs: plugin.skills.map((skill) => ({
39
+ name: skill.name,
40
+ sourceDir: skill.sourceDir,
41
+ })),
42
+ generatedSkills,
43
+ extensions,
44
+ mcporterConfig: plugin.mcpServers ? convertMcpToMcporter(plugin.mcpServers) : undefined,
45
+ }
46
+ }
47
+
48
+ function convertPrompt(command: ClaudeCommand, usedNames: Set<string>) {
49
+ const name = uniqueName(normalizeName(command.name), usedNames)
50
+ const frontmatter: Record<string, unknown> = {
51
+ description: command.description,
52
+ "argument-hint": command.argumentHint,
53
+ }
54
+
55
+ let body = transformContentForPi(command.body)
56
+ body = appendCompatibilityNoteIfNeeded(body)
57
+
58
+ return {
59
+ name,
60
+ content: formatFrontmatter(frontmatter, body.trim()),
61
+ }
62
+ }
63
+
64
+ function convertAgent(agent: ClaudeAgent, usedNames: Set<string>): PiGeneratedSkill {
65
+ const name = uniqueName(normalizeName(agent.name), usedNames)
66
+ const description = sanitizeDescription(
67
+ agent.description ?? `Converted from Claude agent ${agent.name}`,
68
+ )
69
+
70
+ const frontmatter: Record<string, unknown> = {
71
+ name,
72
+ description,
73
+ }
74
+
75
+ const sections: string[] = []
76
+ if (agent.capabilities && agent.capabilities.length > 0) {
77
+ sections.push(`## Capabilities\n${agent.capabilities.map((capability) => `- ${capability}`).join("\n")}`)
78
+ }
79
+
80
+ const body = [
81
+ ...sections,
82
+ agent.body.trim().length > 0
83
+ ? agent.body.trim()
84
+ : `Instructions converted from the ${agent.name} agent.`,
85
+ ].join("\n\n")
86
+
87
+ return {
88
+ name,
89
+ content: formatFrontmatter(frontmatter, body),
90
+ }
91
+ }
92
+
93
+ function transformContentForPi(body: string): string {
94
+ let result = body
95
+
96
+ // Task repo-research-analyst(feature_description)
97
+ // -> Run subagent with agent="repo-research-analyst" and task="feature_description"
98
+ const taskPattern = /^(\s*-?\s*)Task\s+([a-z][a-z0-9-]*)\(([^)]+)\)/gm
99
+ result = result.replace(taskPattern, (_match, prefix: string, agentName: string, args: string) => {
100
+ const skillName = normalizeName(agentName)
101
+ const trimmedArgs = args.trim().replace(/\s+/g, " ")
102
+ return `${prefix}Run subagent with agent=\"${skillName}\" and task=\"${trimmedArgs}\".`
103
+ })
104
+
105
+ // Claude-specific tool references
106
+ result = result.replace(/\bAskUserQuestion\b/g, "ask_user_question")
107
+ result = result.replace(/\bTodoWrite\b/g, "file-based todos (todos/ + /skill:file-todos)")
108
+ result = result.replace(/\bTodoRead\b/g, "file-based todos (todos/ + /skill:file-todos)")
109
+
110
+ // /command-name or /workflows:command-name -> /workflows-command-name
111
+ const slashCommandPattern = /(?<![:\w])\/([a-z][a-z0-9_:-]*?)(?=[\s,."')\]}`]|$)/gi
112
+ result = result.replace(slashCommandPattern, (match, commandName: string) => {
113
+ if (commandName.includes("/")) return match
114
+ if (["dev", "tmp", "etc", "usr", "var", "bin", "home"].includes(commandName)) {
115
+ return match
116
+ }
117
+
118
+ if (commandName.startsWith("skill:")) {
119
+ const skillName = commandName.slice("skill:".length)
120
+ return `/skill:${normalizeName(skillName)}`
121
+ }
122
+
123
+ const withoutPrefix = commandName.startsWith("prompts:")
124
+ ? commandName.slice("prompts:".length)
125
+ : commandName
126
+
127
+ return `/${normalizeName(withoutPrefix)}`
128
+ })
129
+
130
+ return result
131
+ }
132
+
133
+ function appendCompatibilityNoteIfNeeded(body: string): string {
134
+ if (!/\bmcp\b/i.test(body)) return body
135
+
136
+ const note = [
137
+ "",
138
+ "## Pi + MCPorter note",
139
+ "For MCP access in Pi, use MCPorter via the generated tools:",
140
+ "- `mcporter_list` to inspect available MCP tools",
141
+ "- `mcporter_call` to invoke a tool",
142
+ "",
143
+ ].join("\n")
144
+
145
+ return body + note
146
+ }
147
+
148
+ function convertMcpToMcporter(servers: Record<string, ClaudeMcpServer>): PiMcporterConfig {
149
+ const mcpServers: Record<string, PiMcporterServer> = {}
150
+
151
+ for (const [name, server] of Object.entries(servers)) {
152
+ if (server.command) {
153
+ mcpServers[name] = {
154
+ command: server.command,
155
+ args: server.args,
156
+ env: server.env,
157
+ headers: server.headers,
158
+ }
159
+ continue
160
+ }
161
+
162
+ if (server.url) {
163
+ mcpServers[name] = {
164
+ baseUrl: server.url,
165
+ headers: server.headers,
166
+ }
167
+ }
168
+ }
169
+
170
+ return { mcpServers }
171
+ }
172
+
173
+ function normalizeName(value: string): string {
174
+ const trimmed = value.trim()
175
+ if (!trimmed) return "item"
176
+ const normalized = trimmed
177
+ .toLowerCase()
178
+ .replace(/[\\/]+/g, "-")
179
+ .replace(/[:\s]+/g, "-")
180
+ .replace(/[^a-z0-9_-]+/g, "-")
181
+ .replace(/-+/g, "-")
182
+ .replace(/^-+|-+$/g, "")
183
+ return normalized || "item"
184
+ }
185
+
186
+ function sanitizeDescription(value: string, maxLength = PI_DESCRIPTION_MAX_LENGTH): string {
187
+ const normalized = value.replace(/\s+/g, " ").trim()
188
+ if (normalized.length <= maxLength) return normalized
189
+ const ellipsis = "..."
190
+ return normalized.slice(0, Math.max(0, maxLength - ellipsis.length)).trimEnd() + ellipsis
191
+ }
192
+
193
+ function uniqueName(base: string, used: Set<string>): string {
194
+ if (!used.has(base)) {
195
+ used.add(base)
196
+ return base
197
+ }
198
+ let index = 2
199
+ while (used.has(`${base}-${index}`)) {
200
+ index += 1
201
+ }
202
+ const name = `${base}-${index}`
203
+ used.add(name)
204
+ return name
205
+ }
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bun
2
+ import { defineCommand, runMain } from "citty"
3
+ import convert from "./commands/convert"
4
+ import install from "./commands/install"
5
+ import listCommand from "./commands/list"
6
+ import sync from "./commands/sync"
7
+
8
+ const main = defineCommand({
9
+ meta: {
10
+ name: "compound-plugin",
11
+ version: "0.1.0",
12
+ description: "Convert Claude Code plugins into other agent formats",
13
+ },
14
+ subCommands: {
15
+ convert: () => convert,
16
+ install: () => install,
17
+ list: () => listCommand,
18
+ sync: () => sync,
19
+ },
20
+ })
21
+
22
+ runMain(main)
@@ -0,0 +1,65 @@
1
+ import path from "path"
2
+ import os from "os"
3
+ import fs from "fs/promises"
4
+ import type { ClaudeSkill, ClaudeMcpServer } from "../types/claude"
5
+
6
+ export interface ClaudeHomeConfig {
7
+ skills: ClaudeSkill[]
8
+ mcpServers: Record<string, ClaudeMcpServer>
9
+ }
10
+
11
+ export async function loadClaudeHome(claudeHome?: string): Promise<ClaudeHomeConfig> {
12
+ const home = claudeHome ?? path.join(os.homedir(), ".claude")
13
+
14
+ const [skills, mcpServers] = await Promise.all([
15
+ loadPersonalSkills(path.join(home, "skills")),
16
+ loadSettingsMcp(path.join(home, "settings.json")),
17
+ ])
18
+
19
+ return { skills, mcpServers }
20
+ }
21
+
22
+ async function loadPersonalSkills(skillsDir: string): Promise<ClaudeSkill[]> {
23
+ try {
24
+ const entries = await fs.readdir(skillsDir, { withFileTypes: true })
25
+ const skills: ClaudeSkill[] = []
26
+
27
+ for (const entry of entries) {
28
+ // Check if directory or symlink (symlinks are common for skills)
29
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue
30
+
31
+ const entryPath = path.join(skillsDir, entry.name)
32
+ const skillPath = path.join(entryPath, "SKILL.md")
33
+
34
+ try {
35
+ await fs.access(skillPath)
36
+ // Resolve symlink to get the actual source directory
37
+ const sourceDir = entry.isSymbolicLink()
38
+ ? await fs.realpath(entryPath)
39
+ : entryPath
40
+ skills.push({
41
+ name: entry.name,
42
+ sourceDir,
43
+ skillPath,
44
+ })
45
+ } catch {
46
+ // No SKILL.md, skip
47
+ }
48
+ }
49
+ return skills
50
+ } catch {
51
+ return [] // Directory doesn't exist
52
+ }
53
+ }
54
+
55
+ async function loadSettingsMcp(
56
+ settingsPath: string,
57
+ ): Promise<Record<string, ClaudeMcpServer>> {
58
+ try {
59
+ const content = await fs.readFile(settingsPath, "utf-8")
60
+ const settings = JSON.parse(content) as { mcpServers?: Record<string, ClaudeMcpServer> }
61
+ return settings.mcpServers ?? {}
62
+ } catch {
63
+ return {} // File doesn't exist or invalid JSON
64
+ }
65
+ }
@@ -0,0 +1,252 @@
1
+ import path from "path"
2
+ import { parseFrontmatter } from "../utils/frontmatter"
3
+ import { readJson, readText, pathExists, walkFiles } from "../utils/files"
4
+ import type {
5
+ ClaudeAgent,
6
+ ClaudeCommand,
7
+ ClaudeHooks,
8
+ ClaudeManifest,
9
+ ClaudeMcpServer,
10
+ ClaudePlugin,
11
+ ClaudeSkill,
12
+ } from "../types/claude"
13
+
14
+ const PLUGIN_MANIFEST = path.join(".claude-plugin", "plugin.json")
15
+
16
+ export async function loadClaudePlugin(inputPath: string): Promise<ClaudePlugin> {
17
+ const root = await resolveClaudeRoot(inputPath)
18
+ const manifestPath = path.join(root, PLUGIN_MANIFEST)
19
+ const manifest = await readJson<ClaudeManifest>(manifestPath)
20
+
21
+ const agents = await loadAgents(resolveComponentDirs(root, "agents", manifest.agents))
22
+ const commands = await loadCommands(resolveComponentDirs(root, "commands", manifest.commands))
23
+ const skills = await loadSkills(resolveComponentDirs(root, "skills", manifest.skills))
24
+ const hooks = await loadHooks(root, manifest.hooks)
25
+
26
+ const mcpServers = await loadMcpServers(root, manifest)
27
+
28
+ return {
29
+ root,
30
+ manifest,
31
+ agents,
32
+ commands,
33
+ skills,
34
+ hooks,
35
+ mcpServers,
36
+ }
37
+ }
38
+
39
+ async function resolveClaudeRoot(inputPath: string): Promise<string> {
40
+ const absolute = path.resolve(inputPath)
41
+ const manifestAtPath = path.join(absolute, PLUGIN_MANIFEST)
42
+ if (await pathExists(manifestAtPath)) {
43
+ return absolute
44
+ }
45
+
46
+ if (absolute.endsWith(PLUGIN_MANIFEST)) {
47
+ return path.dirname(path.dirname(absolute))
48
+ }
49
+
50
+ if (absolute.endsWith("plugin.json")) {
51
+ return path.dirname(path.dirname(absolute))
52
+ }
53
+
54
+ throw new Error(`Could not find ${PLUGIN_MANIFEST} under ${inputPath}`)
55
+ }
56
+
57
+ async function loadAgents(agentsDirs: string[]): Promise<ClaudeAgent[]> {
58
+ const files = await collectMarkdownFiles(agentsDirs)
59
+
60
+ const agents: ClaudeAgent[] = []
61
+ for (const file of files) {
62
+ const raw = await readText(file)
63
+ const { data, body } = parseFrontmatter(raw)
64
+ const name = (data.name as string) ?? path.basename(file, ".md")
65
+ agents.push({
66
+ name,
67
+ description: data.description as string | undefined,
68
+ capabilities: data.capabilities as string[] | undefined,
69
+ model: data.model as string | undefined,
70
+ body: body.trim(),
71
+ sourcePath: file,
72
+ })
73
+ }
74
+ return agents
75
+ }
76
+
77
+ async function loadCommands(commandsDirs: string[]): Promise<ClaudeCommand[]> {
78
+ const files = await collectMarkdownFiles(commandsDirs)
79
+
80
+ const commands: ClaudeCommand[] = []
81
+ for (const file of files) {
82
+ const raw = await readText(file)
83
+ const { data, body } = parseFrontmatter(raw)
84
+ const name = (data.name as string) ?? path.basename(file, ".md")
85
+ const allowedTools = parseAllowedTools(data["allowed-tools"])
86
+ const disableModelInvocation = data["disable-model-invocation"] === true ? true : undefined
87
+ commands.push({
88
+ name,
89
+ description: data.description as string | undefined,
90
+ argumentHint: data["argument-hint"] as string | undefined,
91
+ model: data.model as string | undefined,
92
+ allowedTools,
93
+ disableModelInvocation,
94
+ body: body.trim(),
95
+ sourcePath: file,
96
+ })
97
+ }
98
+ return commands
99
+ }
100
+
101
+ async function loadSkills(skillsDirs: string[]): Promise<ClaudeSkill[]> {
102
+ const entries = await collectFiles(skillsDirs)
103
+ const skillFiles = entries.filter((file) => path.basename(file) === "SKILL.md")
104
+ const skills: ClaudeSkill[] = []
105
+ for (const file of skillFiles) {
106
+ const raw = await readText(file)
107
+ const { data } = parseFrontmatter(raw)
108
+ const name = (data.name as string) ?? path.basename(path.dirname(file))
109
+ const disableModelInvocation = data["disable-model-invocation"] === true ? true : undefined
110
+ skills.push({
111
+ name,
112
+ description: data.description as string | undefined,
113
+ disableModelInvocation,
114
+ sourceDir: path.dirname(file),
115
+ skillPath: file,
116
+ })
117
+ }
118
+ return skills
119
+ }
120
+
121
+ async function loadHooks(root: string, hooksField?: ClaudeManifest["hooks"]): Promise<ClaudeHooks | undefined> {
122
+ const hookConfigs: ClaudeHooks[] = []
123
+
124
+ const defaultPath = path.join(root, "hooks", "hooks.json")
125
+ if (await pathExists(defaultPath)) {
126
+ hookConfigs.push(await readJson<ClaudeHooks>(defaultPath))
127
+ }
128
+
129
+ if (hooksField) {
130
+ if (typeof hooksField === "string" || Array.isArray(hooksField)) {
131
+ const hookPaths = toPathList(hooksField)
132
+ for (const hookPath of hookPaths) {
133
+ const resolved = resolveWithinRoot(root, hookPath, "hooks path")
134
+ if (await pathExists(resolved)) {
135
+ hookConfigs.push(await readJson<ClaudeHooks>(resolved))
136
+ }
137
+ }
138
+ } else {
139
+ hookConfigs.push(hooksField)
140
+ }
141
+ }
142
+
143
+ if (hookConfigs.length === 0) return undefined
144
+ return mergeHooks(hookConfigs)
145
+ }
146
+
147
+ async function loadMcpServers(
148
+ root: string,
149
+ manifest: ClaudeManifest,
150
+ ): Promise<Record<string, ClaudeMcpServer> | undefined> {
151
+ const field = manifest.mcpServers
152
+ if (field) {
153
+ if (typeof field === "string" || Array.isArray(field)) {
154
+ return mergeMcpConfigs(await loadMcpPaths(root, field))
155
+ }
156
+ return field as Record<string, ClaudeMcpServer>
157
+ }
158
+
159
+ const mcpPath = path.join(root, ".mcp.json")
160
+ if (await pathExists(mcpPath)) {
161
+ return readJson<Record<string, ClaudeMcpServer>>(mcpPath)
162
+ }
163
+
164
+ return undefined
165
+ }
166
+
167
+ function parseAllowedTools(value: unknown): string[] | undefined {
168
+ if (!value) return undefined
169
+ if (Array.isArray(value)) {
170
+ return value.map((item) => String(item))
171
+ }
172
+ if (typeof value === "string") {
173
+ return value
174
+ .split(/,/)
175
+ .map((item) => item.trim())
176
+ .filter(Boolean)
177
+ }
178
+ return undefined
179
+ }
180
+
181
+ function resolveComponentDirs(
182
+ root: string,
183
+ defaultDir: string,
184
+ custom?: string | string[],
185
+ ): string[] {
186
+ const dirs = [path.join(root, defaultDir)]
187
+ for (const entry of toPathList(custom)) {
188
+ dirs.push(resolveWithinRoot(root, entry, `${defaultDir} path`))
189
+ }
190
+ return dirs
191
+ }
192
+
193
+ function toPathList(value?: string | string[]): string[] {
194
+ if (!value) return []
195
+ if (Array.isArray(value)) return value
196
+ return [value]
197
+ }
198
+
199
+ async function collectMarkdownFiles(dirs: string[]): Promise<string[]> {
200
+ const entries = await collectFiles(dirs)
201
+ return entries.filter((file) => file.endsWith(".md"))
202
+ }
203
+
204
+ async function collectFiles(dirs: string[]): Promise<string[]> {
205
+ const files: string[] = []
206
+ for (const dir of dirs) {
207
+ if (!(await pathExists(dir))) continue
208
+ const entries = await walkFiles(dir)
209
+ files.push(...entries)
210
+ }
211
+ return files
212
+ }
213
+
214
+ function mergeHooks(hooksList: ClaudeHooks[]): ClaudeHooks {
215
+ const merged: ClaudeHooks = { hooks: {} }
216
+ for (const hooks of hooksList) {
217
+ for (const [event, matchers] of Object.entries(hooks.hooks)) {
218
+ if (!merged.hooks[event]) {
219
+ merged.hooks[event] = []
220
+ }
221
+ merged.hooks[event].push(...matchers)
222
+ }
223
+ }
224
+ return merged
225
+ }
226
+
227
+ async function loadMcpPaths(
228
+ root: string,
229
+ value: string | string[],
230
+ ): Promise<Record<string, ClaudeMcpServer>[]> {
231
+ const configs: Record<string, ClaudeMcpServer>[] = []
232
+ for (const entry of toPathList(value)) {
233
+ const resolved = resolveWithinRoot(root, entry, "mcpServers path")
234
+ if (await pathExists(resolved)) {
235
+ configs.push(await readJson<Record<string, ClaudeMcpServer>>(resolved))
236
+ }
237
+ }
238
+ return configs
239
+ }
240
+
241
+ function mergeMcpConfigs(configs: Record<string, ClaudeMcpServer>[]): Record<string, ClaudeMcpServer> {
242
+ return configs.reduce((acc, config) => ({ ...acc, ...config }), {})
243
+ }
244
+
245
+ function resolveWithinRoot(root: string, entry: string, label: string): string {
246
+ const resolvedRoot = path.resolve(root)
247
+ const resolvedPath = path.resolve(root, entry)
248
+ if (resolvedPath === resolvedRoot || resolvedPath.startsWith(resolvedRoot + path.sep)) {
249
+ return resolvedPath
250
+ }
251
+ throw new Error(`Invalid ${label}: ${entry}. Paths must stay within the plugin root.`)
252
+ }
@@ -0,0 +1,92 @@
1
+ import fs from "fs/promises"
2
+ import path from "path"
3
+ import type { ClaudeHomeConfig } from "../parsers/claude-home"
4
+ import type { ClaudeMcpServer } from "../types/claude"
5
+ import { forceSymlink, isValidSkillName } from "../utils/symlink"
6
+
7
+ export async function syncToCodex(
8
+ config: ClaudeHomeConfig,
9
+ outputRoot: string,
10
+ ): Promise<void> {
11
+ // Ensure output directories exist
12
+ const skillsDir = path.join(outputRoot, "skills")
13
+ await fs.mkdir(skillsDir, { recursive: true })
14
+
15
+ // Symlink skills (with validation)
16
+ for (const skill of config.skills) {
17
+ if (!isValidSkillName(skill.name)) {
18
+ console.warn(`Skipping skill with invalid name: ${skill.name}`)
19
+ continue
20
+ }
21
+ const target = path.join(skillsDir, skill.name)
22
+ await forceSymlink(skill.sourceDir, target)
23
+ }
24
+
25
+ // Write MCP servers to config.toml (TOML format)
26
+ if (Object.keys(config.mcpServers).length > 0) {
27
+ const configPath = path.join(outputRoot, "config.toml")
28
+ const mcpToml = convertMcpForCodex(config.mcpServers)
29
+
30
+ // Read existing config and merge idempotently
31
+ let existingContent = ""
32
+ try {
33
+ existingContent = await fs.readFile(configPath, "utf-8")
34
+ } catch (err) {
35
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
36
+ throw err
37
+ }
38
+ }
39
+
40
+ // Remove any existing Claude Code MCP section to make idempotent
41
+ const marker = "# MCP servers synced from Claude Code"
42
+ const markerIndex = existingContent.indexOf(marker)
43
+ if (markerIndex !== -1) {
44
+ existingContent = existingContent.slice(0, markerIndex).trimEnd()
45
+ }
46
+
47
+ const newContent = existingContent
48
+ ? existingContent + "\n\n" + marker + "\n" + mcpToml
49
+ : "# Codex config - synced from Claude Code\n\n" + mcpToml
50
+
51
+ await fs.writeFile(configPath, newContent, { mode: 0o600 })
52
+ }
53
+ }
54
+
55
+ /** Escape a string for TOML double-quoted strings */
56
+ function escapeTomlString(str: string): string {
57
+ return str
58
+ .replace(/\\/g, "\\\\")
59
+ .replace(/"/g, '\\"')
60
+ .replace(/\n/g, "\\n")
61
+ .replace(/\r/g, "\\r")
62
+ .replace(/\t/g, "\\t")
63
+ }
64
+
65
+ function convertMcpForCodex(servers: Record<string, ClaudeMcpServer>): string {
66
+ const sections: string[] = []
67
+
68
+ for (const [name, server] of Object.entries(servers)) {
69
+ if (!server.command) continue
70
+
71
+ const lines: string[] = []
72
+ lines.push(`[mcp_servers.${name}]`)
73
+ lines.push(`command = "${escapeTomlString(server.command)}"`)
74
+
75
+ if (server.args && server.args.length > 0) {
76
+ const argsStr = server.args.map((arg) => `"${escapeTomlString(arg)}"`).join(", ")
77
+ lines.push(`args = [${argsStr}]`)
78
+ }
79
+
80
+ if (server.env && Object.keys(server.env).length > 0) {
81
+ lines.push("")
82
+ lines.push(`[mcp_servers.${name}.env]`)
83
+ for (const [key, value] of Object.entries(server.env)) {
84
+ lines.push(`${key} = "${escapeTomlString(value)}"`)
85
+ }
86
+ }
87
+
88
+ sections.push(lines.join("\n"))
89
+ }
90
+
91
+ return sections.join("\n\n") + "\n"
92
+ }