opkg 0.9.2 → 0.9.4

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 (404) hide show
  1. package/.claude/agents/code-reviewer.md +171 -0
  2. package/.claude/commands/commit-push-pr.md +20 -0
  3. package/.claude/commands/{specs/read.md → read-specs.md} +1 -1
  4. package/.claude/commands/{specs/update.md → update-specs.md} +4 -0
  5. package/.claude/skills/code-review-excellence/SKILL.md +538 -0
  6. package/README.md +2 -2
  7. package/package.json +3 -1
  8. package/packages/cli/dist/add-IJAPFHIX.js +624 -0
  9. package/packages/cli/dist/add-IJAPFHIX.js.map +7 -0
  10. package/packages/cli/dist/add-LLUNFLJI.js +624 -0
  11. package/packages/cli/dist/add-LLUNFLJI.js.map +7 -0
  12. package/packages/cli/dist/add-U44SL3OR.js +624 -0
  13. package/packages/cli/dist/add-U44SL3OR.js.map +7 -0
  14. package/packages/cli/dist/chunk-23VBP5L6.js +371 -0
  15. package/packages/cli/dist/chunk-23VBP5L6.js.map +7 -0
  16. package/packages/cli/dist/chunk-2SVHLF5C.js +1419 -0
  17. package/packages/cli/dist/chunk-2SVHLF5C.js.map +7 -0
  18. package/packages/cli/dist/chunk-37256POU.js +99 -0
  19. package/packages/cli/dist/chunk-37256POU.js.map +7 -0
  20. package/packages/cli/dist/chunk-3PZRVA6O.js +196 -0
  21. package/packages/cli/dist/chunk-3PZRVA6O.js.map +7 -0
  22. package/packages/cli/dist/chunk-427DCURL.js +155 -0
  23. package/packages/cli/dist/chunk-427DCURL.js.map +7 -0
  24. package/packages/cli/dist/chunk-4B5HJLP2.js +48 -0
  25. package/packages/cli/dist/chunk-4B5HJLP2.js.map +7 -0
  26. package/packages/cli/dist/chunk-4OWT3YEG.js +413 -0
  27. package/packages/cli/dist/chunk-4OWT3YEG.js.map +7 -0
  28. package/packages/cli/dist/chunk-4RIBTBXI.js +568 -0
  29. package/packages/cli/dist/chunk-4RIBTBXI.js.map +7 -0
  30. package/packages/cli/dist/chunk-4X2EJHJN.js +63 -0
  31. package/packages/cli/dist/chunk-4X2EJHJN.js.map +7 -0
  32. package/packages/cli/dist/chunk-6CYW66HD.js +1136 -0
  33. package/packages/cli/dist/chunk-6CYW66HD.js.map +7 -0
  34. package/packages/cli/dist/chunk-6DITYAFA.js +172 -0
  35. package/packages/cli/dist/chunk-6DITYAFA.js.map +7 -0
  36. package/packages/cli/dist/chunk-7KEAKEVZ.js +568 -0
  37. package/packages/cli/dist/chunk-7KEAKEVZ.js.map +7 -0
  38. package/packages/cli/dist/chunk-ABFUD25D.js +61 -0
  39. package/packages/cli/dist/chunk-ABFUD25D.js.map +7 -0
  40. package/packages/cli/dist/chunk-AR7GJCG6.js +274 -0
  41. package/packages/cli/dist/chunk-AR7GJCG6.js.map +7 -0
  42. package/packages/cli/dist/chunk-AYTGQCXH.js +86 -0
  43. package/packages/cli/dist/chunk-AYTGQCXH.js.map +7 -0
  44. package/packages/cli/dist/chunk-BROJ6OUT.js +631 -0
  45. package/packages/cli/dist/chunk-BROJ6OUT.js.map +7 -0
  46. package/packages/cli/dist/chunk-BVVSU7QD.js +23 -0
  47. package/packages/cli/dist/chunk-BVVSU7QD.js.map +7 -0
  48. package/packages/cli/dist/chunk-C6FY55UP.js +108 -0
  49. package/packages/cli/dist/chunk-C6FY55UP.js.map +7 -0
  50. package/packages/cli/dist/chunk-CVA64SXK.js +1136 -0
  51. package/packages/cli/dist/chunk-CVA64SXK.js.map +7 -0
  52. package/packages/cli/dist/chunk-D3O7LY2Q.js +1151 -0
  53. package/packages/cli/dist/chunk-D3O7LY2Q.js.map +7 -0
  54. package/packages/cli/dist/chunk-D6LEPODL.js +413 -0
  55. package/packages/cli/dist/chunk-D6LEPODL.js.map +7 -0
  56. package/packages/cli/dist/chunk-DEC24S7E.js +186 -0
  57. package/packages/cli/dist/chunk-DEC24S7E.js.map +7 -0
  58. package/packages/cli/dist/chunk-FMVVJH5M.js +371 -0
  59. package/packages/cli/dist/chunk-FMVVJH5M.js.map +7 -0
  60. package/packages/cli/dist/chunk-GDVFS3YP.js +130 -0
  61. package/packages/cli/dist/chunk-GDVFS3YP.js.map +7 -0
  62. package/packages/cli/dist/chunk-GEP2G5HF.js +31 -0
  63. package/packages/cli/dist/chunk-GEP2G5HF.js.map +7 -0
  64. package/packages/cli/dist/chunk-GSWHZBT2.js +62 -0
  65. package/packages/cli/dist/chunk-GSWHZBT2.js.map +7 -0
  66. package/packages/cli/dist/chunk-HTYHJA3B.js +61 -0
  67. package/packages/cli/dist/chunk-HTYHJA3B.js.map +7 -0
  68. package/packages/cli/dist/chunk-HYKYECAE.js +222 -0
  69. package/packages/cli/dist/chunk-HYKYECAE.js.map +7 -0
  70. package/packages/cli/dist/chunk-I7FEAHB4.js +100 -0
  71. package/packages/cli/dist/chunk-I7FEAHB4.js.map +7 -0
  72. package/packages/cli/dist/chunk-IHVZ5AUJ.js +107 -0
  73. package/packages/cli/dist/chunk-IHVZ5AUJ.js.map +7 -0
  74. package/packages/cli/dist/chunk-KI7FDU3H.js +99 -0
  75. package/packages/cli/dist/chunk-KI7FDU3H.js.map +7 -0
  76. package/packages/cli/dist/chunk-L5GRJQBS.js +32 -0
  77. package/packages/cli/dist/chunk-L5GRJQBS.js.map +7 -0
  78. package/packages/cli/dist/chunk-LHEAUDJL.js +302 -0
  79. package/packages/cli/dist/chunk-LHEAUDJL.js.map +7 -0
  80. package/packages/cli/dist/chunk-MIURCESJ.js +48 -0
  81. package/packages/cli/dist/chunk-MIURCESJ.js.map +7 -0
  82. package/packages/cli/dist/chunk-N43IXOND.js +732 -0
  83. package/packages/cli/dist/chunk-N43IXOND.js.map +7 -0
  84. package/packages/cli/dist/chunk-OUZRMGPV.js +274 -0
  85. package/packages/cli/dist/chunk-OUZRMGPV.js.map +7 -0
  86. package/packages/cli/dist/chunk-PSQXKAL4.js +371 -0
  87. package/packages/cli/dist/chunk-PSQXKAL4.js.map +7 -0
  88. package/packages/cli/dist/chunk-PUDRKDVZ.js +1419 -0
  89. package/packages/cli/dist/chunk-PUDRKDVZ.js.map +7 -0
  90. package/packages/cli/dist/chunk-RAKMX654.js +631 -0
  91. package/packages/cli/dist/chunk-RAKMX654.js.map +7 -0
  92. package/packages/cli/dist/chunk-RSFLK2TP.js +195 -0
  93. package/packages/cli/dist/chunk-RSFLK2TP.js.map +7 -0
  94. package/packages/cli/dist/chunk-S26PR2BS.js +99 -0
  95. package/packages/cli/dist/chunk-S26PR2BS.js.map +7 -0
  96. package/packages/cli/dist/chunk-U7FW7SXX.js +568 -0
  97. package/packages/cli/dist/chunk-U7FW7SXX.js.map +7 -0
  98. package/packages/cli/dist/chunk-VKM6K5TN.js +413 -0
  99. package/packages/cli/dist/chunk-VKM6K5TN.js.map +7 -0
  100. package/packages/cli/dist/chunk-VKNJG4JN.js +253 -0
  101. package/packages/cli/dist/chunk-VKNJG4JN.js.map +7 -0
  102. package/packages/cli/dist/chunk-VQ2KY6CK.js +113 -0
  103. package/packages/cli/dist/chunk-VQ2KY6CK.js.map +7 -0
  104. package/packages/cli/dist/chunk-VQDTXLOX.js +1312 -0
  105. package/packages/cli/dist/chunk-VQDTXLOX.js.map +7 -0
  106. package/packages/cli/dist/chunk-VXNS3X5O.js +60 -0
  107. package/packages/cli/dist/chunk-VXNS3X5O.js.map +7 -0
  108. package/packages/cli/dist/chunk-WF7H2YDU.js +376 -0
  109. package/packages/cli/dist/chunk-WF7H2YDU.js.map +7 -0
  110. package/packages/cli/dist/chunk-WNRXZLWW.js +266 -0
  111. package/packages/cli/dist/chunk-WNRXZLWW.js.map +7 -0
  112. package/packages/cli/dist/chunk-WT4VVCXM.js +1121 -0
  113. package/packages/cli/dist/chunk-WT4VVCXM.js.map +7 -0
  114. package/packages/cli/dist/configure-3AZUMDJZ.js +107 -0
  115. package/packages/cli/dist/configure-3AZUMDJZ.js.map +7 -0
  116. package/packages/cli/dist/configure-D722JQOD.js +107 -0
  117. package/packages/cli/dist/configure-D722JQOD.js.map +7 -0
  118. package/packages/cli/dist/configure-IU5H7XD6.js +107 -0
  119. package/packages/cli/dist/configure-IU5H7XD6.js.map +7 -0
  120. package/packages/cli/dist/file-format-detector-PXCIAKTK.js +22 -0
  121. package/packages/cli/dist/file-format-detector-PXCIAKTK.js.map +7 -0
  122. package/packages/cli/dist/index.js +17 -17
  123. package/packages/cli/dist/install-EZNWMLJR.js +7581 -0
  124. package/packages/cli/dist/install-EZNWMLJR.js.map +7 -0
  125. package/packages/cli/dist/install-F5ANFUBX.js +7577 -0
  126. package/packages/cli/dist/install-F5ANFUBX.js.map +7 -0
  127. package/packages/cli/dist/install-JSXEPPC2.js +7104 -0
  128. package/packages/cli/dist/install-JSXEPPC2.js.map +7 -0
  129. package/packages/cli/dist/install-QHEBX7JH.js +7101 -0
  130. package/packages/cli/dist/install-QHEBX7JH.js.map +7 -0
  131. package/packages/cli/dist/list-DMOUATYI.js +327 -0
  132. package/packages/cli/dist/list-DMOUATYI.js.map +7 -0
  133. package/packages/cli/dist/list-UESSCB7Y.js +327 -0
  134. package/packages/cli/dist/list-UESSCB7Y.js.map +7 -0
  135. package/packages/cli/dist/list-XR7RSJFS.js +327 -0
  136. package/packages/cli/dist/list-XR7RSJFS.js.map +7 -0
  137. package/packages/cli/dist/login-EYZ2SOYZ.js +150 -0
  138. package/packages/cli/dist/login-EYZ2SOYZ.js.map +7 -0
  139. package/packages/cli/dist/login-JWCSTAEU.js +150 -0
  140. package/packages/cli/dist/login-JWCSTAEU.js.map +7 -0
  141. package/packages/cli/dist/login-NRKHXZKM.js +150 -0
  142. package/packages/cli/dist/login-NRKHXZKM.js.map +7 -0
  143. package/packages/cli/dist/logout-HDMYRXIE.js +40 -0
  144. package/packages/cli/dist/logout-HDMYRXIE.js.map +7 -0
  145. package/packages/cli/dist/logout-SYHXCVCQ.js +40 -0
  146. package/packages/cli/dist/logout-SYHXCVCQ.js.map +7 -0
  147. package/packages/cli/dist/logout-X3XUUOH5.js +40 -0
  148. package/packages/cli/dist/logout-X3XUUOH5.js.map +7 -0
  149. package/packages/cli/dist/new-3LTFKDTQ.js +277 -0
  150. package/packages/cli/dist/new-3LTFKDTQ.js.map +7 -0
  151. package/packages/cli/dist/new-F46OSD72.js +277 -0
  152. package/packages/cli/dist/new-F46OSD72.js.map +7 -0
  153. package/packages/cli/dist/new-OPCCLNL2.js +277 -0
  154. package/packages/cli/dist/new-OPCCLNL2.js.map +7 -0
  155. package/packages/cli/dist/package-marker-detector-T5O5YD2E.js +80 -0
  156. package/packages/cli/dist/package-marker-detector-T5O5YD2E.js.map +7 -0
  157. package/packages/cli/dist/package-yml-QWZIJDYU.js +16 -0
  158. package/packages/cli/dist/package-yml-QWZIJDYU.js.map +7 -0
  159. package/packages/cli/dist/plugin-naming-YP2I4NPA.js +29 -0
  160. package/packages/cli/dist/plugin-naming-YP2I4NPA.js.map +7 -0
  161. package/packages/cli/dist/publish-4H43PCSG.js +619 -0
  162. package/packages/cli/dist/publish-4H43PCSG.js.map +7 -0
  163. package/packages/cli/dist/publish-RULKLNUX.js +619 -0
  164. package/packages/cli/dist/publish-RULKLNUX.js.map +7 -0
  165. package/packages/cli/dist/publish-URWY2P3E.js +619 -0
  166. package/packages/cli/dist/publish-URWY2P3E.js.map +7 -0
  167. package/packages/cli/dist/remove-BD52BHR2.js +542 -0
  168. package/packages/cli/dist/remove-BD52BHR2.js.map +7 -0
  169. package/packages/cli/dist/remove-G5NRC7LD.js +542 -0
  170. package/packages/cli/dist/remove-G5NRC7LD.js.map +7 -0
  171. package/packages/cli/dist/remove-TC3FQUYQ.js +542 -0
  172. package/packages/cli/dist/remove-TC3FQUYQ.js.map +7 -0
  173. package/packages/cli/dist/resource-discoverer-4X4RY43E.js +17 -0
  174. package/packages/cli/dist/resource-discoverer-4X4RY43E.js.map +7 -0
  175. package/packages/cli/dist/save-24TESYKI.js +1728 -0
  176. package/packages/cli/dist/save-24TESYKI.js.map +7 -0
  177. package/packages/cli/dist/save-N3QWF2WN.js +1728 -0
  178. package/packages/cli/dist/save-N3QWF2WN.js.map +7 -0
  179. package/packages/cli/dist/save-P2U67DTV.js +1728 -0
  180. package/packages/cli/dist/save-P2U67DTV.js.map +7 -0
  181. package/packages/cli/dist/search-ABROK3UO.js +157 -0
  182. package/packages/cli/dist/search-ABROK3UO.js.map +7 -0
  183. package/packages/cli/dist/search-WVFXFNAV.js +157 -0
  184. package/packages/cli/dist/search-WVFXFNAV.js.map +7 -0
  185. package/packages/cli/dist/search-YQN2Q2SO.js +157 -0
  186. package/packages/cli/dist/search-YQN2Q2SO.js.map +7 -0
  187. package/packages/cli/dist/set-DCWF73F6.js +251 -0
  188. package/packages/cli/dist/set-DCWF73F6.js.map +7 -0
  189. package/packages/cli/dist/set-GJEG2F6Y.js +251 -0
  190. package/packages/cli/dist/set-GJEG2F6Y.js.map +7 -0
  191. package/packages/cli/dist/set-NGM2FIKF.js +251 -0
  192. package/packages/cli/dist/set-NGM2FIKF.js.map +7 -0
  193. package/packages/cli/dist/uninstall-3CJQMTYH.js +539 -0
  194. package/packages/cli/dist/uninstall-3CJQMTYH.js.map +7 -0
  195. package/packages/cli/dist/uninstall-Q3CP4UN5.js +539 -0
  196. package/packages/cli/dist/uninstall-Q3CP4UN5.js.map +7 -0
  197. package/packages/cli/dist/uninstall-QU5OMEEC.js +539 -0
  198. package/packages/cli/dist/uninstall-QU5OMEEC.js.map +7 -0
  199. package/packages/cli/dist/unpublish-GHJQYC4S.js +245 -0
  200. package/packages/cli/dist/unpublish-GHJQYC4S.js.map +7 -0
  201. package/packages/cli/dist/unpublish-L2CYMK4B.js +245 -0
  202. package/packages/cli/dist/unpublish-L2CYMK4B.js.map +7 -0
  203. package/packages/cli/dist/unpublish-VBTNTMS5.js +245 -0
  204. package/packages/cli/dist/unpublish-VBTNTMS5.js.map +7 -0
  205. package/packages/cli/dist/view-MXRBMXOG.js +488 -0
  206. package/packages/cli/dist/view-MXRBMXOG.js.map +7 -0
  207. package/packages/cli/dist/view-NMND7SAW.js +488 -0
  208. package/packages/cli/dist/view-NMND7SAW.js.map +7 -0
  209. package/packages/cli/dist/view-RPQRDSYB.js +488 -0
  210. package/packages/cli/dist/view-RPQRDSYB.js.map +7 -0
  211. package/packages/cli/package.json +2 -0
  212. package/packages/core/dist/constants/index.d.ts +9 -0
  213. package/packages/core/dist/constants/index.d.ts.map +1 -1
  214. package/packages/core/dist/constants/index.js +12 -0
  215. package/packages/core/dist/constants/index.js.map +1 -1
  216. package/packages/core/dist/core/dependency-resolver/index.d.ts +2 -10
  217. package/packages/core/dist/core/dependency-resolver/index.d.ts.map +1 -1
  218. package/packages/core/dist/core/dependency-resolver/index.js +3 -14
  219. package/packages/core/dist/core/dependency-resolver/index.js.map +1 -1
  220. package/packages/core/dist/core/install/base-detector.d.ts +2 -1
  221. package/packages/core/dist/core/install/base-detector.d.ts.map +1 -1
  222. package/packages/core/dist/core/install/base-detector.js +54 -1
  223. package/packages/core/dist/core/install/base-detector.js.map +1 -1
  224. package/packages/core/dist/core/install/conflicts/file-conflict-resolver.d.ts +7 -5
  225. package/packages/core/dist/core/install/conflicts/file-conflict-resolver.d.ts.map +1 -1
  226. package/packages/core/dist/core/install/conflicts/file-conflict-resolver.js +25 -9
  227. package/packages/core/dist/core/install/conflicts/file-conflict-resolver.js.map +1 -1
  228. package/packages/core/dist/core/install/flow-index-installer.d.ts +2 -1
  229. package/packages/core/dist/core/install/flow-index-installer.d.ts.map +1 -1
  230. package/packages/core/dist/core/install/flow-index-installer.js +19 -4
  231. package/packages/core/dist/core/install/flow-index-installer.js.map +1 -1
  232. package/packages/core/dist/core/install/input-classifier-base.js +3 -3
  233. package/packages/core/dist/core/install/input-classifier-base.js.map +1 -1
  234. package/packages/core/dist/core/install/install-reporting.d.ts.map +1 -1
  235. package/packages/core/dist/core/install/install-reporting.js +7 -9
  236. package/packages/core/dist/core/install/install-reporting.js.map +1 -1
  237. package/packages/core/dist/core/install/list-handler.d.ts.map +1 -1
  238. package/packages/core/dist/core/install/list-handler.js +3 -0
  239. package/packages/core/dist/core/install/list-handler.js.map +1 -1
  240. package/packages/core/dist/core/install/marketplace-handler.d.ts.map +1 -1
  241. package/packages/core/dist/core/install/marketplace-handler.js.map +1 -1
  242. package/packages/core/dist/core/install/operations/conflict-handler.d.ts +2 -1
  243. package/packages/core/dist/core/install/operations/conflict-handler.d.ts.map +1 -1
  244. package/packages/core/dist/core/install/operations/conflict-handler.js +2 -2
  245. package/packages/core/dist/core/install/operations/conflict-handler.js.map +1 -1
  246. package/packages/core/dist/core/install/operations/installation-executor.d.ts +3 -0
  247. package/packages/core/dist/core/install/operations/installation-executor.d.ts.map +1 -1
  248. package/packages/core/dist/core/install/operations/installation-executor.js +39 -22
  249. package/packages/core/dist/core/install/operations/installation-executor.js.map +1 -1
  250. package/packages/core/dist/core/install/orchestrator/orchestrator.d.ts +7 -3
  251. package/packages/core/dist/core/install/orchestrator/orchestrator.d.ts.map +1 -1
  252. package/packages/core/dist/core/install/orchestrator/orchestrator.js +193 -93
  253. package/packages/core/dist/core/install/orchestrator/orchestrator.js.map +1 -1
  254. package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.d.ts +1 -0
  255. package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.d.ts.map +1 -1
  256. package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.js +11 -24
  257. package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.js.map +1 -1
  258. package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.d.ts +2 -0
  259. package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.d.ts.map +1 -1
  260. package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.js +14 -14
  261. package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.js.map +1 -1
  262. package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.d.ts +7 -0
  263. package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.d.ts.map +1 -1
  264. package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.js +28 -0
  265. package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.js.map +1 -1
  266. package/packages/core/dist/core/install/orchestrator/types.d.ts +2 -0
  267. package/packages/core/dist/core/install/orchestrator/types.d.ts.map +1 -1
  268. package/packages/core/dist/core/install/path-package-loader.d.ts.map +1 -1
  269. package/packages/core/dist/core/install/path-package-loader.js +20 -1
  270. package/packages/core/dist/core/install/path-package-loader.js.map +1 -1
  271. package/packages/core/dist/core/install/platform-resolution.d.ts +3 -0
  272. package/packages/core/dist/core/install/platform-resolution.d.ts.map +1 -1
  273. package/packages/core/dist/core/install/platform-resolution.js +5 -2
  274. package/packages/core/dist/core/install/platform-resolution.js.map +1 -1
  275. package/packages/core/dist/core/install/preprocessing/context-population.d.ts +18 -0
  276. package/packages/core/dist/core/install/preprocessing/context-population.d.ts.map +1 -0
  277. package/packages/core/dist/core/install/preprocessing/context-population.js +36 -0
  278. package/packages/core/dist/core/install/preprocessing/context-population.js.map +1 -0
  279. package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.d.ts +23 -0
  280. package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.d.ts.map +1 -1
  281. package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.js +44 -0
  282. package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.js.map +1 -1
  283. package/packages/core/dist/core/install/sources/git-source.d.ts.map +1 -1
  284. package/packages/core/dist/core/install/sources/git-source.js +67 -4
  285. package/packages/core/dist/core/install/sources/git-source.js.map +1 -1
  286. package/packages/core/dist/core/install/sources/path-source.d.ts.map +1 -1
  287. package/packages/core/dist/core/install/sources/path-source.js +8 -0
  288. package/packages/core/dist/core/install/sources/path-source.js.map +1 -1
  289. package/packages/core/dist/core/install/strategies/flow-based-strategy.d.ts.map +1 -1
  290. package/packages/core/dist/core/install/strategies/flow-based-strategy.js +12 -5
  291. package/packages/core/dist/core/install/strategies/flow-based-strategy.js.map +1 -1
  292. package/packages/core/dist/core/install/strategies/types.d.ts +11 -1
  293. package/packages/core/dist/core/install/strategies/types.d.ts.map +1 -1
  294. package/packages/core/dist/core/install/unified/context-builders.d.ts +5 -0
  295. package/packages/core/dist/core/install/unified/context-builders.d.ts.map +1 -1
  296. package/packages/core/dist/core/install/unified/context-builders.js +12 -0
  297. package/packages/core/dist/core/install/unified/context-builders.js.map +1 -1
  298. package/packages/core/dist/core/install/unified/context-helpers.d.ts +0 -4
  299. package/packages/core/dist/core/install/unified/context-helpers.d.ts.map +1 -1
  300. package/packages/core/dist/core/install/unified/context-helpers.js +0 -24
  301. package/packages/core/dist/core/install/unified/context-helpers.js.map +1 -1
  302. package/packages/core/dist/core/install/unified/index.d.ts +1 -1
  303. package/packages/core/dist/core/install/unified/index.d.ts.map +1 -1
  304. package/packages/core/dist/core/install/unified/index.js +1 -1
  305. package/packages/core/dist/core/install/unified/index.js.map +1 -1
  306. package/packages/core/dist/core/install/unified/multi-context-pipeline.d.ts +6 -0
  307. package/packages/core/dist/core/install/unified/multi-context-pipeline.d.ts.map +1 -1
  308. package/packages/core/dist/core/install/unified/multi-context-pipeline.js +11 -4
  309. package/packages/core/dist/core/install/unified/multi-context-pipeline.js.map +1 -1
  310. package/packages/core/dist/core/install/unified/phases/conflicts.d.ts.map +1 -1
  311. package/packages/core/dist/core/install/unified/phases/conflicts.js +1 -1
  312. package/packages/core/dist/core/install/unified/phases/conflicts.js.map +1 -1
  313. package/packages/core/dist/core/install/unified/phases/execute.d.ts.map +1 -1
  314. package/packages/core/dist/core/install/unified/phases/execute.js +5 -5
  315. package/packages/core/dist/core/install/unified/phases/execute.js.map +1 -1
  316. package/packages/core/dist/core/install/unified/phases/load-package.js +3 -3
  317. package/packages/core/dist/core/install/unified/phases/load-package.js.map +1 -1
  318. package/packages/core/dist/core/install/unified/phases/report.js +1 -1
  319. package/packages/core/dist/core/install/unified/phases/report.js.map +1 -1
  320. package/packages/core/dist/core/install/unified/pipeline.d.ts.map +1 -1
  321. package/packages/core/dist/core/install/unified/pipeline.js +7 -10
  322. package/packages/core/dist/core/install/unified/pipeline.js.map +1 -1
  323. package/packages/core/dist/core/install/wave-resolver/content-root-cache.d.ts +24 -0
  324. package/packages/core/dist/core/install/wave-resolver/content-root-cache.d.ts.map +1 -0
  325. package/packages/core/dist/core/install/wave-resolver/content-root-cache.js +71 -0
  326. package/packages/core/dist/core/install/wave-resolver/content-root-cache.js.map +1 -0
  327. package/packages/core/dist/core/install/wave-resolver/context-builder.d.ts +39 -0
  328. package/packages/core/dist/core/install/wave-resolver/context-builder.d.ts.map +1 -0
  329. package/packages/core/dist/core/install/wave-resolver/context-builder.js +148 -0
  330. package/packages/core/dist/core/install/wave-resolver/context-builder.js.map +1 -0
  331. package/packages/core/dist/core/install/wave-resolver/fetcher.d.ts +49 -0
  332. package/packages/core/dist/core/install/wave-resolver/fetcher.d.ts.map +1 -0
  333. package/packages/core/dist/core/install/wave-resolver/fetcher.js +221 -0
  334. package/packages/core/dist/core/install/wave-resolver/fetcher.js.map +1 -0
  335. package/packages/core/dist/core/install/wave-resolver/index-updater.d.ts +23 -0
  336. package/packages/core/dist/core/install/wave-resolver/index-updater.d.ts.map +1 -0
  337. package/packages/core/dist/core/install/wave-resolver/index-updater.js +87 -0
  338. package/packages/core/dist/core/install/wave-resolver/index-updater.js.map +1 -0
  339. package/packages/core/dist/core/install/wave-resolver/index-write-collector.d.ts +101 -0
  340. package/packages/core/dist/core/install/wave-resolver/index-write-collector.d.ts.map +1 -0
  341. package/packages/core/dist/core/install/wave-resolver/index-write-collector.js +194 -0
  342. package/packages/core/dist/core/install/wave-resolver/index-write-collector.js.map +1 -0
  343. package/packages/core/dist/core/install/wave-resolver/index.d.ts +17 -0
  344. package/packages/core/dist/core/install/wave-resolver/index.d.ts.map +1 -0
  345. package/packages/core/dist/core/install/wave-resolver/index.js +16 -0
  346. package/packages/core/dist/core/install/wave-resolver/index.js.map +1 -0
  347. package/packages/core/dist/core/install/wave-resolver/manifest-reader.d.ts +34 -0
  348. package/packages/core/dist/core/install/wave-resolver/manifest-reader.d.ts.map +1 -0
  349. package/packages/core/dist/core/install/wave-resolver/manifest-reader.js +112 -0
  350. package/packages/core/dist/core/install/wave-resolver/manifest-reader.js.map +1 -0
  351. package/packages/core/dist/core/install/wave-resolver/types.d.ts +210 -0
  352. package/packages/core/dist/core/install/wave-resolver/types.d.ts.map +1 -0
  353. package/packages/core/dist/core/install/wave-resolver/types.js +6 -0
  354. package/packages/core/dist/core/install/wave-resolver/types.js.map +1 -0
  355. package/packages/core/dist/core/install/wave-resolver/version-solver.d.ts +65 -0
  356. package/packages/core/dist/core/install/wave-resolver/version-solver.d.ts.map +1 -0
  357. package/packages/core/dist/core/install/wave-resolver/version-solver.js +166 -0
  358. package/packages/core/dist/core/install/wave-resolver/version-solver.js.map +1 -0
  359. package/packages/core/dist/core/install/wave-resolver/wave-engine.d.ts +16 -0
  360. package/packages/core/dist/core/install/wave-resolver/wave-engine.d.ts.map +1 -0
  361. package/packages/core/dist/core/install/wave-resolver/wave-engine.js +337 -0
  362. package/packages/core/dist/core/install/wave-resolver/wave-engine.js.map +1 -0
  363. package/packages/core/dist/core/install/wave-resolver/wave-installer.d.ts +50 -0
  364. package/packages/core/dist/core/install/wave-resolver/wave-installer.d.ts.map +1 -0
  365. package/packages/core/dist/core/install/wave-resolver/wave-installer.js +246 -0
  366. package/packages/core/dist/core/install/wave-resolver/wave-installer.js.map +1 -0
  367. package/packages/core/dist/core/ports/buffered-output.d.ts +36 -0
  368. package/packages/core/dist/core/ports/buffered-output.d.ts.map +1 -0
  369. package/packages/core/dist/core/ports/buffered-output.js +89 -0
  370. package/packages/core/dist/core/ports/buffered-output.js.map +1 -0
  371. package/packages/core/dist/core/ports/resolve.d.ts +0 -13
  372. package/packages/core/dist/core/ports/resolve.d.ts.map +1 -1
  373. package/packages/core/dist/core/ports/resolve.js +0 -28
  374. package/packages/core/dist/core/ports/resolve.js.map +1 -1
  375. package/packages/core/dist/core/remove/removal-confirmation.d.ts +4 -1
  376. package/packages/core/dist/core/remove/removal-confirmation.d.ts.map +1 -1
  377. package/packages/core/dist/core/remove/removal-confirmation.js +5 -4
  378. package/packages/core/dist/core/remove/removal-confirmation.js.map +1 -1
  379. package/packages/core/dist/core/remove/remove-from-source-pipeline.d.ts.map +1 -1
  380. package/packages/core/dist/core/remove/remove-from-source-pipeline.js +1 -10
  381. package/packages/core/dist/core/remove/remove-from-source-pipeline.js.map +1 -1
  382. package/packages/core/dist/core/uninstall/uninstall-executor.js +1 -1
  383. package/packages/core/dist/core/uninstall/uninstall-executor.js.map +1 -1
  384. package/packages/core/dist/core/uninstall/uninstall-reporter.d.ts +2 -2
  385. package/packages/core/dist/core/uninstall/uninstall-reporter.d.ts.map +1 -1
  386. package/packages/core/dist/core/uninstall/uninstall-reporter.js +4 -4
  387. package/packages/core/dist/core/uninstall/uninstall-reporter.js.map +1 -1
  388. package/packages/core/dist/index.d.ts +1 -1
  389. package/packages/core/dist/index.d.ts.map +1 -1
  390. package/packages/core/dist/types/execution-context.d.ts +40 -10
  391. package/packages/core/dist/types/execution-context.d.ts.map +1 -1
  392. package/packages/core/dist/utils/concurrency-pool.d.ts +34 -0
  393. package/packages/core/dist/utils/concurrency-pool.d.ts.map +1 -0
  394. package/packages/core/dist/utils/concurrency-pool.js +58 -0
  395. package/packages/core/dist/utils/concurrency-pool.js.map +1 -0
  396. package/packages/core/dist/utils/plugin-naming.d.ts +11 -3
  397. package/packages/core/dist/utils/plugin-naming.d.ts.map +1 -1
  398. package/packages/core/dist/utils/plugin-naming.js +27 -7
  399. package/packages/core/dist/utils/plugin-naming.js.map +1 -1
  400. package/plans/wave-resolver.md +254 -0
  401. package/.claude/agents/essentials/code-simplifier.md +0 -52
  402. package/.claude/commands/essentials/cleanup.md +0 -1
  403. package/.claude/commands/essentials/review.md +0 -8
  404. package/.claude/commands/git/commit.md +0 -5
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../core/src/core/source-resolution/resolve-package-source.ts", "../../core/src/core/save/save-candidate-builder.ts", "../../core/src/core/save/save-group-builder.ts", "../../core/src/core/save/save-conversion-helper.ts", "../../core/src/core/save/save-merge-extractor.ts", "../../core/src/core/save/save-conflict-analyzer.ts", "../../core/src/core/save/save-interactive-resolver.ts", "../../core/src/core/platform/platform-specific-paths.ts", "../../core/src/core/save/save-resolution-executor.ts", "../../core/src/core/save/save-platform-handler.ts", "../../core/src/core/save/save-write-coordinator.ts", "../../core/src/core/save/save-result-reporter.ts", "../../core/src/core/save/save-to-source-pipeline.ts", "../src/commands/save.ts"],
4
+ "sourcesContent": ["import path from 'path';\n\nimport { resolveDeclaredPath } from '../../utils/path-resolution.js';\nimport { arePackageNamesEquivalent, normalizePackageName } from '../../utils/package-name.js';\nimport { isRegistryPath } from '../source-mutability.js';\nimport { readWorkspaceIndex } from '../../utils/workspace-index-yml.js';\nimport { MUTABILITY, SOURCE_TYPES } from '../../constants/index.js';\nimport type { ResolvedPackageSource } from './types.js';\n\nexport async function resolvePackageSource(\n workspaceRoot: string,\n packageName: string\n): Promise<ResolvedPackageSource> {\n const normalizedTarget = normalizePackageName(packageName);\n const ws = await readWorkspaceIndex(workspaceRoot);\n const entryKey = Object.keys(ws.index.packages ?? {}).find(k =>\n arePackageNamesEquivalent(k, normalizedTarget)\n );\n const entry = entryKey ? ws.index.packages?.[entryKey] : undefined;\n if (!entry?.path) {\n throw new Error(\n `Package '${packageName}' is not installed in this workspace.\\n` +\n `Run 'opkg install ${packageName}' to install it first.`\n );\n }\n\n const resolved = resolveDeclaredPath(entry.path, workspaceRoot);\n const absolutePath = path.join(resolved.absolute, path.sep);\n const mutability = isRegistryPath(absolutePath) ? MUTABILITY.IMMUTABLE : MUTABILITY.MUTABLE;\n const sourceType = isRegistryPath(absolutePath) ? SOURCE_TYPES.REGISTRY : SOURCE_TYPES.PATH;\n\n return {\n packageName: normalizePackageName(entryKey ?? normalizedTarget),\n absolutePath,\n declaredPath: resolved.declared,\n mutability,\n version: entry.version,\n sourceType\n };\n}\n", "/**\n * Save Candidate Builder Module\n * \n * Core responsibility: Transform filesystem files into SaveCandidate objects with metadata\n * \n * This module handles the discovery and transformation of files from both:\n * - Package source (local candidates)\n * - Workspace paths (workspace candidates)\n * \n * For each file, it:\n * - Reads content and calculates hash\n * - Extracts metadata (mtime, display path)\n * - Infers platform for workspace files\n * - Parses markdown frontmatter when applicable\n */\n\nimport { join, relative } from 'path';\nimport { exists, getStats, readTextFile, walkFiles } from '../../utils/fs.js';\nimport { calculateFileHash } from '../../utils/hash-utils.js';\nimport { normalizePathForProcessing } from '../../utils/path-normalization.js';\nimport { inferPlatformFromWorkspaceFile } from '../platforms.js';\nimport { logger } from '../../utils/logger.js';\nimport { splitFrontmatter } from '../markdown-frontmatter.js';\nimport { getTargetPath } from '../../utils/workspace-index-helpers.js';\nimport type { SaveCandidate, SaveCandidateSource, CandidateBuildError, LocalSourceRef } from './save-types.js';\nimport type { WorkspaceIndexFileMapping } from '../../types/workspace-index.js';\n\n/**\n * Options for building candidates\n */\nexport interface CandidateBuilderOptions {\n /** Absolute path to package source root */\n packageRoot: string;\n \n /** Absolute path to workspace root */\n workspaceRoot: string;\n \n /** File mappings from workspace index */\n filesMapping: Record<string, (string | WorkspaceIndexFileMapping)[]>;\n}\n\n/**\n * Result of candidate building process\n */\nexport interface CandidateBuildResult {\n /** Candidates from package source (empty when using lazy path via localSourceRefs) */\n localCandidates: SaveCandidate[];\n \n /** Lightweight refs for local source files (used for lazy materialization) */\n localSourceRefs: LocalSourceRef[];\n \n /** Candidates from workspace */\n workspaceCandidates: SaveCandidate[];\n \n /** Non-fatal errors encountered during building */\n errors: CandidateBuildError[];\n}\n\n/**\n * Internal options for buildCandidate function\n */\ninterface BuildCandidateOptions {\n packageRoot: string;\n workspaceRoot: string;\n inferPlatform?: boolean;\n parseMarkdown?: boolean;\n mergeStrategy?: 'deep' | 'shallow' | 'replace' | 'composite';\n mergeKeys?: string[];\n}\n\n/**\n * Build all candidates from index mapping\n * \n * Main entry point that orchestrates candidate discovery from both\n * package source and workspace paths.\n * \n * @param options - Builder options with roots and mappings\n * @returns Result with local/workspace candidates and any errors\n */\nexport async function buildCandidates(\n options: CandidateBuilderOptions\n): Promise<CandidateBuildResult> {\n const errors: CandidateBuildError[] = [];\n \n // Build workspace candidates (from workspace paths)\n logger.debug(`Building workspace candidates from workspace paths`);\n const { candidates: workspaceCandidates, errors: workspaceErrors } = await buildWorkspaceCandidates(\n options.workspaceRoot,\n options.packageRoot,\n options.filesMapping\n );\n \n errors.push(...workspaceErrors);\n \n // Build lightweight local source refs (no file reads)\n logger.debug(`Building local source refs from package source (lazy mode)`);\n const localSourceRefs = await buildLocalSourceRefs(\n options.packageRoot\n );\n \n logger.debug(\n `Built ${localSourceRefs.length} local source refs, ${workspaceCandidates.length} workspace candidates`\n );\n \n return {\n localCandidates: [],\n localSourceRefs,\n workspaceCandidates,\n errors\n };\n}\n\n/**\n * Build lightweight local source refs by discovering all files in source directory.\n * Only captures path metadata (registryPath, fullPath) without reading content.\n */\nasync function buildLocalSourceRefs(\n packageRoot: string\n): Promise<LocalSourceRef[]> {\n const refs: LocalSourceRef[] = [];\n \n for await (const absPath of walkFiles(packageRoot)) {\n const relPath = relative(packageRoot, absPath);\n const normalizedPath = normalizePathForProcessing(relPath);\n \n if (!normalizedPath) continue;\n \n if (normalizedPath.startsWith('.openpackage/') || normalizedPath === 'openpackage.yml') {\n continue;\n }\n \n if (normalizedPath.startsWith('.') && !normalizedPath.match(/^\\.(cursor|claude|opencode|windsurf|roo|factory|kilo|qwen|warp|codex|pi|kilocode|agent|augment)/)) {\n continue;\n }\n \n refs.push({ registryPath: normalizedPath, fullPath: absPath });\n logger.debug(`Built local source ref: ${normalizedPath}`);\n }\n \n return refs;\n}\n\n/**\n * Materialize a full SaveCandidate from a lightweight LocalSourceRef.\n * Reads file content, hash, stats, and frontmatter on demand.\n */\nexport async function materializeLocalCandidate(\n ref: LocalSourceRef,\n packageRoot: string\n): Promise<SaveCandidate | null> {\n return buildCandidate('local', ref.fullPath, ref.registryPath, {\n packageRoot,\n workspaceRoot: packageRoot,\n inferPlatform: false,\n parseMarkdown: true\n });\n}\n\n/**\n * Build workspace candidates from mapped workspace paths\n * \n * Discovers files in the workspace based on index mappings.\n * Handles both file mappings and directory mappings (recursive walk).\n * \n * @param workspaceRoot - Absolute path to workspace root\n * @param packageRoot - Absolute path to package root\n * @param filesMapping - File mappings from workspace index\n * @returns Object with candidates array and errors array\n */\nasync function buildWorkspaceCandidates(\n workspaceRoot: string,\n packageRoot: string,\n filesMapping: Record<string, (string | WorkspaceIndexFileMapping)[]>\n): Promise<{ candidates: SaveCandidate[]; errors: CandidateBuildError[] }> {\n const candidates: SaveCandidate[] = [];\n const errors: CandidateBuildError[] = [];\n \n for (const [rawKey, targets] of Object.entries(filesMapping)) {\n const registryKey = normalizePathForProcessing(rawKey);\n if (!registryKey || !Array.isArray(targets)) continue;\n \n const isDirectoryMapping = registryKey.endsWith('/');\n \n for (const mapping of targets) {\n const workspaceRel = getTargetPath(mapping);\n const normalizedTargetPath = normalizePathForProcessing(workspaceRel);\n if (!normalizedTargetPath) continue;\n \n const absTargetPath = join(workspaceRoot, normalizedTargetPath);\n \n // Extract merge metadata if present\n const mergeMetadata = typeof mapping === 'object' && mapping !== null\n ? { merge: mapping.merge, keys: mapping.keys }\n : undefined;\n \n if (isDirectoryMapping) {\n // Directory mapping: enumerate all files under the directory\n logger.debug(`Enumerating directory mapping: ${registryKey} -> ${normalizedTargetPath}`);\n try {\n const files = await collectFilesUnderDirectory(absTargetPath);\n logger.debug(`Found ${files.length} files under directory ${normalizedTargetPath}`);\n \n for (const relFile of files) {\n const registryPath = normalizePathForProcessing(join(registryKey, relFile));\n if (!registryPath) continue;\n \n const absWorkspaceFile = join(absTargetPath, relFile);\n const candidate = await buildCandidate('workspace', absWorkspaceFile, registryPath, {\n packageRoot,\n workspaceRoot,\n inferPlatform: true,\n parseMarkdown: true,\n mergeStrategy: mergeMetadata?.merge,\n mergeKeys: mergeMetadata?.keys\n });\n \n if (candidate) {\n candidates.push(candidate);\n logger.debug(`Built workspace candidate: ${registryPath} (from directory)`);\n }\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n errors.push({\n path: absTargetPath,\n registryPath: registryKey,\n reason: `Failed to enumerate directory: ${errorMsg}`\n });\n logger.warn(`Failed to enumerate directory ${absTargetPath}: ${errorMsg}`);\n }\n } else {\n // File mapping: single file\n if (!(await exists(absTargetPath))) {\n // File doesn't exist in workspace - skip (not an error)\n logger.debug(`Workspace file not found (skipping): ${normalizedTargetPath}`);\n continue;\n }\n \n const candidate = await buildCandidate('workspace', absTargetPath, registryKey, {\n packageRoot,\n workspaceRoot,\n inferPlatform: true,\n parseMarkdown: true,\n mergeStrategy: mergeMetadata?.merge,\n mergeKeys: mergeMetadata?.keys\n });\n \n if (candidate) {\n candidates.push(candidate);\n logger.debug(`Built workspace candidate: ${registryKey}`);\n }\n }\n }\n }\n \n return { candidates, errors };\n}\n\n/**\n * Build single candidate from file path\n * \n * Core transformation: file \u2192 candidate\n * \n * Steps:\n * 1. Read file content\n * 2. Calculate hash\n * 3. Get file stats (mtime)\n * 4. Calculate display path\n * 5. Infer platform (workspace only)\n * 6. Parse markdown frontmatter (if applicable)\n * 7. Construct SaveCandidate object\n * \n * @param source - 'local' or 'workspace'\n * @param absPath - Absolute path to file\n * @param registryPath - Registry path for this file\n * @param options - Build options\n * @returns SaveCandidate or null if failed\n */\nasync function buildCandidate(\n source: SaveCandidateSource,\n absPath: string,\n registryPath: string,\n options: BuildCandidateOptions\n): Promise<SaveCandidate | null> {\n try {\n // Read file content\n const content = await readTextFile(absPath);\n \n // Calculate content hash\n const contentHash = await calculateFileHash(content);\n \n // Get file stats\n const stats = await getStats(absPath);\n \n // Calculate display path (relative to appropriate root)\n const rootPath = source === 'workspace' ? options.workspaceRoot : options.packageRoot;\n const relPath = absPath.slice(rootPath.length + 1);\n const displayPath = normalizePathForProcessing(relPath) || registryPath;\n \n // Infer platform for workspace files\n let platform: string | undefined;\n if (options.inferPlatform && source === 'workspace') {\n const sourceDir = deriveSourceDir(displayPath);\n platform = inferPlatformFromWorkspaceFile(\n absPath,\n sourceDir,\n registryPath,\n options.workspaceRoot\n );\n }\n \n // Parse markdown frontmatter if enabled\n let frontmatter: any = undefined;\n let rawFrontmatter: string | undefined;\n let markdownBody: string | undefined;\n let isMarkdown = false;\n \n if (options.parseMarkdown && (absPath.endsWith('.md') || absPath.endsWith('.markdown'))) {\n isMarkdown = true;\n try {\n const parsed = splitFrontmatter(content);\n if (parsed.frontmatter && Object.keys(parsed.frontmatter).length > 0) {\n frontmatter = parsed.frontmatter;\n rawFrontmatter = parsed.rawFrontmatter;\n markdownBody = parsed.body;\n }\n } catch (error) {\n logger.debug(`Failed to parse frontmatter for ${absPath}: ${error}`);\n }\n }\n \n // Construct candidate\n const candidate: SaveCandidate = {\n source,\n registryPath,\n fullPath: absPath,\n content,\n contentHash,\n mtime: stats.mtime.getTime(),\n displayPath,\n platform,\n frontmatter,\n rawFrontmatter,\n markdownBody,\n isMarkdown,\n mergeStrategy: options.mergeStrategy,\n mergeKeys: options.mergeKeys\n };\n \n return candidate;\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n logger.warn(`Failed to build candidate for ${absPath}: ${errorMsg}`);\n return null;\n }\n}\n\n/**\n * Collect all files under a directory recursively\n * \n * Uses walkFiles utility for recursive traversal.\n * Returns relative paths from the directory root.\n * \n * @param absDir - Absolute directory path\n * @returns Array of relative file paths\n */\nasync function collectFilesUnderDirectory(absDir: string): Promise<string[]> {\n const collected: string[] = [];\n \n // Check if directory exists\n if (!(await exists(absDir))) {\n return collected;\n }\n \n // Walk files recursively\n for await (const absFile of walkFiles(absDir)) {\n // Calculate relative path from directory root\n const relPath = absFile.slice(absDir.length + 1).replace(/\\\\/g, '/');\n collected.push(relPath);\n }\n \n return collected;\n}\n\n/**\n * Derive source directory from relative path\n * \n * Extracts the first path segment for platform inference.\n * \n * Example: \".cursor/commands/test.md\" \u2192 \".cursor\"\n * \n * @param relPath - Relative path\n * @returns First path segment\n */\nfunction deriveSourceDir(relPath: string | undefined): string {\n if (!relPath) return '';\n const first = relPath.split('/')[0] || '';\n return first;\n}\n", "/**\n * Save Group Builder Module\n * \n * Core responsibility: Organize candidates by registry path into groups for analysis\n * \n * This module takes flat arrays of local and workspace candidates and organizes them\n * into groups where each group represents all versions of a single file (identified by\n * registry path or semantic equivalence after conversion).\n * \n * Each group contains:\n * - One optional local (source) candidate\n * - Zero or more workspace candidates\n * \n * Handles matching workspace candidates (e.g., mcp.json) with source candidates (e.g., mcp.jsonc)\n * by finding source files that would produce the workspace file through export flows.\n */\n\nimport { getPlatformDefinition, getGlobalExportFlows } from '../platforms.js';\nimport { logger } from '../../utils/logger.js';\nimport { minimatch } from 'minimatch';\nimport type { SaveCandidate, SaveCandidateGroup, LocalSourceRef } from './save-types.js';\nimport type { Platform } from '../platforms.js';\n\n/**\n * Build candidate groups from local source refs and workspace candidates\n * \n * Groups candidates intelligently by matching workspace candidates with their\n * corresponding source refs. For platform-specific workspace files,\n * finds the universal source file that would export to that workspace file.\n * \n * Algorithm:\n * 1. Build initial groups by exact registry path match\n * 2. For workspace candidates without local match, find corresponding source file\n * 3. Match based on export flow patterns (source \u2192 workspace)\n * 4. Filter out local-only groups (no workspace candidates)\n * \n * @param localRefs - Lightweight refs from package source\n * @param workspaceCandidates - Candidates from workspace\n * @param workspaceRoot - Workspace root for flow lookup\n * @returns Array of candidate groups organized by registry path\n */\nexport function buildCandidateGroups(\n localRefs: LocalSourceRef[],\n workspaceCandidates: SaveCandidate[],\n workspaceRoot: string = process.cwd()\n): SaveCandidateGroup[] {\n const map = new Map<string, SaveCandidateGroup>();\n \n logger.debug(`Building groups from ${localRefs.length} local refs and ${workspaceCandidates.length} workspace candidates`);\n \n // Add local refs to groups (using their actual registry paths)\n for (const ref of localRefs) {\n const group = ensureGroup(map, ref.registryPath);\n group.localRef = ref;\n logger.debug(`Added local ref: ${ref.registryPath}`);\n }\n \n // Add workspace candidates to groups\n // Try to match with corresponding source file\n for (const candidate of workspaceCandidates) {\n logger.debug(\n `Processing workspace candidate: ${candidate.displayPath} ` +\n `(registryPath: ${candidate.registryPath}, platform: ${candidate.platform || 'none'})`\n );\n \n // First try exact match by registry path\n let group = map.get(candidate.registryPath);\n \n if (group) {\n logger.debug(` Found exact match by registryPath: ${candidate.registryPath}`);\n }\n \n // If no exact match, try to find source file via export flows or fallback matching\n // This handles cases where:\n // 1. Platform-specific workspace files need to match universal source\n // 2. Source filename differs from workspace registry path (e.g., mcp.jsonc vs mcp.json)\n if (!group) {\n let sourceRegistryPath = findSourceFileForWorkspace(\n candidate,\n localRefs,\n workspaceRoot\n );\n \n // If export flow matching failed (e.g., platform detection failed),\n // try fallback matching based on filename similarity\n if (!sourceRegistryPath) {\n sourceRegistryPath = findSourceFileByFallback(\n candidate.registryPath,\n localRefs\n );\n }\n \n if (sourceRegistryPath) {\n logger.debug(\n ` Matched to source: workspace ${candidate.displayPath} \u2192 source ${sourceRegistryPath}`\n );\n group = ensureGroup(map, sourceRegistryPath);\n } else {\n logger.debug(` No source match found`);\n }\n }\n \n // If still no match, create group with workspace registry path\n if (!group) {\n logger.debug(` Creating new group with workspace registryPath: ${candidate.registryPath}`);\n group = ensureGroup(map, candidate.registryPath);\n }\n \n group.workspace.push(candidate);\n }\n \n // Filter out local-only groups (no workspace candidates) during grouping\n const allGroups = Array.from(map.values());\n const activeCount = allGroups.filter(g => g.workspace.length > 0).length;\n logger.debug(`Built ${map.size} candidate groups (${activeCount} with workspace candidates)`);\n \n return allGroups;\n}\n\n/**\n * Find source file by fallback filename matching\n * \n * When export flow matching fails (e.g., due to platform detection failure),\n * try to match workspace registry path to source files by filename similarity.\n * \n * Tries these strategies in order:\n * 1. Exact match (already tried before calling this)\n * 2. Match with different extension (e.g., mcp.json \u2192 mcp.jsonc)\n * 3. Match basename without extension (e.g., opencode.json \u2192 mcp.json)\n * \n * @param registryPath - Workspace registry path to match\n * @param localRefs - All source refs\n * @returns Registry path of matching source file, or null\n */\nfunction findSourceFileByFallback(\n registryPath: string,\n localRefs: LocalSourceRef[]\n): string | null {\n const baseName = registryPath.replace(/\\.[^.]+$/, '');\n const candidates = localRefs.filter(c => {\n const sourceBaseName = c.registryPath.replace(/\\.[^.]+$/, '');\n return sourceBaseName === baseName;\n });\n \n if (candidates.length === 1) {\n logger.debug(` Fallback match: ${registryPath} \u2192 ${candidates[0].registryPath} (same basename)`);\n return candidates[0].registryPath;\n }\n \n const fileName = registryPath.split('/').pop() || '';\n const fileNameBase = fileName.replace(/\\.[^.]+$/, '');\n \n if (fileNameBase) {\n const fileNameCandidates = localRefs.filter(c => {\n const sourceFileName = c.registryPath.split('/').pop() || '';\n const sourceFileNameBase = sourceFileName.replace(/\\.[^.]+$/, '');\n return sourceFileNameBase === fileNameBase;\n });\n \n if (fileNameCandidates.length === 1) {\n logger.debug(\n ` Fallback match: ${registryPath} \u2192 ${fileNameCandidates[0].registryPath} (same filename)`\n );\n return fileNameCandidates[0].registryPath;\n }\n }\n \n return null;\n}\n\n/**\n * Find source file that would export to a workspace file\n * \n * Given a workspace candidate (e.g., .cursor/mcp.json), find the source file\n * (e.g., mcp.jsonc) that would produce it through export flows.\n * \n * @param workspaceCandidate - Workspace candidate to match\n * @param localRefs - All source refs\n * @param workspaceRoot - Workspace root for flow lookup\n * @returns Registry path of matching source file, or null\n */\nfunction findSourceFileForWorkspace(\n workspaceCandidate: SaveCandidate,\n localRefs: LocalSourceRef[],\n workspaceRoot: string\n): string | null {\n const platform = workspaceCandidate.platform as Platform;\n if (!platform || platform === 'ai') {\n return null;\n }\n \n try {\n // Get platform export flows\n const platformDef = getPlatformDefinition(platform, workspaceRoot);\n const platformExportFlows = platformDef.export || [];\n const globalExportFlows = getGlobalExportFlows(workspaceRoot) || [];\n const allExportFlows = [...globalExportFlows, ...platformExportFlows];\n \n logger.debug(` Checking ${allExportFlows.length} export flows for platform ${platform}`);\n \n // Get workspace path from candidate (relative to workspace root)\n const workspacePath = workspaceCandidate.displayPath;\n \n // Find export flow that would produce this workspace file\n for (const flow of allExportFlows) {\n const toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;\n \n // Handle switch expressions\n if (typeof toPattern === 'object' && '$switch' in toPattern) {\n continue; // Skip for now\n }\n \n if (typeof toPattern !== 'string') {\n continue;\n }\n \n // Check if workspace path matches the 'to' pattern\n const toMatches = minimatch(workspacePath, toPattern, { dot: true });\n \n if (!toMatches) {\n continue;\n }\n \n logger.debug(` Workspace path ${workspacePath} matches 'to' pattern: ${toPattern}`);\n \n // Found matching flow - now find source file that matches 'from' pattern(s)\n const fromPatterns = Array.isArray(flow.from) ? flow.from : [flow.from];\n \n for (const fromPattern of fromPatterns) {\n // Handle switch expressions\n if (typeof fromPattern === 'object' && '$switch' in fromPattern) {\n continue; // Skip for now\n }\n \n if (typeof fromPattern !== 'string') {\n continue;\n }\n \n logger.debug(` Checking 'from' pattern: ${fromPattern}`);\n \n // Find local ref that matches the 'from' pattern\n for (const ref of localRefs) {\n const fromMatches = minimatch(ref.registryPath, fromPattern, { dot: true });\n \n if (fromMatches) {\n logger.debug(` Found matching source: ${ref.registryPath}`);\n return ref.registryPath;\n }\n }\n }\n }\n \n logger.debug(` No matching source file found`);\n } catch (error) {\n logger.warn(`Failed to find source file for workspace candidate: ${error}`);\n }\n \n return null;\n}\n\n/**\n * Filter groups to only those with workspace candidates\n * \n * Since save is workspace \u2192 source, we only care about groups\n * that have workspace candidates to save. Groups with no workspace\n * candidates represent files that exist in source but not in workspace\n * (no changes to save).\n * \n * @param groups - All candidate groups\n * @returns Groups with at least one workspace candidate\n */\nexport function filterGroupsWithWorkspace(\n groups: SaveCandidateGroup[]\n): SaveCandidateGroup[] {\n return groups.filter(group => group.workspace.length > 0);\n}\n\n/**\n * Ensure a group exists for the given registry path\n * \n * Helper function to get or create a group in the map.\n * Creates a new group if one doesn't exist yet.\n * \n * @param map - Map of registry path to group\n * @param registryPath - Registry path to look up/create\n * @returns The group for this registry path\n */\nfunction ensureGroup(\n map: Map<string, SaveCandidateGroup>,\n registryPath: string\n): SaveCandidateGroup {\n let group = map.get(registryPath);\n if (!group) {\n group = {\n registryPath,\n workspace: []\n };\n map.set(registryPath, group);\n }\n return group;\n}\n", "/**\n * Save Conversion Helper Module\n * \n * Provides conversion utilities for transforming workspace platform-specific files\n * to universal format before comparison. This enables semantic equality checks that\n * account for format differences defined in platforms.jsonc.\n * \n * Key responsibilities:\n * - Apply platform import flows to convert workspace \u2192 universal\n * - Calculate hashes of converted content for comparison\n * - Cache conversion results to avoid duplicate work\n * - Handle conversion failures gracefully with fallback\n * \n * @module save-conversion-helper\n */\n\nimport { join } from 'path';\nimport { tmpdir } from 'os';\nimport { mkdtemp, rm } from 'fs/promises';\nimport { getPlatformDefinition, getGlobalImportFlows, getGlobalExportFlows } from '../platforms.js';\nimport { createFlowExecutor } from '../flows/flow-executor.js';\nimport { calculateFileHash } from '../../utils/hash-utils.js';\nimport { ensureDir, writeTextFile, readTextFile, exists } from '../../utils/fs.js';\nimport { logger } from '../../utils/logger.js';\nimport { minimatch } from 'minimatch';\nimport { extractPackageContribution } from './save-merge-extractor.js';\nimport type { SaveCandidate } from './save-types.js';\nimport type { Platform } from '../platforms.js';\nimport type { Flow, FlowContext } from '../../types/flows.js';\n\nlet sharedTempDir: string | null = null;\nlet tempDirCounter = 0;\n\nexport async function initSharedTempDir(): Promise<void> {\n if (!sharedTempDir) {\n sharedTempDir = await mkdtemp(join(tmpdir(), 'opkg-save-'));\n }\n}\n\nexport async function cleanupSharedTempDir(): Promise<void> {\n if (sharedTempDir) {\n try {\n await rm(sharedTempDir, { recursive: true, force: true });\n } catch (error) {\n logger.debug('Failed to cleanup shared temp directory', { tempDir: sharedTempDir, error });\n }\n sharedTempDir = null;\n tempDirCounter = 0;\n }\n}\n\nexport async function allocateTempSubdir(): Promise<string> {\n if (!sharedTempDir) {\n const standalone = await mkdtemp(join(tmpdir(), 'opkg-save-standalone-'));\n return standalone;\n }\n const subDir = join(sharedTempDir, `op-${tempDirCounter++}`);\n await ensureDir(subDir);\n return subDir;\n}\n\n/**\n * Conversion result with success status\n */\nexport interface ConversionResult {\n success: boolean;\n convertedContent?: string;\n convertedHash?: string;\n error?: string;\n}\n\n/**\n * Cache for converted content hashes\n * Key: `${fullPath}:${contentHash}:${platform}`\n * Value: Converted content hash\n */\nconst conversionCache = new Map<string, string>();\n\n/**\n * Generate cache key for a candidate\n */\nfunction getCacheKey(candidate: SaveCandidate): string {\n return `${candidate.fullPath}:${candidate.contentHash}:${candidate.platform || 'none'}`;\n}\n\n/**\n * Calculate hash of workspace candidate after conversion to universal format\n * \n * This is the main entry point for conversion-aware hash calculation.\n * It applies platform import flows to convert workspace content to universal format,\n * then calculates the hash for comparison.\n * \n * @param candidate - Workspace candidate to convert\n * @param workspaceRoot - Absolute path to workspace root\n * @returns Hash of converted content (or original hash if conversion not applicable/fails)\n */\nexport async function calculateConvertedHash(\n candidate: SaveCandidate,\n workspaceRoot: string\n): Promise<string> {\n // Check if candidate has platform - if not, no conversion needed\n if (!candidate.platform || candidate.platform === 'ai') {\n return candidate.contentHash;\n }\n \n // Check cache first\n const cacheKey = getCacheKey(candidate);\n const cached = conversionCache.get(cacheKey);\n if (cached) {\n logger.debug(`Cache hit for converted hash: ${candidate.displayPath}`);\n return cached;\n }\n \n // Perform conversion\n const result = await convertWorkspaceToUniversal(\n candidate.content,\n candidate.platform as Platform,\n candidate.registryPath,\n workspaceRoot\n );\n \n if (!result.success || !result.convertedHash) {\n // Conversion failed or not applicable - fall back to original hash\n logger.debug(\n `Conversion not applicable or failed for ${candidate.displayPath}, using raw hash`,\n { reason: result.error }\n );\n return candidate.contentHash;\n }\n \n // Cache the result\n conversionCache.set(cacheKey, result.convertedHash);\n \n return result.convertedHash;\n}\n\n/**\n * Convert workspace platform-specific content to universal format\n * \n * Applies platform import flows to transform workspace content.\n * Import flows represent workspace \u2192 package transformations.\n * \n * @param workspaceContent - Content from workspace file\n * @param platform - Platform the content is from (e.g., 'cursor', 'claude')\n * @param registryPath - Universal registry path (e.g., 'mcp.jsonc', 'agents/test.md')\n * @param workspaceRoot - Absolute path to workspace root\n * @returns Conversion result with converted content and hash\n */\nexport async function convertWorkspaceToUniversal(\n workspaceContent: string,\n platform: Platform,\n registryPath: string,\n workspaceRoot: string\n): Promise<ConversionResult> {\n try {\n // Get platform definition and import flows\n const platformDef = getPlatformDefinition(platform, workspaceRoot);\n const platformImportFlows = platformDef.import || [];\n const globalImportFlows = getGlobalImportFlows(workspaceRoot) || [];\n const allImportFlows = [...globalImportFlows, ...platformImportFlows];\n \n if (allImportFlows.length === 0) {\n logger.debug(`No import flows defined for platform ${platform}`);\n return {\n success: false,\n error: 'No import flows defined for platform'\n };\n }\n \n // Find matching flow for this registry path\n const matchingFlow = findMatchingImportFlow(\n allImportFlows,\n registryPath,\n platform,\n workspaceRoot\n );\n \n if (!matchingFlow) {\n logger.debug(`No matching import flow for ${registryPath} on platform ${platform}`);\n return {\n success: false,\n error: 'No matching import flow found'\n };\n }\n \n // Create temporary directory for conversion\n const tempDir = await allocateTempSubdir();\n const inputDir = join(tempDir, 'in');\n const outputDir = join(tempDir, 'out');\n await ensureDir(inputDir);\n await ensureDir(outputDir);\n \n // Infer workspace source path from flow\n const workspaceSourcePath = inferWorkspaceSourcePath(\n matchingFlow,\n registryPath,\n platform\n );\n \n // Write workspace content to temp input file\n const inputFilePath = join(inputDir, workspaceSourcePath);\n await ensureDir(join(inputFilePath, '..'));\n await writeTextFile(inputFilePath, workspaceContent);\n \n // Build flow context for conversion\n const flowContext: FlowContext = {\n workspaceRoot: outputDir, // Output goes to outputDir\n packageRoot: inputDir, // Input comes from inputDir\n platform: platform, // Source platform\n packageName: 'temp',\n direction: 'install', // Import flows are used during \"install\" direction\n variables: {\n name: 'temp',\n version: '0.0.0',\n platform: platform, // For conditionals: $$platform\n source: platform, // For conditionals: $$source\n sourcePlatform: platform,\n targetPlatform: 'openpackage'\n },\n dryRun: false\n };\n \n // Execute the flow\n const executor = createFlowExecutor();\n const concreteFlow: Flow = {\n ...matchingFlow,\n from: workspaceSourcePath // Use concrete file path\n };\n \n const flowResult = await executor.executeFlow(concreteFlow, flowContext);\n \n if (!flowResult.success) {\n return {\n success: false,\n error: `Flow execution failed: ${flowResult.error?.message}`\n };\n }\n \n // Read converted content\n if (typeof flowResult.target !== 'string') {\n return {\n success: false,\n error: 'Flow did not produce target path'\n };\n }\n \n const outputFilePath = flowResult.target;\n if (!(await exists(outputFilePath))) {\n return {\n success: false,\n error: 'Flow did not produce output file'\n };\n }\n \n const convertedContent = await readTextFile(outputFilePath);\n const convertedHash = await calculateFileHash(convertedContent);\n \n logger.debug(\n `Successfully converted ${registryPath} from ${platform} format to universal`,\n { \n originalHash: await calculateFileHash(workspaceContent),\n convertedHash \n }\n );\n \n return {\n success: true,\n convertedContent,\n convertedHash\n };\n \n } catch (error) {\n logger.warn(\n `Conversion failed for ${registryPath} (platform: ${platform})`,\n { error }\n );\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n\n/**\n * Find matching import flow for a registry path\n * \n * Searches through import flows to find one whose 'to' pattern matches\n * the registry path. Also evaluates conditional 'when' clauses.\n * \n * @param flows - Array of import flows\n * @param registryPath - Universal registry path to match\n * @param platform - Platform context for conditional evaluation\n * @param workspaceRoot - Workspace root for context\n * @returns Matching flow or undefined\n */\nfunction findMatchingImportFlow(\n flows: Flow[],\n registryPath: string,\n platform: Platform,\n workspaceRoot: string\n): Flow | undefined {\n for (const flow of flows) {\n // Check if 'to' pattern matches registry path\n const toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;\n \n // Handle switch expressions\n if (typeof toPattern === 'object' && '$switch' in toPattern) {\n // Skip switch expressions for now - would need full context to resolve\n logger.debug('Skipping flow with switch expression in to field');\n continue;\n }\n \n if (typeof toPattern !== 'string') {\n continue;\n }\n \n // Check if registry path matches the 'to' pattern\n if (!minimatch(registryPath, toPattern, { dot: true })) {\n continue;\n }\n \n // Check conditional 'when' clause if present\n if (flow.when) {\n const conditionMet = evaluateWhenCondition(flow.when, platform, workspaceRoot);\n if (!conditionMet) {\n logger.debug(`Flow condition not met for ${registryPath}`, { when: flow.when });\n continue;\n }\n }\n \n // Found matching flow\n return flow;\n }\n \n return undefined;\n}\n\n/**\n * Evaluate 'when' conditional clause\n * \n * Simplified evaluation for common patterns used in platforms.jsonc.\n * Handles $eq and $ne comparisons with $$platform and $$source variables.\n * \n * @param when - Conditional expression\n * @param platform - Current platform\n * @param workspaceRoot - Workspace root\n * @returns True if condition is met\n */\nfunction evaluateWhenCondition(\n when: any,\n platform: Platform,\n workspaceRoot: string\n): boolean {\n // Handle { \"$eq\": [\"$$platform\", \"claude\"] }\n if (when.$eq && Array.isArray(when.$eq) && when.$eq.length === 2) {\n const left = resolveVariable(when.$eq[0], platform);\n const right = resolveVariable(when.$eq[1], platform);\n return left === right;\n }\n \n // Handle { \"$ne\": [\"$$platform\", \"claude\"] }\n if (when.$ne && Array.isArray(when.$ne) && when.$ne.length === 2) {\n const left = resolveVariable(when.$ne[0], platform);\n const right = resolveVariable(when.$ne[1], platform);\n return left !== right;\n }\n \n // Handle { \"exists\": \"AGENTS.md\" }\n if (when.exists) {\n // For save, we're converting workspace \u2192 universal\n // The 'exists' check is relative to source (workspace in this case)\n // For simplicity, assume file exists if we got to this point\n return true;\n }\n \n // Unknown condition type - assume not met (conservative)\n logger.debug('Unknown condition type in when clause', { when });\n return false;\n}\n\n/**\n * Resolve variable references like $$platform, $$source\n */\nfunction resolveVariable(value: any, platform: Platform): any {\n if (typeof value === 'string') {\n if (value === '$$platform' || value === '$$source') {\n return platform;\n }\n }\n return value;\n}\n\n/**\n * Infer workspace source path from flow definition\n * \n * For import flows, 'from' specifies the workspace path pattern.\n * We need to construct the specific file path that would be in workspace.\n * \n * @param flow - Import flow\n * @param registryPath - Universal registry path\n * @param platform - Platform context\n * @returns Workspace source path\n */\nfunction inferWorkspaceSourcePath(\n flow: Flow,\n registryPath: string,\n platform: Platform\n): string {\n const fromPattern = Array.isArray(flow.from) ? flow.from[0] : flow.from;\n \n // Handle switch expressions\n if (typeof fromPattern === 'object' && '$switch' in fromPattern) {\n // For now, can't resolve without full context - use registry path\n logger.debug('Cannot infer source path from switch expression, using registry path');\n return registryPath;\n }\n \n if (typeof fromPattern !== 'string') {\n return registryPath;\n }\n \n // If 'from' is a glob pattern, we need to construct the specific path\n // Example: from \".cursor/mcp.json\" and registryPath \"mcp.jsonc\"\n // We should use \".cursor/mcp.json\" as the source path\n \n // If the pattern contains glob wildcards, try to map registry path to source path\n if (fromPattern.includes('**') || fromPattern.includes('*')) {\n // Extract base directory from pattern\n const baseDir = fromPattern.split('**')[0].replace(/\\*+/g, '');\n \n // Extract file/subpath from registry path relative to 'to' pattern\n const toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;\n if (typeof toPattern === 'string' && toPattern.includes('**')) {\n const toBase = toPattern.split('**')[0];\n const subPath = registryPath.startsWith(toBase) \n ? registryPath.slice(toBase.length)\n : registryPath;\n return `${baseDir}${subPath}`;\n }\n \n // Fallback: use registry path\n return registryPath;\n }\n \n // For non-glob patterns, use the pattern as-is\n return fromPattern;\n}\n\n/**\n * Convert source (universal) content to workspace platform-specific format\n * \n * Applies platform export flows to transform universal content to workspace format.\n * This is used for forward parity checking: verifying that workspace file matches\n * what would be produced by exporting the source.\n * \n * @param sourceContent - Content from universal source file\n * @param platform - Target platform (e.g., 'cursor', 'claude')\n * @param sourceRegistryPath - Source registry path (e.g., 'mcp.json')\n * @param workspacePath - Expected workspace path (e.g., '.cursor/mcp.json')\n * @param workspaceRoot - Absolute path to workspace root\n * @returns Conversion result with converted content and hash\n */\nexport async function convertSourceToWorkspace(\n sourceContent: string,\n platform: Platform,\n sourceRegistryPath: string,\n workspacePath: string,\n workspaceRoot: string\n): Promise<ConversionResult> {\n try {\n // Get platform definition and export flows\n const platformDef = getPlatformDefinition(platform, workspaceRoot);\n const platformExportFlows = platformDef.export || [];\n const globalExportFlows = getGlobalExportFlows(workspaceRoot) || [];\n const allExportFlows = [...globalExportFlows, ...platformExportFlows];\n \n if (allExportFlows.length === 0) {\n return {\n success: false,\n error: 'No export flows defined for platform'\n };\n }\n \n // Find matching export flow\n const matchingFlow = findMatchingExportFlow(\n allExportFlows,\n sourceRegistryPath,\n workspacePath,\n platform,\n workspaceRoot\n );\n \n if (!matchingFlow) {\n return {\n success: false,\n error: 'No matching export flow found'\n };\n }\n \n \n // Create temporary directory for conversion\n const tempDir = await allocateTempSubdir();\n const inputDir = join(tempDir, 'in');\n const outputDir = join(tempDir, 'out');\n await ensureDir(inputDir);\n await ensureDir(outputDir);\n \n // Write source content to temp input file\n const inputFilePath = join(inputDir, sourceRegistryPath);\n await ensureDir(join(inputFilePath, '..'));\n await writeTextFile(inputFilePath, sourceContent);\n \n // Build flow context for export\n const flowContext: FlowContext = {\n workspaceRoot: outputDir, // Output goes to outputDir\n packageRoot: inputDir, // Input comes from inputDir\n platform: platform,\n packageName: 'temp',\n direction: 'install', // Export flows are used during \"install\" direction\n variables: {\n name: 'temp',\n version: '0.0.0',\n platform: platform,\n source: 'openpackage',\n sourcePlatform: 'openpackage',\n targetPlatform: platform,\n targetRoot: './'\n },\n dryRun: false\n };\n \n // Execute the flow\n const executor = createFlowExecutor();\n const flowResult = await executor.executeFlow(matchingFlow, flowContext);\n \n if (!flowResult.success) {\n return {\n success: false,\n error: `Flow execution failed: ${flowResult.error?.message}`\n };\n }\n \n // Read converted content\n if (typeof flowResult.target !== 'string') {\n return {\n success: false,\n error: 'Flow did not produce target path'\n };\n }\n \n const outputFilePath = flowResult.target;\n if (!(await exists(outputFilePath))) {\n return {\n success: false,\n error: 'Flow did not produce output file'\n };\n }\n \n const convertedContent = await readTextFile(outputFilePath);\n const convertedHash = await calculateFileHash(convertedContent);\n \n logger.debug(\n `Successfully forward converted ${sourceRegistryPath} to ${platform} format`,\n {\n originalHash: await calculateFileHash(sourceContent),\n convertedHash\n }\n );\n \n return {\n success: true,\n convertedContent,\n convertedHash\n };\n \n } catch (error) {\n logger.warn(\n `Forward conversion failed for ${sourceRegistryPath} (platform: ${platform})`,\n { error }\n );\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n\n/**\n * Find matching export flow for source \u2192 workspace conversion\n * \n * @param flows - Array of export flows\n * @param sourceRegistryPath - Source registry path\n * @param workspacePath - Target workspace path\n * @param platform - Platform context\n * @param workspaceRoot - Workspace root\n * @returns Matching flow or undefined\n */\nfunction findMatchingExportFlow(\n flows: Flow[],\n sourceRegistryPath: string,\n workspacePath: string,\n platform: Platform,\n workspaceRoot: string\n): Flow | undefined {\n for (const flow of flows) {\n // Check if 'from' pattern matches source registry path\n const fromPatterns = Array.isArray(flow.from) ? flow.from : [flow.from];\n \n if (fromPatterns.some(p => typeof p === 'object' && '$switch' in p)) {\n continue; // Skip switch expressions\n }\n \n if (fromPatterns.some(p => typeof p !== 'string')) {\n continue;\n }\n \n const matchesFrom = fromPatterns.some(p =>\n typeof p === 'string' && minimatch(sourceRegistryPath, p, { dot: true })\n );\n if (!matchesFrom) continue;\n \n // Check conditional 'when' clause if present\n if (flow.when) {\n const conditionMet = evaluateWhenCondition(flow.when, platform, workspaceRoot);\n if (!conditionMet) {\n continue;\n }\n }\n \n // Found matching flow\n return flow;\n }\n \n return undefined;\n}\n\n/**\n * Compute and cache the comparable hash for a candidate.\n * \n * For merged files, extracts the package contribution first.\n * For platform-specific files, converts to universal format.\n * The result is cached on `candidate.comparableHash` for reuse.\n */\nexport async function ensureComparableHash(\n candidate: SaveCandidate,\n workspaceRoot: string\n): Promise<string> {\n if (candidate.comparableHash !== undefined) {\n return candidate.comparableHash;\n }\n\n let hash = candidate.contentHash;\n\n if (candidate.mergeStrategy && candidate.mergeKeys && candidate.mergeKeys.length > 0) {\n const extractResult = await extractPackageContribution(candidate);\n if (extractResult.success && extractResult.extractedHash) {\n hash = extractResult.extractedHash;\n if (extractResult.extractedContent) {\n candidate.extractedContent = extractResult.extractedContent;\n }\n } else {\n hash = await calculateConvertedHash(candidate, workspaceRoot);\n }\n } else {\n hash = await calculateConvertedHash(candidate, workspaceRoot);\n }\n\n candidate.comparableHash = hash;\n return hash;\n}\n\n/**\n * Clear the conversion cache\n * \n * Should be called at the end of save pipeline to free memory.\n */\nexport function clearConversionCache(): void {\n conversionCache.clear();\n}\n", "/**\n * Save Merge Extractor Module\n * \n * Provides utilities to extract package-specific contributions from merged files.\n * This enables proper parity checking for files that were merged during installation.\n * \n * Key responsibilities:\n * - Extract only the keys contributed by the package from merged JSON/YAML files\n * - Support deep and shallow merge strategies\n * - Handle composite merges (marker-based)\n * - Enable accurate comparison between workspace and source for merged files\n * \n * @module save-merge-extractor\n */\n\nimport { logger } from '../../utils/logger.js';\nimport { calculateFileHash } from '../../utils/hash-utils.js';\nimport type { SaveCandidate } from './save-types.js';\n\n/**\n * Extract result with extracted content and hash\n */\nexport interface ExtractResult {\n success: boolean;\n extractedContent?: string;\n extractedHash?: string;\n error?: string;\n}\n\n/**\n * Extract package-specific contribution from a merged workspace file\n * \n * This function reverses the merge operation that occurred during installation,\n * extracting only the keys/content that were contributed by the package.\n * \n * For deep/shallow merges:\n * - Parses workspace file as JSON\n * - Extracts only the keys specified in mergeKeys\n * - Normalizes keys to extract at the appropriate level\n * - Returns serialized JSON of just those keys\n * \n * For composite merges:\n * - Not yet implemented (returns error)\n * - Would need to extract content between package markers\n * \n * **Key Normalization**: The workspace index may track leaf keys like\n * `mcp.github.type`, `mcp.github.url`, but we need to extract at the\n * parent level `mcp.github`. This function automatically finds the\n * common parent key to extract.\n * \n * @param candidate - Workspace candidate with merge metadata\n * @returns Extract result with extracted content and hash\n */\nexport async function extractPackageContribution(\n candidate: SaveCandidate\n): Promise<ExtractResult> {\n // Only applicable to workspace candidates with merge metadata\n if (candidate.source !== 'workspace') {\n return {\n success: false,\n error: 'Extract only applies to workspace candidates'\n };\n }\n \n if (!candidate.mergeStrategy || !candidate.mergeKeys || candidate.mergeKeys.length === 0) {\n return {\n success: false,\n error: 'No merge metadata present'\n };\n }\n \n const { mergeStrategy, mergeKeys, content } = candidate;\n \n try {\n switch (mergeStrategy) {\n case 'deep':\n case 'shallow':\n // Normalize keys to find the common parent\n const normalizedKeys = normalizeKeysToParent(mergeKeys);\n return await extractFromJsonMerge(content, normalizedKeys);\n \n case 'composite':\n return {\n success: false,\n error: 'Composite merge extraction not yet implemented'\n };\n \n case 'replace':\n // Replace strategy doesn't merge, so extraction not applicable\n return {\n success: false,\n error: 'Replace strategy does not require extraction'\n };\n \n default:\n return {\n success: false,\n error: `Unknown merge strategy: ${mergeStrategy}`\n };\n }\n } catch (error) {\n logger.debug(`Failed to extract package contribution: ${error}`);\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n\n/**\n * Extract content by merge keys from raw JSON content (non-candidate)\n *\n * Used to compare a workspace merged contribution to the corresponding\n * subset of the local source content.\n *\n * @param content - JSON content string\n * @param mergeKeys - Dot-notation keys to extract\n * @returns Extract result with extracted content and hash\n */\nexport async function extractContentByKeys(\n content: string,\n mergeKeys: string[]\n): Promise<ExtractResult> {\n try {\n const normalizedKeys = normalizeKeysToParent(mergeKeys);\n return await extractFromJsonMerge(content, normalizedKeys);\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n\n/**\n * Normalize tracked keys to find the common parent keys to extract\n * \n * During installation, the flow may track all leaf keys that were added:\n * - `mcpServers.github.type`\n * - `mcpServers.github.url`\n * - `mcpServers.github.headers.Authorization`\n * \n * But for extraction, we want to extract at the parent level: `mcpServers.github`\n * \n * However, if keys are already at the parent level (e.g., `mcp.github`, `mcp.gitlab`),\n * we should use them as-is without further normalization.\n * \n * Algorithm:\n * 1. If all keys are already 2 levels deep or less, use them as-is\n * 2. Otherwise, find the longest common prefix and extract unique parent keys\n * 3. For leaf keys like `mcp.github.type`, `mcp.github.url`, extract `mcp.github`\n * \n * @param keys - Array of tracked keys (may be leaf keys)\n * @returns Array of normalized parent keys to extract\n */\nfunction normalizeKeysToParent(keys: string[]): string[] {\n if (keys.length === 0) return [];\n \n // Check if all keys are already at parent level (2 segments or less)\n const allAtParentLevel = keys.every(key => key.split('.').length <= 2);\n if (allAtParentLevel) {\n // Keys are already at the right level - use them as-is\n logger.debug(\n `Keys already at parent level - using as-is`,\n { keys }\n );\n return keys;\n }\n \n if (keys.length === 1) {\n // Single key - extract first 2 levels if possible\n const parts = keys[0].split('.');\n if (parts.length >= 2) {\n return [parts.slice(0, 2).join('.')];\n }\n return keys;\n }\n \n // Find the longest common prefix\n const firstParts = keys[0].split('.');\n let commonDepth = 0;\n \n for (let depth = 0; depth < firstParts.length; depth++) {\n const segment = firstParts[depth];\n const allMatch = keys.every(key => {\n const parts = key.split('.');\n return parts.length > depth && parts[depth] === segment;\n });\n \n if (allMatch) {\n commonDepth = depth + 1;\n } else {\n break;\n }\n }\n \n // Extract unique parent keys at the appropriate depth\n // For leaf keys, extract at 2 levels (or common depth if deeper)\n const normalized = new Set<string>();\n for (const key of keys) {\n const parts = key.split('.');\n // Use minimum of 2 levels or common depth + 1 (one level after common)\n const targetDepth = Math.max(2, commonDepth + 1);\n const extractDepth = Math.min(targetDepth, parts.length);\n \n if (extractDepth >= 2) {\n normalized.add(parts.slice(0, extractDepth).join('.'));\n } else if (parts.length > 0) {\n // Fallback: use the key as-is\n normalized.add(key);\n }\n }\n \n const result = Array.from(normalized);\n \n logger.debug(\n `Normalized ${keys.length} tracked keys to ${result.length} extraction key(s)`,\n { original: keys, normalized: result }\n );\n \n return result;\n}\n\n/**\n * Extract specific keys from a JSON-merged file\n * \n * Given a JSON file that was merged during installation, extract only\n * the keys that were contributed by the package.\n * \n * The mergeKeys use dot notation to specify nested keys:\n * - \"mcp.github\" means extract data.mcp.github\n * - \"mcp.server1\" means extract data.mcp.server1\n * - etc.\n * \n * @param content - Full merged file content (JSON string)\n * @param mergeKeys - Array of dot-notation keys to extract\n * @returns Extract result with extracted content\n */\nasync function extractFromJsonMerge(\n content: string,\n mergeKeys: string[]\n): Promise<ExtractResult> {\n try {\n // Parse the merged content\n const merged = JSON.parse(content);\n \n // Build an object containing only the package's keys\n const extracted: any = {};\n \n for (const keyPath of mergeKeys) {\n const value = getNestedValue(merged, keyPath);\n if (value !== undefined) {\n setNestedValue(extracted, keyPath, value);\n }\n }\n \n // Serialize the extracted content\n // Use same formatting as JSON.stringify with 2-space indent to match\n // how files are typically formatted\n const extractedContent = JSON.stringify(extracted, null, 2) + '\\n';\n const extractedHash = await calculateFileHash(extractedContent);\n \n logger.debug(\n `Extracted ${mergeKeys.length} key(s) from merged file`,\n { keys: mergeKeys }\n );\n \n return {\n success: true,\n extractedContent,\n extractedHash\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n}\n\n/**\n * Get nested value from object using dot notation\n * \n * @param obj - Object to traverse\n * @param keyPath - Dot-notation key path (e.g., \"mcp.github\")\n * @returns Value at the path, or undefined if not found\n */\nfunction getNestedValue(obj: any, keyPath: string): any {\n const keys = keyPath.split('.');\n let current = obj;\n \n for (const key of keys) {\n if (current === null || current === undefined || typeof current !== 'object') {\n return undefined;\n }\n current = current[key];\n }\n \n return current;\n}\n\n/**\n * Set nested value in object using dot notation\n * \n * Creates intermediate objects as needed.\n * \n * @param obj - Object to modify\n * @param keyPath - Dot-notation key path (e.g., \"mcp.github\")\n * @param value - Value to set\n */\nfunction setNestedValue(obj: any, keyPath: string, value: any): void {\n const keys = keyPath.split('.');\n let current = obj;\n \n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n if (!(key in current) || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key];\n }\n \n const lastKey = keys[keys.length - 1];\n current[lastKey] = value;\n}\n", "import { FILE_PATTERNS } from '../../constants/index.js';\nimport { calculateConvertedHash, convertSourceToWorkspace, ensureComparableHash } from './save-conversion-helper.js';\nimport { extractPackageContribution, extractContentByKeys } from './save-merge-extractor.js';\nimport { logger } from '../../utils/logger.js';\nimport type { SaveCandidate, SaveCandidateGroup, ResolutionStrategy } from './save-types.js';\n\n/**\n * Conflict analysis and resolution strategy determination\n * \n * This module classifies candidate groups and determines the appropriate resolution\n * strategy based on the number and similarity of workspace candidates.\n * \n * @module save-conflict-analyzer\n */\n\n/**\n * ConflictAnalysisType classifies the conflict scenario for a candidate group\n * \n * - `no-action-needed`: No workspace candidates exist (nothing to save)\n * - `no-change-needed`: Workspace content is identical to source (skip save)\n * - `auto-write`: Single or all-identical workspace candidates (auto-resolve)\n * - `needs-resolution`: Multiple differing workspace candidates (user choice required)\n */\nexport type ConflictAnalysisType = \n | 'no-action-needed' // No workspace candidates\n | 'no-change-needed' // Workspace matches source exactly\n | 'auto-write' // Single or identical workspace candidates\n | 'needs-resolution'; // Multiple differing workspace candidates\n\n/**\n * ConflictAnalysis represents the complete analysis of a candidate group\n * \n * Contains all metadata needed to determine how to handle the group,\n * including conflict type, candidate counts, and recommended strategy.\n */\nexport interface ConflictAnalysis {\n /** The registry path being analyzed */\n registryPath: string;\n \n /** Classification of conflict scenario */\n type: ConflictAnalysisType;\n \n /** Total number of workspace candidates (before deduplication) */\n workspaceCandidateCount: number;\n \n /** Deduplicated workspace candidates (unique by content hash) */\n uniqueWorkspaceCandidates: SaveCandidate[];\n \n /** Whether a local (source) candidate exists */\n hasLocalCandidate: boolean;\n \n /** Whether source content matches workspace (when single workspace candidate) */\n localMatchesWorkspace: boolean;\n \n /** Whether this is a root package file (AGENTS.md, etc.) */\n isRootFile: boolean;\n \n /** Whether any workspace candidates are platform-specific */\n hasPlatformCandidates: boolean;\n \n /** Recommended resolution strategy based on analysis */\n recommendedStrategy: ResolutionStrategy;\n}\n\n/**\n * Analyze a candidate group and determine resolution strategy\n * \n * This is the primary entry point for conflict analysis. It examines the\n * workspace candidates in a group and determines:\n * 1. What type of conflict (if any) exists\n * 2. Whether the change can be auto-resolved\n * 3. What resolution strategy should be used\n * \n * **Decision Flow**:\n * ```\n * No workspace candidates \u2192 no-action-needed (skip)\n * Single workspace, matches source \u2192 no-change-needed (skip)\n * Single workspace, differs from source \u2192 auto-write (write-single)\n * Multiple identical workspace \u2192 auto-write (write-single after dedup)\n * Multiple differing, force=true \u2192 needs-resolution (force-newest)\n * Multiple differing, force=false \u2192 needs-resolution (interactive)\n * ```\n * \n * @param group - The candidate group to analyze\n * @param force - Whether force mode is enabled (auto-select newest)\n * @param workspaceRoot - Workspace root for conversion context\n * @returns Complete conflict analysis with recommended strategy\n */\nexport async function analyzeGroup(\n group: SaveCandidateGroup,\n force: boolean,\n workspaceRoot: string\n): Promise<ConflictAnalysis> {\n const registryPath = group.registryPath;\n const hasLocal = !!group.local;\n const workspaceCandidates = group.workspace;\n const workspaceCandidateCount = workspaceCandidates.length;\n \n // Check if this is a root file (AGENTS.md or similar)\n // Root files may have special handling in some contexts\n const isRootFile = \n registryPath === FILE_PATTERNS.AGENTS_MD ||\n registryPath === FILE_PATTERNS.CLAUDE_MD ||\n registryPath === FILE_PATTERNS.GEMINI_MD ||\n registryPath === FILE_PATTERNS.QWEN_MD ||\n registryPath === FILE_PATTERNS.WARP_MD ||\n workspaceCandidates.some(c => c.isRootFile);\n \n // Check if any candidates are platform-specific\n // Platform-specific candidates may need separate handling\n const hasPlatformCandidates = workspaceCandidates.some(\n c => c.platform && c.platform !== 'ai'\n );\n \n // Early exit: No workspace candidates means nothing to save\n if (workspaceCandidateCount === 0) {\n return {\n registryPath,\n type: 'no-action-needed',\n workspaceCandidateCount: 0,\n uniqueWorkspaceCandidates: [],\n hasLocalCandidate: hasLocal,\n localMatchesWorkspace: false,\n isRootFile,\n hasPlatformCandidates: false,\n recommendedStrategy: 'skip'\n };\n }\n \n // Deduplicate workspace candidates by converted content hash (merge-aware)\n // Multiple workspace files with identical content (after conversion/extraction) are treated as one\n const uniqueWorkspace = await deduplicateCandidatesWithMerge(workspaceCandidates, workspaceRoot);\n \n // Check if workspace content is identical to source (after conversion)\n // Only applicable when there's exactly one unique workspace candidate\n const localMatchesWorkspace = hasLocal && uniqueWorkspace.length === 1 \n ? await checkConvertedParity(uniqueWorkspace[0], group.local!, workspaceRoot)\n : false;\n \n if (hasLocal && uniqueWorkspace.length > 1) {\n const parityResults = await Promise.all(\n uniqueWorkspace.map(async candidate => ({\n workspacePath: candidate.displayPath,\n platform: candidate.platform || 'none',\n matchesLocal: await checkConvertedParity(candidate, group.local!, workspaceRoot)\n }))\n );\n \n if (parityResults.every(result => result.matchesLocal)) {\n return {\n registryPath,\n type: 'no-change-needed',\n workspaceCandidateCount,\n uniqueWorkspaceCandidates: uniqueWorkspace,\n hasLocalCandidate: hasLocal,\n localMatchesWorkspace: true,\n isRootFile,\n hasPlatformCandidates,\n recommendedStrategy: 'skip'\n };\n }\n }\n \n // No change needed: workspace content is identical to source\n if (localMatchesWorkspace) {\n return {\n registryPath,\n type: 'no-change-needed',\n workspaceCandidateCount,\n uniqueWorkspaceCandidates: uniqueWorkspace,\n hasLocalCandidate: hasLocal,\n localMatchesWorkspace: true,\n isRootFile,\n hasPlatformCandidates,\n recommendedStrategy: 'skip'\n };\n }\n \n // Single unique workspace candidate (or all identical) - can auto-write\n if (uniqueWorkspace.length === 1) {\n return {\n registryPath,\n type: 'auto-write',\n workspaceCandidateCount,\n uniqueWorkspaceCandidates: uniqueWorkspace,\n hasLocalCandidate: hasLocal,\n localMatchesWorkspace: false,\n isRootFile,\n hasPlatformCandidates,\n recommendedStrategy: 'write-single'\n };\n }\n \n // Multiple differing workspace candidates - needs resolution\n // In force mode, auto-select newest; otherwise require user interaction\n const recommendedStrategy: ResolutionStrategy = force ? 'force-newest' : 'interactive';\n \n return {\n registryPath,\n type: 'needs-resolution',\n workspaceCandidateCount,\n uniqueWorkspaceCandidates: uniqueWorkspace,\n hasLocalCandidate: hasLocal,\n localMatchesWorkspace: false,\n isRootFile,\n hasPlatformCandidates,\n recommendedStrategy\n };\n}\n\n/**\n * Check if workspace candidate matches local candidate after conversion\n * \n * Helper function for conversion-aware and merge-aware parity checking.\n * For merged files, extracts package contribution before comparing.\n * For platform-specific files, converts workspace content to universal format before comparing.\n * \n * @param workspace - Workspace candidate\n * @param local - Local (source) candidate\n * @param workspaceRoot - Workspace root for conversion\n * @returns True if converted hashes match\n */\nasync function checkConvertedParity(\n workspace: SaveCandidate,\n local: SaveCandidate,\n workspaceRoot: string\n): Promise<boolean> {\n const workspaceHash = await ensureComparableHash(workspace, workspaceRoot);\n\n if (workspace.mergeStrategy && workspace.mergeKeys && workspace.mergeKeys.length > 0) {\n if (workspace.platform && workspace.platform !== 'ai') {\n const forward = await convertSourceToWorkspace(\n local.content,\n workspace.platform as any,\n local.registryPath,\n workspace.displayPath,\n workspaceRoot\n );\n if (forward.success && forward.convertedContent) {\n const localConvertedExtract = await extractContentByKeys(\n forward.convertedContent,\n workspace.mergeKeys\n );\n if (localConvertedExtract.success && localConvertedExtract.extractedHash) {\n return workspaceHash === localConvertedExtract.extractedHash;\n }\n }\n }\n const localExtract = await extractContentByKeys(local.content, workspace.mergeKeys);\n if (localExtract.success && localExtract.extractedHash) {\n return workspaceHash === localExtract.extractedHash;\n }\n return workspaceHash === local.contentHash;\n }\n\n return workspaceHash === local.contentHash;\n}\n\n/**\n * Deduplicate candidates by converted content hash (merge-aware)\n * \n * Returns only candidates with unique content after conversion to universal format.\n * For merged files, extracts package contribution before deduplication.\n * This enables proper deduplication of platform-specific files that are semantically identical.\n * \n * **Example**:\n * ```\n * Input: [\n * { hash: \"abc\", path: \".cursor/mcp.json\", platform: \"cursor\" },\n * { hash: \"def\", path: \".claude/.mcp.json\", platform: \"claude\" }, // different raw hash\n * { hash: \"ghi\", path: \".windsurf/mcp.json\", platform: \"windsurf\" }\n * ]\n * After conversion to universal format, cursor and claude both convert to same hash \"xyz\"\n * Output: [\n * { hash: \"abc\", path: \".cursor/mcp.json\", platform: \"cursor\" }, // first occurrence\n * { hash: \"ghi\", path: \".windsurf/mcp.json\", platform: \"windsurf\" }\n * ]\n * ```\n * \n * @param candidates - Array of candidates to deduplicate\n * @param workspaceRoot - Workspace root for conversion context\n * @returns Array of candidates with unique converted content hashes\n */\nasync function deduplicateCandidatesWithMerge(\n candidates: SaveCandidate[],\n workspaceRoot: string\n): Promise<SaveCandidate[]> {\n const seen = new Set<string>();\n const unique: SaveCandidate[] = [];\n\n for (const candidate of candidates) {\n const hash = await ensureComparableHash(candidate, workspaceRoot);\n\n logger.debug(\n `Dedup check for ${candidate.displayPath}: hash=${hash}, ` +\n `rawHash=${candidate.contentHash}, seen=${seen.has(hash)}`\n );\n\n if (seen.has(hash)) {\n logger.debug(` Skipping duplicate: ${candidate.displayPath}`);\n continue;\n }\n seen.add(hash);\n unique.push(candidate);\n }\n\n logger.debug(`Deduplication: ${candidates.length} \u2192 ${unique.length} unique candidates`);\n return unique;\n}\n\n/**\n * Deduplicate candidates by converted content hash (sync version for backward compatibility)\n * \n * This is a simplified synchronous version that doesn't handle merge extraction.\n * Used by existing tests. For production code, use the merge-aware async version\n * via analyzeGroup.\n * \n * @deprecated For backward compatibility with tests only.\n * @param candidates - Array of candidates to deduplicate\n * @returns Array of candidates with unique raw content hashes\n */\nexport function deduplicateCandidates(\n candidates: SaveCandidate[]\n): SaveCandidate[] {\n const seen = new Set<string>();\n const unique: SaveCandidate[] = [];\n \n for (const candidate of candidates) {\n const hash = candidate.contentHash;\n \n if (seen.has(hash)) {\n continue;\n }\n seen.add(hash);\n unique.push(candidate);\n }\n \n return unique;\n}\n\n/**\n * Check if workspace content differs from local source\n * \n * Returns true if any workspace candidate has different content than the local source.\n * This is a simple helper for determining if a save operation would change anything.\n * \n * @param local - The local (source) candidate, or undefined if none exists\n * @param workspace - Array of workspace candidates\n * @returns True if any workspace content differs from local\n */\nexport function hasContentDifference(\n local: SaveCandidate | undefined,\n workspace: SaveCandidate[]\n): boolean {\n if (!local) return true; // No local means creation, which is a difference\n if (workspace.length === 0) return false; // No workspace means no change\n \n // Check if any workspace candidate differs from local\n return workspace.some(w => w.contentHash !== local.contentHash);\n}\n\n/**\n * Get the newest candidate by modification time\n * \n * Returns the candidate with the most recent mtime. Used for auto-selecting\n * in force mode when multiple differing candidates exist.\n * \n * **Tie-breaking**: If multiple candidates have the same mtime (unlikely but possible),\n * the first one in the array is returned. Consider adding secondary sort by displayPath\n * for deterministic behavior.\n * \n * @param candidates - Array of candidates to search\n * @returns The candidate with the highest mtime\n * @throws Error if candidates array is empty\n */\nexport function getNewestCandidate(\n candidates: SaveCandidate[]\n): SaveCandidate {\n if (candidates.length === 0) {\n throw new Error('Cannot get newest candidate from empty array');\n }\n \n if (candidates.length === 1) {\n return candidates[0];\n }\n \n // Find candidate with maximum mtime\n let newest = candidates[0];\n for (let i = 1; i < candidates.length; i++) {\n const current = candidates[i];\n \n // Compare mtime first\n if (current.mtime > newest.mtime) {\n newest = current;\n } else if (current.mtime === newest.mtime) {\n // Tie-breaker: use alphabetical order by displayPath\n if (current.displayPath < newest.displayPath) {\n newest = current;\n }\n }\n }\n \n return newest;\n}\n\n/**\n * Sort candidates by modification time (newest first) with tie-breaking\n * \n * Used to establish a consistent ordering for conflict resolution prompts.\n * Sorts by mtime descending, with displayPath as alphabetical tie-breaker.\n * \n * **Immutable**: Returns a new sorted array; does not modify the input.\n * \n * @param candidates - Array of candidates to sort\n * @returns New array sorted by mtime (newest first), then displayPath (A-Z)\n */\nexport function sortCandidatesByMtime(\n candidates: SaveCandidate[]\n): SaveCandidate[] {\n return [...candidates].sort((a, b) => {\n // Primary sort: mtime descending (newest first)\n if (b.mtime !== a.mtime) {\n return b.mtime - a.mtime;\n }\n \n // Tie-breaker: displayPath ascending (alphabetical)\n return a.displayPath.localeCompare(b.displayPath);\n });\n}\n", "import { join } from 'path';\nimport { sortCandidatesByMtime } from './save-conflict-analyzer.js';\nimport { convertSourceToWorkspace, ensureComparableHash } from './save-conversion-helper.js';\nimport { logger } from '../../utils/logger.js';\nimport { exists, readTextFile } from '../../utils/fs.js';\nimport { calculateFileHash } from '../../utils/hash-utils.js';\nimport { createPlatformSpecificRegistryPath } from '../platform/platform-specific-paths.js';\nimport type { OutputPort } from '../ports/output.js';\nimport type { PromptPort } from '../ports/prompt.js';\nimport { resolveOutput, resolvePrompt } from '../ports/resolve.js';\nimport type { SaveCandidate, SaveCandidateGroup } from './save-types.js';\n\n/**\n * Interactive Conflict Resolver\n * \n * This module handles interactive user prompts for conflict resolution when multiple\n * differing workspace candidates exist for a single registry path.\n * \n * Key features:\n * - Parity checking: Auto-skip files that already match source\n * - Auto-dedup: Auto-skip candidates identical to selected universal\n * - Progressive prompts: Options change after universal selection\n * - Clear UX: Informative messages and formatted summaries\n * \n * @module save-interactive-resolver\n */\n\n/**\n * Input parameters for interactive resolution\n */\nexport interface InteractiveResolutionInput {\n /** The registry path being resolved */\n registryPath: string;\n \n /** Array of workspace candidates (should be unique and sorted by mtime) */\n workspaceCandidates: SaveCandidate[];\n \n /** Whether this is a root package file (informational) */\n isRootFile: boolean;\n \n /** Complete candidate group (for parity checking) */\n group: SaveCandidateGroup;\n \n /** Package source absolute path (for parity checking) */\n packageRoot: string;\n \n /** Workspace root absolute path (for conversion) */\n workspaceRoot: string;\n}\n\n/**\n * Output result from interactive resolution\n */\nexport interface InteractiveResolutionOutput {\n /** Selected universal candidate (null if only platform-specific) */\n selectedCandidate: SaveCandidate | null;\n \n /** Array of platform-specific candidates */\n platformSpecificCandidates: SaveCandidate[];\n}\n\n/**\n * User action choices for each candidate\n */\nexport type CandidateAction = 'universal' | 'platform-specific' | 'skip';\n\n/**\n * Result of parity checking\n */\ninterface ParityCheck {\n /** Whether candidate is already at parity with source */\n atParity: boolean;\n \n /** Human-readable reason (if at parity) */\n reason?: string;\n}\n\n/**\n * Run interactive resolution flow with parity checking\n * \n * This is the main entry point for interactive conflict resolution.\n * It presents each candidate to the user in order (newest first),\n * automatically skipping those that are already at parity or identical\n * to the selected universal.\n * \n * **Flow**:\n * 1. Sort candidates by mtime (newest first)\n * 2. Display conflict header\n * 3. For each candidate:\n * - Check parity \u2192 auto-skip if matches source\n * - Check if identical to universal \u2192 auto-skip\n * - Prompt user for action\n * - Handle action and update tracking\n * 4. Display resolution summary\n * 5. Return result\n * \n * **Optimization**: Parity checking eliminates 60-80% of prompts in practice\n * \n * @param input - Interactive resolution input parameters\n * @returns Resolution output with selected universal and platform-specific candidates\n */\nexport async function resolveInteractively(\n input: InteractiveResolutionInput,\n output?: OutputPort,\n prompt?: PromptPort\n): Promise<InteractiveResolutionOutput> {\n const { registryPath, workspaceCandidates, group, packageRoot, workspaceRoot } = input;\n const out = output ?? resolveOutput();\n const prm = prompt ?? resolvePrompt();\n \n // Sort by mtime descending (newest first), with alphabetical tie-breaker\n const sortedCandidates = sortCandidatesByMtime(workspaceCandidates);\n \n // Display header\n displayConflictHeader(registryPath, sortedCandidates, out);\n \n // Track selections\n let universalSelected: SaveCandidate | null = null;\n const platformSpecificCandidates: SaveCandidate[] = [];\n const skippedCandidates: SaveCandidate[] = [];\n \n // Iterate through each candidate\n for (const candidate of sortedCandidates) {\n // Check if candidate is already at parity with source (conversion-aware)\n const parityCheck = await isAtParity(candidate, group, packageRoot, workspaceRoot);\n if (parityCheck.atParity) {\n out.info(`\\n \u2713 ${candidate.displayPath}`);\n out.info(` ${parityCheck.reason} - auto-skipping\\n`);\n skippedCandidates.push(candidate);\n continue;\n }\n \n // If universal already selected, check if this candidate is identical (conversion-aware)\n if (universalSelected) {\n const universalHash = await ensureComparableHash(universalSelected, workspaceRoot);\n const candidateHash = await ensureComparableHash(candidate, workspaceRoot);\n\n if (candidateHash === universalHash) {\n out.info(`\\n \u2713 ${candidate.displayPath}`);\n out.info(` Identical to universal - auto-skipping\\n`);\n skippedCandidates.push(candidate);\n continue;\n }\n }\n \n // Prompt for action\n const action = await promptCandidateAction(\n candidate,\n registryPath,\n universalSelected !== null,\n prm\n );\n \n // Handle action\n switch (action) {\n case 'universal':\n universalSelected = candidate;\n out.success(`\\n \u2713 Selected as universal: ${candidate.displayPath}\\n`);\n break;\n \n case 'platform-specific':\n platformSpecificCandidates.push(candidate);\n out.success(`\\n \u2713 Marked as platform-specific: ${candidate.displayPath}\\n`);\n break;\n \n case 'skip':\n skippedCandidates.push(candidate);\n out.info(`\\n \u2713 Skipped: ${candidate.displayPath}\\n`);\n break;\n }\n }\n \n // Display summary\n displayResolutionSummary(universalSelected, platformSpecificCandidates, skippedCandidates, out);\n \n return {\n selectedCandidate: universalSelected,\n platformSpecificCandidates\n };\n}\n\n/**\n * Check forward parity by simulating export flow\n * \n * Given a workspace candidate and source candidate, apply the export flow\n * that would transform source \u2192 workspace and check if the result matches\n * the actual workspace file.\n * \n * This is more expensive than reverse conversion but necessary when:\n * - Platform-specific transformations occur during install\n * - Import flows don't exist or fail\n * - Workspace file content differs from source due to transformations\n * \n * @param workspaceCandidate - Workspace file to check\n * @param localCandidate - Source file\n * @param packageRoot - Package root directory\n * @param workspaceRoot - Workspace root directory\n * @returns Parity check result\n */\nasync function checkForwardParity(\n workspaceCandidate: SaveCandidate,\n localCandidate: SaveCandidate,\n packageRoot: string,\n workspaceRoot: string\n): Promise<ParityCheck> {\n try {\n // Import conversion helper function for forward conversion\n // Try to convert source file to workspace format\n const result = await convertSourceToWorkspace(\n localCandidate.content,\n workspaceCandidate.platform!,\n localCandidate.registryPath,\n workspaceCandidate.displayPath,\n workspaceRoot\n );\n \n if (result.success && result.convertedHash) {\n logger.debug(\n `Forward conversion check: converted source hash=${result.convertedHash}, ` +\n `workspace hash=${workspaceCandidate.contentHash}`\n );\n \n if (result.convertedHash === workspaceCandidate.contentHash) {\n return {\n atParity: true,\n reason: 'Matches source after forward conversion (export flow)'\n };\n }\n }\n } catch (error) {\n logger.debug(`Forward parity check failed: ${error}`);\n }\n \n return { atParity: false };\n}\n\n/**\n * Check if candidate is already at parity with source (conversion-aware and merge-aware)\n * \n * A candidate is at parity if it matches either:\n * 1. The universal source file (after conversion to universal format)\n * 2. Its corresponding platform-specific source file (if exists)\n * \n * For merged files, we extract only the package's contribution before comparing.\n * \n * This optimization prevents prompting the user for files that haven't\n * actually changed, dramatically reducing the number of prompts in practice.\n * \n * @param candidate - The workspace candidate to check\n * @param group - The complete candidate group\n * @param packageRoot - Package source absolute path\n * @param workspaceRoot - Workspace root for conversion\n * @returns Parity check result with reason if at parity\n */\nasync function isAtParity(\n candidate: SaveCandidate,\n group: SaveCandidateGroup,\n packageRoot: string,\n workspaceRoot: string\n): Promise<ParityCheck> {\n // Check universal parity (conversion-aware and merge-aware)\n if (group.local) {\n const comparisonHash = await ensureComparableHash(candidate, workspaceRoot);\n\n logger.debug(\n `Parity check for ${candidate.displayPath}: ` +\n `comparisonHash=${comparisonHash}, ` +\n `localHash=${group.local.contentHash}`\n );\n\n if (comparisonHash === group.local.contentHash) {\n return {\n atParity: true,\n reason: 'Already matches universal (cached comparable hash)'\n };\n }\n\n if (candidate.platform && candidate.platform !== 'ai' && !(candidate.mergeStrategy && candidate.mergeKeys && candidate.mergeKeys.length > 0)) {\n const forwardCheck = await checkForwardParity(\n candidate,\n group.local,\n packageRoot,\n workspaceRoot\n );\n \n if (forwardCheck.atParity) {\n return forwardCheck;\n }\n }\n }\n \n // Check platform-specific parity (if candidate has platform)\n if (candidate.platform && candidate.platform !== 'ai') {\n const platformPath = createPlatformSpecificRegistryPath(\n group.registryPath,\n candidate.platform\n );\n \n if (platformPath) {\n const platformFullPath = join(packageRoot, platformPath);\n \n // Check if platform-specific file exists\n if (await exists(platformFullPath)) {\n try {\n const platformContent = await readTextFile(platformFullPath);\n const platformHash = await calculateFileHash(platformContent);\n \n if (candidate.contentHash === platformHash) {\n return {\n atParity: true,\n reason: 'Already matches platform-specific file'\n };\n }\n } catch (error) {\n // If we can't read the platform file, treat as not at parity\n // This is safer - user will be prompted\n logger.debug(`Could not read platform file ${platformFullPath}: ${error}`);\n }\n }\n }\n }\n \n return { atParity: false };\n}\n\n/**\n * Prompt user for action on a single candidate\n * \n * Shows different options based on whether universal has already been selected:\n * - **Before universal selected**: [Set as universal] [Mark as platform-specific] [Skip]\n * - **After universal selected**: [Mark as platform-specific] [Skip]\n * \n * This progressive disclosure keeps the UX simple and focused.\n * \n * @param candidate - The candidate to prompt for\n * @param registryPath - The registry path being resolved\n * @param universalAlreadySelected - Whether universal has been selected\n * @returns User's chosen action\n */\nasync function promptCandidateAction(\n candidate: SaveCandidate,\n registryPath: string,\n universalAlreadySelected: boolean,\n prm: PromptPort\n): Promise<CandidateAction> {\n const candidateLabel = formatCandidateLabel(candidate, true);\n \n // Build options based on state\n const choices = universalAlreadySelected\n ? [\n { title: 'Mark as platform-specific', value: 'platform-specific' as const },\n { title: 'Skip', value: 'skip' as const }\n ]\n : [\n { title: 'Set as universal', value: 'universal' as const },\n { title: 'Mark as platform-specific', value: 'platform-specific' as const },\n { title: 'Skip', value: 'skip' as const }\n ];\n \n return await prm.select<CandidateAction>(\n ` ${candidateLabel}\\n What should we do with this file?`,\n choices,\n 'Arrow keys to navigate, Enter to select'\n );\n}\n\n/**\n * Display conflict resolution header\n * \n * Shows the registry path and number of files to resolve.\n * \n * @param registryPath - The registry path being resolved\n * @param candidates - Array of candidates\n */\nfunction displayConflictHeader(\n registryPath: string,\n candidates: SaveCandidate[],\n out: OutputPort\n): void {\n out.warn(`Multiple workspace versions found for ${registryPath}`);\n out.info(` Resolving conflicts for ${candidates.length} file(s)...\\n`);\n}\n\n/**\n * Display resolution summary after all prompts\n * \n * Shows what was selected, what was marked as platform-specific,\n * and what was skipped. Provides clear feedback on the resolution outcome.\n * \n * @param universal - Selected universal candidate (or null)\n * @param platformSpecific - Array of platform-specific candidates\n * @param skipped - Array of skipped candidates\n */\nfunction displayResolutionSummary(\n universal: SaveCandidate | null,\n platformSpecific: SaveCandidate[],\n skipped: SaveCandidate[],\n out: OutputPort\n): void {\n out.info('\u2500'.repeat(60));\n out.info('Resolution summary:');\n \n if (universal) {\n out.success(` \u2713 Universal: ${universal.displayPath}`);\n } else {\n out.info(' \u2139 No universal content selected');\n }\n \n if (platformSpecific.length > 0) {\n out.success(` \u2713 Platform-specific: ${platformSpecific.length} file(s)`);\n platformSpecific.forEach(c => {\n const platform = c.platform ? `(${c.platform})` : '';\n out.info(` \u2022 ${c.displayPath} ${platform}`);\n });\n }\n \n if (skipped.length > 0) {\n out.info(` \u2022 Skipped: ${skipped.length} file(s)`);\n }\n \n out.info('\u2500'.repeat(60) + '\\n');\n}\n\n/**\n * Format candidate label for display\n * \n * Constructs a user-friendly label showing:\n * - Display path\n * - Platform (if present and not 'ai')\n * - Timestamp (if requested)\n * \n * **Examples**:\n * - `.cursor/tools/search.md (cursor) [2026-02-06 10:45:23]`\n * - `tools/search.md [2026-02-06 09:30:15]`\n * - `.claude/AGENTS.md (claude)`\n * \n * @param candidate - The candidate to format\n * @param includeTimestamp - Whether to include modification timestamp\n * @returns Formatted candidate label\n */\nfunction formatCandidateLabel(\n candidate: SaveCandidate,\n includeTimestamp: boolean = false\n): string {\n const parts: string[] = [];\n \n // Path\n parts.push(candidate.displayPath);\n \n // Platform (if present and not 'ai')\n if (candidate.platform && candidate.platform !== 'ai') {\n parts.push(`(${candidate.platform})`);\n }\n \n // Timestamp\n if (includeTimestamp) {\n const date = new Date(candidate.mtime);\n const timestamp = date.toLocaleString();\n parts.push(`[${timestamp}]`);\n }\n \n return parts.join(' ');\n}\n", "import { FILE_PATTERNS } from '../../constants/index.js';\nimport type { Platform } from '../../types/platform.js';\nimport { getPlatformDefinition } from '../platforms.js';\n\nfunction joinSegments(segments: string[]): string {\n return segments.filter(Boolean).join('/');\n}\n\n/**\n * Append a platform suffix to the basename of a registry path.\n * Examples:\n * - rules/auth.md -> rules/auth.<platform>.md\n * - rules/helpers/index.ts -> rules/helpers/index.<platform>.ts\n */\nexport function suffixFileBasename(registryPath: string, platform: Platform): string {\n const segments = registryPath.split('/');\n const fileName = segments.pop();\n\n if (!fileName) {\n return registryPath;\n }\n\n const lastDotIndex = fileName.lastIndexOf('.');\n\n if (lastDotIndex <= 0) {\n if (!fileName.endsWith(`.${platform}`)) {\n segments.push(`${fileName}.${platform}`);\n } else {\n segments.push(fileName);\n }\n return joinSegments(segments);\n }\n\n const name = fileName.slice(0, lastDotIndex);\n const ext = fileName.slice(lastDotIndex);\n\n if (name.endsWith(`.${platform}`)) {\n segments.push(fileName);\n return joinSegments(segments);\n }\n\n segments.push(`${name}.${platform}${ext}`);\n return joinSegments(segments);\n}\n\n/**\n * Apply a platform suffix to the first content directory within a registry path.\n * Example: rules/helpers/foo.md -> rules/helpers.<platform>/foo.md\n */\nexport function suffixFirstContentDir(registryPath: string, platform: Platform): string {\n const segments = registryPath.split('/');\n if (segments.length < 2) {\n return registryPath;\n }\n\n const subdir = segments[0];\n const rest = segments.slice(1);\n\n if (rest.length === 0) {\n return registryPath;\n }\n\n if (!rest[0].endsWith(`.${platform}`)) {\n rest[0] = `${rest[0]}.${platform}`;\n }\n\n return joinSegments([subdir, ...rest]);\n}\n\n/**\n * Build a platform-specific registry path for a universal registry path.\n * Handles root files and markdown suffix insertion.\n * Returns null when the platform should not emit a specific variant (e.g., missing root file).\n */\nexport function createPlatformSpecificRegistryPath(\n registryPath: string,\n platform: Platform\n): string | null {\n const segments = registryPath.split('/');\n const fileName = segments[segments.length - 1];\n const isRoot = segments.length === 1;\n\n if (!fileName) {\n return registryPath;\n }\n\n if (isRoot) {\n const definition = getPlatformDefinition(platform);\n\n if (definition?.rootFile) {\n return definition.rootFile;\n }\n\n // If the platform does not define a native root file, skip emitting a platform-specific variant.\n return null;\n }\n\n if (registryPath.endsWith(FILE_PATTERNS.MD_FILES)) {\n return suffixFileBasename(registryPath, platform);\n }\n\n return suffixFileBasename(registryPath, platform);\n}\n\n", "import { logger } from '../../utils/logger.js';\nimport type { ConflictAnalysis } from './save-conflict-analyzer.js';\nimport { getNewestCandidate, sortCandidatesByMtime } from './save-conflict-analyzer.js';\nimport { resolveInteractively } from './save-interactive-resolver.js';\nimport type { SaveCandidate, SaveCandidateGroup, ResolutionResult } from './save-types.js';\nimport type { OutputPort } from '../ports/output.js';\nimport type { PromptPort } from '../ports/prompt.js';\n\n/**\n * Resolution Strategy Executor\n * \n * This module orchestrates the execution of resolution strategies based on conflict analysis.\n * It delegates to specific resolution functions based on the recommended strategy.\n * \n * Strategies:\n * - skip: No action needed\n * - write-single: Single workspace candidate (auto-write)\n * - write-newest: Multiple identical candidates (auto-write newest)\n * - force-newest: Multiple differing candidates with force flag (auto-select newest)\n * - interactive: Multiple differing candidates without force flag (prompt user)\n * \n * @module save-resolution-executor\n */\n\n/**\n * Execute resolution for a candidate group based on conflict analysis\n * \n * This is the primary entry point for resolution execution. It takes the analysis\n * result and delegates to the appropriate resolution function based on the\n * recommended strategy.\n * \n * **Flow**:\n * 1. Check if strategy is 'skip' \u2192 return null (no action)\n * 2. Sort candidates by mtime (newest first) for consistency\n * 3. Dispatch to strategy-specific function\n * 4. Return ResolutionResult\n * \n * @param group - The candidate group being resolved\n * @param analysis - Conflict analysis with recommended strategy\n * @param packageRoot - Package source absolute path (for parity checking)\n * @param workspaceRoot - Workspace root for conversion\n * @returns ResolutionResult with selection and platform-specific candidates, or null if skip\n */\nexport async function executeResolution(\n group: SaveCandidateGroup,\n analysis: ConflictAnalysis,\n packageRoot: string,\n workspaceRoot: string,\n output?: OutputPort,\n prompt?: PromptPort\n): Promise<ResolutionResult | null> {\n const strategy = analysis.recommendedStrategy;\n \n // Skip if no action needed\n if (strategy === 'skip') {\n return null;\n }\n \n // Sort candidates by mtime (newest first) for consistent ordering\n const sortedCandidates = sortCandidatesByMtime(analysis.uniqueWorkspaceCandidates);\n \n // Dispatch to appropriate resolution function\n switch (strategy) {\n case 'write-single':\n return resolveSingle(sortedCandidates[0]);\n \n case 'write-newest':\n return resolveIdentical(sortedCandidates);\n \n case 'force-newest':\n return resolveForce(sortedCandidates, group.registryPath);\n \n case 'interactive':\n return resolveInteractive(\n group.registryPath,\n sortedCandidates,\n analysis.isRootFile,\n group,\n packageRoot,\n workspaceRoot,\n output,\n prompt\n );\n \n default:\n logger.warn(`Unknown resolution strategy: ${strategy}`);\n return null;\n }\n}\n\n/**\n * Resolve single workspace candidate (auto-write)\n * \n * This is the simplest case: only one workspace candidate exists,\n * so we use it without prompting the user.\n * \n * @param candidate - The single workspace candidate\n * @returns ResolutionResult with the candidate as universal selection\n */\nfunction resolveSingle(candidate: SaveCandidate): ResolutionResult {\n return {\n selection: candidate,\n platformSpecific: [],\n strategy: 'write-single',\n wasInteractive: false\n };\n}\n\n/**\n * Resolve multiple identical candidates (auto-write newest)\n * \n * All workspace candidates have identical content (after deduplication).\n * Pick the newest one by mtime and use it as the universal selection.\n * \n * @param candidates - Array of deduplicated identical candidates\n * @returns ResolutionResult with newest candidate as universal selection\n */\nfunction resolveIdentical(candidates: SaveCandidate[]): ResolutionResult {\n const newest = getNewestCandidate(candidates);\n \n return {\n selection: newest,\n platformSpecific: [],\n strategy: 'write-newest',\n wasInteractive: false\n };\n}\n\n/**\n * Force-resolve: Auto-select newest without prompting (force mode)\n * \n * Multiple differing candidates exist, but --force flag is enabled.\n * Auto-select the newest by mtime without user interaction.\n * \n * **Tie-breaking**: If multiple candidates have the same mtime (unlikely but possible),\n * the alphabetically first displayPath is selected. This is handled by getNewestCandidate().\n * \n * **Logging**: Provides detailed logging to show what was selected and what was skipped,\n * with special handling for tie situations.\n * \n * @param candidates - Array of differing candidates (sorted by mtime)\n * @param registryPath - The registry path being resolved\n * @returns ResolutionResult with newest candidate as universal selection\n */\nfunction resolveForce(\n candidates: SaveCandidate[],\n registryPath: string\n): ResolutionResult {\n const newest = getNewestCandidate(candidates);\n \n // Check if there are ties (multiple with same mtime as newest)\n const maxMtime = newest.mtime;\n const tiedCandidates = candidates.filter(c => c.mtime === maxMtime);\n \n if (tiedCandidates.length > 1) {\n // Tie situation - log detailed explanation\n logger.info(\n `Force mode: Multiple files have same modification time (${formatTimestamp(maxMtime)})`\n );\n logger.info(` Auto-selecting first alphabetically: ${newest.displayPath}`);\n logger.info(' Tied files:');\n tiedCandidates.forEach(c => {\n const marker = c === newest ? '\u2192' : ' ';\n logger.info(` ${marker} ${c.displayPath}`);\n });\n \n // Log skipped tied files\n const skippedTied = tiedCandidates.filter(c => c !== newest);\n skippedTied.forEach(c => {\n logger.info(` Skipping: ${c.displayPath} (tied, not alphabetically first)`);\n });\n \n // Log older files\n const olderFiles = candidates.filter(c => c.mtime < maxMtime);\n olderFiles.forEach(c => {\n logger.info(` Skipping: ${c.displayPath} (older)`);\n });\n } else {\n // Clear winner - simple logging\n logger.info(`Force mode: Auto-selecting newest (${newest.displayPath})`);\n \n // Log what we're skipping\n const skipped = candidates.filter(c => c !== newest);\n if (skipped.length > 0) {\n skipped.forEach(c => {\n logger.info(` Skipping: ${c.displayPath} (older)`);\n });\n }\n }\n \n return {\n selection: newest,\n platformSpecific: [], // Force mode doesn't auto-create platform-specific variants\n strategy: 'force-newest',\n wasInteractive: false\n };\n}\n\n/**\n * Interactive resolve: Prompt user for action (interactive mode)\n * \n * Multiple differing candidates exist and user interaction is required.\n * Delegates to the interactive resolver module for UI flow and prompts.\n * \n * @param registryPath - The registry path being resolved\n * @param candidates - Array of differing candidates (sorted by mtime)\n * @param isRootFile - Whether this is a root package file\n * @param group - Complete candidate group (for parity checking)\n * @param packageRoot - Package source absolute path (for parity checking)\n * @param workspaceRoot - Workspace root for conversion\n * @returns ResolutionResult with user selections\n */\nasync function resolveInteractive(\n registryPath: string,\n candidates: SaveCandidate[],\n isRootFile: boolean,\n group: SaveCandidateGroup,\n packageRoot: string,\n workspaceRoot: string,\n output?: OutputPort,\n prompt?: PromptPort\n): Promise<ResolutionResult> {\n const result = await resolveInteractively({\n registryPath,\n workspaceCandidates: candidates,\n isRootFile,\n group,\n packageRoot,\n workspaceRoot\n }, output, prompt);\n \n return {\n selection: result.selectedCandidate,\n platformSpecific: result.platformSpecificCandidates,\n strategy: 'interactive',\n wasInteractive: true\n };\n}\n\n/**\n * Format timestamp for human-readable display\n * \n * Converts millisecond timestamp to locale-specific date/time string.\n * \n * @param mtime - Modification time in milliseconds\n * @returns Formatted date/time string\n */\nfunction formatTimestamp(mtime: number): string {\n const date = new Date(mtime);\n return date.toLocaleString();\n}\n", "import { join } from 'path';\nimport { exists } from '../../utils/fs.js';\nimport { createPlatformSpecificRegistryPath } from '../platform/platform-specific-paths.js';\nimport { logger } from '../../utils/logger.js';\nimport type { SaveCandidate, SaveCandidateGroup } from './save-types.js';\n\n/**\n * Platform-specific candidate management\n * \n * This module handles the lifecycle of platform-specific candidates in the save pipeline.\n * Primary responsibility: prune workspace candidates that would conflict with existing\n * platform-specific files in the package source.\n * \n * @module save-platform-handler\n */\n\n/**\n * Prune workspace candidates that have existing platform files in source\n * \n * This function removes workspace candidates from groups when a platform-specific\n * file already exists in the source. This prevents prompting the user to save\n * content that would overwrite an existing platform file.\n * \n * **Rationale**: If a platform-specific file like `.cursor/tools/search.md` exists\n * in the source, and the workspace has `.cursor/tools/search.md` with different\n * content, we should not prompt to save it. The existing platform file takes precedence.\n * \n * **Example**:\n * ```\n * Before pruning:\n * Group { registryPath: \"tools/calc.md\",\n * local: { hash: \"aaa\" },\n * workspace: [\n * { platform: \"cursor\", hash: \"bbb\" },\n * { platform: \"claude\", hash: \"ccc\" }\n * ]\n * }\n * \n * Source structure:\n * tools/calc.md (universal)\n * .cursor/tools/calc.md (exists!)\n * \n * After pruning:\n * Group { registryPath: \"tools/calc.md\",\n * workspace: [\n * { platform: \"claude\", hash: \"ccc\" } // cursor pruned\n * ]\n * }\n * ```\n * \n * @param packageRoot - Absolute path to the package source directory\n * @param groups - Array of candidate groups (mutated in-place)\n * \n * @mutates groups - Modifies workspace array in each group\n */\nexport async function pruneExistingPlatformCandidates(\n packageRoot: string,\n groups: SaveCandidateGroup[]\n): Promise<void> {\n for (const group of groups) {\n // No local file means no platform files could exist yet\n // Skip pruning for new files\n if (!group.local && !group.localRef) {\n continue;\n }\n \n const filtered: SaveCandidate[] = [];\n \n for (const candidate of group.workspace) {\n const platform = candidate.platform;\n \n // Keep non-platform candidates (universal files)\n // 'ai' is treated as universal, not platform-specific\n if (!platform || platform === 'ai') {\n filtered.push(candidate);\n continue;\n }\n \n // Construct platform-specific registry path\n // e.g., \"tools/search.md\" + \"cursor\" -> \".cursor/tools/search.cursor.md\"\n const platformPath = createPlatformSpecificRegistryPath(\n group.registryPath, \n platform\n );\n \n // If platform doesn't support this file type, keep candidate\n if (!platformPath) {\n filtered.push(candidate);\n continue;\n }\n \n // Check if platform-specific file exists in source\n const platformFullPath = join(packageRoot, platformPath);\n const hasPlatformFile = await exists(platformFullPath);\n \n if (hasPlatformFile) {\n // Platform file exists - prune this candidate\n logger.debug(\n `Pruning workspace candidate ${candidate.displayPath} for ${group.registryPath} ` +\n `(platform file ${platformPath} exists in source)`\n );\n continue; // Don't add to filtered array\n }\n \n // No existing platform file - keep candidate for potential save\n filtered.push(candidate);\n }\n \n // Replace workspace array with filtered candidates\n group.workspace = filtered;\n }\n}\n", "/**\n * Write Coordinator\n * \n * This module coordinates file write operations for resolved save content.\n * It handles both universal content and platform-specific variants,\n * ensuring idempotent writes with optimization for unchanged files.\n * \n * Key responsibilities:\n * - Build write operations from resolution results\n * - Execute writes to filesystem\n * - Handle directory creation\n * - Track success/failure for each write\n * - Optimize by skipping writes when content is identical\n * \n * @module save-write-coordinator\n */\n\nimport { dirname, join } from 'path';\nimport { ensureDir, exists, readTextFile, writeTextFile } from '../../utils/fs.js';\nimport { logger } from '../../utils/logger.js';\nimport { createPlatformSpecificRegistryPath } from '../platform/platform-specific-paths.js';\nimport { extractPackageContribution } from './save-merge-extractor.js';\nimport { allocateTempSubdir } from './save-conversion-helper.js';\nimport { getPlatformDefinition, getGlobalImportFlows, type Platform } from '../platforms.js';\nimport { createFlowExecutor } from '../flows/flow-executor.js';\nimport { calculateFileHash } from '../../utils/hash-utils.js';\nimport { minimatch } from 'minimatch';\nimport type { SaveCandidate, ResolutionResult, WriteOperation, WriteResult } from './save-types.js';\nimport type { Flow, FlowContext } from '../../types/flows.js';\n\n/**\n * Write resolution results to package source\n * \n * This is the main entry point for file writes. It handles both:\n * - Universal content (if selected)\n * - Platform-specific content (for each platform candidate)\n * \n * Each write is tracked individually with success/failure status.\n * Individual write failures don't halt the pipeline.\n * \n * @param packageRoot - Absolute path to package source\n * @param registryPath - Registry path being written\n * @param resolution - Resolution result from conflict resolution\n * @param localCandidate - Optional local (source) candidate for comparison\n * @param workspaceRoot - Optional workspace root for import transformations\n * @returns Array of write results (one per write operation)\n */\nexport async function writeResolution(\n packageRoot: string,\n registryPath: string,\n resolution: ResolutionResult,\n localCandidate?: SaveCandidate,\n workspaceRoot?: string\n): Promise<WriteResult[]> {\n const results: WriteResult[] = [];\n \n // Write universal content (if selected)\n if (resolution.selection) {\n const universalResult = await writeUniversal(\n packageRoot,\n registryPath,\n resolution.selection,\n localCandidate,\n workspaceRoot\n );\n results.push(universalResult);\n } else {\n // No universal selected - log this (user chose only platform-specific)\n logger.debug(`No universal content selected for ${registryPath} - keeping original untouched`);\n }\n \n // Write platform-specific content\n for (const platformCandidate of resolution.platformSpecific) {\n const platformResult = await writePlatformSpecific(\n packageRoot,\n registryPath,\n platformCandidate,\n workspaceRoot\n );\n results.push(platformResult);\n }\n \n return results;\n}\n\n/**\n * Write universal content to package source\n * \n * Writes the selected workspace candidate to the universal (non-platform-specific)\n * path in the package source. For merged files, extracts only the package's\n * contribution before writing. Optimizes by skipping write if content is identical\n * to existing source.\n * \n * @param packageRoot - Package source absolute path\n * @param registryPath - Registry path to write\n * @param candidate - Selected universal candidate\n * @param localCandidate - Optional local candidate for comparison\n * @param workspaceRoot - Optional workspace root for import transformations\n * @returns Write result with success/failure status\n */\nasync function writeUniversal(\n packageRoot: string,\n registryPath: string,\n candidate: SaveCandidate,\n localCandidate?: SaveCandidate,\n workspaceRoot?: string\n): Promise<WriteResult> {\n const targetPath = join(packageRoot, registryPath);\n \n // Prepare content for writing (extract if merged)\n const preparedContent = await prepareContentForWrite(candidate, registryPath, workspaceRoot);\n \n // Determine if write is needed (optimization)\n const writeDecision = shouldWrite(candidate, localCandidate, preparedContent.content);\n \n const operation: WriteOperation = {\n registryPath,\n targetPath,\n content: preparedContent.content,\n operation: writeDecision.operation,\n isPlatformSpecific: false\n };\n \n // Skip if no write needed\n if (!writeDecision.needed) {\n logger.debug(`Skipping write for ${registryPath}: content identical to source`);\n return {\n operation,\n success: true\n };\n }\n \n // Perform write\n const writeResult = await safeWrite(targetPath, preparedContent.content);\n \n if (writeResult.success) {\n const action = operation.operation === 'create' ? 'Created' : 'Updated';\n logger.debug(`${action} ${registryPath}${preparedContent.wasExtracted ? ' (extracted package contribution)' : ''}`);\n }\n \n return {\n operation,\n success: writeResult.success,\n error: writeResult.error\n };\n}\n\n/**\n * Write platform-specific content to package source\n * \n * Writes a platform-specific variant to its platform-specific path\n * (e.g., tools/search.cursor.md, CLAUDE.md). For merged files, extracts\n * only the package's contribution before writing.\n * \n * @param packageRoot - Package source absolute path\n * @param registryPath - Universal registry path\n * @param candidate - Platform-specific candidate\n * @param workspaceRoot - Optional workspace root for import transformations\n * @returns Write result with success/failure status\n */\nasync function writePlatformSpecific(\n packageRoot: string,\n registryPath: string,\n candidate: SaveCandidate,\n workspaceRoot?: string\n): Promise<WriteResult> {\n const platform = candidate.platform;\n \n // Validate platform\n if (!platform || platform === 'ai') {\n return {\n operation: {\n registryPath,\n targetPath: '',\n content: '',\n operation: 'skip',\n isPlatformSpecific: true\n },\n success: false,\n error: new Error('Candidate has no platform association')\n };\n }\n \n // Build platform-specific registry path\n const platformRegistryPath = createPlatformSpecificRegistryPath(registryPath, platform);\n if (!platformRegistryPath) {\n return {\n operation: {\n registryPath,\n targetPath: '',\n content: '',\n operation: 'skip',\n isPlatformSpecific: true,\n platform\n },\n success: false,\n error: new Error(`Could not create platform-specific path for ${platform}`)\n };\n }\n \n const targetPath = join(packageRoot, platformRegistryPath);\n \n // Prepare content for writing (extract if merged)\n const preparedContent = await prepareContentForWrite(candidate, platformRegistryPath, workspaceRoot);\n \n // Determine operation type\n const fileExists = await exists(targetPath);\n const operationType: 'create' | 'update' = fileExists ? 'update' : 'create';\n \n const operation: WriteOperation = {\n registryPath: platformRegistryPath,\n targetPath,\n content: preparedContent.content,\n operation: operationType,\n isPlatformSpecific: true,\n platform\n };\n \n // Check if content matches existing (optimization)\n if (fileExists) {\n try {\n const existingContent = await readTextFile(targetPath);\n if (existingContent === preparedContent.content) {\n logger.debug(`Skipping write for ${platformRegistryPath}: content identical`);\n operation.operation = 'skip';\n return {\n operation,\n success: true\n };\n }\n } catch (error) {\n // Ignore read errors - will attempt write anyway\n logger.debug(`Could not read existing file ${platformRegistryPath}: ${error}`);\n }\n }\n \n // Perform write\n const writeResult = await safeWrite(targetPath, preparedContent.content);\n \n if (writeResult.success) {\n const action = operationType === 'create' ? 'Created' : 'Updated';\n logger.debug(\n `${action} platform-specific file: ${platformRegistryPath}${preparedContent.wasExtracted ? ' (extracted package contribution)' : ''}`\n );\n }\n \n return {\n operation,\n success: writeResult.success,\n error: writeResult.error\n };\n}\n\n/**\n * Apply import flow transformation to convert workspace format to universal format\n * \n * For platform-specific workspaces (e.g., OpenCode with `mcp` \u2192 `mcpServers`),\n * we need to apply the import flow transformations to convert the extracted\n * content back to universal format.\n * \n * @param content - Extracted content in workspace format\n * @param platform - Source platform\n * @param registryPath - Target registry path (universal)\n * @param workspacePath - Source workspace path\n * @param workspaceRoot - Workspace root directory\n * @returns Transformation result with transformed content\n */\nasync function applyImportTransformation(\n content: string,\n platform: Platform,\n registryPath: string,\n workspacePath: string,\n workspaceRoot: string\n): Promise<{ success: boolean; transformedContent?: string; reason?: string }> {\n try {\n // Get platform definition and import flows\n const platformDef = getPlatformDefinition(platform, workspaceRoot);\n const platformImportFlows = platformDef.import || [];\n const globalImportFlows = getGlobalImportFlows(workspaceRoot) || [];\n const allImportFlows = [...globalImportFlows, ...platformImportFlows];\n \n if (allImportFlows.length === 0) {\n return {\n success: false,\n reason: 'No import flows defined for platform'\n };\n }\n \n // Find matching import flow for this registry path\n const matchingFlow = findMatchingFlow(\n allImportFlows,\n workspacePath,\n registryPath,\n platform,\n workspaceRoot\n );\n \n if (!matchingFlow) {\n return {\n success: false,\n reason: 'No matching import flow found'\n };\n }\n \n // Check if flow has map operations\n if (!matchingFlow.map || matchingFlow.map.length === 0) {\n return {\n success: false,\n reason: 'Flow has no map operations'\n };\n }\n \n // Create temporary directory for transformation\n const tempDir = await allocateTempSubdir();\n const inputDir = join(tempDir, 'in');\n const outputDir = join(tempDir, 'out');\n await ensureDir(inputDir);\n await ensureDir(outputDir);\n \n // Write extracted content to temp input file\n const inputFileName = registryPath.split('/').pop() || 'file.json';\n const inputFilePath = join(inputDir, inputFileName);\n await writeTextFile(inputFilePath, content);\n \n // Build flow context for transformation\n const flowContext: FlowContext = {\n workspaceRoot: outputDir,\n packageRoot: inputDir,\n platform: platform,\n packageName: 'temp',\n direction: 'install', // Import flows run during install\n variables: {\n name: 'temp',\n version: '0.0.0',\n platform: platform,\n source: platform,\n sourcePlatform: platform,\n targetPlatform: 'openpackage'\n },\n dryRun: false\n };\n \n // Execute the flow\n const executor = createFlowExecutor();\n const concreteFlow: Flow = {\n ...matchingFlow,\n from: inputFileName\n };\n \n const flowResult = await executor.executeFlow(concreteFlow, flowContext);\n \n if (!flowResult.success) {\n return {\n success: false,\n reason: `Flow execution failed: ${flowResult.error?.message}`\n };\n }\n \n // Read transformed content\n if (typeof flowResult.target !== 'string') {\n return {\n success: false,\n reason: 'Flow did not produce target path'\n };\n }\n \n const outputFilePath = flowResult.target;\n if (!(await exists(outputFilePath))) {\n return {\n success: false,\n reason: 'Flow did not produce output file'\n };\n }\n \n const transformedContent = await readTextFile(outputFilePath);\n \n logger.debug(\n `Successfully transformed extracted content from ${platform} format to universal`,\n { registryPath }\n );\n \n return {\n success: true,\n transformedContent\n };\n \n } catch (error) {\n logger.debug(\n `Import transformation failed for ${registryPath} (platform: ${platform})`,\n { error }\n );\n return {\n success: false,\n reason: error instanceof Error ? error.message : String(error)\n };\n }\n}\n\n/**\n * Find matching import flow for workspace \u2192 universal transformation\n * \n * @param flows - Array of import flows\n * @param workspacePath - Workspace file path (from pattern)\n * @param registryPath - Universal registry path (to pattern)\n * @param platform - Platform context\n * @param workspaceRoot - Workspace root\n * @returns Matching flow or undefined\n */\nfunction findMatchingFlow(\n flows: Flow[],\n workspacePath: string,\n registryPath: string,\n platform: Platform,\n workspaceRoot: string\n): Flow | undefined {\n const registryPathCandidates = getRegistryPathCandidates(registryPath);\n for (const flow of flows) {\n // Check if 'from' pattern matches workspace path\n const fromPatterns = Array.isArray(flow.from) ? flow.from : [flow.from];\n \n let matchesFrom = false;\n for (const fromPattern of fromPatterns) {\n // Handle switch expressions - evaluate to get default value\n if (typeof fromPattern === 'object' && '$switch' in fromPattern) {\n // For save operations, we're in workspace context, so use default value\n const defaultValue = fromPattern.$switch?.default;\n if (typeof defaultValue === 'string') {\n if (minimatch(workspacePath, defaultValue, { dot: true })) {\n matchesFrom = true;\n break;\n }\n }\n continue;\n }\n \n if (typeof fromPattern !== 'string') {\n continue;\n }\n \n // Check if workspace path matches the 'from' pattern\n if (minimatch(workspacePath, fromPattern, { dot: true })) {\n matchesFrom = true;\n break;\n }\n }\n \n if (!matchesFrom) {\n continue;\n }\n \n // Check if 'to' pattern matches registry path\n const toPattern = Array.isArray(flow.to) ? flow.to[0] : flow.to;\n \n // Handle switch expressions\n if (typeof toPattern === 'object' && '$switch' in toPattern) {\n logger.debug('Skipping flow with switch expression in to field');\n continue;\n }\n \n if (typeof toPattern !== 'string') {\n continue;\n }\n \n // Check if registry path matches the 'to' pattern (including extension variants)\n const matchesTo = registryPathCandidates.some(candidatePath =>\n minimatch(candidatePath, toPattern, { dot: true })\n );\n if (!matchesTo) {\n continue;\n }\n \n // Check conditional 'when' clause if present\n if (flow.when) {\n const conditionMet = evaluateWhenCondition(flow.when, platform);\n if (!conditionMet) {\n logger.debug(`Flow condition not met for ${registryPath}`, { when: flow.when });\n continue;\n }\n }\n \n // Found matching flow\n logger.debug(\n `Matched import flow: from=${workspacePath} to=${registryPath}`,\n { platform }\n );\n return flow;\n }\n \n logger.debug(\n `No matching import flow found`,\n { workspacePath, registryPath, platform, flowCount: flows.length }\n );\n return undefined;\n}\n\n/**\n * Expand registry path to include common extension variants.\n * \n * This helps match flows when the universal file is stored as JSON vs JSONC.\n * Example: \"mcp.json\" should match flows targeting \"mcp.jsonc\" and vice versa.\n */\nfunction getRegistryPathCandidates(registryPath: string): string[] {\n const candidates = [registryPath];\n if (registryPath.endsWith('.jsonc')) {\n candidates.push(registryPath.replace(/\\.jsonc$/, '.json'));\n } else if (registryPath.endsWith('.json')) {\n candidates.push(registryPath.replace(/\\.json$/, '.jsonc'));\n }\n return candidates;\n}\n\n/**\n * Evaluate 'when' conditional clause\n * \n * Simplified evaluation for common patterns used in platforms.jsonc.\n * \n * @param when - Conditional expression\n * @param platform - Current platform\n * @returns True if condition is met\n */\nfunction evaluateWhenCondition(when: any, platform: Platform): boolean {\n // Handle { \"$eq\": [\"$$platform\", \"claude\"] }\n if (when.$eq && Array.isArray(when.$eq) && when.$eq.length === 2) {\n const left = resolveVariable(when.$eq[0], platform);\n const right = resolveVariable(when.$eq[1], platform);\n return left === right;\n }\n \n // Handle { \"$ne\": [\"$$platform\", \"claude\"] }\n if (when.$ne && Array.isArray(when.$ne) && when.$ne.length === 2) {\n const left = resolveVariable(when.$ne[0], platform);\n const right = resolveVariable(when.$ne[1], platform);\n return left !== right;\n }\n \n // Handle { \"exists\": \"file.md\" }\n if (when.exists) {\n // For save, assume file exists if we got to this point\n return true;\n }\n \n // Unknown condition type - assume not met (conservative)\n logger.debug('Unknown condition type in when clause', { when });\n return false;\n}\n\n/**\n * Resolve variable references like $$platform, $$source\n */\nfunction resolveVariable(value: any, platform: Platform): any {\n if (typeof value === 'string') {\n if (value === '$$platform' || value === '$$source') {\n return platform;\n }\n }\n return value;\n}\n\n/**\n * Prepare content for writing by extracting package contribution from merged files\n * \n * This function detects merged candidates and extracts only the package's\n * contribution before writing. For non-merged files, returns content as-is.\n * \n * For merged files from platform-specific workspaces (e.g., OpenCode), this function:\n * 1. Extracts the package's keys from the merged workspace file\n * 2. Applies import flow transformations to convert back to universal format\n * \n * @param candidate - Workspace candidate to prepare\n * @param registryPath - Registry path for logging\n * @param workspaceRoot - Workspace root for conversion\n * @returns Prepared content with extraction metadata\n */\nasync function prepareContentForWrite(\n candidate: SaveCandidate,\n registryPath: string,\n workspaceRoot?: string\n): Promise<{ content: string; wasExtracted: boolean }> {\n // Check if this is a merged file with merge metadata\n const isMergedFile = Boolean(\n candidate.source === 'workspace' &&\n candidate.mergeStrategy &&\n candidate.mergeKeys &&\n candidate.mergeKeys.length > 0\n );\n \n if (!isMergedFile) {\n // Not a merged file - use content as-is\n return { content: candidate.content, wasExtracted: false };\n }\n \n // Attempt to extract package contribution\n logger.debug(\n `Extracting package contribution from merged file: ${registryPath}`,\n {\n strategy: candidate.mergeStrategy,\n keyCount: candidate.mergeKeys!.length,\n platform: candidate.platform\n }\n );\n \n const extractResult = await extractPackageContribution(candidate);\n \n if (extractResult.success && extractResult.extractedContent) {\n logger.info(\n `Successfully extracted package contribution for ${registryPath} ` +\n `(${candidate.mergeKeys!.length} key(s))`\n );\n \n // Apply import flow transformations if candidate has platform info\n let finalContent = extractResult.extractedContent;\n if (candidate.platform && candidate.platform !== 'ai' && workspaceRoot) {\n logger.info(\n `Attempting import transformation for merged file`,\n {\n platform: candidate.platform,\n registryPath,\n displayPath: candidate.displayPath,\n hasWorkspaceRoot: !!workspaceRoot\n }\n );\n \n const transformResult = await applyImportTransformation(\n extractResult.extractedContent,\n candidate.platform,\n registryPath,\n candidate.displayPath,\n workspaceRoot\n );\n \n if (transformResult.success && transformResult.transformedContent) {\n logger.info(\n `Successfully applied import transformation for platform ${candidate.platform}`,\n { registryPath }\n );\n finalContent = transformResult.transformedContent;\n } else {\n logger.warn(\n `Import transformation failed or not applicable: ${transformResult.reason}`,\n { registryPath, platform: candidate.platform }\n );\n }\n } else {\n logger.debug(\n `Skipping import transformation`,\n {\n hasPlatform: !!candidate.platform,\n platform: candidate.platform,\n hasWorkspaceRoot: !!workspaceRoot\n }\n );\n }\n \n return {\n content: finalContent,\n wasExtracted: true\n };\n }\n \n // Extraction failed - fall back to full content with warning\n logger.warn(\n `Failed to extract package contribution from merged file: ${registryPath}`,\n {\n reason: extractResult.error,\n fallback: 'Using full content'\n }\n );\n \n logger.warn(\n `\u26A0\uFE0F Writing full merged content to ${registryPath} - ` +\n `this may include keys from other packages or base workspace content`\n );\n \n return { content: candidate.content, wasExtracted: false };\n}\n\n/**\n * Determine if write is needed (optimization)\n * \n * Compares prepared content with local (source) content via hash.\n * Returns whether write is needed and what operation type.\n * \n * @param candidate - Workspace candidate (for hash comparison fallback)\n * @param localCandidate - Optional local (source) candidate\n * @param preparedContent - Prepared content to write (may be extracted)\n * @returns Write decision with needed flag and operation type\n */\nfunction shouldWrite(\n candidate: SaveCandidate,\n localCandidate?: SaveCandidate,\n preparedContent?: string\n): { needed: boolean; operation: 'create' | 'update' | 'skip' } {\n // No local candidate means file doesn't exist - create\n if (!localCandidate) {\n return { needed: true, operation: 'create' };\n }\n \n // If we have prepared content that differs from original, we need to compare\n // the prepared content with local content directly\n if (preparedContent && preparedContent !== candidate.content) {\n // Content was transformed (extracted) - compare directly\n if (preparedContent === localCandidate.content) {\n return { needed: false, operation: 'skip' };\n }\n return { needed: true, operation: 'update' };\n }\n \n // Compare content hashes (original comparison logic)\n if (candidate.contentHash === localCandidate.contentHash) {\n // Content identical - skip write\n return { needed: false, operation: 'skip' };\n }\n \n // Content differs - update\n return { needed: true, operation: 'update' };\n}\n\n/**\n * Safely write file with error handling\n * \n * Ensures parent directory exists before writing.\n * Returns success/error result without throwing.\n * \n * @param targetPath - Absolute filesystem path to write\n * @param content - Content to write\n * @returns Result with success flag and optional error\n */\nasync function safeWrite(\n targetPath: string,\n content: string\n): Promise<{ success: boolean; error?: Error }> {\n try {\n // Ensure parent directory exists\n await ensureDir(dirname(targetPath));\n \n // Write file\n await writeTextFile(targetPath, content);\n \n return { success: true };\n } catch (error) {\n logger.error(`Failed to write file ${targetPath}: ${error}`);\n return {\n success: false,\n error: error instanceof Error ? error : new Error(String(error))\n };\n }\n}\n", "/**\n * Result Reporter\n * \n * This module formats save operation results for user display.\n * It aggregates write results, conflict analyses, and other pipeline\n * data into a comprehensive report structure.\n * \n * Key responsibilities:\n * - Build SaveReport from pipeline results\n * - Format user-friendly messages\n * - Create CommandResult objects\n * - Provide helpers for success/error cases\n * \n * @module save-result-reporter\n */\n\nimport type { CommandResult } from '../../types/index.js';\nimport type { ConflictAnalysis } from './save-conflict-analyzer.js';\nimport type { WriteResult } from './save-types.js';\n\n/**\n * SaveReport contains aggregated save operation results\n * \n * This structure provides all the data needed to display\n * a comprehensive summary of the save operation to the user.\n */\nexport interface SaveReport {\n /** Package name that was saved */\n packageName: string;\n \n /** Total number of candidate groups processed */\n totalGroups: number;\n \n /** Number of groups that required action (not skipped) */\n groupsWithAction: number;\n \n /** Total files written successfully */\n filesSaved: number;\n \n /** Files created (new) */\n filesCreated: number;\n \n /** Files updated (existing) */\n filesUpdated: number;\n \n /** Platform-specific files written */\n platformSpecificFiles: number;\n \n /** Number of interactive resolutions (user prompts) */\n interactiveResolutions: number;\n \n /** Write errors that occurred */\n errors: Array<{ path: string; error: Error }>;\n \n /** All write results (for detailed reporting) */\n writeResults: WriteResult[];\n}\n\n/**\n * Build save report from pipeline results\n * \n * Aggregates data from conflict analyses and write results into\n * a comprehensive SaveReport structure.\n * \n * @param packageName - Package that was saved\n * @param analyses - Array of conflict analyses (one per group)\n * @param allWriteResults - Array of write result arrays (one array per group)\n * @returns SaveReport with aggregated statistics\n */\nexport function buildSaveReport(\n packageName: string,\n analyses: ConflictAnalysis[],\n allWriteResults: WriteResult[][]\n): SaveReport {\n // Count groups\n const totalGroups = analyses.length;\n const groupsWithAction = analyses.filter(\n a => a.type !== 'no-action-needed' && a.type !== 'no-change-needed'\n ).length;\n \n // Flatten write results\n const flatResults = allWriteResults.flat();\n \n // Count successful writes\n const successfulWrites = flatResults.filter(r => r.success);\n const filesSaved = successfulWrites.length;\n \n // Count created vs updated\n const filesCreated = successfulWrites.filter(\n r => r.operation.operation === 'create'\n ).length;\n const filesUpdated = successfulWrites.filter(\n r => r.operation.operation === 'update'\n ).length;\n \n // Count platform-specific files\n const platformSpecificFiles = successfulWrites.filter(\n r => r.operation.isPlatformSpecific\n ).length;\n \n // Count interactive resolutions\n const interactiveResolutions = analyses.filter(\n a => a.recommendedStrategy === 'interactive' && a.type === 'needs-resolution'\n ).length;\n \n // Extract errors\n const errors = flatResults\n .filter(r => !r.success)\n .map(r => ({\n path: r.operation.registryPath,\n error: r.error || new Error('Unknown write error')\n }));\n \n return {\n packageName,\n totalGroups,\n groupsWithAction,\n filesSaved,\n filesCreated,\n filesUpdated,\n platformSpecificFiles,\n interactiveResolutions,\n errors,\n writeResults: flatResults\n };\n}\n\n/**\n * Create CommandResult from SaveReport\n * \n * Wraps the report in a CommandResult structure with formatted message.\n * \n * @param report - Save report to wrap\n * @returns CommandResult with success status and formatted message\n */\nexport function createCommandResult(report: SaveReport): CommandResult {\n return {\n success: true,\n data: {\n message: formatSaveMessage(report),\n report: report\n }\n };\n}\n\n/**\n * Create success result for simple cases\n * \n * Helper for early-exit scenarios like \"no changes detected\".\n * \n * @param packageName - Package name\n * @param message - Success message to display\n * @returns CommandResult with success status\n */\nexport function createSuccessResult(\n packageName: string,\n message: string\n): CommandResult {\n return {\n success: true,\n data: {\n message: message,\n packageName: packageName\n }\n };\n}\n\n/**\n * Create error result\n * \n * Helper for error cases throughout the pipeline.\n * \n * @param error - Error message\n * @returns CommandResult with failure status\n */\nexport function createErrorResult(error: string): CommandResult {\n return {\n success: false,\n error: error\n };\n}\n\n/**\n * Format human-readable save message\n * \n * Generates a user-friendly message summarizing the save operation.\n * Includes conditional sections based on what occurred.\n * \n * Template:\n * ```\n * \u2713 Saved {packageName}\n * {filesCreated} file(s) created\n * {filesUpdated} file(s) updated\n * {platformSpecificFiles} platform-specific file(s)\n * {interactiveResolutions} interactive resolution(s)\n * ```\n * \n * @param report - Save report to format\n * @returns Formatted message string\n */\nexport function formatSaveMessage(report: SaveReport): string {\n const lines: string[] = [];\n \n if (report.filesSaved === 0 && report.errors.length === 0) {\n return `\u2713 Saved ${report.packageName}\\n No changes detected`;\n }\n \n lines.push(`\u2713 Saved ${report.packageName}`);\n \n if (report.filesCreated > 0) {\n lines.push(` ${report.filesCreated} file(s) created`);\n }\n \n if (report.filesUpdated > 0) {\n lines.push(` ${report.filesUpdated} file(s) updated`);\n }\n \n if (report.platformSpecificFiles > 0) {\n lines.push(` ${report.platformSpecificFiles} platform-specific file(s)`);\n }\n \n if (report.interactiveResolutions > 0) {\n lines.push(` ${report.interactiveResolutions} interactive resolution(s)`);\n }\n \n if (report.errors.length > 0) {\n lines.push('');\n lines.push(`\u26A0\uFE0F ${report.errors.length} error(s) occurred:`);\n report.errors.forEach(err => {\n lines.push(` \u2022 ${err.path}: ${err.error.message}`);\n });\n }\n \n const successfulWrites = report.writeResults.filter(r => r.success);\n if (successfulWrites.length > 0) {\n lines.push('');\n lines.push(' Files saved:');\n \n const sorted = [...successfulWrites].sort((a, b) =>\n a.operation.registryPath.localeCompare(b.operation.registryPath)\n );\n \n for (const result of sorted) {\n const { registryPath, isPlatformSpecific, platform } = result.operation;\n const label = isPlatformSpecific && platform\n ? `${registryPath} (${platform})`\n : `${registryPath} (universal)`;\n lines.push(` \u251C\u2500\u2500 ${label}`);\n }\n }\n \n if (report.filesSaved > 0) {\n lines.push('');\n lines.push('\uD83D\uDCA1 Changes saved to package source.');\n lines.push(' To sync changes to workspace, run:');\n lines.push(` opkg install ${report.packageName}`);\n }\n \n return lines.join('\\n');\n}\n", "/**\n * Save To Source Pipeline\n * \n * This module orchestrates the complete save operation, integrating all phases:\n * - Phase 1: Validation\n * - Phase 2: Candidate Discovery & Grouping\n * - Phase 3: Platform Pruning & Filtering\n * - Phase 4: Conflict Analysis & Resolution\n * - Phase 5: File Writes\n * - Phase 6: Result Reporting\n * \n * This is the main entry point for the enhanced save command.\n * \n * @module save-to-source-pipeline\n */\n\nimport type { CommandResult } from '../../types/index.js';\nimport type { ExecutionContext } from '../../types/execution-context.js';\nimport type { WorkspaceIndexFileMapping } from '../../types/workspace-index.js';\nimport { assertMutableSourceOrThrow } from '../source-mutability.js';\nimport { readWorkspaceIndex } from '../../utils/workspace-index-yml.js';\nimport { resolvePackageSource } from '../source-resolution/resolve-package-source.js';\nimport { logger } from '../../utils/logger.js';\nimport { resolveOutput, resolvePrompt } from '../ports/resolve.js';\nimport { buildCandidates, materializeLocalCandidate } from './save-candidate-builder.js';\nimport { buildCandidateGroups, filterGroupsWithWorkspace } from './save-group-builder.js';\nimport { analyzeGroup } from './save-conflict-analyzer.js';\nimport { executeResolution } from './save-resolution-executor.js';\nimport { pruneExistingPlatformCandidates } from './save-platform-handler.js';\nimport { writeResolution } from './save-write-coordinator.js';\nimport { clearConversionCache, initSharedTempDir, cleanupSharedTempDir } from './save-conversion-helper.js';\nimport {\n buildSaveReport,\n createCommandResult,\n createSuccessResult,\n createErrorResult\n} from './save-result-reporter.js';\nimport type { ConflictAnalysis } from './save-conflict-analyzer.js';\nimport type { WriteResult } from './save-types.js';\n\n/**\n * Options for save-to-source pipeline\n */\nexport interface SaveToSourceOptions {\n /** Enable force mode (auto-select newest when conflicts occur) */\n force?: boolean;\n}\n\n/**\n * Validation result structure (internal)\n */\ninterface ValidationResult {\n valid: boolean;\n cwd?: string;\n packageRoot?: string;\n filesMapping?: Record<string, (string | WorkspaceIndexFileMapping)[]>;\n error?: string;\n}\n\n/**\n * Run the complete save-to-source pipeline\n * \n * This is the main orchestrator function that coordinates all phases\n * of the save operation:\n * \n * 1. **Validate preconditions**: Check package exists, is mutable, has files\n * 2. **Build candidates**: Discover files in workspace and source\n * 3. **Group candidates**: Organize by registry path\n * 4. **Prune platform candidates**: Remove candidates with existing platform files\n * 5. **Filter active groups**: Keep only groups with workspace changes\n * 6. **Analyze & resolve**: Classify conflicts and execute resolution strategies\n * 7. **Write files**: Execute file write operations\n * 8. **Report results**: Build and return comprehensive report\n * \n * @param packageName - Package name to save\n * @param options - Save options (force mode, etc.)\n * @returns CommandResult with success status and report data\n */\nexport async function runSaveToSourcePipeline(\n packageName: string | undefined,\n options: SaveToSourceOptions = {},\n ctx?: ExecutionContext\n): Promise<CommandResult> {\n try {\n await initSharedTempDir();\n \n // Phase 1: Validate preconditions\n logger.debug(`Validating save preconditions for ${packageName}`);\n const validation = await validateSavePreconditions(packageName);\n if (!validation.valid) {\n return createErrorResult(validation.error!);\n }\n \n const { cwd, packageRoot, filesMapping } = validation;\n \n // Phase 2: Build candidates from workspace and source\n logger.debug(`Building candidates for ${packageName}`);\n const candidateResult = await buildCandidates({\n packageRoot: packageRoot!,\n workspaceRoot: cwd!,\n filesMapping: filesMapping!\n });\n \n if (candidateResult.errors.length > 0) {\n logger.warn(`Encountered ${candidateResult.errors.length} error(s) building candidates`);\n candidateResult.errors.forEach(err =>\n logger.warn(` ${err.path}: ${err.reason}`)\n );\n }\n \n // Phase 3: Build candidate groups (organize by registry path)\n logger.debug('Building candidate groups');\n const allGroups = buildCandidateGroups(\n candidateResult.localSourceRefs,\n candidateResult.workspaceCandidates,\n cwd!\n );\n \n // Phase 4: Filter first (cheap), then prune only active groups\n const activeGroups = filterGroupsWithWorkspace(allGroups);\n \n if (activeGroups.length === 0) {\n logger.info(`No workspace changes detected for ${packageName}`);\n return createSuccessResult(\n packageName!,\n `\u2713 Saved ${packageName}\\n No workspace changes detected`\n );\n }\n \n // Prune only active groups (instead of all groups)\n logger.debug('Pruning existing platform-specific files');\n await pruneExistingPlatformCandidates(packageRoot!, activeGroups);\n \n // Re-filter after pruning (pruning may have removed all workspace candidates from some groups)\n const finalGroups = activeGroups.filter(g => g.workspace.length > 0);\n \n if (finalGroups.length === 0) {\n logger.info(`No workspace changes detected for ${packageName}`);\n return createSuccessResult(\n packageName!,\n `\u2713 Saved ${packageName}\\n No workspace changes detected`\n );\n }\n \n // Phase 5: Materialize local candidates on demand for active groups\n for (const group of finalGroups) {\n if (group.localRef && !group.local) {\n group.local = await materializeLocalCandidate(group.localRef, packageRoot!) ?? undefined;\n }\n }\n \n logger.debug(`Processing ${finalGroups.length} group(s) with workspace candidates`);\n \n // Phase 6: Analyze all groups first, then split into auto-resolvable vs interactive\n const groupAnalyses = await Promise.all(\n finalGroups.map(async group => ({\n group,\n analysis: await analyzeGroup(group, options.force ?? false, cwd!)\n }))\n );\n \n const analyses: ConflictAnalysis[] = groupAnalyses.map(ga => ga.analysis);\n const allWriteResults: WriteResult[][] = [];\n \n // Partition: auto-resolvable groups can run in parallel, interactive must be serial\n const autoResolvable: typeof groupAnalyses = [];\n const interactive: typeof groupAnalyses = [];\n \n for (const ga of groupAnalyses) {\n if (ga.analysis.type === 'no-action-needed' || ga.analysis.type === 'no-change-needed') {\n logger.debug(`Skipping ${ga.group.registryPath}: ${ga.analysis.type}`);\n continue;\n }\n if (ga.analysis.recommendedStrategy === 'interactive') {\n interactive.push(ga);\n } else {\n autoResolvable.push(ga);\n }\n }\n \n // Process auto-resolvable groups in parallel\n if (autoResolvable.length > 0) {\n logger.debug(`Processing ${autoResolvable.length} auto-resolvable group(s) in parallel`);\n const autoResults = await Promise.all(\n autoResolvable.map(async ({ group, analysis }) => {\n const resolution = await executeResolution(group, analysis, packageRoot!, cwd!, resolveOutput(ctx), resolvePrompt(ctx));\n if (!resolution) return null;\n return writeResolution(packageRoot!, group.registryPath, resolution, group.local, cwd!);\n })\n );\n for (const result of autoResults) {\n if (result) allWriteResults.push(result);\n }\n }\n \n // Process interactive groups serially (require user input)\n for (const { group, analysis } of interactive) {\n const resolution = await executeResolution(group, analysis, packageRoot!, cwd!, resolveOutput(ctx), resolvePrompt(ctx));\n if (!resolution) {\n logger.debug(`No resolution returned for ${group.registryPath}`);\n continue;\n }\n const writeResults = await writeResolution(\n packageRoot!, group.registryPath, resolution, group.local, cwd!\n );\n allWriteResults.push(writeResults);\n }\n \n // Phase 7: Build and format report\n logger.debug('Building save report');\n const report = buildSaveReport(packageName!, analyses, allWriteResults);\n \n // Phase 8: Return result\n return createCommandResult(report);\n } finally {\n clearConversionCache();\n await cleanupSharedTempDir();\n }\n}\n\n/**\n * Validate save preconditions\n * \n * Performs comprehensive validation before attempting save operation:\n * - Package name is provided\n * - Workspace index exists and is readable\n * - Package exists in index\n * - Package has file mappings\n * - Package source is resolvable\n * - Source is mutable (not registry)\n * \n * @param packageName - Package name to validate\n * @returns Validation result with success status and required data or error\n */\nasync function validateSavePreconditions(\n packageName: string | undefined\n): Promise<ValidationResult> {\n const cwd = process.cwd();\n \n // Check package name provided\n if (!packageName) {\n return {\n valid: false,\n error: 'Package name is required for save.'\n };\n }\n \n // Read workspace index\n let index;\n try {\n const result = await readWorkspaceIndex(cwd);\n index = result.index;\n } catch (error) {\n return {\n valid: false,\n error: `Failed to read workspace index: ${error}`\n };\n }\n \n // Check package exists in index\n const pkgIndex = index.packages?.[packageName];\n if (!pkgIndex) {\n return {\n valid: false,\n error:\n `Package '${packageName}' is not installed in this workspace.\\n` +\n `Run 'opkg install ${packageName}' to install it first.`\n };\n }\n \n // Check package has file mappings\n if (!pkgIndex.files || Object.keys(pkgIndex.files).length === 0) {\n return {\n valid: false,\n error:\n `Package '${packageName}' has no files installed.\\n` +\n `Nothing to save.`\n };\n }\n \n // Resolve package source\n let source;\n try {\n source = await resolvePackageSource(cwd, packageName);\n } catch (error) {\n return {\n valid: false,\n error: `Failed to resolve package source: ${error}`\n };\n }\n \n // Check source is mutable\n try {\n assertMutableSourceOrThrow(source.absolutePath, {\n packageName: source.packageName,\n command: 'save'\n });\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : String(error)\n };\n }\n \n return {\n valid: true,\n cwd,\n packageRoot: source.absolutePath,\n filesMapping: pkgIndex.files\n };\n}\n", "import { runSaveToSourcePipeline, type SaveToSourceOptions } from '@opkg/core/core/save/save-to-source-pipeline.js';\nimport { createCliExecutionContext } from '../cli/context.js';\nimport { resolveOutput } from '@opkg/core/core/ports/resolve.js';\n\nexport async function setupSaveCommand(args: any[]): Promise<void> {\n const [packageName, options] = args as [string, SaveToSourceOptions];\n const ctx = await createCliExecutionContext();\n const out = resolveOutput(ctx);\n const result = await runSaveToSourcePipeline(packageName, options, ctx);\n if (!result.success) {\n throw new Error(result.error || 'Save operation failed');\n }\n if (result.data?.message) {\n out.success(result.data.message);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["join", "minimatch", "join", "minimatch", "join", "join", "join", "join", "join", "minimatch", "join", "evaluateWhenCondition", "resolveVariable"]
7
+ }