coding-tool-x 3.8.4 → 3.8.5

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 (434) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +86 -15
  3. package/dist/web/assets/AgentsDrawer-BG6P3DX-.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics--z_FmL5P.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-CrJtRKHy.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-BiuEryJj.js +1 -0
  10. package/dist/web/assets/CollapseItem-BAYEsL_Q.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-YJ-U7BXN.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-Ce_hAknW.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-Bvr1NN6K.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-BjIeHfF-.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-AV00tm_D.js +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  20. package/dist/web/assets/Divider-D28g1gwj.js +1 -0
  21. package/dist/web/assets/DrawerContent-ChXgZ1Xm.js +1 -0
  22. package/dist/web/assets/FavoritesDrawer-CG9eI9Ay.css +1 -0
  23. package/dist/web/assets/FavoritesDrawer-Cz95o4CD.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-DLlbMz27.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-D7CCOFQC.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  29. package/dist/web/assets/MarkdownEditor-rEZ-c72L.js +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-DfoCyxU2.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-SKu8HNUR.js +1 -0
  34. package/dist/web/assets/PluginManager-BbeK-3rV.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-CWVJ7yjS.js +1 -0
  36. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  37. package/dist/web/assets/PluginsPanel-Cm2N3StG.js +1 -0
  38. package/dist/web/assets/PluginsPanel-D6ZqITcR.css +1 -0
  39. package/dist/web/assets/ProjectList-677RLZ8O.css +1 -0
  40. package/dist/web/assets/ProjectList-CsjnsNPv.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-EGd3djFa.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-D1vtoLAs.js +1 -0
  45. package/dist/web/assets/SessionCard-BgLg49U3.js +1 -0
  46. package/dist/web/assets/SessionCard-aM2NHu6i.css +1 -0
  47. package/dist/web/assets/SessionList-BpYRz1cX.css +1 -0
  48. package/dist/web/assets/SessionList-TRiTklWk.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-d5liXjHq.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-rz2WejGY.js +1 -0
  51. package/dist/web/assets/SkillManager-BTjTUbHq.js +1 -0
  52. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-pN0hbkK3.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-gmBWEoiF.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-DcNzuVJk.js +1 -0
  59. package/dist/web/assets/Upload-v5rV0qTT.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-DxmicM4U.js +1 -0
  62. package/dist/web/assets/WorkspaceManager-CJHCjARN.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +1 -0
  64. package/dist/web/assets/WorkspacePanel-D8_Zq-du.js +1 -0
  65. package/dist/web/assets/_plugin-vue_export-helper-BeLZJZij.js +1 -0
  66. package/dist/web/assets/client-UDwXBP9i.js +1 -0
  67. package/dist/web/assets/clipboard-B46yyoz_.js +1 -0
  68. package/dist/web/assets/headers-cf-QapUq.js +1 -0
  69. package/dist/web/assets/icons-B2Vg-IOC.js +1 -0
  70. package/dist/web/assets/index-C0EHRym0.css +1 -0
  71. package/dist/web/assets/index-DnEocV0M.js +2 -0
  72. package/dist/web/assets/light-Cp0t7v32.js +1 -0
  73. package/dist/web/assets/markdown-u082bbtj.js +1 -0
  74. package/dist/web/assets/platforms-CBkB45JP.js +1 -0
  75. package/dist/web/assets/platforms-FTpjxAT6.js +1 -0
  76. package/dist/web/assets/project-config-B2baPzYL.js +1 -0
  77. package/dist/web/assets/sessions-Cmy31slD.js +1 -0
  78. package/dist/web/assets/sessions-yC_oVVh8.js +1 -0
  79. package/dist/web/assets/skill-repo-input-B_fkGlUK.js +1 -0
  80. package/dist/web/assets/use-lock-html-scroll-DXIqmUW4.js +1 -0
  81. package/dist/web/assets/use-message-DoLM8C1C.js +1 -0
  82. package/dist/web/assets/useResponsiveDrawer-B3uojyvV.js +1 -0
  83. package/dist/web/assets/vendors-D0c_0KLP.js +1 -0
  84. package/dist/web/assets/vue-vendor-BqZbbXHA.js +1 -0
  85. package/dist/web/assets/workspaces-mjyPVGS8.js +1 -0
  86. package/dist/web/assets/xml-CgY3RMlX.js +1 -0
  87. package/dist/web/index.html +12 -8
  88. package/docs/native-log-memory-2026-09-16.md +43 -0
  89. package/docs/superpowers/plans/2026-08-26-configuration-driven-cli-platform.md +1578 -0
  90. package/docs/superpowers/plans/2026-08-30-project-scoped-config-control.md +1260 -0
  91. package/docs/superpowers/plans/2026-09-01-omp-skill-refresh-boundary.md +213 -0
  92. package/docs/superpowers/plans/2026-09-01-realtime-logs-panel.md +125 -0
  93. package/docs/superpowers/plans/2026-09-02-config-driven-platform-boundary.md +86 -0
  94. package/docs/superpowers/plans/2026-09-02-platform-implementation-drivers.md +508 -0
  95. package/docs/superpowers/plans/2026-09-03-cli-platform-correctness-boundary.md +760 -0
  96. package/docs/superpowers/plans/2026-09-03-dependency-security-remediation.md +133 -0
  97. package/docs/superpowers/plans/2026-09-04-model-catalog-pricing-sync.md +1078 -0
  98. package/docs/superpowers/plans/2026-09-05-codex-project-scan-500.md +128 -0
  99. package/docs/superpowers/plans/2026-09-06-project-skill-path-fix.md +97 -0
  100. package/docs/superpowers/specs/2026-08-26-configuration-driven-cli-platform-design.md +408 -0
  101. package/docs/superpowers/specs/2026-08-30-project-scoped-config-control-design.md +467 -0
  102. package/docs/superpowers/specs/2026-09-01-claude-session-content-repair-design.md +111 -0
  103. package/docs/superpowers/specs/2026-09-01-omp-skill-refresh-boundary-design.md +83 -0
  104. package/docs/superpowers/specs/2026-09-01-realtime-logs-panel-design.md +27 -0
  105. package/docs/superpowers/specs/2026-09-02-config-driven-platform-boundary-design.md +64 -0
  106. package/docs/superpowers/specs/2026-09-02-platform-implementation-driver-boundary-design.md +123 -0
  107. package/docs/superpowers/specs/2026-09-03-cli-platform-correctness-boundary-design.md +249 -0
  108. package/docs/superpowers/specs/2026-09-03-dependency-security-remediation-design.md +87 -0
  109. package/docs/superpowers/specs/2026-09-04-model-catalog-pricing-sync-design.md +301 -0
  110. package/docs/superpowers/specs/2026-09-05-codex-project-scan-500-design.md +24 -0
  111. package/package.json +8 -4
  112. package/src/commands/channels.js +88 -191
  113. package/src/commands/cli-type.js +19 -11
  114. package/src/commands/daemon.js +94 -32
  115. package/src/commands/doctor.js +16 -9
  116. package/src/commands/export-config.js +23 -5
  117. package/src/commands/list.js +130 -52
  118. package/src/commands/logs.js +44 -27
  119. package/src/commands/platform-command-registry.js +78 -0
  120. package/src/commands/plugin.js +11 -5
  121. package/src/commands/port-config.js +34 -52
  122. package/src/commands/proxy-control.js +207 -145
  123. package/src/commands/proxy.js +2 -2
  124. package/src/commands/resume.js +87 -91
  125. package/src/commands/search.js +137 -67
  126. package/src/commands/stats.js +30 -22
  127. package/src/commands/switch.js +1 -1
  128. package/src/commands/toggle-proxy.js +94 -111
  129. package/src/commands/ui.js +67 -6
  130. package/src/commands/workspace.js +90 -8
  131. package/src/config/default.js +6 -53
  132. package/src/config/loader.js +61 -24
  133. package/src/config/model-metadata.js +49 -59
  134. package/src/config/model-metadata.json +4095 -99
  135. package/src/config/paths.js +181 -8
  136. package/src/index.js +134 -71
  137. package/src/platforms/access.js +196 -0
  138. package/src/{server/services → platforms}/agents-service.js +354 -197
  139. package/src/platforms/channel-auth-registry.js +162 -0
  140. package/src/platforms/channel-auth-service.js +82 -0
  141. package/src/{server/services → platforms}/commands-service.js +195 -115
  142. package/src/platforms/driver-registry.js +59 -0
  143. package/src/platforms/drivers/claude/api-operations.js +62 -0
  144. package/src/{server/services/channels.js → platforms/drivers/claude/channels-implementation.js} +131 -33
  145. package/src/platforms/drivers/claude/channels.js +39 -0
  146. package/src/{server/services/config-sync-service.js → platforms/drivers/claude/config-sync.js} +29 -5
  147. package/src/{server/services → platforms/drivers/claude}/health-check.js +4 -4
  148. package/src/platforms/drivers/claude/health.js +22 -0
  149. package/src/platforms/drivers/claude/mcp.js +9 -0
  150. package/src/{server/services/settings-manager.js → platforms/drivers/claude/native-config-implementation.js} +13 -4
  151. package/src/platforms/drivers/claude/native-config.js +46 -0
  152. package/src/platforms/drivers/claude/native-logs.js +72 -0
  153. package/src/{server/services/claude-openai-gateway.js → platforms/drivers/claude/openai-gateway.js} +8 -65
  154. package/src/platforms/drivers/claude/project-config.js +21 -0
  155. package/src/platforms/drivers/claude/projects.js +62 -0
  156. package/src/platforms/drivers/claude/prompts.js +9 -0
  157. package/src/{server/proxy-server.js → platforms/drivers/claude/proxy-implementation.js} +41 -278
  158. package/src/platforms/drivers/claude/proxy.js +16 -0
  159. package/src/platforms/drivers/claude/resource-sync.js +9 -0
  160. package/src/platforms/drivers/claude/session-history-adapter.js +313 -0
  161. package/src/{server/services/sessions.js → platforms/drivers/claude/sessions-implementation.js} +145 -35
  162. package/src/platforms/drivers/claude/sessions.js +76 -0
  163. package/src/{server/services/claude-statistics-service.js → platforms/drivers/claude/statistics-implementation.js} +2 -2
  164. package/src/platforms/drivers/claude/statistics.js +16 -0
  165. package/src/{server/services/claude-wire.js → platforms/drivers/claude/wire.js} +1 -1
  166. package/src/platforms/drivers/codex/api-operations.js +9 -0
  167. package/src/{server/services/codex-channels.js → platforms/drivers/codex/channels-implementation.js} +149 -14
  168. package/src/platforms/drivers/codex/channels.js +45 -0
  169. package/src/{server/services/codex-config.js → platforms/drivers/codex/config.js} +12 -2
  170. package/src/{server/services/codex-env-manager.js → platforms/drivers/codex/env-manager.js} +1 -1
  171. package/src/platforms/drivers/codex/mcp.js +9 -0
  172. package/src/{server/services/codex-settings-manager.js → platforms/drivers/codex/native-config-implementation.js} +15 -6
  173. package/src/platforms/drivers/codex/native-config.js +47 -0
  174. package/src/platforms/drivers/codex/native-logs.js +166 -0
  175. package/src/platforms/drivers/codex/project-config.js +21 -0
  176. package/src/platforms/drivers/codex/projects.js +26 -0
  177. package/src/platforms/drivers/codex/prompts.js +9 -0
  178. package/src/{server/codex-proxy-server.js → platforms/drivers/codex/proxy-implementation.js} +30 -233
  179. package/src/platforms/drivers/codex/proxy.js +16 -0
  180. package/src/platforms/drivers/codex/resource-sync.js +9 -0
  181. package/src/{server/services/session-history-adapters/codex.js → platforms/drivers/codex/session-history-adapter.js} +37 -23
  182. package/src/{server/services/codex-sessions.js → platforms/drivers/codex/sessions-implementation.js} +81 -75
  183. package/src/platforms/drivers/codex/sessions.js +36 -0
  184. package/src/{server/services/codex-statistics-service.js → platforms/drivers/codex/statistics-implementation.js} +2 -2
  185. package/src/platforms/drivers/codex/statistics.js +16 -0
  186. package/src/platforms/drivers/gemini/api-operations.js +9 -0
  187. package/src/{server/services/gemini-channels.js → platforms/drivers/gemini/channels-implementation.js} +202 -37
  188. package/src/platforms/drivers/gemini/channels.js +39 -0
  189. package/src/{server/services/gemini-config.js → platforms/drivers/gemini/config.js} +12 -2
  190. package/src/platforms/drivers/gemini/mcp.js +9 -0
  191. package/src/{server/services/gemini-settings-manager.js → platforms/drivers/gemini/native-config-implementation.js} +14 -5
  192. package/src/platforms/drivers/gemini/native-config.js +39 -0
  193. package/src/platforms/drivers/gemini/native-logs.js +180 -0
  194. package/src/platforms/drivers/gemini/project-config.js +21 -0
  195. package/src/platforms/drivers/gemini/projects.js +26 -0
  196. package/src/platforms/drivers/gemini/prompts.js +9 -0
  197. package/src/{server/gemini-proxy-server.js → platforms/drivers/gemini/proxy-implementation.js} +36 -267
  198. package/src/platforms/drivers/gemini/proxy.js +16 -0
  199. package/src/platforms/drivers/gemini/resource-sync.js +9 -0
  200. package/src/{server/services/session-history-adapters/gemini.js → platforms/drivers/gemini/session-history-adapter.js} +70 -18
  201. package/src/{server/services/gemini-sessions.js → platforms/drivers/gemini/sessions-implementation.js} +53 -37
  202. package/src/platforms/drivers/gemini/sessions.js +47 -0
  203. package/src/{server/services/gemini-statistics-service.js → platforms/drivers/gemini/statistics-implementation.js} +2 -2
  204. package/src/platforms/drivers/gemini/statistics.js +16 -0
  205. package/src/{server/services/gemini-wire.js → platforms/drivers/gemini/wire.js} +1 -1
  206. package/src/platforms/drivers/generic-filesystem.js +166 -0
  207. package/src/platforms/drivers/generic-jsonl.js +141 -0
  208. package/src/platforms/drivers/generic-mcp.js +44 -0
  209. package/src/platforms/drivers/generic-openai-compatible.js +45 -0
  210. package/src/platforms/drivers/generic-prompt.js +41 -0
  211. package/src/platforms/drivers/legacy.js +677 -0
  212. package/src/platforms/drivers/native-config-snapshot.js +125 -0
  213. package/src/platforms/drivers/native-log-utils.js +1292 -0
  214. package/src/platforms/drivers/notification-hooks.js +578 -0
  215. package/src/{server/api/omp-config.js → platforms/drivers/omp/api-operations.js} +52 -64
  216. package/src/{server/services/omp-auth-provider-worker.js → platforms/drivers/omp/auth-provider-worker.js} +1 -1
  217. package/src/{server/services/omp-auth-providers.js → platforms/drivers/omp/auth-providers.js} +1 -1
  218. package/src/{server/services/omp-channels.js → platforms/drivers/omp/channels-implementation.js} +166 -39
  219. package/src/platforms/drivers/omp/channels.js +58 -0
  220. package/src/{server/services/omp-config.js → platforms/drivers/omp/config.js} +53 -13
  221. package/src/{server/services/omp-gateway-routing.js → platforms/drivers/omp/gateway-routing.js} +14 -7
  222. package/src/{server/services/omp-gateway.js → platforms/drivers/omp/gateway.js} +4 -262
  223. package/src/platforms/drivers/omp/mcp.js +9 -0
  224. package/src/{server/services/omp-settings-manager.js → platforms/drivers/omp/native-config-implementation.js} +75 -10
  225. package/src/platforms/drivers/omp/native-config.js +46 -0
  226. package/src/platforms/drivers/omp/native-logs.js +52 -0
  227. package/src/{server/services/omp-native-plugin-adapter.js → platforms/drivers/omp/native-plugin-adapter.js} +91 -4
  228. package/src/platforms/drivers/omp/project-config.js +29 -0
  229. package/src/platforms/drivers/omp/projects.js +20 -0
  230. package/src/platforms/drivers/omp/prompts.js +9 -0
  231. package/src/{server/omp-proxy-server.js → platforms/drivers/omp/proxy-implementation.js} +6 -15
  232. package/src/platforms/drivers/omp/proxy.js +16 -0
  233. package/src/platforms/drivers/omp/resource-sync.js +9 -0
  234. package/src/{server/services/session-history-adapters/omp.js → platforms/drivers/omp/session-history-adapter.js} +42 -18
  235. package/src/{server/services/omp-sessions.js → platforms/drivers/omp/sessions-implementation.js} +130 -94
  236. package/src/platforms/drivers/omp/sessions.js +36 -0
  237. package/src/{server/services/omp-skill-discovery.js → platforms/drivers/omp/skill-discovery.js} +159 -32
  238. package/src/{server/services/omp-skill-settings-service.js → platforms/drivers/omp/skill-settings.js} +1 -1
  239. package/src/{server/services/omp-statistics-service.js → platforms/drivers/omp/statistics-implementation.js} +3 -3
  240. package/src/platforms/drivers/omp/statistics.js +16 -0
  241. package/src/platforms/drivers/opencode/api-operations.js +9 -0
  242. package/src/{server/services/opencode-channels.js → platforms/drivers/opencode/channels-implementation.js} +28 -9
  243. package/src/platforms/drivers/opencode/channels.js +40 -0
  244. package/src/platforms/drivers/opencode/conversion.js +41 -0
  245. package/src/{server/services/opencode-gateway-converter.js → platforms/drivers/opencode/gateway-converter.js} +1 -1
  246. package/src/platforms/drivers/opencode/mcp.js +9 -0
  247. package/src/{server/services/opencode-settings-manager.js → platforms/drivers/opencode/native-config-implementation.js} +17 -6
  248. package/src/platforms/drivers/opencode/native-config.js +26 -0
  249. package/src/platforms/drivers/opencode/native-logs.js +133 -0
  250. package/src/platforms/drivers/opencode/project-config.js +98 -0
  251. package/src/platforms/drivers/opencode/projects.js +20 -0
  252. package/src/platforms/drivers/opencode/prompts.js +9 -0
  253. package/src/{server/opencode-proxy-server.js → platforms/drivers/opencode/proxy-implementation.js} +27 -362
  254. package/src/platforms/drivers/opencode/proxy.js +16 -0
  255. package/src/platforms/drivers/opencode/resource-sync.js +9 -0
  256. package/src/{server/services/opencode-sessions.js → platforms/drivers/opencode/sessions-implementation.js} +211 -64
  257. package/src/platforms/drivers/opencode/sessions.js +48 -0
  258. package/src/{server/services/opencode-statistics-service.js → platforms/drivers/opencode/statistics-implementation.js} +2 -2
  259. package/src/platforms/drivers/opencode/statistics.js +16 -0
  260. package/src/platforms/drivers/secure-file-driver.js +272 -0
  261. package/src/platforms/drivers/unsupported.js +7 -0
  262. package/src/platforms/manifest-schema.js +398 -0
  263. package/src/platforms/manifests/claude.json +489 -0
  264. package/src/platforms/manifests/codex.json +448 -0
  265. package/src/platforms/manifests/gemini.json +447 -0
  266. package/src/platforms/manifests/omp.json +496 -0
  267. package/src/platforms/manifests/opencode.json +422 -0
  268. package/src/{server/services → platforms}/native-oauth-adapters.js +87 -417
  269. package/src/{server/services → platforms}/notification-hooks.js +140 -1023
  270. package/src/platforms/oauth-credentials-service.js +506 -0
  271. package/src/platforms/path-resolver.js +179 -0
  272. package/src/platforms/platform-path-context.js +305 -0
  273. package/src/platforms/registry.js +248 -0
  274. package/src/platforms/remote-notification-providers.js +678 -0
  275. package/src/platforms/runtime.js +259 -0
  276. package/src/{server/services/session-history-adapters/index.js → platforms/session-history-adapters.js} +8 -1
  277. package/src/plugins/plugin-loader.js +2 -2
  278. package/src/plugins/plugin-manager.js +2 -2
  279. package/src/plugins/registry.js +27 -10
  280. package/src/reset-config.js +21 -5
  281. package/src/server/api/agents.js +46 -72
  282. package/src/server/api/commands.js +16 -6
  283. package/src/server/api/config-registry.js +206 -59
  284. package/src/server/api/config-sync.js +3 -2
  285. package/src/server/api/config-templates.js +20 -4
  286. package/src/server/api/config.js +111 -102
  287. package/src/server/api/convert.js +54 -178
  288. package/src/server/api/dashboard.js +168 -102
  289. package/src/server/api/hooks.js +1 -1
  290. package/src/server/api/mcp.js +224 -84
  291. package/src/server/api/platform-api-config.js +102 -0
  292. package/src/server/api/platform-route-factory.js +215 -0
  293. package/src/server/api/platform-routes.js +26 -0
  294. package/src/server/api/platforms.js +110 -0
  295. package/src/server/api/plugins.js +57 -57
  296. package/src/server/api/project-config.js +168 -0
  297. package/src/server/api/prompts.js +58 -45
  298. package/src/server/api/settings.js +55 -8
  299. package/src/server/api/skills.js +262 -133
  300. package/src/server/api/ui-config.js +2 -2
  301. package/src/server/api/validation-errors.js +18 -4
  302. package/src/server/api/workspaces.js +47 -12
  303. package/src/server/index.js +164 -213
  304. package/src/server/platform-context.js +81 -0
  305. package/src/server/services/channel-balance.js +11 -20
  306. package/src/server/services/channel-health.js +21 -6
  307. package/src/server/services/channel-scheduler.js +29 -39
  308. package/src/server/services/channel-sync-utils.js +23 -13
  309. package/src/server/services/config-export-service.js +350 -319
  310. package/src/server/services/config-registry-service.js +280 -54
  311. package/src/server/services/config-sync-manager.js +172 -944
  312. package/src/server/services/config-templates-service.js +419 -353
  313. package/src/server/services/control-manifest-store.js +206 -0
  314. package/src/server/services/dashboard-snapshot-worker.js +656 -100
  315. package/src/server/services/effective-control-service.js +557 -0
  316. package/src/server/services/local-resource-index.js +314 -0
  317. package/src/server/services/mcp-service.js +607 -977
  318. package/src/server/services/model-detector.js +15 -10
  319. package/src/server/services/native-log-channel-resolver.js +214 -0
  320. package/src/server/services/native-log-observer.js +291 -0
  321. package/src/server/services/network-access.js +1 -1
  322. package/src/server/services/platform-catalog.js +82 -0
  323. package/src/server/services/platform-resolution.js +32 -29
  324. package/src/server/services/plugins-service.js +376 -122
  325. package/src/server/services/project-config-service.js +550 -0
  326. package/src/server/services/project-path-validation.js +31 -0
  327. package/src/server/services/prompts-service.js +267 -233
  328. package/src/server/services/proxy-runtime.js +7 -4
  329. package/src/server/services/remote-credential-cache.js +43 -0
  330. package/src/server/services/repo-scanner-base.js +182 -75
  331. package/src/server/services/request-logger.js +7 -4
  332. package/src/server/services/session-history-index.js +780 -213
  333. package/src/server/services/session-history-worker.js +228 -33
  334. package/src/server/services/session-snapshot-worker.js +124 -34
  335. package/src/server/services/session-snapshots.js +4 -2
  336. package/src/server/services/skill-artifact-store.js +355 -0
  337. package/src/server/services/skill-format-adapters.js +65 -0
  338. package/src/server/services/skill-projection-service.js +323 -0
  339. package/src/server/services/skill-refresh-task-service.js +275 -0
  340. package/src/server/services/skill-service.js +2020 -677
  341. package/src/server/services/snapshot-cache.js +30 -8
  342. package/src/server/services/speed-test.js +8 -8
  343. package/src/server/services/sqlite-connection.js +19 -3
  344. package/src/server/services/statistics-service.js +240 -57
  345. package/src/server/services/ui-config.js +107 -76
  346. package/src/server/services/usage-log-utils.js +98 -0
  347. package/src/server/services/web-build.js +1 -1
  348. package/src/server/services/workspace-service.js +59 -29
  349. package/src/server/utils/pricing.js +10 -19
  350. package/src/server/websocket-server.js +153 -28
  351. package/src/{server/services/base → shared}/base-channel-service.js +156 -28
  352. package/src/shared/capability-driver.js +104 -0
  353. package/src/shared/channel-auth-policy.js +27 -0
  354. package/src/shared/channel-driver.js +161 -0
  355. package/src/shared/driver-factories/api.js +180 -0
  356. package/src/shared/driver-factories/mcp.js +244 -0
  357. package/src/shared/driver-factories/projects.js +121 -0
  358. package/src/shared/driver-factories/prompts.js +58 -0
  359. package/src/shared/driver-factories/resource-sync.js +375 -0
  360. package/src/shared/driver-result.js +78 -0
  361. package/src/shared/mcp-format.js +189 -0
  362. package/src/shared/platforms.js +104 -167
  363. package/src/shared/project-config.js +512 -0
  364. package/src/shared/proxy-driver.js +57 -0
  365. package/src/{server/services/base → shared}/response-usage-parser.js +8 -4
  366. package/src/ui/menu.js +91 -67
  367. package/src/utils/port-helper.js +35 -19
  368. package/src/utils/session.js +1 -1
  369. package/dist/web/assets/Analytics-COEUXmd4.js +0 -25
  370. package/dist/web/assets/Analytics-RNn1BUbG.css +0 -1
  371. package/dist/web/assets/ConfigTemplates-DLLkS53H.js +0 -1
  372. package/dist/web/assets/Home-DCEyLzwX.js +0 -1
  373. package/dist/web/assets/Home-DYVy4cGZ.css +0 -1
  374. package/dist/web/assets/PluginManager-Oe1ZHoMO.js +0 -1
  375. package/dist/web/assets/ProjectList-CWjuAFof.js +0 -1
  376. package/dist/web/assets/ProjectList-VeMNXfgO.css +0 -1
  377. package/dist/web/assets/SessionList-C4chCPxK.css +0 -1
  378. package/dist/web/assets/SessionList-v3ehGg9U.js +0 -1
  379. package/dist/web/assets/SkillManager-BsX8Qpj0.css +0 -1
  380. package/dist/web/assets/SkillManager-DUF_qP5S.js +0 -1
  381. package/dist/web/assets/WorkspaceManager-CjAWBosO.js +0 -1
  382. package/dist/web/assets/icons-nGdSKQn2.js +0 -1
  383. package/dist/web/assets/index-D5hyHPj7.js +0 -2
  384. package/dist/web/assets/index-bJRG3RXH.css +0 -1
  385. package/dist/web/assets/markdown-BNJfEgCA.js +0 -1
  386. package/dist/web/assets/markdown-BfC0goYb.css +0 -10
  387. package/dist/web/assets/naive-ui-BALVpMgd.js +0 -1
  388. package/dist/web/assets/vendors-DQA4Hw9v.js +0 -7
  389. package/dist/web/assets/vue-vendor-B5RqjEjQ.js +0 -45
  390. package/src/config/model-pricing.js +0 -35
  391. package/src/server/api/channels.js +0 -439
  392. package/src/server/api/claude-hooks.js +0 -56
  393. package/src/server/api/claude-statistics.js +0 -57
  394. package/src/server/api/codex-channels.js +0 -491
  395. package/src/server/api/codex-projects.js +0 -117
  396. package/src/server/api/codex-proxy.js +0 -293
  397. package/src/server/api/codex-sessions.js +0 -652
  398. package/src/server/api/codex-statistics.js +0 -57
  399. package/src/server/api/gemini-channels.js +0 -392
  400. package/src/server/api/gemini-projects.js +0 -103
  401. package/src/server/api/gemini-proxy.js +0 -231
  402. package/src/server/api/gemini-sessions.js +0 -550
  403. package/src/server/api/gemini-statistics.js +0 -57
  404. package/src/server/api/health-check.js +0 -31
  405. package/src/server/api/oauth-credentials.js +0 -198
  406. package/src/server/api/omp-channels.js +0 -575
  407. package/src/server/api/omp-projects.js +0 -88
  408. package/src/server/api/omp-proxy.js +0 -125
  409. package/src/server/api/omp-sessions.js +0 -386
  410. package/src/server/api/omp-statistics.js +0 -40
  411. package/src/server/api/opencode-channels.js +0 -471
  412. package/src/server/api/opencode-projects.js +0 -110
  413. package/src/server/api/opencode-proxy.js +0 -354
  414. package/src/server/api/opencode-sessions.js +0 -466
  415. package/src/server/api/opencode-statistics.js +0 -57
  416. package/src/server/api/projects.js +0 -120
  417. package/src/server/api/proxy.js +0 -330
  418. package/src/server/api/sessions.js +0 -810
  419. package/src/server/services/oauth-credentials-service.js +0 -945
  420. package/src/server/services/omp-session-log-observer.js +0 -138
  421. package/src/server/services/opencode-gateway-adapters.js +0 -43
  422. package/src/server/services/project-snapshots.js +0 -59
  423. package/src/server/services/proxy-log-helper.js +0 -298
  424. package/src/server/services/session-cache.js +0 -127
  425. package/src/server/services/session-converter.js +0 -582
  426. package/src/server/services/session-history-adapters/claude.js +0 -235
  427. /package/src/{server/services/claude-token-recovery.js → platforms/drivers/claude/token-recovery.js} +0 -0
  428. /package/src/{server/services/codex-parser.js → platforms/drivers/codex/parser.js} +0 -0
  429. /package/src/{server/services/codex-speed-test-template.json → platforms/drivers/codex/speed-test-template.json} +0 -0
  430. /package/src/{server/services/codex-wire.js → platforms/drivers/codex/wire.js} +0 -0
  431. /package/src/{server/services/omp-auth-gateway-client.js → platforms/drivers/omp/auth-gateway-client.js} +0 -0
  432. /package/src/{server/services/opencode-normalization.js → platforms/drivers/opencode/normalization.js} +0 -0
  433. /package/src/{server/services → shared}/config-artifact-paths.js +0 -0
  434. /package/src/{server/services/base → shared}/proxy-utils.js +0 -0
