coding-tool-x 3.8.4 → 3.8.6

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-Db9wAyaZ.js +1 -0
  4. package/dist/web/assets/AgentsDrawer-Pw2k-ZAa.css +1 -0
  5. package/dist/web/assets/Analytics-C4gMzv7g.js +1 -0
  6. package/dist/web/assets/Analytics-CyHhR126.css +1 -0
  7. package/dist/web/assets/AssetPathField-Bd_nMFu3.js +1 -0
  8. package/dist/web/assets/ChatHistoryDrawer-B2qBQs9L.css +1 -0
  9. package/dist/web/assets/ChatHistoryDrawer-CwzJHyNP.js +1 -0
  10. package/dist/web/assets/CollapseItem-CTCfSiue.js +1 -0
  11. package/dist/web/assets/CommandsDrawer-BZJxlPvV.css +1 -0
  12. package/dist/web/assets/CommandsDrawer-vDghB1U9.js +1 -0
  13. package/dist/web/assets/ConfigExportDrawer-C-XDvlHc.js +1 -0
  14. package/dist/web/assets/ConfigExportDrawer-dwruRKWr.css +1 -0
  15. package/dist/web/assets/ConfigTemplates-CYY8XRED.js +1 -0
  16. package/dist/web/assets/ConfigTemplatesDrawer-BfDi8yO3.css +1 -0
  17. package/dist/web/assets/ConfigTemplatesDrawer-CbsJp76X.js +1 -0
  18. package/dist/web/assets/ConfigTemplatesPanel-CWyPtk67.css +1 -0
  19. package/dist/web/assets/ConfigTemplatesPanel-liMSTh7s.js +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-D6QSiYaL.js +1 -0
  24. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +1 -0
  25. package/dist/web/assets/GatewayConvertDrawer-RvET6iNG.js +1 -0
  26. package/dist/web/assets/Home-CSuFuJ9Y.css +1 -0
  27. package/dist/web/assets/Home-P8eseZcf.js +1 -0
  28. package/dist/web/assets/MarkdownEditor-CYmLGYju.js +1 -0
  29. package/dist/web/assets/MarkdownEditor-qfVgSre_.css +1 -0
  30. package/dist/web/assets/McpDrawer-C0dnsZhQ.css +1 -0
  31. package/dist/web/assets/McpDrawer-Ch3bkKIq.js +1 -0
  32. package/dist/web/assets/McpFormDrawer-BRbDcawn.css +1 -0
  33. package/dist/web/assets/McpFormDrawer-BcTlgSpN.js +1 -0
  34. package/dist/web/assets/PluginManager-Dh2o0xkZ.js +1 -0
  35. package/dist/web/assets/PluginsDrawer-DM3QjprE.css +1 -0
  36. package/dist/web/assets/PluginsDrawer-deKQ5u3I.js +1 -0
  37. package/dist/web/assets/PluginsPanel-BofsgQgj.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-DhSWJHTA.js +1 -0
  41. package/dist/web/assets/PromptsDrawer-BIgiYCEQ.css +1 -0
  42. package/dist/web/assets/PromptsDrawer-Cfb2FeeU.js +1 -0
  43. package/dist/web/assets/RecentSessionsDrawer-3eXamJhr.js +1 -0
  44. package/dist/web/assets/RecentSessionsDrawer-CKFsOYDM.css +1 -0
  45. package/dist/web/assets/SessionCard-CW0eHURI.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-DQfyDGWU.js +1 -0
  49. package/dist/web/assets/SettingsDrawer-Dw5NUgHC.css +1 -0
  50. package/dist/web/assets/SettingsDrawer-u_vwUuPS.js +1 -0
  51. package/dist/web/assets/SkillManager-C0GCT2Kb.css +1 -0
  52. package/dist/web/assets/SkillManager-DHMprJLA.js +1 -0
  53. package/dist/web/assets/SkillsDrawer-BCNme-aJ.css +1 -0
  54. package/dist/web/assets/SkillsDrawer-CkF3BpyC.js +1 -0
  55. package/dist/web/assets/SkillsPanel-B3Nm5j32.css +1 -0
  56. package/dist/web/assets/SkillsPanel-DWE4pFGx.js +1 -0
  57. package/dist/web/assets/SpeedTestDrawer-BgfG1Ked.js +1 -0
  58. package/dist/web/assets/SpeedTestDrawer-Dc0WToJ_.css +1 -0
  59. package/dist/web/assets/Upload-CNwaGOws.js +1 -0
  60. package/dist/web/assets/WorkspaceDrawer-Br5CeCzL.js +1 -0
  61. package/dist/web/assets/WorkspaceDrawer-CGnMu9p1.css +1 -0
  62. package/dist/web/assets/WorkspaceManager-B2O8-qIw.js +1 -0
  63. package/dist/web/assets/WorkspacePanel-5oDKit3c.js +1 -0
  64. package/dist/web/assets/WorkspacePanel-D0-zHTHP.css +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-BMAUPq5K.js +2 -0
  71. package/dist/web/assets/index-Bi1MQHYX.css +1 -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-jbmqZuJs.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 +104 -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 +165 -0
  140. package/src/platforms/channel-auth-service.js +107 -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 +98 -418
  269. package/src/{server/services → platforms}/notification-hooks.js +190 -1023
  270. package/src/platforms/oauth-credentials-service.js +635 -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 +133 -24
  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 +12 -2
  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 +876 -214
  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,6 +10,11 @@
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
12
  const { PATHS, NATIVE_PATHS } = require('../../config/paths');