@@ -10,16 +10,93 @@ const toml = require('toml');
10
10
  const tomlStringify = require('@iarna/toml').stringify;
11
11
  const yaml = require('js-yaml');
12
12
  const configTemplatesService = require('./config-templates-service');
13
- const channelsService = require('./channels');
14
- const codexChannelsService = require('./codex-channels');
15
- const geminiChannelsService = require('./gemini-channels');
16
- const opencodeChannelsService = require('./opencode-channels');
17
- const ompChannelsService = require('./omp-channels');
18
- const { AgentsService } = require('./agents-service');
19
- const { CommandsService } = require('./commands-service');
13
+ const { AgentsService } = require('../../platforms/agents-service');
14
+ const { CommandsService } = require('../../platforms/commands-service');
20
15
  const { SkillService } = require('./skill-service');
21
16
  const { PluginsService } = require('./plugins-service');
22
17
  const { PATHS, NATIVE_PATHS } = require('../../config/paths');
18
+ const { getPlatformCatalog } = require('./platform-catalog');
19
+ const { getPlatformContext } = require('../platform-context');
20
+
21
+ function getChannelDriver(platform) {
22
+ return getPlatformCatalog().driver(platform, 'channels');
23
+ }
24
+
25
+ function getExportRegistry() {
26
+ try {
27
+ return getPlatformContext().registry;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ function getPlatformPathContext(platform, registry = getExportRegistry()) {
34
+ try {
35
+ return registry?.resolvePathContext?.(platform) || null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function getConfiguredNativePaths(platform, registry = getExportRegistry()) {
42
+ const pathContext = getPlatformPathContext(platform, registry);
43
+ return pathContext?.customized ? (pathContext.native || {}) : (NATIVE_PATHS[platform] || {});
44
+ }
45
+
46
+ function getConfiguredStatePath(category, platform, registry = getExportRegistry()) {
47
+ const pathContext = getPlatformPathContext(platform, registry);
48
+ if (pathContext?.customized && pathContext.state?.[category]) {
49
+ return pathContext.state[category];
50
+ }
51
+ try {
52
+ const pathsModule = require('../../config/paths');
53
+ if (typeof pathsModule.getPlatformStatePath === 'function') {
54
+ return pathsModule.getPlatformStatePath(category, platform);
55
+ }
56
+ } catch {
57
+ // Fall back to the built-in maps for isolated tests.
58
+ }
59
+ return PATHS[category]?.[platform];
60
+ }
61
+
62
+ function getPlatformPluginPaths(platform, registry = getExportRegistry()) {
63
+ const native = getConfiguredNativePaths(platform, registry);
64
+ if (platform === 'claude') {
65
+ const pluginsDir = native.plugins
66
+ || path.join(native.dir || path.dirname(native.settings || CLAUDE_SETTINGS_PATH), 'plugins');
67
+ return {
68
+ pluginsDir,
69
+ installedRegistry: path.join(pluginsDir, 'installed_plugins.json'),
70
+ marketplacesRegistry: path.join(pluginsDir, 'known_marketplaces.json'),
71
+ cacheDir: path.join(pluginsDir, 'cache')
72
+ };
73
+ }
74
+ if (platform === 'codex') {
75
+ const pluginsDir = path.join(native.dir || path.dirname(native.config || NATIVE_PATHS.codex.config), 'plugins');
76
+ return {
77
+ pluginsDir,
78
+ cacheDir: path.join(pluginsDir, 'cache'),
79
+ configPath: native.config || NATIVE_PATHS.codex.config
80
+ };
81
+ }
82
+ if (platform === 'opencode') {
83
+ const pluginsDir = path.join(native.config || NATIVE_PATHS.opencode.config, 'plugins');
84
+ return {
85
+ pluginsDir,
86
+ legacyPluginsDir: path.join(native.config || NATIVE_PATHS.opencode.config, 'plugin'),
87
+ configDir: native.config || NATIVE_PATHS.opencode.config
88
+ };
89
+ }
90
+ if (platform === 'omp') {
91
+ const root = native.dir || NATIVE_PATHS.omp.dir;
92
+ return {
93
+ extensionsDir: native.extensions || path.join(root, 'extensions'),
94
+ settingsPath: native.settings || native.config || path.join(root, 'config.yml')
95
+ };
96
+ }
97
+ return {};
98
+ }
99
+
23
100
 
24
101
  const CONFIG_VERSION = '1.4.0';
25
102
  const SKILL_FILE_ENCODING = 'base64';
@@ -52,12 +129,50 @@ const CC_UI_CONFIG_PATH = PATHS.uiConfig;
52
129
  const CC_PROMPTS_PATH = PATHS.prompts;
53
130
  const CC_SECURITY_PATH = PATHS.security;
54
131
  const LEGACY_UI_CONFIG_PATH = PATHS.uiConfig;
55
- const LEGACY_NOTIFY_HOOK_PATH = PATHS.notifyHook;
56
132
  const GEMINI_SETTINGS_PATH = path.join(path.dirname(NATIVE_PATHS.gemini.env), 'settings.json');
57
- const AGENT_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode'];
58
- const COMMAND_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
59
- const SKILL_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
60
- const PLUGIN_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
133
+ const LEGACY_NOTIFY_HOOK_PATH = PATHS.notifyHook;
134
+ function getPlatformKeysForType(type, registry) {
135
+ if (registry && typeof registry.list === 'function') {
136
+ return registry.list({ enabledOnly: true })
137
+ .filter(platform => platform && platform.key && platform.capabilities?.resourceSync !== 'unsupported')
138
+ .filter(platform => platform.resourceTypes?.[type] !== false)
139
+ .map(platform => platform.key);
140
+ }
141
+ return getPlatformCatalog().keys({ resourceType: type });
142
+ }
143
+
144
+ function getAgentPlatforms(registry = getExportRegistry()) {
145
+ return getPlatformKeysForType('agents', registry);
146
+ }
147
+
148
+ function getCommandPlatforms(registry = getExportRegistry()) {
149
+ return getPlatformKeysForType('commands', registry);
150
+ }
151
+
152
+ function getSkillPlatforms(registry = getExportRegistry()) {
153
+ return getPlatformKeysForType('skills', registry);
154
+ }
155
+
156
+ function getPluginPlatforms(registry = getExportRegistry()) {
157
+ if (registry && typeof registry.list === 'function') {
158
+ return registry.list({ enabledOnly: true })
159
+ .filter(platform => platform && platform.key && platform.capabilities?.resourceSync !== 'unsupported')
160
+ .map(platform => platform.key);
161
+ }
162
+ return getPlatformCatalog().keys({ capability: 'resourceSync' });
163
+ }
164
+
165
+ function exportPlatformSnapshots({ registry, exportByPlatform } = {}) {
166
+ if (typeof exportByPlatform !== 'function') return {};
167
+ const platforms = registry && typeof registry.list === 'function'
168
+ ? registry.list({ enabledOnly: true })
169
+ : getPlatformCatalog().list({ enabledOnly: true });
170
+ return Object.fromEntries(
171
+ platforms
172
+ .filter(platform => platform && platform.key)
173
+ .map(platform => [platform.key, exportByPlatform(platform.key)])
174
+ );
175
+ }
61
176
  const CLAUDE_MARKETPLACES_REGISTRY = path.join(CLAUDE_PLUGINS_DIR, 'known_marketplaces.json');
62
177
  const CODEX_PLUGINS_DIR = path.join(path.dirname(NATIVE_PATHS.codex.config), 'plugins');
63
178
  const CODEX_PLUGINS_CACHE_DIR = path.join(CODEX_PLUGINS_DIR, 'cache');
@@ -111,7 +226,7 @@ function normalizeOmpResourceTypes(input = []) {
111
226
 
112
227
  function getOpenCodeConfigPaths() {
113
228
  try {
114
- const { CONFIG_PATHS } = require('./opencode-settings-manager');
229
+ const { CONFIG_PATHS } = require('../../platforms/drivers/opencode/native-config-implementation');
115
230
  return CONFIG_PATHS || {};
116
231
  } catch (err) {
117
232
  return {};
@@ -120,52 +235,13 @@ function getOpenCodeConfigPaths() {
120
235
 
121
236
  function getOpenCodeNotificationPluginPath() {
122
237
  try {
123
- const { getOpenCodeManagedPluginPath } = require('./notification-hooks');
238
+ const { getOpenCodeManagedPluginPath } = require('../../platforms/notification-hooks');
124
239
  return typeof getOpenCodeManagedPluginPath === 'function' ? getOpenCodeManagedPluginPath() : '';
125
240
  } catch (err) {
126
241
  return '';
127
242
  }
128
243
  }
129
244
 
130
- function getNativeConfigSpecs() {
131
- const openCodeConfigPaths = getOpenCodeConfigPaths();
132
- const openCodeNotificationPluginPath = getOpenCodeNotificationPluginPath();
133
- const ompDir = NATIVE_PATHS.omp.dir || path.dirname(NATIVE_PATHS.omp.settings);
134
- return {
135
- claude: {
136
- settings: { path: NATIVE_PATHS.claude.settings, format: 'json' }
137
- },
138
- codex: {
139
- config: { path: NATIVE_PATHS.codex.config, format: 'text' },
140
- auth: { path: NATIVE_PATHS.codex.auth, format: 'json', mode: 0o600 }
141
- },
142
- gemini: {
143
- env: { path: NATIVE_PATHS.gemini.env, format: 'text', mode: 0o600 },
144
- settings: { path: GEMINI_SETTINGS_PATH, format: 'json' }
145
- },
146
- opencode: {
147
- opencodeJsonc: { path: openCodeConfigPaths.opencodec, format: 'text' },
148
- opencodeJson: { path: openCodeConfigPaths.opencode, format: 'text' },
149
- configJson: { path: openCodeConfigPaths.config, format: 'text' },
150
- ...(openCodeNotificationPluginPath
151
- ? { codingToolNotifyPlugin: { path: openCodeNotificationPluginPath, format: 'text' } }
152
- : {})
153
- },
154
- omp: {
155
- settings: { path: NATIVE_PATHS.omp.settings, format: 'yaml' },
156
- auth: { path: NATIVE_PATHS.omp.auth, format: 'json', mode: 0o600 },
157
- models: { path: NATIVE_PATHS.omp.models, format: 'yaml' },
158
- commands: { path: NATIVE_PATHS.omp.commands || path.join(ompDir, 'commands'), format: 'directory' },
159
- prompts: { path: NATIVE_PATHS.omp.prompts, format: 'directory' },
160
- skills: { path: NATIVE_PATHS.omp.skills, format: 'directory' },
161
- extensions: { path: NATIVE_PATHS.omp.extensions, format: 'directory' },
162
- themes: { path: NATIVE_PATHS.omp.themes || path.join(ompDir, 'themes'), format: 'directory' },
163
- packages: { path: NATIVE_PATHS.omp.packages || path.join(ompDir, 'packages'), format: 'directory' },
164
- npmPackages: { path: path.join(ompDir, 'npm'), format: 'directory' },
165
- gitPackages: { path: path.join(ompDir, 'git'), format: 'directory' }
166
- }
167
- };
168
- }
169
245
 
170
246
  function ensureDir(dirPath) {
171
247
  if (!fs.existsSync(dirPath)) {
@@ -531,29 +607,41 @@ function buildCommandExportItem(command, platform) {
531
607
  };
532
608
  }
533
609
 
534
- function exportAgentsSnapshotByPlatform() {
535
- return AGENT_PLATFORMS.reduce((result, platform) => {
610
+ function exportAgentsSnapshotByPlatform(registry = getExportRegistry()) {
611
+ return getAgentPlatforms(registry).reduce((result, platform) => {
612
+ let agentsService;
536
613
  try {
537
- const agentsService = new AgentsService(platform);
614
+ agentsService = new AgentsService(platform, { registry });
538
615
  const { agents: rawAgents = [] } = agentsService.listAgents();
539
- result[platform] = rawAgents.map(agent => buildAgentExportItem(agent, platform));
616
+ result[platform] = rawAgents.map(agent => {
617
+ const detail = typeof agentsService.getAgent === 'function' ? (agentsService.getAgent(agent.fileName, agent.scope || 'user') || agent) : agent;
618
+ return buildAgentExportItem({ ...agent, ...detail }, platform);
619
+ });
540
620
  } catch (err) {
541
621
  console.warn(`[ConfigExport] Failed to export agents for ${platform}:`, err.message);
542
622
  result[platform] = [];
623
+ } finally {
624
+ agentsService?.dispose?.();
543
625
  }
544
626
  return result;
545
627
  }, {});
546
628
  }
547
629
 
548
- function exportCommandsSnapshotByPlatform() {
549
- return COMMAND_PLATFORMS.reduce((result, platform) => {
630
+ function exportCommandsSnapshotByPlatform(registry = getExportRegistry()) {
631
+ return getCommandPlatforms(registry).reduce((result, platform) => {
632
+ let commandsService;
550
633
  try {
551
- const commandsService = new CommandsService(platform);
634
+ commandsService = new CommandsService(platform, { registry });
552
635
  const { commands: rawCommands = [] } = commandsService.listCommands();
553
- result[platform] = rawCommands.map(command => buildCommandExportItem(command, platform));
636
+ result[platform] = rawCommands.map(command => {
637
+ const detail = typeof commandsService.getCommand === 'function' ? (commandsService.getCommand(command.name, command.scope || 'user', null, command.namespace) || command) : command;
638
+ return buildCommandExportItem({ ...command, ...detail }, platform);
639
+ });
554
640
  } catch (err) {
555
641
  console.warn(`[ConfigExport] Failed to export commands for ${platform}:`, err.message);
556
642
  result[platform] = [];
643
+ } finally {
644
+ commandsService?.dispose?.();
557
645
  }
558
646
  return result;
559
647
  }, {});
@@ -716,71 +804,33 @@ function buildOpenCodeNativeConfig(channels = []) {
716
804
  }
717
805
 
718
806
  function syncImportedChannelsToNativeConfigs(importChannelsByType, nativeConfigs, overwrite) {
719
- const hasNativeConfigFor = (platform) => !!nativeConfigs?.[platform] && Object.keys(nativeConfigs[platform]).length > 0;
720
-
721
- try {
722
- if (!hasNativeConfigFor('claude')) {
723
- const primaryClaudeChannel = pickPrimaryChannel(importChannelsByType.claude);
724
- const persistedClaudeChannel = findMatchingPersistedChannel(
725
- channelsService.getAllChannels?.() || [],
726
- primaryClaudeChannel,
727
- [
728
- (channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
729
- ]
730
- );
731
- if (persistedClaudeChannel?.id) {
732
- ensureDir(path.dirname(NATIVE_PATHS.claude.settings));
733
- channelsService.applyChannelToSettings(persistedClaudeChannel.id);
734
- }
807
+ const catalog = getPlatformCatalog();
808
+ for (const platform of catalog.keys({ capability: 'channels' })) {
809
+ const channels = importChannelsByType?.[platform] || [];
810
+ if (channels.length === 0 || (nativeConfigs?.[platform] && Object.keys(nativeConfigs[platform]).length > 0)) {
811
+ continue;
735
812
  }
736
- } catch (err) {
737
- console.warn('[ConfigImport] Claude native sync fallback failed:', err.message);
738
- }
739
813
 
740
- try {
741
- if (!hasNativeConfigFor('codex') && (importChannelsByType.codex || []).length > 0) {
742
- codexChannelsService.writeCodexConfigForMultiChannel(importChannelsByType.codex || []);
743
- }
744
- } catch (err) {
745
- console.warn('[ConfigImport] Codex native sync fallback failed:', err.message);
746
- }
814
+ try {
815
+ const nativeDriver = catalog.driver(platform, 'nativeConfig');
816
+ if (typeof nativeDriver?.syncChannels === 'function') {
817
+ nativeDriver.syncChannels(channels, { overwrite });
818
+ continue;
819
+ }
747
820
 
748
- try {
749
- if (!hasNativeConfigFor('gemini')) {
750
- const primaryGeminiChannel = pickPrimaryChannel(importChannelsByType.gemini);
751
- const persistedGeminiChannel = findMatchingPersistedChannel(
752
- geminiChannelsService.getChannels?.().channels || [],
753
- primaryGeminiChannel,
754
- [
755
- (channel, imported) => channel.name === imported.name && channel.baseUrl === imported.baseUrl
756
- ]
757
- );
758
- if (persistedGeminiChannel?.id) {
759
- geminiChannelsService.applyChannelToSettings(persistedGeminiChannel.id);
821
+ const channelDriver = catalog.driver(platform, 'channels');
822
+ if (typeof channelDriver?.writeMultiChannelConfig === 'function') {
823
+ channelDriver.writeMultiChannelConfig(channels);
824
+ continue;
760
825
  }
761
- }
762
- } catch (err) {
763
- console.warn('[ConfigImport] Gemini native sync fallback failed:', err.message);
764
- }
765
826
 
766
- try {
767
- if (!hasNativeConfigFor('opencode') && (importChannelsByType.opencode || []).length > 0) {
768
- const openCodeSpecs = getNativeConfigSpecs().opencode || {};
769
- const preferredSpec = [
770
- openCodeSpecs.opencodeJsonc,
771
- openCodeSpecs.opencodeJson,
772
- openCodeSpecs.configJson
773
- ].find(spec => spec?.path && fs.existsSync(spec.path))
774
- || openCodeSpecs.opencodeJson
775
- || openCodeSpecs.configJson;
776
-
777
- if (preferredSpec?.path) {
778
- const payload = buildOpenCodeNativeConfig(importChannelsByType.opencode || []);
779
- writeTextFileAbsolute(preferredSpec.path, JSON.stringify(payload, null, 2), overwrite);
827
+ if (typeof channelDriver?.applyChannelToSettings === 'function') {
828
+ const primary = pickPrimaryChannel(channels);
829
+ if (primary?.id) channelDriver.applyChannelToSettings(primary.id);
780
830
  }
831
+ } catch (err) {
832
+ console.warn(`[ConfigImport] ${platform} native sync fallback failed:`, err.message);
781
833
  }
782
- } catch (err) {
783
- console.warn('[ConfigImport] OpenCode native sync fallback failed:', err.message);
784
834
  }
785
835
  }
786
836
 
@@ -968,8 +1018,8 @@ function readJsonConfigSnapshot(filePath) {
968
1018
 
969
1019
  function exportPluginControlSnapshot(platform, service) {
970
1020
  const snapshot = {};
971
- const reposPath = PATHS.pluginRepos?.[platform];
972
- const marketCachePath = PATHS.pluginMarketCache?.[platform];
1021
+ const reposPath = service?.getReposConfigPath?.() || getConfiguredStatePath('pluginRepos', platform, service?.registry);
1022
+ const marketCachePath = service?.marketCachePath || getConfiguredStatePath('pluginMarketCache', platform, service?.registry);
973
1023
  const repos = readJsonConfigSnapshot(reposPath);
974
1024
  const marketCache = readJsonConfigSnapshot(marketCachePath);
975
1025
 
@@ -993,12 +1043,15 @@ function exportPluginControlSnapshot(platform, service) {
993
1043
  }
994
1044
 
995
1045
  function exportClaudePluginsByPlatform(service) {
1046
+ const pluginsDir = service.claudePluginsDir || CLAUDE_PLUGINS_DIR;
1047
+ const installedRegistryPath = service.claudeInstalledFile || NATIVE_PLUGINS_REGISTRY;
1048
+ const marketplacesRegistryPath = service.claudeMarketplacesFile || CLAUDE_MARKETPLACES_REGISTRY;
996
1049
  const installedPlugins = service.listPlugins().plugins || [];
997
1050
  const plugins = [];
998
1051
 
999
1052
  for (const plugin of installedPlugins) {
1000
1053
  if (plugin.installPath && fs.existsSync(plugin.installPath)) {
1001
- const pluginDir = assertPathInside(CLAUDE_PLUGINS_DIR, plugin.installPath)
1054
+ const pluginDir = assertPathInside(pluginsDir, plugin.installPath)
1002
1055
  || assertPathInside(LEGACY_PLUGINS_DIR, plugin.installPath);
1003
1056
  if (pluginDir) {
1004
1057
  const { manifest, manifestPath } = detectPluginManifest(pluginDir, [
@@ -1018,7 +1071,7 @@ function exportClaudePluginsByPlatform(service) {
1018
1071
  version: manifest.version || plugin.version || '1.0.0',
1019
1072
  description: manifest.description || plugin.description || '',
1020
1073
  author: manifest.author || plugin.author || '',
1021
- directory: path.relative(CLAUDE_PLUGINS_DIR, pluginDir),
1074
+ directory: path.relative(pluginsDir, pluginDir),
1022
1075
  enabled: plugin.enabled !== false,
1023
1076
  installedAt: plugin.installedAt,
1024
1077
  scope: plugin.scope,
@@ -1050,8 +1103,8 @@ function exportClaudePluginsByPlatform(service) {
1050
1103
  }
1051
1104
 
1052
1105
  const control = exportPluginControlSnapshot('claude', service);
1053
- const installedRegistry = readJsonConfigSnapshot(NATIVE_PLUGINS_REGISTRY);
1054
- const knownMarketplaces = readJsonConfigSnapshot(CLAUDE_MARKETPLACES_REGISTRY);
1106
+ const installedRegistry = readJsonConfigSnapshot(installedRegistryPath);
1107
+ const knownMarketplaces = readJsonConfigSnapshot(marketplacesRegistryPath);
1055
1108
  const legacyRegistry = readJsonConfigSnapshot(LEGACY_PLUGINS_REGISTRY);
1056
1109
  if (installedRegistry) control.installedRegistry = installedRegistry;
1057
1110
  if (knownMarketplaces) control.knownMarketplaces = knownMarketplaces;
@@ -1062,14 +1115,16 @@ function exportClaudePluginsByPlatform(service) {
1062
1115
 
1063
1116
  function exportCodexPluginsByPlatform(service) {
1064
1117
  const plugins = (service.listPlugins().plugins || [])
1065
- .map(plugin => buildManagedPluginExportItem(plugin, 'codex', CODEX_PLUGINS_CACHE_DIR, {
1118
+ .map(plugin => buildManagedPluginExportItem(plugin, 'codex', service.codexPluginsCacheDir || CODEX_PLUGINS_CACHE_DIR, {
1066
1119
  type: 'codex-cache',
1067
1120
  pluginType: 'cache',
1068
1121
  manifestCandidates: ['.codex-plugin/plugin.json', 'plugin.json', 'package.json']
1069
1122
  }))
1070
1123
  .filter(Boolean);
1071
1124
  const control = exportPluginControlSnapshot('codex', service);
1072
- const codexConfig = readNativeConfigSnapshot({ path: NATIVE_PATHS.codex.config, format: 'text' });
1125
+ const codexConfig = readNativeConfigSnapshot({ path: service.pathContext?.customized
1126
+ ? service.pathContext.native?.config
1127
+ : NATIVE_PATHS.codex.config, format: 'text' });
1073
1128
  if (codexConfig) {
1074
1129
  control.nativeConfig = codexConfig;
1075
1130
  }
@@ -1077,7 +1132,7 @@ function exportCodexPluginsByPlatform(service) {
1077
1132
  }
1078
1133
 
1079
1134
  function exportOpenCodePluginsByPlatform(service) {
1080
- const pluginsDir = getOpenCodePluginsDir();
1135
+ const pluginsDir = service._getOpenCodePluginsDir?.() || getOpenCodePluginsDir();
1081
1136
  const plugins = (service.listPlugins().plugins || [])
1082
1137
  .map((plugin) => {
1083
1138
  if (plugin.pluginType === 'npm' || plugin.source === 'opencode-config') {
@@ -1109,6 +1164,9 @@ function exportOpenCodePluginsByPlatform(service) {
1109
1164
  }
1110
1165
 
1111
1166
  function exportOmpPluginsByPlatform(service) {
1167
+ const extensionsDir = service.pathContext?.customized
1168
+ ? (service.pathContext.native?.extensions || path.join(service.pathContext.native?.dir || '', 'extensions'))
1169
+ : OMP_EXTENSIONS_DIR;
1112
1170
  const plugins = (service.listPlugins().plugins || [])
1113
1171
  .map((plugin) => {
1114
1172
  if (plugin.pluginType === 'package' || plugin.source === 'omp-settings') {
@@ -1126,7 +1184,7 @@ function exportOmpPluginsByPlatform(service) {
1126
1184
  source: plugin.source || 'omp-settings'
1127
1185
  };
1128
1186
  }
1129
- return buildManagedPluginExportItem(plugin, 'omp', OMP_EXTENSIONS_DIR, {
1187
+ return buildManagedPluginExportItem(plugin, 'omp', extensionsDir, {
1130
1188
  type: 'omp-extension',
1131
1189
  pluginType: plugin.pluginType || 'extension',
1132
1190
  pluginKind: plugin.pluginKind || 'extension',
@@ -1138,10 +1196,10 @@ function exportOmpPluginsByPlatform(service) {
1138
1196
  return { plugins, control };
1139
1197
  }
1140
1198
 
1141
- function exportPluginsSnapshotByPlatform() {
1142
- return PLUGIN_PLATFORMS.reduce((result, platform) => {
1199
+ function exportPluginsSnapshotByPlatform(registry = getExportRegistry()) {
1200
+ return getPluginPlatforms(registry).reduce((result, platform) => {
1143
1201
  try {
1144
- const service = new PluginsService(platform);
1202
+ const service = new PluginsService(platform, { registry });
1145
1203
  if (platform === 'claude') {
1146
1204
  result[platform] = exportClaudePluginsByPlatform(service);
1147
1205
  } else if (platform === 'codex') {
@@ -1164,8 +1222,8 @@ function exportPluginsSnapshotByPlatform() {
1164
1222
  }, {});
1165
1223
  }
1166
1224
 
1167
- function exportSkillsSnapshot(platform = 'claude') {
1168
- const skillService = new SkillService(platform);
1225
+ function exportSkillsSnapshot(platform = 'claude', registry = getExportRegistry()) {
1226
+ const skillService = new SkillService(platform, { registry });
1169
1227
  const installedSkills = skillService.getInstalledSkills();
1170
1228
  const baseDir = skillService.installDir;
1171
1229
 
@@ -1185,10 +1243,10 @@ function exportSkillsSnapshot(platform = 'claude') {
1185
1243
  }).filter(Boolean);
1186
1244
  }
1187
1245
 
1188
- function exportSkillsSnapshotByPlatform() {
1189
- return SKILL_PLATFORMS.reduce((result, platform) => {
1246
+ function exportSkillsSnapshotByPlatform(registry = getExportRegistry()) {
1247
+ return getSkillPlatforms(registry).reduce((result, platform) => {
1190
1248
  try {
1191
- result[platform] = exportSkillsSnapshot(platform);
1249
+ result[platform] = exportSkillsSnapshot(platform, registry);
1192
1250
  } catch (err) {
1193
1251
  console.warn(`[ConfigExport] Failed to export skills for ${platform}:`, err.message);
1194
1252
  result[platform] = [];
@@ -1197,22 +1255,20 @@ function exportSkillsSnapshotByPlatform() {
1197
1255
  }, {});
1198
1256
  }
1199
1257
 
1200
- function exportNativeConfigs() {
1201
- const specs = getNativeConfigSpecs();
1202
- return Object.entries(specs).reduce((result, [platform, platformSpecs]) => {
1203
- const exportedEntries = Object.entries(platformSpecs).reduce((entries, [key, spec]) => {
1204
- const snapshot = readNativeConfigSnapshot(spec);
1205
- if (snapshot) {
1206
- entries[key] = snapshot;
1207
- }
1208
- return entries;
1209
- }, {});
1210
-
1211
- if (Object.keys(exportedEntries).length > 0) {
1212
- result[platform] = exportedEntries;
1213
- }
1214
- return result;
1215
- }, {});
1258
+ function exportNativeConfigs(registry = getExportRegistry()) {
1259
+ const result = {};
1260
+ const platforms = registry && typeof registry.list === 'function'
1261
+ ? registry.list({ enabledOnly: true })
1262
+ .filter(platform => platform?.key && platform.capabilities?.nativeConfig !== 'unsupported')
1263
+ .map(platform => platform.key)
1264
+ : getPlatformCatalog().keys({ capability: 'nativeConfig' });
1265
+ for (const platform of platforms) {
1266
+ const driver = getPlatformCatalog().driver(platform, 'nativeConfig');
1267
+ if (!driver || typeof driver.exportSnapshot !== 'function') continue;
1268
+ const snapshot = driver.exportSnapshot();
1269
+ if (snapshot && Object.keys(snapshot).length > 0) result[platform] = snapshot;
1270
+ }
1271
+ return result;
1216
1272
  }
1217
1273
 
1218
1274
  function exportLegacyPlugins() {
@@ -1322,14 +1378,15 @@ function exportPluginsSnapshot() {
1322
1378
  return [...legacyPlugins, ...nativePlugins];
1323
1379
  }
1324
1380
 
1325
- function getPluginsByPlatformItems(plugins = [], pluginsByPlatform = {}) {
1326
- const result = Object.fromEntries(PLUGIN_PLATFORMS.map(platform => [platform, []]));
1381
+ function getPluginsByPlatformItems(plugins = [], pluginsByPlatform = {}, registry = getExportRegistry()) {
1382
+ const supportedPlatforms = getPluginPlatforms(registry);
1383
+ const result = Object.fromEntries(supportedPlatforms.map(platform => [platform, []]));
1327
1384
  const hasStructuredPlugins = pluginsByPlatform
1328
1385
  && typeof pluginsByPlatform === 'object'
1329
1386
  && Object.keys(pluginsByPlatform).length > 0;
1330
1387
 
1331
1388
  if (hasStructuredPlugins) {
1332
- for (const platform of PLUGIN_PLATFORMS) {
1389
+ for (const platform of supportedPlatforms) {
1333
1390
  const platformSnapshot = pluginsByPlatform[platform];
1334
1391
  const platformPlugins = Array.isArray(platformSnapshot?.plugins)
1335
1392
  ? platformSnapshot.plugins
@@ -1343,7 +1400,10 @@ function getPluginsByPlatformItems(plugins = [], pluginsByPlatform = {}) {
1343
1400
 
1344
1401
  if (!hasStructuredPlugins && Array.isArray(plugins)) {
1345
1402
  for (const plugin of plugins) {
1346
- const platform = PLUGIN_PLATFORMS.includes(plugin?.platform) ? plugin.platform : 'claude';
1403
+ const platform = supportedPlatforms.includes(plugin?.platform)
1404
+ ? plugin.platform
1405
+ : (supportedPlatforms.includes('claude') ? 'claude' : null);
1406
+ if (!platform) continue;
1347
1407
  result[platform].push({ ...plugin, platform });
1348
1408
  }
1349
1409
  }
@@ -1439,9 +1499,9 @@ function mergeTomlFile(filePath, updates = {}, overwrite = true) {
1439
1499
  return 'success';
1440
1500
  }
1441
1501
 
1442
- function mergePluginRepoConfig(platform, snapshot = {}, overwrite = true) {
1443
- const reposPath = PATHS.pluginRepos?.[platform];
1444
- const marketCachePath = PATHS.pluginMarketCache?.[platform];
1502
+ function mergePluginRepoConfig(platform, snapshot = {}, overwrite = true, registry = getExportRegistry()) {
1503
+ const reposPath = getConfiguredStatePath('pluginRepos', platform, registry);
1504
+ const marketCachePath = getConfiguredStatePath('pluginMarketCache', platform, registry);
1445
1505
  let changed = 0;
1446
1506
  let skipped = 0;
1447
1507
  let failed = 0;
@@ -1465,10 +1525,10 @@ function mergePluginRepoConfig(platform, snapshot = {}, overwrite = true) {
1465
1525
  return { changed, skipped, failed };
1466
1526
  }
1467
1527
 
1468
- function mergeCodexPluginConfig(snapshot = {}, plugins = [], overwrite = true) {
1528
+ function mergeCodexPluginConfig(snapshot = {}, plugins = [], overwrite = true, configPath = NATIVE_PATHS.codex.config) {
1469
1529
  const nativeConfig = snapshot.control?.nativeConfig;
1470
1530
  if (nativeConfig?.content !== undefined) {
1471
- return writeNativeConfigAbsolute({ path: NATIVE_PATHS.codex.config, format: 'text' }, nativeConfig, overwrite);
1531
+ return writeNativeConfigAbsolute({ path: configPath, format: 'text' }, nativeConfig, overwrite);
1472
1532
  }
1473
1533
 
1474
1534
  const pluginEntries = {};
@@ -1490,13 +1550,13 @@ function mergeCodexPluginConfig(snapshot = {}, plugins = [], overwrite = true) {
1490
1550
  return 'skipped';
1491
1551
  }
1492
1552
 
1493
- return mergeTomlFile(NATIVE_PATHS.codex.config, {
1553
+ return mergeTomlFile(configPath, {
1494
1554
  ...(Object.keys(pluginEntries).length > 0 ? { plugins: pluginEntries } : {}),
1495
1555
  ...(Object.keys(marketplaces).length > 0 ? { marketplaces } : {})
1496
1556
  }, overwrite);
1497
1557
  }
1498
1558
 
1499
- function writeOpenCodePluginConfig(packages = [], overwrite = true) {
1559
+ function writeOpenCodePluginConfig(packages = [], overwrite = true, configDir = NATIVE_PATHS.opencode.config) {
1500
1560
  if (packages.length === 0) return 'skipped';
1501
1561
  const openCodePaths = getOpenCodeConfigPaths();
1502
1562
  const configPath = [
@@ -1505,7 +1565,7 @@ function writeOpenCodePluginConfig(packages = [], overwrite = true) {
1505
1565
  openCodePaths.config
1506
1566
  ].find(filePath => filePath && fs.existsSync(filePath))
1507
1567
  || openCodePaths.opencode
1508
- || path.join(NATIVE_PATHS.opencode.config, 'opencode.json');
1568
+ || path.join(configDir, 'opencode.json');
1509
1569
  const existing = readJsonFileSafe(configPath) || {};
1510
1570
  if (fs.existsSync(configPath) && !overwrite) {
1511
1571
  return 'skipped';
@@ -1515,17 +1575,17 @@ function writeOpenCodePluginConfig(packages = [], overwrite = true) {
1515
1575
  return writeJsonFileAbsolute(configPath, { ...existing, plugin: mergedPlugins }, true);
1516
1576
  }
1517
1577
 
1518
- function writeOmpPluginSettings(plugins = [], snapshot = {}, overwrite = true) {
1578
+ function writeOmpPluginSettings(plugins = [], snapshot = {}, overwrite = true, settingsPath = OMP_SETTINGS_PATH) {
1519
1579
  const nativeSettings = snapshot.control?.nativeSettings;
1520
1580
  if (nativeSettings?.content !== undefined) {
1521
- return writeNativeConfigAbsolute({ path: OMP_SETTINGS_PATH, format: 'yaml' }, {
1581
+ return writeNativeConfigAbsolute({ path: settingsPath, format: 'yaml' }, {
1522
1582
  ...nativeSettings,
1523
1583
  format: 'yaml'
1524
1584
  }, overwrite);
1525
1585
  }
1526
1586
 
1527
- const settings = readYamlFileSafe(OMP_SETTINGS_PATH) || {};
1528
- if (fs.existsSync(OMP_SETTINGS_PATH) && !overwrite) {
1587
+ const settings = readYamlFileSafe(settingsPath) || {};
1588
+ if (fs.existsSync(settingsPath) && !overwrite) {
1529
1589
  return 'skipped';
1530
1590
  }
1531
1591
  const existingPackages = Array.isArray(settings.packages) ? settings.packages : [];
@@ -1573,14 +1633,14 @@ function writeOmpPluginSettings(plugins = [], snapshot = {}, overwrite = true) {
1573
1633
  return 'skipped';
1574
1634
  }
1575
1635
 
1576
- return writeYamlFileAbsolute(OMP_SETTINGS_PATH, {
1636
+ return writeYamlFileAbsolute(settingsPath, {
1577
1637
  ...settings,
1578
1638
  packages: mergePackageEntries(existingPackages, packages),
1579
1639
  disabledPackages: mergePackageEntries(existingDisabled, disabledPackages)
1580
1640
  }, true);
1581
1641
  }
1582
1642
 
1583
- function importPluginToDirectory(platform, plugin, baseDir, overwrite) {
1643
+ function importPluginToDirectory(platform, plugin, baseDir, overwrite, options = {}) {
1584
1644
  const files = Array.isArray(plugin.files) ? plugin.files : [];
1585
1645
  if (files.length === 0) {
1586
1646
  return 'skipped';
@@ -1623,7 +1683,7 @@ function importPluginToDirectory(platform, plugin, baseDir, overwrite) {
1623
1683
  }
1624
1684
 
1625
1685
  if (platform === 'claude') {
1626
- updateClaudePluginRegistry(plugin, pluginDir);
1686
+ updateClaudePluginRegistry(plugin, pluginDir, options);
1627
1687
  }
1628
1688
 
1629
1689
  return 'success';
@@ -1639,14 +1699,15 @@ function applyImportStatus(results, status) {
1639
1699
  }
1640
1700
  }
1641
1701
 
1642
- function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins = [], overwrite = true, results) {
1643
- const pluginsByPlatform = getPluginsByPlatformItems(legacyPlugins, snapshotByPlatform);
1702
+ function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins = [], overwrite = true, results, registry = getExportRegistry()) {
1703
+ const pluginsByPlatform = getPluginsByPlatformItems(legacyPlugins, snapshotByPlatform, registry);
1644
1704
 
1645
- for (const platform of PLUGIN_PLATFORMS) {
1705
+ for (const platform of getPluginPlatforms(registry)) {
1706
+ const pluginPaths = getPlatformPluginPaths(platform, registry);
1646
1707
  const snapshot = snapshotByPlatform?.[platform] && typeof snapshotByPlatform[platform] === 'object'
1647
1708
  ? snapshotByPlatform[platform]
1648
1709
  : {};
1649
- const repoStatus = mergePluginRepoConfig(platform, snapshot, overwrite);
1710
+ const repoStatus = mergePluginRepoConfig(platform, snapshot, overwrite, registry);
1650
1711
  results.plugins.success += repoStatus.changed;
1651
1712
  results.plugins.skipped += repoStatus.skipped;
1652
1713
  results.plugins.failed += repoStatus.failed;
@@ -1655,38 +1716,45 @@ function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins
1655
1716
  const hasControl = snapshot?.control && Object.keys(snapshot.control).length > 0;
1656
1717
 
1657
1718
  if (platform === 'codex' && (hasControl || plugins.length > 0)) {
1658
- applyImportStatus(results, mergeCodexPluginConfig(snapshot, plugins, overwrite));
1719
+ applyImportStatus(results, mergeCodexPluginConfig(snapshot, plugins, overwrite, pluginPaths.configPath));
1659
1720
  } else if (platform === 'opencode' && (hasControl || plugins.length > 0)) {
1660
1721
  const packages = plugins
1661
1722
  .filter(plugin => plugin.type === 'opencode-package' || plugin.pluginType === 'npm')
1662
1723
  .map(plugin => plugin.name || plugin.directory)
1663
1724
  .filter(Boolean);
1664
- applyImportStatus(results, writeOpenCodePluginConfig(packages, overwrite));
1725
+ applyImportStatus(results, writeOpenCodePluginConfig(packages, overwrite, pluginPaths.configDir));
1665
1726
  } else if (platform === 'omp' && (hasControl || plugins.length > 0)) {
1666
- applyImportStatus(results, writeOmpPluginSettings(plugins, snapshot, overwrite));
1727
+ applyImportStatus(results, writeOmpPluginSettings(plugins, snapshot, overwrite, pluginPaths.settingsPath));
1667
1728
  }
1668
1729
 
1669
1730
  for (const plugin of plugins) {
1670
1731
  try {
1671
1732
  if (platform === 'codex') {
1672
- applyImportStatus(results, importPluginToDirectory(platform, plugin, CODEX_PLUGINS_CACHE_DIR, overwrite));
1733
+ applyImportStatus(results, importPluginToDirectory(platform, plugin, pluginPaths.cacheDir || CODEX_PLUGINS_CACHE_DIR, overwrite));
1673
1734
  } else if (platform === 'opencode') {
1674
1735
  if (plugin.type === 'opencode-package' || plugin.pluginType === 'npm') {
1675
1736
  continue;
1676
1737
  }
1677
- applyImportStatus(results, importPluginToDirectory(platform, plugin, getOpenCodePluginsDir(), overwrite));
1738
+ const openCodePluginsDir = fs.existsSync(pluginPaths.legacyPluginsDir) && !fs.existsSync(pluginPaths.pluginsDir)
1739
+ ? pluginPaths.legacyPluginsDir
1740
+ : pluginPaths.pluginsDir;
1741
+ applyImportStatus(results, importPluginToDirectory(platform, plugin, openCodePluginsDir || getOpenCodePluginsDir(), overwrite));
1678
1742
  } else if (platform === 'omp') {
1679
1743
  if (plugin.type === 'omp-package' || plugin.pluginType === 'package') {
1680
1744
  continue;
1681
1745
  }
1682
- applyImportStatus(results, importPluginToDirectory(platform, plugin, OMP_EXTENSIONS_DIR, overwrite));
1746
+ applyImportStatus(results, importPluginToDirectory(platform, plugin, pluginPaths.extensionsDir || OMP_EXTENSIONS_DIR, overwrite));
1683
1747
  } else if (platform === 'claude') {
1684
1748
  const isLegacy = plugin.type === 'legacy';
1685
1749
  applyImportStatus(results, importPluginToDirectory(
1686
1750
  platform,
1687
1751
  plugin,
1688
- isLegacy ? LEGACY_PLUGINS_DIR : CLAUDE_PLUGINS_DIR,
1689
- overwrite
1752
+ isLegacy ? LEGACY_PLUGINS_DIR : (pluginPaths.pluginsDir || CLAUDE_PLUGINS_DIR),
1753
+ overwrite,
1754
+ isLegacy ? {} : {
1755
+ installedRegistryPath: pluginPaths.installedRegistry,
1756
+ settingsPath: claudeSettingsPath
1757
+ }
1690
1758
  ));
1691
1759
  }
1692
1760
  } catch (err) {
@@ -1697,11 +1765,11 @@ function importPluginsByPlatformSnapshot(snapshotByPlatform = {}, legacyPlugins
1697
1765
  }
1698
1766
  }
1699
1767
 
1700
- function updateClaudePluginRegistry(plugin, pluginDir) {
1768
+ function updateClaudePluginRegistry(plugin, pluginDir, { installedRegistryPath = NATIVE_PLUGINS_REGISTRY, settingsPath = CLAUDE_SETTINGS_PATH } = {}) {
1701
1769
  const pluginName = plugin.name || path.basename(pluginDir);
1702
1770
  const marketplace = plugin.marketplace || '';
1703
1771
  const nativeKey = marketplace ? `${pluginName}@${marketplace}` : pluginName;
1704
- const installed = readJsonFileSafe(NATIVE_PLUGINS_REGISTRY) || { version: 2, plugins: {} };
1772
+ const installed = readJsonFileSafe(installedRegistryPath) || { version: 2, plugins: {} };
1705
1773
  installed.version = installed.version || 2;
1706
1774
  installed.plugins = installed.plugins && typeof installed.plugins === 'object' ? installed.plugins : {};
1707
1775
  installed.plugins[nativeKey] = [{
@@ -1722,14 +1790,14 @@ function updateClaudePluginRegistry(plugin, pluginDir) {
1722
1790
  ...(plugin.repoLocalPath ? { repoLocalPath: plugin.repoLocalPath } : {}),
1723
1791
  ...(plugin.repoId ? { repoId: plugin.repoId } : {})
1724
1792
  }];
1725
- writeJsonFileAbsolute(NATIVE_PLUGINS_REGISTRY, installed, true);
1793
+ writeJsonFileAbsolute(installedRegistryPath, installed, true);
1726
1794
 
1727
- const settings = readJsonFileSafe(CLAUDE_SETTINGS_PATH) || {};
1795
+ const settings = readJsonFileSafe(settingsPath) || {};
1728
1796
  settings.enabledPlugins = settings.enabledPlugins && typeof settings.enabledPlugins === 'object'
1729
1797
  ? settings.enabledPlugins
1730
1798
  : {};
1731
1799
  settings.enabledPlugins[nativeKey] = plugin.enabled !== false;
1732
- writeJsonFileAbsolute(CLAUDE_SETTINGS_PATH, settings, true);
1800
+ writeJsonFileAbsolute(settingsPath, settings, true);
1733
1801
  }
1734
1802
 
1735
1803
  function writeTextFile(baseDir, relativePath, content, overwrite) {
@@ -1745,21 +1813,23 @@ function writeTextFile(baseDir, relativePath, content, overwrite) {
1745
1813
  return 'success';
1746
1814
  }
1747
1815
 
1748
- function getAllChannelsByType() {
1749
- const claude = channelsService.getAllChannels() || [];
1750
- const codex = codexChannelsService.getChannels()?.channels || [];
1751
- const gemini = geminiChannelsService.getChannels()?.channels || [];
1752
- const opencode = opencodeChannelsService.getChannels()?.channels || [];
1753
- const omp = ompChannelsService.getChannels()?.channels || [];
1754
- return { claude, codex, gemini, opencode, omp };
1816
+ function readDriverChannels(driver) {
1817
+ const result = typeof driver?.list === 'function' ? driver.list() : null;
1818
+ const data = result?.status === 'ok' ? result.data : result;
1819
+ return Array.isArray(data) ? data : data?.channels || [];
1755
1820
  }
1756
1821
 
1757
- /**
1758
- * 导出所有配置为JSON
1759
- * @returns {Object} 配置导出对象
1760
- */
1822
+ function getAllChannelsByType() {
1823
+ return Object.fromEntries(
1824
+ getPlatformCatalog().keys({ capability: 'channels' })
1825
+ .map(platform => [platform, readDriverChannels(getChannelDriver(platform))])
1826
+ );
1827
+ }
1761
1828
  function exportAllConfigs() {
1762
1829
  try {
1830
+ const registry = getExportRegistry();
1831
+ const claudeNative = getConfiguredNativePaths('claude', registry);
1832
+ const claudeSettingsPath = claudeNative.settings || CLAUDE_SETTINGS_PATH;
1763
1833
  // 获取所有配置模板(只导出自定义模板)
1764
1834
  const allConfigTemplates = configTemplatesService.getAllTemplates();
1765
1835
  const customConfigTemplates = allConfigTemplates.filter(t => !t.isBuiltin);
@@ -1777,9 +1847,9 @@ function exportAllConfigs() {
1777
1847
  const favorites = favoritesService.loadFavorites();
1778
1848
 
1779
1849
  // 获取 Agents / Skills / Commands 配置(多平台)
1780
- const agentsByPlatform = exportAgentsSnapshotByPlatform();
1781
- const skillsByPlatform = exportSkillsSnapshotByPlatform();
1782
- const commandsByPlatform = exportCommandsSnapshotByPlatform();
1850
+ const agentsByPlatform = exportAgentsSnapshotByPlatform(registry);
1851
+ const skillsByPlatform = exportSkillsSnapshotByPlatform(registry);
1852
+ const commandsByPlatform = exportCommandsSnapshotByPlatform(registry);
1783
1853
  const agents = agentsByPlatform.claude || [];
1784
1854
  const skills = skillsByPlatform.claude || [];
1785
1855
  const commands = commandsByPlatform.claude || [];
@@ -1789,9 +1859,10 @@ function exportAllConfigs() {
1789
1859
  const mcpServers = mcpService.getAllServers();
1790
1860
 
1791
1861
  // 获取 Plugins 配置
1792
- const plugins = exportPluginsSnapshot();
1793
- const pluginsByPlatform = exportPluginsSnapshotByPlatform();
1794
- const nativeConfigs = exportNativeConfigs();
1862
+ const pluginsByPlatform = exportPluginsSnapshotByPlatform(registry);
1863
+ const plugins = Object.values(pluginsByPlatform)
1864
+ .flatMap(snapshot => Array.isArray(snapshot?.plugins) ? snapshot.plugins : []);
1865
+ const nativeConfigs = exportNativeConfigs(registry);
1795
1866
  const oauthCredentials = readJsonFileSafe(PATHS.oauthCredentials);
1796
1867
 
1797
1868
  // 读取 Markdown 配置文件
@@ -1819,7 +1890,7 @@ function exportAllConfigs() {
1819
1890
  const security = readJsonFileSafe(CC_SECURITY_PATH);
1820
1891
  const appConfig = loadConfig();
1821
1892
 
1822
- const claudeSettings = readJsonFileSafe(CLAUDE_SETTINGS_PATH);
1893
+ const claudeSettings = readJsonFileSafe(claudeSettingsPath);
1823
1894
  const claudeHooks = {
1824
1895
  uiConfig: readJsonFileSafe(LEGACY_UI_CONFIG_PATH),
1825
1896
  notifyScript: readTextFileSafe(LEGACY_NOTIFY_HOOK_PATH),
@@ -1900,6 +1971,10 @@ function exportAllConfigsZip() {
1900
1971
  */
1901
1972
  async function importConfigs(importData, options = {}) {
1902
1973
  const { overwrite = true } = options; // 默认覆盖模式
1974
+ const registry = options.registry || getExportRegistry();
1975
+ const claudeNative = getConfiguredNativePaths('claude', registry);
1976
+ const claudeSettingsPath = claudeNative.settings || CLAUDE_SETTINGS_PATH;
1977
+ const claudePluginPaths = getPlatformPluginPaths('claude', registry);
1903
1978
  const results = {
1904
1979
  configTemplates: { success: 0, failed: 0, skipped: 0 },
1905
1980
  channels: { success: 0, failed: 0, skipped: 0 },
@@ -1951,20 +2026,18 @@ async function importConfigs(importData, options = {}) {
1951
2026
  claudeHooks = null
1952
2027
  } = importData.data;
1953
2028
 
1954
- const importAgentsByPlatform = normalizePlatformItems(agents, agentsByPlatform, AGENT_PLATFORMS);
1955
- const importSkillsByPlatform = normalizePlatformItems(skills, skillsByPlatform, SKILL_PLATFORMS);
1956
- const importCommandsByPlatform = normalizePlatformItems(commands, commandsByPlatform, COMMAND_PLATFORMS);
2029
+ const importAgentsByPlatform = normalizePlatformItems(agents, agentsByPlatform, getAgentPlatforms(registry));
2030
+ const importSkillsByPlatform = normalizePlatformItems(skills, skillsByPlatform, getSkillPlatforms(registry));
2031
+ const importCommandsByPlatform = normalizePlatformItems(commands, commandsByPlatform, getCommandPlatforms(registry));
1957
2032
 
2033
+ const channelPlatforms = getPlatformCatalog().keys({ capability: 'channels' });
1958
2034
  const hasTypedChannels = channelsByType && typeof channelsByType === 'object';
1959
- const importChannelsByType = {
1960
- claude: hasTypedChannels && Array.isArray(channelsByType.claude)
1961
- ? channelsByType.claude
1962
- : (Array.isArray(channels) ? channels : []),
1963
- codex: hasTypedChannels && Array.isArray(channelsByType.codex) ? channelsByType.codex : [],
1964
- gemini: hasTypedChannels && Array.isArray(channelsByType.gemini) ? channelsByType.gemini : [],
1965
- opencode: hasTypedChannels && Array.isArray(channelsByType.opencode) ? channelsByType.opencode : [],
1966
- omp: hasTypedChannels && Array.isArray(channelsByType.omp) ? channelsByType.omp: []
1967
- };
2035
+ const importChannelsByType = Object.fromEntries(channelPlatforms.map(platform => [
2036
+ platform,
2037
+ hasTypedChannels && Array.isArray(channelsByType[platform])
2038
+ ? channelsByType[platform]
2039
+ : (platform === 'claude' && Array.isArray(channels) ? channels : [])
2040
+ ]));
1968
2041
 
1969
2042
  // 导入配置模板
1970
2043
  for (const template of configTemplates) {
@@ -1994,16 +2067,23 @@ async function importConfigs(importData, options = {}) {
1994
2067
  }
1995
2068
 
1996
2069
  // 导入频道配置(兼容旧结构 channels 和新结构 channelsByType)
1997
- const importTypedChannels = (type, service, createChannel, findExisting = null) => {
1998
- const sourceChannels = importChannelsByType[type];
2070
+ const importTypedChannels = (platform) => {
2071
+ const driver = getChannelDriver(platform);
2072
+ const sourceChannels = importChannelsByType[platform] || [];
2073
+ if (!driver) {
2074
+ results.channels.skipped += sourceChannels.length;
2075
+ return;
2076
+ }
1999
2077
  for (const channel of sourceChannels) {
2000
2078
  try {
2001
- const existingChannels = service.getChannels
2002
- ? (service.getChannels()?.channels || [])
2003
- : (service.getAllChannels?.() || []);
2004
- const existing = typeof findExisting === 'function'
2005
- ? findExisting(existingChannels, channel)
2006
- : existingChannels.find(c => c.id === channel.id);
2079
+ const existingChannels = readDriverChannels(driver);
2080
+ const existing = existingChannels.find(candidate => (
2081
+ (channel.id && candidate.id === channel.id)
2082
+ || (channel.providerKey && candidate.providerKey === channel.providerKey)
2083
+ || (channel.name && channel.baseUrl
2084
+ && candidate.name === channel.name
2085
+ && candidate.baseUrl === channel.baseUrl)
2086
+ ));
2007
2087
 
2008
2088
  if (existing && !overwrite) {
2009
2089
  results.channels.skipped++;
@@ -2011,68 +2091,28 @@ async function importConfigs(importData, options = {}) {
2011
2091
  }
2012
2092
 
2013
2093
  if (existing && overwrite) {
2014
- service.updateChannel(existing.id, { ...channel, id: existing.id });
2094
+ driver.update(existing.id, { ...channel, id: existing.id });
2015
2095
  } else {
2016
- createChannel(channel);
2096
+ driver.create(channel);
2017
2097
  }
2018
2098
  results.channels.success++;
2019
2099
  } catch (err) {
2020
- console.error(`[ConfigImport] 导入${type}频道失败: ${channel.name}`, err);
2100
+ console.error(`[ConfigImport] 导入${platform}频道失败: ${channel.name}`, err);
2021
2101
  results.channels.failed++;
2022
2102
  }
2023
2103
  }
2024
2104
  };
2025
2105
 
2026
- importTypedChannels('claude', channelsService, channel => {
2027
- const { name, baseUrl, apiKey, websiteUrl, ...extraConfig } = channel;
2028
- channelsService.createChannel(name, baseUrl, apiKey, websiteUrl, extraConfig);
2029
- }, (existingChannels, channel) => existingChannels.find(c => c.id === channel.id));
2030
-
2031
- importTypedChannels('codex', codexChannelsService, channel => {
2032
- const {
2033
- name,
2034
- providerKey,
2035
- baseUrl,
2036
- apiKey,
2037
- wireApi,
2038
- ...extraConfig
2039
- } = channel;
2040
- codexChannelsService.createChannel(name, providerKey, baseUrl, apiKey, wireApi, extraConfig);
2041
- }, (existingChannels, channel) => existingChannels.find(c =>
2042
- (channel.id && c.id === channel.id) ||
2043
- (channel.providerKey && c.providerKey === channel.providerKey)
2044
- ));
2045
-
2046
- importTypedChannels('gemini', geminiChannelsService, channel => {
2047
- const { name, baseUrl, apiKey, model, ...extraConfig } = channel;
2048
- geminiChannelsService.createChannel(name, baseUrl, apiKey, model, extraConfig);
2049
- }, (existingChannels, channel) => existingChannels.find(c =>
2050
- (channel.id && c.id === channel.id) ||
2051
- (channel.name && c.name === channel.name)
2052
- ));
2053
-
2054
- importTypedChannels('opencode', opencodeChannelsService, channel => {
2055
- const { name, baseUrl, apiKey, ...extraConfig } = channel;
2056
- opencodeChannelsService.createChannel(name, baseUrl, apiKey, extraConfig);
2057
- }, (existingChannels, channel) => existingChannels.find(c =>
2058
- (channel.id && c.id === channel.id) ||
2059
- (channel.providerKey && c.providerKey === channel.providerKey) ||
2060
- (channel.name && channel.baseUrl && c.name === channel.name && c.baseUrl === channel.baseUrl)
2061
- ));
2062
-
2063
- importTypedChannels('omp', ompChannelsService, channel => {
2064
- const { name, baseUrl, apiKey, ...extraConfig } = channel;
2065
- ompChannelsService.createChannel(name, baseUrl, apiKey, extraConfig);
2066
- }, (existingChannels, channel) => existingChannels.find(c =>
2067
- (channel.id && c.id === channel.id) ||
2068
- (channel.providerKey && c.providerKey === channel.providerKey) ||
2069
- (channel.name && channel.baseUrl && c.name === channel.name && c.baseUrl === channel.baseUrl)
2070
- ));
2071
- if ((importChannelsByType.omp || []).length > 0 && typeof ompChannelsService.syncManagedProviderExtension === 'function') {
2072
- ompChannelsService.syncManagedProviderExtension();
2073
- }
2074
-
2075
- // 导入工作区配置
2106
+ for (const platform of channelPlatforms) {
2107
+ importTypedChannels(platform);
2108
+ }
2109
+ for (const platform of channelPlatforms) {
2110
+ const driver = getChannelDriver(platform);
2111
+ const imported = importChannelsByType[platform] || [];
2112
+ if (imported.length > 0 && typeof driver?.syncManagedProviderExtension === 'function') {
2113
+ driver.syncManagedProviderExtension(imported);
2114
+ }
2115
+ }
2076
2116
  if (workspaces && overwrite) {
2077
2117
  try {
2078
2118
  const workspaceService = require('./workspace-service');
@@ -2098,13 +2138,13 @@ async function importConfigs(importData, options = {}) {
2098
2138
  }
2099
2139
 
2100
2140
  // 导入 Agents(多平台)
2101
- if (AGENT_PLATFORMS.some(platform => importAgentsByPlatform[platform]?.length > 0)) {
2141
+ if (getAgentPlatforms(registry).some(platform => importAgentsByPlatform[platform]?.length > 0)) {
2102
2142
  try {
2103
- for (const platform of AGENT_PLATFORMS) {
2143
+ for (const platform of getAgentPlatforms(registry)) {
2104
2144
  const platformAgents = importAgentsByPlatform[platform] || [];
2105
2145
  if (platformAgents.length === 0) continue;
2106
2146
 
2107
- const agentsService = new AgentsService(platform);
2147
+ const agentsService = new AgentsService(platform, { registry });
2108
2148
  const baseDir = agentsService.userAgentsDir;
2109
2149
 
2110
2150
  for (const agent of platformAgents) {
@@ -2127,13 +2167,13 @@ async function importConfigs(importData, options = {}) {
2127
2167
  }
2128
2168
 
2129
2169
  // 导入 Skills(多平台)
2130
- if (SKILL_PLATFORMS.some(platform => importSkillsByPlatform[platform]?.length > 0)) {
2170
+ if (getSkillPlatforms(registry).some(platform => importSkillsByPlatform[platform]?.length > 0)) {
2131
2171
  try {
2132
- for (const platform of SKILL_PLATFORMS) {
2172
+ for (const platform of getSkillPlatforms(registry)) {
2133
2173
  const platformSkills = importSkillsByPlatform[platform] || [];
2134
2174
  if (platformSkills.length === 0) continue;
2135
2175
 
2136
- const skillService = new SkillService(platform);
2176
+ const skillService = new SkillService(platform, { registry });
2137
2177
  const baseDir = skillService.installDir;
2138
2178
  ensureDir(baseDir);
2139
2179
 
@@ -2190,7 +2230,7 @@ async function importConfigs(importData, options = {}) {
2190
2230
 
2191
2231
  // 导入 Plugins(优先使用多平台结构,兼容旧结构 plugins)
2192
2232
  if (pluginsByPlatform && typeof pluginsByPlatform === 'object' && Object.keys(pluginsByPlatform).length > 0) {
2193
- importPluginsByPlatformSnapshot(pluginsByPlatform, plugins, overwrite, results);
2233
+ importPluginsByPlatformSnapshot(pluginsByPlatform, plugins, overwrite, results, registry);
2194
2234
  }
2195
2235
 
2196
2236
  // 导入旧版 Plugins 结构。新包已经通过 pluginsByPlatform 处理过,避免重复写入。
@@ -2221,8 +2261,8 @@ async function importConfigs(importData, options = {}) {
2221
2261
  results.plugins.failed++;
2222
2262
  continue;
2223
2263
  }
2224
- targetDir = path.join(CLAUDE_PLUGINS_DIR, pluginId);
2225
- registryPath = NATIVE_PLUGINS_REGISTRY;
2264
+ targetDir = path.join(claudePluginPaths.pluginsDir || CLAUDE_PLUGINS_DIR, pluginId);
2265
+ registryPath = claudePluginPaths.installedRegistry || NATIVE_PLUGINS_REGISTRY;
2226
2266
  } else {
2227
2267
  console.warn(`[ConfigImport] Unknown plugin type: ${pluginType}`);
2228
2268
  results.plugins.failed++;
@@ -2335,13 +2375,13 @@ async function importConfigs(importData, options = {}) {
2335
2375
  }
2336
2376
 
2337
2377
  // 导入 Commands(多平台)
2338
- if (COMMAND_PLATFORMS.some(platform => importCommandsByPlatform[platform]?.length > 0)) {
2378
+ if (getCommandPlatforms(registry).some(platform => importCommandsByPlatform[platform]?.length > 0)) {
2339
2379
  try {
2340
- for (const platform of COMMAND_PLATFORMS) {
2380
+ for (const platform of getCommandPlatforms(registry)) {
2341
2381
  const platformCommands = importCommandsByPlatform[platform] || [];
2342
2382
  if (platformCommands.length === 0) continue;
2343
2383
 
2344
- const commandsService = new CommandsService(platform);
2384
+ const commandsService = new CommandsService(platform, { registry });
2345
2385
  const baseDir = commandsService.userCommandsDir;
2346
2386
 
2347
2387
  for (const command of platformCommands) {
@@ -2482,34 +2522,23 @@ async function importConfigs(importData, options = {}) {
2482
2522
 
2483
2523
  // 导入各平台原生配置
2484
2524
  if (nativeConfigs && typeof nativeConfigs === 'object' && Object.keys(nativeConfigs).length > 0) {
2485
- const nativeConfigSpecs = getNativeConfigSpecs();
2486
-
2525
+ const catalog = getPlatformCatalog();
2487
2526
  for (const [platform, platformEntries] of Object.entries(nativeConfigs)) {
2488
- const platformSpecs = nativeConfigSpecs[platform];
2489
- if (!platformSpecs || !platformEntries || typeof platformEntries !== 'object') {
2490
- continue;
2491
- }
2492
-
2493
- for (const [key, entry] of Object.entries(platformEntries)) {
2494
- const spec = platformSpecs[key];
2495
- if (!spec) {
2496
- continue;
2497
- }
2498
-
2527
+ if (!platformEntries || typeof platformEntries !== 'object') continue;
2528
+ const driver = catalog.driver(platform, 'nativeConfig');
2529
+ if (typeof driver?.importSnapshot === 'function') {
2499
2530
  try {
2500
- const status = writeNativeConfigAbsolute(spec, entry, overwrite);
2501
- if (status === 'success') {
2502
- results.nativeConfigs.success++;
2503
- } else if (status === 'skipped') {
2504
- results.nativeConfigs.skipped++;
2505
- } else {
2506
- results.nativeConfigs.failed++;
2507
- }
2531
+ const imported = driver.importSnapshot(platformEntries, { overwrite });
2532
+ results.nativeConfigs.success += imported?.imported ?? imported?.success ?? 0;
2533
+ results.nativeConfigs.skipped += imported?.skipped || 0;
2534
+ results.nativeConfigs.failed += imported?.failed || 0;
2508
2535
  } catch (err) {
2509
- console.error(`[ConfigImport] 导入 ${platform}.${key} 原生配置失败:`, err);
2510
- results.nativeConfigs.failed++;
2536
+ console.error(`[ConfigImport] 导入 ${platform} 原生配置失败:`, err);
2537
+ results.nativeConfigs.failed += Object.keys(platformEntries).length;
2511
2538
  }
2539
+ continue;
2512
2540
  }
2541
+ results.nativeConfigs.skipped += Object.keys(platformEntries).length;
2513
2542
  }
2514
2543
  }
2515
2544
 
@@ -2551,10 +2580,10 @@ async function importConfigs(importData, options = {}) {
2551
2580
  }
2552
2581
 
2553
2582
  if (claudeHooks.stopHook !== undefined) {
2554
- if (!overwrite && fs.existsSync(CLAUDE_SETTINGS_PATH)) {
2583
+ if (!overwrite && fs.existsSync(claudeSettingsPath)) {
2555
2584
  didSkip = true;
2556
2585
  } else {
2557
- const settings = readJsonFileSafe(CLAUDE_SETTINGS_PATH) || {};
2586
+ const settings = readJsonFileSafe(claudeSettingsPath) || {};
2558
2587
  settings.hooks = settings.hooks || {};
2559
2588
  if (claudeHooks.stopHook) {
2560
2589
  settings.hooks.Stop = claudeHooks.stopHook;
@@ -2564,7 +2593,7 @@ async function importConfigs(importData, options = {}) {
2564
2593
  delete settings.hooks;
2565
2594
  }
2566
2595
  }
2567
- writeJsonFileAbsolute(CLAUDE_SETTINGS_PATH, settings, true);
2596
+ writeJsonFileAbsolute(claudeSettingsPath, settings, true);
2568
2597
  didWrite = true;
2569
2598
  }
2570
2599
  }
@@ -2647,5 +2676,7 @@ function generateImportSummary(results) {
2647
2676
  module.exports = {
2648
2677
  exportAllConfigs,
2649
2678
  exportAllConfigsZip,
2650
- importConfigs
2679
+ importConfigs,
2680
+ exportPlatformSnapshots,
2681
+ getPlatformKeysForType
2651
2682
  };