13
+ const { getPlatformContext } = require('../platform-context');
14
+ const { ControlManifestStore } = require('./control-manifest-store');
15
+ const { EffectiveControlService } = require('./effective-control-service');
16
+ const { SkillArtifactStore } = require('./skill-artifact-store');
17
+ const { SkillFormatAdapter } = require('./skill-format-adapters');
13
18
 
14
19
  // Configuration paths
15
20
  const CC_TOOL_DIR = PATHS.base;
@@ -24,33 +29,57 @@ const CLAUDE_DIRS = {
24
29
  agents: NATIVE_PATHS.claude.agents || path.join(CLAUDE_HOME_DIR, 'agents'),
25
30
  plugins: NATIVE_PATHS.claude.plugins || path.join(CLAUDE_HOME_DIR, 'plugins')
26
31
  };
27
-
28
32
  // Valid config types
29
33
  const CONFIG_TYPES = ['skills', 'commands', 'agents', 'plugins'];
30
- const SUPPORTED_PLATFORMS = ['claude', 'codex', 'gemini', 'opencode', 'omp'];
31
34
 
32
- const PLATFORM_SUPPORT = {
33
- skills: { claude: true, codex: true, gemini: true, opencode: true, omp: true },
34
- commands: { claude: true, codex: true, gemini: true, opencode: true, omp: true },
35
- agents: { claude: true, codex: true, gemini: true, opencode: true, omp: false },
36
- plugins: { claude: true, codex: false, gemini: false, opencode: true, omp: true }
37
- };
35
+ function getSupportedPlatforms(registry = getPlatformContext().registry) {
36
+ if (!registry || typeof registry.list !== 'function') return [];
37
+ return registry.list()
38
+ .map(platform => platform && platform.key)
39
+ .filter(Boolean);
40
+ }
41
+
42
+ function buildPlatformSupport(registry = getPlatformContext().registry) {
43
+ const support = Object.fromEntries(CONFIG_TYPES.map(type => [type, {}]));
44
+ for (const platform of (registry?.list?.() || [])) {
45
+ const key = platform && platform.key;
46
+ if (!key) continue;
47
+ const resourceDriver = platform.capabilities?.resourceSync;
48
+ for (const type of CONFIG_TYPES) {
49
+ const typeCapability = platform.capabilities && Object.prototype.hasOwnProperty.call(platform.capabilities, type)
50
+ ? platform.capabilities[type]
51
+ : null;
52
+ const declared = resourceDriver && resourceDriver !== 'unsupported' && typeCapability !== 'unsupported';
53
+ const typeDeclared = platform.resourceTypes && Object.prototype.hasOwnProperty.call(platform.resourceTypes, type)
54
+ ? platform.resourceTypes[type]
55
+ : declared;
56
+ support[type][key] = declared && typeDeclared !== false;
57
+ }
58
+ }
59
+ return support;
60
+ }
38
61
 
39
62
  function normalizePlatforms(type, platforms = {}) {
40
- const support = PLATFORM_SUPPORT[type] || {};
63
+ const supportedPlatforms = getSupportedPlatforms();
64
+ const support = buildPlatformSupport();
41
65
  const normalized = {};
42
66
 
43
- for (const platform of SUPPORTED_PLATFORMS) {
67
+ for (const platform of supportedPlatforms) {
44
68
  normalized[platform] = !!platforms?.[platform];
45
69
  }
46
70
 
47
- for (const platform of SUPPORTED_PLATFORMS) {
48
- if (support[platform] === false) {
49
- normalized[platform] = false;
71
+ for (const platform of supportedPlatforms) {
72
+ if (support[type]?.[platform] === false) normalized[platform] = false;
73
+ }
74
+
75
+ // Preserve historical flags for platforms absent from the current registry.
76
+ for (const [platform, enabled] of Object.entries(platforms || {})) {
77
+ if (!Object.prototype.hasOwnProperty.call(normalized, platform)) {
78
+ normalized[platform] = !!enabled;
50
79
  }
51
80
  }
52
81
 
53
- // Default to Claude enabled when no platform explicitly configured
82
+ // Default to Claude enabled when no platform explicitly configured.
54
83
  if (!platforms || Object.keys(platforms).length === 0) {
55
84
  normalized.claude = true;
56
85
  }
@@ -58,6 +87,8 @@ function normalizePlatforms(type, platforms = {}) {
58
87
  return normalized;
59
88
  }
60
89
 
90
+ const RESERVED_CONFIG_NAMES = new Set(['__proto__', 'constructor', 'prototype']);
91
+
61
92
  function normalizeRelativeConfigName(name) {
62
93
  const raw = String(name || '').replace(/\\/g, '/').trim();
63
94
  if (!raw || raw.includes('\0')) {
@@ -72,6 +103,9 @@ function normalizeRelativeConfigName(name) {
72
103
  if (path.isAbsolute(raw) || raw.startsWith('/')) {
73
104
  throw new Error('Absolute path is not allowed');
74
105
  }
106
+ if (RESERVED_CONFIG_NAMES.has(normalized)) {
107
+ throw new Error('Invalid config name');
108
+ }
75
109
 
76
110
  return normalized;
77
111
  }
@@ -94,13 +128,67 @@ function ensureDir(dirPath) {
94
128
  }
95
129
  }
96
130
 
131
+ function collectSkillFiles(rootDir) {
132
+ const files = [];
133
+ const visit = currentDir => {
134
+ if (fs.lstatSync(currentDir).isSymbolicLink()) {
135
+ throw new Error(`Skill artifact contains symlink: ${currentDir}`);
136
+ }
137
+ for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
138
+ const sourcePath = path.join(currentDir, entry.name);
139
+ if (entry.isSymbolicLink()) {
140
+ throw new Error(`Skill artifact contains symlink: ${sourcePath}`);
141
+ }
142
+ if (entry.isDirectory()) {
143
+ visit(sourcePath);
144
+ } else if (entry.isFile()) {
145
+ files.push({
146
+ relativePath: path.relative(rootDir, sourcePath).replace(/\\/g, '/'),
147
+ content: fs.readFileSync(sourcePath)
148
+ });
149
+ }
150
+ }
151
+ };
152
+ visit(rootDir);
153
+ return files;
154
+ }
155
+
97
156
  /**
98
157
  * Config Registry Service
99
158
  */
100
159
  class ConfigRegistryService {
101
- constructor() {
160
+ constructor({ controlService = null, artifactStore = null, formatAdapter = null, registry = getPlatformContext().registry } = {}) {
161
+ this.registry = registry;
102
162
  this.registryPath = REGISTRY_FILE;
103
163
  this.configsDir = CONFIGS_DIR;
164
+ let native = NATIVE_PATHS.claude || {};
165
+ try {
166
+ const pathContext = this.registry?.resolvePathContext?.('claude');
167
+ if (pathContext?.customized) native = pathContext.native || native;
168
+ } catch {
169
+ // Keep the compatibility defaults when the registry is unavailable.
170
+ }
171
+ const claudeHomeDir = native.dir || path.dirname(native.settings || CLAUDE_HOME_DIR);
172
+ this.claudeDirs = {
173
+ skills: native.skills || path.join(claudeHomeDir, 'skills'),
174
+ commands: native.commands || path.join(claudeHomeDir, 'commands'),
175
+ agents: native.agents || path.join(claudeHomeDir, 'agents'),
176
+ plugins: native.plugins || path.join(claudeHomeDir, 'plugins')
177
+ };
178
+ this.artifactStore = artifactStore || (
179
+ PATHS.skillArtifacts ? new SkillArtifactStore({ root: PATHS.skillArtifacts }) : null
180
+ );
181
+ this.formatAdapter = formatAdapter || new SkillFormatAdapter();
182
+ this.controlService = controlService || (
183
+ PATHS.effectiveControlManifest
184
+ ? new EffectiveControlService({
185
+ store: new ControlManifestStore({
186
+ userPath: PATHS.effectiveControlManifest,
187
+ projectPathResolver: ({ projectPath }) => path.join(projectPath, '.ctx-control.json')
188
+ })
189
+ })
190
+ : null
191
+ );
104
192
 
105
193
  // Ensure directories exist
106
194
  this._ensureDirs();
@@ -120,6 +208,141 @@ class ConfigRegistryService {
120
208
  }
121
209
  }
122
210
 
211
+ migrateSkillControls() {
212
+ if (!this.controlService) return { migrated: 0, skipped: 0, migrationVersion: 0 };
213
+ const registry = this._readRegistry();
214
+ let migrated = 0;
215
+ let skipped = 0;
216
+ for (const [name, item] of Object.entries(registry.skills || {})) {
217
+ let safeName;
218
+ try {
219
+ safeName = normalizeRelativeConfigName(name);
220
+ } catch {
221
+ skipped++;
222
+ continue;
223
+ }
224
+ if (!item || typeof item !== 'object') continue;
225
+ const platforms = Object.keys(item.platforms || {});
226
+ for (const platform of platforms.length > 0 ? platforms : ['claude']) {
227
+ const sourceKey = `local:${platform}:${safeName}`;
228
+ const controlKey = `skill:${platform}:user:user:${sourceKey}`;
229
+ if (this.controlService.getSkill(controlKey, { scope: 'user' })) {
230
+ skipped++;
231
+ continue;
232
+ }
233
+
234
+ const legacyRoot = path.join(this.configsDir, 'skills', safeName);
235
+ const legacySkillPath = path.join(legacyRoot, 'SKILL.md');
236
+ const legacyReady = fs.existsSync(legacySkillPath);
237
+ let artifactRoot = legacyReady ? legacyRoot : null;
238
+ let artifactFormat = null;
239
+ let artifactHash = null;
240
+ let artifactState = legacyReady ? 'ready' : 'metadata_only';
241
+ let lastError = null;
242
+
243
+ if (legacyReady && this.artifactStore) {
244
+ try {
245
+ const normalized = this.formatAdapter.normalize({
246
+ platform,
247
+ files: collectSkillFiles(legacyRoot),
248
+ sourceMetadata: { name: safeName }
249
+ });
250
+ const published = this.artifactStore.publishSkill({
251
+ platform,
252
+ sourceKey,
253
+ format: normalized.format,
254
+ files: normalized.files,
255
+ metadata: {
256
+ name: safeName,
257
+ directory: safeName,
258
+ sourceProvider: 'local',
259
+ sourceScope: 'user',
260
+ revision: null
261
+ }
262
+ });
263
+ artifactRoot = published.root;
264
+ artifactFormat = published.format || normalized.format;
265
+ artifactHash = published.contentHash || null;
266
+ artifactState = published.state || 'ready';
267
+ } catch (error) {
268
+ artifactRoot = null;
269
+ artifactState = 'missing';
270
+ lastError = error.message;
271
+ }
272
+ }
273
+
274
+ const platformEnabled = platforms.length === 0
275
+ ? platform === 'claude'
276
+ : item.platforms?.[platform] === true;
277
+ const enabled = artifactState !== 'metadata_only'
278
+ && artifactState !== 'missing'
279
+ && item.enabled !== false
280
+ && platformEnabled;
281
+ const registered = this.controlService.registerSkill({
282
+ kind: 'skill',
283
+ controlKey,
284
+ platform,
285
+ scope: 'user',
286
+ projectPath: null,
287
+ sourceKey,
288
+ source: {
289
+ kind: 'local',
290
+ repoId: null,
291
+ fullDirectory: safeName,
292
+ revision: null
293
+ },
294
+ artifact: {
295
+ root: artifactRoot,
296
+ format: artifactFormat,
297
+ contentHash: artifactHash,
298
+ state: artifactState,
299
+ fetchedAt: null
300
+ },
301
+ targetDirectory: safeName,
302
+ cached: Boolean(artifactRoot) && artifactState !== 'missing',
303
+ enabled,
304
+ trust: enabled ? 'approved' : (artifactState === 'missing' ? 'needs_review' : 'pending'),
305
+ projection: {
306
+ mode: 'native-copy',
307
+ state: enabled ? 'enabled' : 'disabled',
308
+ sourceKey,
309
+ path: null,
310
+ updatedAt: null
311
+ },
312
+ managed: true,
313
+ lastError,
314
+ createdAt: Date.now(),
315
+ updatedAt: Date.now()
316
+ });
317
+ if (!enabled && this.controlService.setSkillEnabled) {
318
+ try {
319
+ this.controlService.setSkillEnabled({
320
+ platform,
321
+ controlKey: registered.controlKey,
322
+ scope: 'user',
323
+ projectPath: null,
324
+ enabled: false
325
+ });
326
+ } catch (error) {
327
+ console.warn(`[ConfigRegistry] Failed to disable migrated Skill ${safeName}:`, error.message);
328
+ }
329
+ }
330
+ migrated++;
331
+ }
332
+ }
333
+
334
+ const store = this.controlService.store;
335
+ if (store?.read && store?.write) {
336
+ const scope = { scope: 'user', projectPath: null };
337
+ const manifest = store.read(scope);
338
+ if (manifest.migrationVersion !== 1) {
339
+ manifest.migrationVersion = 1;
340
+ store.write(scope, manifest);
341
+ }
342
+ }
343
+ return { migrated, skipped, migrationVersion: 1 };
344
+ }
345
+
123
346
  /**
124
347
  * Read registry from file
125
348
  * @returns {Object} Registry data
@@ -180,9 +403,11 @@ class ConfigRegistryService {
180
403
  if (!CONFIG_TYPES.includes(type)) {
181
404
  throw new Error(`Invalid config type: ${type}`);
182
405
  }
183
-
406
+ const safeName = normalizeRelativeConfigName(name);
184
407
  const registry = this._readRegistry();
185
- return registry[type][name] || null;
408
+ return Object.prototype.hasOwnProperty.call(registry[type], safeName)
409
+ ? registry[type][safeName]
410
+ : null;
186
411
  }
187
412
 
188
413
  /**
@@ -190,17 +415,18 @@ class ConfigRegistryService {
190
415
  * @param {string} type - Config type
191
416
  * @param {string} name - Item name/key
192
417
  * @param {Object} data - Item data
193
- * @returns {Object} Updated entry
194
418
  */
195
419
  setItem(type, name, data) {
196
420
  if (!CONFIG_TYPES.includes(type)) {
197
421
  throw new Error(`Invalid config type: ${type}`);
198
422
  }
199
-
423
+ const safeName = normalizeRelativeConfigName(name);
200
424
  const registry = this._readRegistry();
201
425
  const now = new Date().toISOString();
202
426
 
203
- const existing = registry[type][name];
427
+ const existing = Object.prototype.hasOwnProperty.call(registry[type], safeName)
428
+ ? registry[type][safeName]
429
+ : null;
204
430
  const entry = {
205
431
  ...data,
206
432
  enabled: data.enabled !== undefined ? data.enabled : true,
@@ -210,7 +436,7 @@ class ConfigRegistryService {
210
436
  source: data.source || 'local'
211
437
  };
212
438
 
213
- registry[type][name] = entry;
439
+ registry[type][safeName] = entry;
214
440
  this._writeRegistry(registry);
215
441
 
216
442
  return entry;
@@ -226,18 +452,18 @@ class ConfigRegistryService {
226
452
  if (!CONFIG_TYPES.includes(type)) {
227
453
  throw new Error(`Invalid config type: ${type}`);
228
454
  }
229
-
455
+ const safeName = normalizeRelativeConfigName(name);
230
456
  const registry = this._readRegistry();
231
457
 
232
- if (!registry[type][name]) {
458
+ if (!Object.prototype.hasOwnProperty.call(registry[type], safeName)) {
233
459
  return false;
234
460
  }
235
461
 
236
- delete registry[type][name];
462
+ delete registry[type][safeName];
237
463
  this._writeRegistry(registry);
238
464
 
239
465
  // Also remove the actual config files
240
- const configPath = this.getConfigPath(type, name);
466
+ const configPath = this.getConfigPath(type, safeName);
241
467
  if (fs.existsSync(configPath)) {
242
468
  try {
243
469
  const stats = fs.statSync(configPath);
@@ -247,7 +473,7 @@ class ConfigRegistryService {
247
473
  fs.unlinkSync(configPath);
248
474
  }
249
475
  } catch (err) {
250
- console.error(`[ConfigRegistry] Failed to remove config files for ${type}/${name}:`, err.message);
476
+ console.error(`[ConfigRegistry] Failed to remove config files for ${type}/${safeName}:`, err.message);
251
477
  }
252
478
  }
253
479
 
@@ -279,9 +505,11 @@ class ConfigRegistryService {
279
505
  if (!CONFIG_TYPES.includes(type)) {
280
506
  throw new Error(`Invalid config type: ${type}`);
281
507
  }
282
-
508
+ const safeName = normalizeRelativeConfigName(name);
283
509
  const registry = this._readRegistry();
284
- const entry = registry[type][name];
510
+ const entry = Object.prototype.hasOwnProperty.call(registry[type], safeName)
511
+ ? registry[type][safeName]
512
+ : null;
285
513
 
286
514
  if (!entry) {
287
515
  throw new Error(`Item "${name}" not found in ${type}`);
@@ -301,23 +529,25 @@ class ConfigRegistryService {
301
529
  * @param {string} name - Item name/key
302
530
  * @param {string} platform - Platform name (claude, codex, gemini, opencode)
303
531
  * @param {boolean} enabled - New platform status
304
- * @returns {Object} Updated entry
305
532
  */
306
533
  togglePlatform(type, name, platform, enabled) {
307
534
  if (!CONFIG_TYPES.includes(type)) {
308
535
  throw new Error(`Invalid config type: ${type}`);
309
536
  }
310
537
 
311
- if (!SUPPORTED_PLATFORMS.includes(platform)) {
538
+ if (!getSupportedPlatforms().includes(platform)) {
312
539
  throw new Error(`Invalid platform: ${platform}`);
313
540
  }
314
541
 
315
- if (PLATFORM_SUPPORT[type] && PLATFORM_SUPPORT[type][platform] === false) {
542
+ if (buildPlatformSupport()[type]?.[platform] === false) {
316
543
  throw new Error(`Platform "${platform}" is not supported for ${type}`);
317
544
  }
318
545
 
546
+ const safeName = normalizeRelativeConfigName(name);
319
547
  const registry = this._readRegistry();
320
- const entry = registry[type][name];
548
+ const entry = Object.prototype.hasOwnProperty.call(registry[type], safeName)
549
+ ? registry[type][safeName]
550
+ : null;
321
551
 
322
552
  if (!entry) {
323
553
  throw new Error(`Item "${name}" not found in ${type}`);
@@ -345,7 +575,7 @@ class ConfigRegistryService {
345
575
  throw new Error(`Invalid config type: ${type}`);
346
576
  }
347
577
 
348
- const sourceDir = CLAUDE_DIRS[type];
578
+ const sourceDir = this.claudeDirs[type];
349
579
  const destDir = path.join(this.configsDir, type);
350
580
 
351
581
  if (!fs.existsSync(sourceDir)) {
@@ -555,6 +785,9 @@ class ConfigRegistryService {
555
785
  * @private
556
786
  */
557
787
  _copyDirRecursive(src, dest) {
788
+ if (fs.lstatSync(src).isSymbolicLink()) {
789
+ throw new Error(`Config source contains symlink: ${src}`);
790
+ }
558
791
  ensureDir(dest);
559
792
 
560
793
  const entries = fs.readdirSync(src, { withFileTypes: true });
@@ -562,10 +795,12 @@ class ConfigRegistryService {
562
795
  for (const entry of entries) {
563
796
  const srcPath = path.join(src, entry.name);
564
797
  const destPath = path.join(dest, entry.name);
565
-
798
+ if (entry.isSymbolicLink()) {
799
+ throw new Error(`Config source contains symlink: ${srcPath}`);
800
+ }
566
801
  if (entry.isDirectory()) {
567
802
  this._copyDirRecursive(srcPath, destPath);
568
- } else {
803
+ } else if (entry.isFile()) {
569
804
  fs.copyFileSync(srcPath, destPath);
570
805
  }
571
806
  }
@@ -577,16 +812,11 @@ class ConfigRegistryService {
577
812
  */
578
813
  getStats() {
579
814
  const registry = this._readRegistry();
815
+ const supportedPlatforms = getSupportedPlatforms();
580
816
  const stats = {
581
817
  total: 0,
582
818
  byType: {},
583
- byPlatform: {
584
- claude: 0,
585
- codex: 0,
586
- gemini: 0,
587
- opencode: 0,
588
- omp: 0
589
- }
819
+ byPlatform: Object.fromEntries(supportedPlatforms.map(platform => [platform, 0]))
590
820
  };
591
821
 
592
822
  for (const type of CONFIG_TYPES) {
@@ -594,21 +824,16 @@ class ConfigRegistryService {
594
824
  const typeStats = {
595
825
  total: items.length,
596
826
  enabled: items.filter(i => i.enabled).length,
597
- disabled: items.filter(i => !i.enabled).length,
598
- claude: items.filter(i => i.platforms?.claude).length,
599
- codex: items.filter(i => i.platforms?.codex).length,
600
- gemini: items.filter(i => i.platforms?.gemini).length,
601
- opencode: items.filter(i => i.platforms?.opencode).length,
602
- omp: items.filter(i => i.platforms?.omp).length
827
+ disabled: items.filter(i => !i.enabled).length
603
828
  };
604
829
 
830
+ for (const platform of supportedPlatforms) {
831
+ typeStats[platform] = items.filter(item => item.platforms?.[platform]).length;
832
+ stats.byPlatform[platform] += typeStats[platform];
833
+ }
834
+
605
835
  stats.byType[type] = typeStats;
606
836
  stats.total += typeStats.total;
607
- stats.byPlatform.claude += typeStats.claude;
608
- stats.byPlatform.codex += typeStats.codex;
609
- stats.byPlatform.gemini += typeStats.gemini;
610
- stats.byPlatform.opencode += typeStats.opencode;
611
- stats.byPlatform.omp += typeStats.omp;
612
837
  }
613
838
 
614
839
  return stats;
@@ -826,7 +1051,8 @@ class ConfigRegistryService {
826
1051
  module.exports = {
827
1052
  ConfigRegistryService,
828
1053
  CONFIG_TYPES,
829
- SUPPORTED_PLATFORMS,
1054
+ getSupportedPlatforms,
1055
+ buildPlatformSupport,
830
1056
  CONFIGS_DIR,
831
1057
  REGISTRY_FILE
832
1058
  };