@rainbow-robotics/muscat-plugin-kit 1.10.1-dev.1 → 1.10.1

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 (205) hide show
  1. package/README.md +282 -282
  2. package/cli/README.md +274 -274
  3. package/cli/bin/create-muscat-plugin.mjs +8 -8
  4. package/cli/bin/muscat-pk.mjs +155 -155
  5. package/cli/bundle-templates.mjs +31 -31
  6. package/cli/docker/Dockerfile.cpp +28 -28
  7. package/cli/docker/Dockerfile.dev +30 -30
  8. package/cli/docker/Dockerfile.python +21 -21
  9. package/cli/m5-e2e.mjs +69 -69
  10. package/cli/prepack.mjs +10 -10
  11. package/cli/src/ai-rules.mjs +70 -70
  12. package/cli/src/build.mjs +447 -447
  13. package/cli/src/check-package.mjs +13 -13
  14. package/cli/src/completion.mjs +166 -166
  15. package/cli/src/cpp.mjs +78 -78
  16. package/cli/src/cppIde.mjs +75 -75
  17. package/cli/src/dev.mjs +165 -165
  18. package/cli/src/devBack.mjs +734 -727
  19. package/cli/src/devUi.mjs +247 -243
  20. package/cli/src/esbuild.mjs +56 -56
  21. package/cli/src/host_agent.py +223 -223
  22. package/cli/src/manifest.mjs +287 -287
  23. package/cli/src/mcp.mjs +1071 -1071
  24. package/cli/src/muscatLocal.mjs +209 -209
  25. package/cli/src/new.mjs +343 -343
  26. package/cli/src/pluginDevServer.mjs +111 -111
  27. package/cli/src/ports.mjs +193 -193
  28. package/cli/src/python.mjs +112 -112
  29. package/cli/src/ros2.mjs +109 -109
  30. package/cli/src/sdk.mjs +117 -117
  31. package/cli/src/storybook.mjs +168 -168
  32. package/cli/src/type.mjs +141 -141
  33. package/cli/src/uninstall.mjs +81 -81
  34. package/cli/src/update.mjs +295 -295
  35. package/cli/src/util.mjs +441 -441
  36. package/cli/src/zenohDev.mjs +83 -81
  37. package/cli/storybook-dist/_design-tokens.css +4957 -4957
  38. package/cli/storybook-dist/_stories-src/NxAvatar.stories.tsx +27 -27
  39. package/cli/storybook-dist/_stories-src/NxPagination.stories.tsx +28 -28
  40. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordion.stories.tsx +46 -46
  41. package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordionGroup.stories.tsx +73 -73
  42. package/cli/storybook-dist/_stories-src/RBBadge/RBBadge.stories.tsx +93 -93
  43. package/cli/storybook-dist/_stories-src/RBBattery/RBBattery.stories.tsx +74 -74
  44. package/cli/storybook-dist/_stories-src/RBButton/RBCheckIconButton.stories.tsx +28 -28
  45. package/cli/storybook-dist/_stories-src/RBButton/RBCheckLabelButton.stories.tsx +84 -84
  46. package/cli/storybook-dist/_stories-src/RBButton/RBIconButton.stories.tsx +57 -57
  47. package/cli/storybook-dist/_stories-src/RBButton/RBInlineLabelButton.stories.tsx +41 -41
  48. package/cli/storybook-dist/_stories-src/RBButton/RBLabelButton.stories.tsx +60 -60
  49. package/cli/storybook-dist/_stories-src/RBDatePicker/RBDateInput.stories.tsx +44 -44
  50. package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +56 -56
  51. package/cli/storybook-dist/_stories-src/RBDialog/RBAlert.stories.tsx +51 -51
  52. package/cli/storybook-dist/_stories-src/RBDialog/RBConfirm.stories.tsx +55 -55
  53. package/cli/storybook-dist/_stories-src/RBDialog/RBPrompt.stories.tsx +52 -52
  54. package/cli/storybook-dist/_stories-src/RBDivider/RBDivider.stories.tsx +93 -93
  55. package/cli/storybook-dist/_stories-src/RBEmptyView/RBEmptyView.stories.tsx +49 -49
  56. package/cli/storybook-dist/_stories-src/RBIcons/RBColorIcons.stories.tsx +108 -108
  57. package/cli/storybook-dist/_stories-src/RBIcons/RBIcons.stories.tsx +106 -106
  58. package/cli/storybook-dist/_stories-src/RBInput/RBCheckBox.stories.tsx +89 -89
  59. package/cli/storybook-dist/_stories-src/RBInput/RBMultiRangeInput.stories.tsx +64 -64
  60. package/cli/storybook-dist/_stories-src/RBInput/RBRadioBox.stories.tsx +65 -65
  61. package/cli/storybook-dist/_stories-src/RBInput/RBRadioButton.stories.tsx +68 -68
  62. package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +216 -216
  63. package/cli/storybook-dist/_stories-src/RBInput/RBSideNumberInput.stories.tsx +90 -90
  64. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextArea.stories.tsx +45 -45
  65. package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextInput.stories.tsx +50 -50
  66. package/cli/storybook-dist/_stories-src/RBInput/RBTextField.stories.tsx +102 -102
  67. package/cli/storybook-dist/_stories-src/RBJoyStick/RBJoyStick.stories.tsx +31 -31
  68. package/cli/storybook-dist/_stories-src/RBLight/RBLight.stories.tsx +36 -36
  69. package/cli/storybook-dist/_stories-src/RBPagination/RBPagination.stories.tsx +49 -49
  70. package/cli/storybook-dist/_stories-src/RBShadcn/RBCard/RBCard.stories.tsx +89 -89
  71. package/cli/storybook-dist/_stories-src/RBShadcn/RBCommand/RBCommand.stories.tsx +116 -116
  72. package/cli/storybook-dist/_stories-src/RBShadcn/RBDialog/RBDialog.stories.tsx +74 -74
  73. package/cli/storybook-dist/_stories-src/RBShadcn/RBDrawer/RBDrawer.stories.tsx +99 -99
  74. package/cli/storybook-dist/_stories-src/RBShadcn/RBDropdownMenu/RBDropdownMenu.stories.tsx +123 -123
  75. package/cli/storybook-dist/_stories-src/RBShadcn/RBMenubar/RBMenubar.stories.tsx +135 -135
  76. package/cli/storybook-dist/_stories-src/RBShadcn/RBPopover/RBPopover.stories.tsx +69 -69
  77. package/cli/storybook-dist/_stories-src/RBShadcn/RBResizable/RBResizable.stories.tsx +49 -49
  78. package/cli/storybook-dist/_stories-src/RBShadcn/RBSheet/RBSheet.stories.tsx +91 -91
  79. package/cli/storybook-dist/_stories-src/RBShadcn/RBSlider/RBSlider.stories.tsx +38 -38
  80. package/cli/storybook-dist/_stories-src/RBShadcn/RBSwitch/RBSwitch.stories.tsx +112 -112
  81. package/cli/storybook-dist/_stories-src/RBShadcn/RBTabs/RBTabs.stories.tsx +75 -75
  82. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggle/RBToggle.stories.tsx +51 -51
  83. package/cli/storybook-dist/_stories-src/RBShadcn/RBToggleGroup/RBToggleGroup.stories.tsx +84 -84
  84. package/cli/storybook-dist/_stories-src/RBTab/RBChipTab.stories.tsx +102 -102
  85. package/cli/storybook-dist/_stories-src/RBTab/RBChipsBoxTab.stories.tsx +78 -78
  86. package/cli/storybook-dist/_stories-src/RBTab/RBTab.stories.tsx +57 -57
  87. package/cli/storybook-dist/_stories-src/RBTable/RBTable.stories.tsx +177 -177
  88. package/cli/storybook-dist/_stories-src/RBTable/RBTableRow.stories.tsx +64 -64
  89. package/cli/storybook-dist/_stories-src/RBToast/RBToast.stories.tsx +117 -117
  90. package/cli/storybook-dist/_stories-src/RBTooltip/RBTooltip.stories.tsx +50 -50
  91. package/cli/storybook-dist/_stories-src/loading/RBSpinner.stories.tsx +30 -30
  92. package/cli/storybook-dist/_stories-src/nx-button/NxButton.stories.tsx +54 -54
  93. package/cli/storybook-dist/_stories-src/nx-button/NxPointerHoldButton.stories.tsx +57 -57
  94. package/cli/storybook-dist/_stories-src/nx-icons/NxIcons.stories.tsx +48 -48
  95. package/cli/storybook-dist/_stories-src/nx-inputs/NxNumberInput.stories.tsx +103 -103
  96. package/cli/storybook-dist/_stories-src/nx-inputs/NxRangeInput.stories.tsx +27 -27
  97. package/cli/storybook-dist/_stories-src/nx-inputs/NxSelectBox.stories.tsx +67 -67
  98. package/cli/storybook-dist/_stories-src/nx-inputs/NxSlideRange.stories.tsx +45 -45
  99. package/cli/storybook-dist/_stories-src/nx-inputs/NxSwitch.stories.tsx +27 -27
  100. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextArea.stories.tsx +48 -48
  101. package/cli/storybook-dist/_stories-src/nx-inputs/NxTextInput.stories.tsx +46 -46
  102. package/cli/storybook-dist/assets/{Color-YHDXOIA2-D6JqZzDJ.js → Color-YHDXOIA2-B4IER17l.js} +1 -1
  103. package/cli/storybook-dist/assets/{Docs-ESb7JAHg.js → Docs-C8GCHFtK.js} +3 -3
  104. package/cli/storybook-dist/assets/{Docs-6gD2kUo1.js → Docs-CgdEMPQP.js} +2 -2
  105. package/cli/storybook-dist/assets/{Docs-B9wd8Ok7.js → Docs-Ck3WHvk6.js} +3 -3
  106. package/cli/storybook-dist/assets/{Docs-CgFbWWXM.js → Docs-D9NHsoeZ.js} +2 -2
  107. package/cli/storybook-dist/assets/{Docs-DBQup8Bu.js → Docs-DNYQC7nl.js} +3 -3
  108. package/cli/storybook-dist/assets/{Docs-CMjovnSY.js → Docs-DUfeiM4I.js} +4 -4
  109. package/cli/storybook-dist/assets/{DocsRenderer-CFRXHY34-BOUI1AHi.js → DocsRenderer-CFRXHY34-C0WhyDoR.js} +1 -1
  110. package/cli/storybook-dist/assets/{NxAvatar.stories-DsYFMUh3.js → NxAvatar.stories-CwnK8AUF.js} +1 -1
  111. package/cli/storybook-dist/assets/{NxButton.stories-CHfNcqsc.js → NxButton.stories-ueYdosfQ.js} +8 -8
  112. package/cli/storybook-dist/assets/{NxNumberInput.stories-BFw_4vFE.js → NxNumberInput.stories-M5ZCLbrK.js} +1 -1
  113. package/cli/storybook-dist/assets/{NxPagination.stories-DVfxyw4M.js → NxPagination.stories-bxxUnHEL.js} +1 -1
  114. package/cli/storybook-dist/assets/{NxPointerHoldButton.stories-BzyxUWoZ.js → NxPointerHoldButton.stories-BHxB2kr5.js} +1 -1
  115. package/cli/storybook-dist/assets/{NxRangeInput.stories-DmRdzBc1.js → NxRangeInput.stories-BKfr-fp4.js} +6 -6
  116. package/cli/storybook-dist/assets/{NxSelectBox.stories-B1KkNUOf.js → NxSelectBox.stories-CG8FuTBq.js} +1 -1
  117. package/cli/storybook-dist/assets/{NxSlideRange.stories-CyOdvfQ6.js → NxSlideRange.stories-BRTFtMTL.js} +1 -1
  118. package/cli/storybook-dist/assets/{NxSwitch.stories-DrDBw0qp.js → NxSwitch.stories-Bwuyzn-R.js} +1 -1
  119. package/cli/storybook-dist/assets/{NxTextArea.stories-DTB2aqNS.js → NxTextArea.stories-COq2sGqu.js} +1 -1
  120. package/cli/storybook-dist/assets/{NxTextInput.stories-DU0rucmM.js → NxTextInput.stories-CjsBLz05.js} +1 -1
  121. package/cli/storybook-dist/assets/{RBAccordion.stories-DTfOtXtp.js → RBAccordion.stories-BARwuZ0Q.js} +3 -3
  122. package/cli/storybook-dist/assets/{RBAccordionGroup.stories-9y9BjrUe.js → RBAccordionGroup.stories-COrPxETW.js} +10 -10
  123. package/cli/storybook-dist/assets/{RBAlert.stories-DLTAv9fT.js → RBAlert.stories-BQrkjZPt.js} +3 -3
  124. package/cli/storybook-dist/assets/{RBBadge.stories-BNMACewA.js → RBBadge.stories-BTsLL-lK.js} +1 -1
  125. package/cli/storybook-dist/assets/{RBBattery.stories-B3kXMAt5.js → RBBattery.stories-DAeQdJbc.js} +1 -1
  126. package/cli/storybook-dist/assets/{RBCard.stories-Y-s8R2n7.js → RBCard.stories-HyhOE80R.js} +39 -39
  127. package/cli/storybook-dist/assets/{RBCheckBox.stories-DBuT9tL4.js → RBCheckBox.stories-KFk0WqX_.js} +1 -1
  128. package/cli/storybook-dist/assets/{RBCheckIconButton.stories-CWu1TL56.js → RBCheckIconButton.stories-CXbbktF5.js} +1 -1
  129. package/cli/storybook-dist/assets/{RBCheckLabelButton.stories-sCu2MGCX.js → RBCheckLabelButton.stories-DWfYz_9Z.js} +17 -17
  130. package/cli/storybook-dist/assets/{RBChipTab.stories-BX9qLJkR.js → RBChipTab.stories-JPEgVFAS.js} +5 -5
  131. package/cli/storybook-dist/assets/{RBChipsBoxTab.stories-35u1aJ-P.js → RBChipsBoxTab.stories-CTPuWXrQ.js} +1 -1
  132. package/cli/storybook-dist/assets/{RBColorIcons.stories-DAOZ5Ies.js → RBColorIcons.stories-DiFRZ5jk.js} +1 -1
  133. package/cli/storybook-dist/assets/RBCommand.stories-Pf1HAVIR.js +73 -0
  134. package/cli/storybook-dist/assets/{RBConfirm.stories-iMXcUh7Z.js → RBConfirm.stories-D1cEPUXk.js} +3 -3
  135. package/cli/storybook-dist/assets/{RBDateInput.stories-hK5rXECf.js → RBDateInput.stories-RPYpCjig.js} +1 -1
  136. package/cli/storybook-dist/assets/RBDialog.stories-DJH-0XSR.js +38 -0
  137. package/cli/storybook-dist/assets/{RBDivider.stories-BAK-iZ1Z.js → RBDivider.stories-uEj1vOgK.js} +1 -1
  138. package/cli/storybook-dist/assets/{RBDrawer.stories-B2nqf2Nh.js → RBDrawer.stories-BiMzElb2.js} +12 -12
  139. package/cli/storybook-dist/assets/RBDropdownMenu.stories-Da_TBkla.js +86 -0
  140. package/cli/storybook-dist/assets/{RBEmptyView.stories-nCenrgBh.js → RBEmptyView.stories-BHeKGfPS.js} +1 -1
  141. package/cli/storybook-dist/assets/{RBIconButton.stories-DRH7O3sp.js → RBIconButton.stories-NEsRFoYp.js} +1 -1
  142. package/cli/storybook-dist/assets/{RBIcons.stories-D3PyJXG8.js → RBIcons.stories-CcZ-TTLS.js} +1 -1
  143. package/cli/storybook-dist/assets/{RBInlineLabelButton.stories-modQbt10.js → RBInlineLabelButton.stories-B0wpdQyR.js} +1 -1
  144. package/cli/storybook-dist/assets/{RBJoyStick.stories-BY14qVgR.js → RBJoyStick.stories-DEgBou3K.js} +1 -1
  145. package/cli/storybook-dist/assets/{RBLabelButton.stories-DzF7vGwm.js → RBLabelButton.stories-DT8drIsF.js} +1 -1
  146. package/cli/storybook-dist/assets/{RBLight.stories-lF4FRmVh.js → RBLight.stories-Dh-aoeyG.js} +1 -1
  147. package/cli/storybook-dist/assets/{RBMenubar.stories-XhrI7bsV.js → RBMenubar.stories-D-H7UqVh.js} +1 -1
  148. package/cli/storybook-dist/assets/{RBMultiRangeInput.stories-DprllQDl.js → RBMultiRangeInput.stories-hlclv6jI.js} +1 -1
  149. package/cli/storybook-dist/assets/{RBPagination.stories-2v2gBJfC.js → RBPagination.stories-DWCeoZ_S.js} +1 -1
  150. package/cli/storybook-dist/assets/{RBPopover.stories-C8iLIZpv.js → RBPopover.stories-C514M09_.js} +1 -1
  151. package/cli/storybook-dist/assets/{RBPrompt.stories-Th_HrWHI.js → RBPrompt.stories-BSItvfiT.js} +1 -1
  152. package/cli/storybook-dist/assets/{RBRadioBox.stories-CoHuLaN3.js → RBRadioBox.stories-BBb4YbFU.js} +1 -1
  153. package/cli/storybook-dist/assets/{RBRadioButton.stories-DULxE_ff.js → RBRadioButton.stories-CXyOGJCv.js} +1 -1
  154. package/cli/storybook-dist/assets/{RBRangeDateInput.stories-D8A9KBSS.js → RBRangeDateInput.stories-qvhubu0e.js} +1 -1
  155. package/cli/storybook-dist/assets/{RBResizable.stories-C41QfrQ3.js → RBResizable.stories-CZKeyGjH.js} +14 -14
  156. package/cli/storybook-dist/assets/{RBSelectBox.stories-C1gNmwzw.js → RBSelectBox.stories-CRKP3ybf.js} +1 -1
  157. package/cli/storybook-dist/assets/RBSheet.stories-sCM3ZJUa.js +39 -0
  158. package/cli/storybook-dist/assets/{RBSideNumberInput.stories-9LlgQ8nh.js → RBSideNumberInput.stories-Bb7gw8RF.js} +3 -3
  159. package/cli/storybook-dist/assets/{RBSlider.stories-oUgo5K9d.js → RBSlider.stories-23OAQU5z.js} +3 -3
  160. package/cli/storybook-dist/assets/{RBSpinner.stories-DDpohbY2.js → RBSpinner.stories-2xcQdcmt.js} +1 -1
  161. package/cli/storybook-dist/assets/{RBSwitch.stories-gW-GN7QO.js → RBSwitch.stories-BACUaJm_.js} +7 -7
  162. package/cli/storybook-dist/assets/{RBTab.stories-CN-PV3HH.js → RBTab.stories-CDHE3sVs.js} +1 -1
  163. package/cli/storybook-dist/assets/{RBTable.stories-BAZLpxtM.js → RBTable.stories-C0Cfhfl7.js} +29 -29
  164. package/cli/storybook-dist/assets/{RBTableRow.stories-CwjVrszB.js → RBTableRow.stories-BF8kIkpp.js} +1 -1
  165. package/cli/storybook-dist/assets/{RBTabs.stories-JuEo2sUL.js → RBTabs.stories-BdJWRq1o.js} +7 -7
  166. package/cli/storybook-dist/assets/{RBTextArea.stories-D0_Pv4qZ.js → RBTextArea.stories-CGy2xeYH.js} +3 -3
  167. package/cli/storybook-dist/assets/{RBTextField.stories-CCsOvZqV.js → RBTextField.stories-IfUOzxCM.js} +15 -15
  168. package/cli/storybook-dist/assets/{RBTextInput.stories-Cccn3VNm.js → RBTextInput.stories-VjXNFo4R.js} +3 -3
  169. package/cli/storybook-dist/assets/{RBToast.stories-I5617t0K.js → RBToast.stories-BzcobblV.js} +1 -1
  170. package/cli/storybook-dist/assets/{RBToggle.stories-DsgKjGsJ.js → RBToggle.stories-DuLE5mKv.js} +4 -4
  171. package/cli/storybook-dist/assets/{RBToggleGroup.stories-BZEzSDuz.js → RBToggleGroup.stories-CuR0N0GJ.js} +5 -5
  172. package/cli/storybook-dist/assets/{RBTooltip.stories-4oSyAWsA.js → RBTooltip.stories--aAgNC8_.js} +1 -1
  173. package/cli/storybook-dist/assets/{SideNumberInput-DSKbd0bG.js → SideNumberInput-DyWQxYUj.js} +743 -743
  174. package/cli/storybook-dist/assets/{entry-preview-B35XREGs.js → entry-preview-D7IHsza_.js} +1 -1
  175. package/cli/storybook-dist/assets/{iframe-BB797lmL.js → iframe-CbaIp5I-.js} +2 -2
  176. package/cli/storybook-dist/assets/{index-BU2567EP.js → index-C6CBgMuG.js} +3 -3
  177. package/cli/storybook-dist/assets/{preview-C7DaSWyc.js → preview-B-iYLDuW.js} +2 -2
  178. package/cli/storybook-dist/assets/{preview-CbuRHit4.js → preview-CRWqc9DH.js} +1 -1
  179. package/cli/storybook-dist/assets/{react-resizable-panels.browser-ChIvlwo7.js → react-resizable-panels.browser-8hC8dr-6.js} +8 -8
  180. package/cli/storybook-dist/iframe.html +2 -2
  181. package/cli/storybook-dist/index.html +1 -1
  182. package/cli/storybook-dist/project.json +1 -1
  183. package/cli/templates/common/.mcp.json +8 -8
  184. package/cli/templates/common/README.md +1231 -1231
  185. package/cli/templates/common/assets/icon.svg +45 -45
  186. package/cli/templates/common/manifest.json +26 -26
  187. package/cli/templates/common/package.json +22 -22
  188. package/cli/templates/common/py/main.py +125 -125
  189. package/cli/templates/common/tsconfig.json +13 -13
  190. package/cli/templates/common/ui/content.tsx +216 -216
  191. package/cli/templates/common/ui/style.css +6 -6
  192. package/cli/templates/common/vite.config.ts +10 -10
  193. package/cli/templates/cpp/cpp/main.cpp +73 -73
  194. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +25 -25
  195. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/package.xml +10 -10
  196. package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/setup.py +18 -18
  197. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -11
  198. package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +23 -23
  199. package/cli/vite.d.mts +25 -25
  200. package/cli/vite.mjs +292 -292
  201. package/package.json +3 -3
  202. package/cli/storybook-dist/assets/RBCommand.stories-BSqXDkKw.js +0 -73
  203. package/cli/storybook-dist/assets/RBDialog.stories-BAktz-ku.js +0 -38
  204. package/cli/storybook-dist/assets/RBDropdownMenu.stories-DbW2fWAj.js +0 -86
  205. package/cli/storybook-dist/assets/RBSheet.stories-_6VH-RUm.js +0 -39
@@ -1,295 +1,295 @@
1
- // muscat-pk update — update the CLI itself AND the plugin project's JS deps.
2
- //
3
- // Three install modes are handled:
4
- // binary : Bun self-contained binary downloaded from S3
5
- // → re-download from S3, replace process.execPath
6
- // npm -g : globally installed Node.js package
7
- // → npm install -g @rainbow-robotics/muscat-plugin-kit@<tag>
8
- // local : plugin project's node_modules (fallback)
9
- // → <pm> add @rainbow-robotics/muscat-plugin-kit@<tag>
10
- //
11
- // After updating the CLI itself, also bumps the plugin project's local deps
12
- // and migrates the project structure if it's outdated (src/ → ui/, root main.py → py/).
13
- import { execSync, spawn, spawnSync } from "node:child_process";
14
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
15
- import { join, resolve } from "node:path";
16
- import { findPluginRoot, isLocalKitInstall, KIT_PUBLISHED_NAME, kitVersion, log, parseArgs, prompt } from "./util.mjs";
17
-
18
- const PLUGIN_UI_PACKAGE = "@rainbow-robotics/plugin-ui";
19
- const S3_BASE = "https://rainbow-deploy.s3.ap-northeast-2.amazonaws.com/muscat-plugin-kit";
20
-
21
- const isBinaryMode = typeof Bun !== "undefined";
22
-
23
- // ── helpers ───────────────────────────────────────────────────────────────────
24
-
25
- function detectPm(root) {
26
- try {
27
- const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
28
- if (pkg.packageManager) return pkg.packageManager.split("@")[0];
29
- } catch { /* ignore */ }
30
- if (existsSync(resolve(root, "pnpm-lock.yaml"))) return "pnpm";
31
- if (existsSync(resolve(root, "yarn.lock"))) return "yarn";
32
- if (existsSync(resolve(root, "bun.lockb"))) return "bun";
33
- return "npm";
34
- }
35
-
36
- async function fetchText(url) {
37
- const res = await fetch(url);
38
- if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
39
- return (await res.text()).trim();
40
- }
41
-
42
- async function fetchBinary(url) {
43
- const res = await fetch(url);
44
- if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
45
- return Buffer.from(await res.arrayBuffer());
46
- }
47
-
48
- function s3BinaryName() {
49
- const platform = process.platform;
50
- const arch = process.arch;
51
- const plat = platform === "win32" ? "win" : platform === "darwin" ? "darwin" : "linux";
52
- const cpu = arch === "arm64" ? "arm64" : "x64";
53
- const ext = platform === "win32" ? ".exe" : "";
54
- return `muscat-pk-${plat}-${cpu}${ext}`;
55
- }
56
-
57
- async function resolveS3Version(tag) {
58
- const pointer = tag === "dev" ? "dev-version.txt" : "latest-version.txt";
59
- return fetchText(`${S3_BASE}/${pointer}`);
60
- }
61
-
62
- // ── self-update: binary ───────────────────────────────────────────────────────
63
-
64
- async function updateBinary(tag) {
65
- const version = await resolveS3Version(tag);
66
- const current = kitVersion();
67
- if (version === current) {
68
- log.ok(`Already up to date (v${current})`);
69
- return;
70
- }
71
-
72
- const binaryName = s3BinaryName();
73
- const url = `${S3_BASE}/v${version}/${binaryName}`;
74
- log.step(`Downloading binary: v${current} → v${version}`);
75
-
76
- const data = await fetchBinary(url);
77
- const execPath = process.execPath;
78
-
79
- if (process.platform === "win32") {
80
- // Windows: cannot overwrite a running .exe — write .new then schedule replacement
81
- const newPath = execPath + ".new";
82
- writeFileSync(newPath, data);
83
-
84
- // PowerShell snippet that waits for this process to exit, then renames
85
- const ps = `Start-Sleep -Milliseconds 500; Move-Item -Force '${newPath}' '${execPath}'`;
86
- spawn("powershell", ["-NoProfile", "-Command", ps], {
87
- detached: true,
88
- stdio: "ignore",
89
- }).unref();
90
-
91
- log.ok(`v${version} will be applied on next run.`);
92
- } else {
93
- // macOS / Linux: 실행 중인 파일에 덮어쓰면 Linux 는 ETXTBSY. 같은 디렉토리에 쓰고 rename 으로 바꿔치기한다.
94
- const newPath = execPath + ".new";
95
- writeFileSync(newPath, data);
96
- chmodSync(newPath, 0o755);
97
- try {
98
- renameSync(newPath, execPath);
99
- } catch (err) {
100
- if (err?.code !== "EACCES" && err?.code !== "EPERM") throw err;
101
- log.info(" sudo is required...");
102
- const r = spawnSync("sudo", ["mv", "-f", newPath, execPath], { stdio: "inherit" });
103
- if (r.status !== 0) throw new Error(`sudo mv failed: ${execPath}`);
104
- }
105
- log.ok(`Updated to v${version}.`);
106
- }
107
- }
108
-
109
- // ── self-update: npm global ───────────────────────────────────────────────────
110
-
111
- function updateNpmGlobal(tag) {
112
- log.step(`npm install -g ${KIT_PUBLISHED_NAME}@${tag}`);
113
- execSync(`npm install -g ${KIT_PUBLISHED_NAME}@${tag}`, { stdio: "inherit" });
114
- log.ok("Global CLI updated.");
115
- }
116
-
117
- // ── project structure migration ───────────────────────────────────────────────
118
-
119
- function detectOldStructure(root) {
120
- const issues = [];
121
-
122
- // src/ exists (old UI dir) or vite.config references src/content.tsx
123
- const hasSrcDir = existsSync(resolve(root, "src"));
124
- const hasUiDir = existsSync(resolve(root, "ui"));
125
- const viteRef = (() => {
126
- try {
127
- return readFileSync(resolve(root, "vite.config.ts"), "utf8").includes("src/content.tsx");
128
- } catch { return false; }
129
- })();
130
- if ((hasSrcDir || viteRef) && !hasUiDir) {
131
- issues.push({ kind: "src→ui", desc: "src/ → ui/ (+ vite.config.ts, tsconfig.json)" });
132
- }
133
-
134
- if (existsSync(resolve(root, "main.py")) && !existsSync(resolve(root, "py"))) {
135
- issues.push({ kind: "main→py", desc: "main.py → py/main.py" });
136
- }
137
- if (existsSync(resolve(root, "backend"))) {
138
- issues.push({ kind: "backend→py", desc: "backend/ → py/" });
139
- }
140
- return issues;
141
- }
142
-
143
- async function migrateStructure(root, issues) {
144
- log.warn("This plugin project's structure is outdated:");
145
- for (const issue of issues) log.info(" • " + issue.desc);
146
- log.warn("Files will be moved and modified. Back up or commit your work before proceeding.");
147
-
148
- const answer = await prompt("Migrate project structure? [y/N] ");
149
- if (answer.toLowerCase() !== "y") {
150
- log.info("Structure migration skipped.");
151
- return;
152
- }
153
-
154
- for (const issue of issues) {
155
- if (issue.kind === "src→ui") {
156
- if (existsSync(resolve(root, "src"))) {
157
- renameSync(resolve(root, "src"), resolve(root, "ui"));
158
- log.ok("src/ → ui/");
159
- }
160
-
161
- const vitePath = resolve(root, "vite.config.ts");
162
- if (existsSync(vitePath)) {
163
- writeFileSync(vitePath, readFileSync(vitePath, "utf8").replace(/["']src\/content\.tsx["']/g, '"ui/content.tsx"'));
164
- log.ok("vite.config.ts entry updated");
165
- }
166
- const tscPath = resolve(root, "tsconfig.json");
167
- if (existsSync(tscPath)) {
168
- writeFileSync(tscPath, readFileSync(tscPath, "utf8").replace(/"src"/g, '"ui"'));
169
- log.ok("tsconfig.json include updated");
170
- }
171
- }
172
-
173
- if (issue.kind === "main→py") {
174
- mkdirSync(resolve(root, "py"), { recursive: true });
175
- renameSync(resolve(root, "main.py"), resolve(root, "py", "main.py"));
176
- log.ok("main.py → py/main.py");
177
- }
178
-
179
- if (issue.kind === "backend→py") {
180
- renameSync(resolve(root, "backend"), resolve(root, "py"));
181
- log.ok("backend/ → py/");
182
- const pyReqs = resolve(root, "py", "requirements.txt");
183
- const rootReqs = resolve(root, "requirements.txt");
184
- if (existsSync(pyReqs) && !existsSync(rootReqs)) {
185
- renameSync(pyReqs, rootReqs);
186
- log.ok("py/requirements.txt → requirements.txt (project root)");
187
- }
188
- }
189
- }
190
-
191
- log.ok("Migration complete. Run `muscat-pk build` to verify.");
192
- }
193
-
194
- // ── plugin project local deps ─────────────────────────────────────────────────
195
-
196
- function updateLocalDeps(root, tag) {
197
- const pm = detectPm(root);
198
- const list = [`${KIT_PUBLISHED_NAME}@${tag}`, `${PLUGIN_UI_PACKAGE}@${tag}`];
199
- const cmd =
200
- pm === "npm" ? `npm install ${list.join(" ")}`
201
- : pm === "yarn" ? `yarn add ${list.join(" ")}`
202
- : `${pm} add ${list.join(" ")}`;
203
-
204
- log.step(`Updating plugin local deps (${pm}): ${list.join(", ")}`);
205
- try {
206
- execSync(cmd, { cwd: root, stdio: "inherit" });
207
- log.ok("Local deps updated.");
208
- } catch {
209
- log.warn("Local deps update failed (offline?). Run manually:\n " + cmd);
210
- }
211
- }
212
-
213
- // ── main ─────────────────────────────────────────────────────────────────────
214
-
215
- async function resolveTag(explicitTag) {
216
- if (explicitTag) return explicitTag;
217
-
218
- const current = kitVersion();
219
- const isDevVersion = current.includes("-dev.");
220
-
221
- if (!isDevVersion) return "latest";
222
-
223
- const answer = await prompt(
224
- `You are on a dev version (v${current}).\n` +
225
- ` [1] Stay on dev — update to latest dev build\n` +
226
- ` [2] Switch to stable — update to latest stable release\n` +
227
- `Choice [1/2, default: 1]: `
228
- );
229
- return answer.trim() === "2" ? "latest" : "dev";
230
- }
231
-
232
- export async function runUpdate(args = []) {
233
- const opts = parseArgs(args, { booleans: ["global"] });
234
- const explicitTag = opts._[0] || opts.version || opts.tag || "";
235
- const tag = await resolveTag(explicitTag);
236
-
237
- // 지금 도는 게 프로젝트의 로컬 설치면 그 프로젝트만 건드린다. 이 머신의 다른
238
- // 프로젝트들이 쓰는 글로벌 CLI까지 같이 올리려면 --global을 명시해야 한다.
239
- const runningLocal = isLocalKitInstall();
240
-
241
- // 1. Update the CLI itself
242
- if (!runningLocal || opts.global) {
243
- if (isBinaryMode) {
244
- log.step("Updating binary…");
245
- try {
246
- await updateBinary(tag);
247
- } catch (err) {
248
- log.warn(`Binary update failed: ${err.message}`);
249
- }
250
- } else {
251
- const isGlobal = (() => {
252
- try {
253
- const prefix = execSync("npm prefix -g", { encoding: "utf8" }).trim();
254
- return (
255
- // Unix: {prefix}/bin/muscat-pk
256
- existsSync(join(prefix, "bin", "muscat-pk")) ||
257
- // Windows npm: {prefix}/muscat-pk.cmd (no bin/ subdir)
258
- existsSync(join(prefix, "muscat-pk.cmd")) ||
259
- existsSync(join(prefix, "bin", "muscat-pk.cmd"))
260
- );
261
- } catch { return false; }
262
- })();
263
-
264
- if (isGlobal) {
265
- try {
266
- updateNpmGlobal(tag);
267
- } catch (err) {
268
- log.warn(`Global CLI update failed: ${err.message}`);
269
- }
270
- }
271
- }
272
- } else {
273
- log.info("Local install detected — updating this project only. Pass --global to also update the global CLI.");
274
- }
275
-
276
- // 2. Plugin project: structure migration + local deps
277
- let root;
278
- try {
279
- root = findPluginRoot();
280
- } catch {
281
- // Not inside a plugin project — skip
282
- return;
283
- }
284
-
285
- try {
286
- const structureIssues = detectOldStructure(root);
287
- if (structureIssues.length > 0) {
288
- await migrateStructure(root, structureIssues);
289
- }
290
- } catch (err) {
291
- log.warn(`Structure check failed: ${err.message}`);
292
- }
293
-
294
- updateLocalDeps(root, tag);
295
- }
1
+ // muscat-pk update — update the CLI itself AND the plugin project's JS deps.
2
+ //
3
+ // Three install modes are handled:
4
+ // binary : Bun self-contained binary downloaded from S3
5
+ // → re-download from S3, replace process.execPath
6
+ // npm -g : globally installed Node.js package
7
+ // → npm install -g @rainbow-robotics/muscat-plugin-kit@<tag>
8
+ // local : plugin project's node_modules (fallback)
9
+ // → <pm> add @rainbow-robotics/muscat-plugin-kit@<tag>
10
+ //
11
+ // After updating the CLI itself, also bumps the plugin project's local deps
12
+ // and migrates the project structure if it's outdated (src/ → ui/, root main.py → py/).
13
+ import { execSync, spawn, spawnSync } from "node:child_process";
14
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
15
+ import { join, resolve } from "node:path";
16
+ import { findPluginRoot, isLocalKitInstall, KIT_PUBLISHED_NAME, kitVersion, log, parseArgs, prompt } from "./util.mjs";
17
+
18
+ const PLUGIN_UI_PACKAGE = "@rainbow-robotics/plugin-ui";
19
+ const S3_BASE = "https://rainbow-deploy.s3.ap-northeast-2.amazonaws.com/muscat-plugin-kit";
20
+
21
+ const isBinaryMode = typeof Bun !== "undefined";
22
+
23
+ // ── helpers ───────────────────────────────────────────────────────────────────
24
+
25
+ function detectPm(root) {
26
+ try {
27
+ const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
28
+ if (pkg.packageManager) return pkg.packageManager.split("@")[0];
29
+ } catch { /* ignore */ }
30
+ if (existsSync(resolve(root, "pnpm-lock.yaml"))) return "pnpm";
31
+ if (existsSync(resolve(root, "yarn.lock"))) return "yarn";
32
+ if (existsSync(resolve(root, "bun.lockb"))) return "bun";
33
+ return "npm";
34
+ }
35
+
36
+ async function fetchText(url) {
37
+ const res = await fetch(url);
38
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
39
+ return (await res.text()).trim();
40
+ }
41
+
42
+ async function fetchBinary(url) {
43
+ const res = await fetch(url);
44
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
45
+ return Buffer.from(await res.arrayBuffer());
46
+ }
47
+
48
+ function s3BinaryName() {
49
+ const platform = process.platform;
50
+ const arch = process.arch;
51
+ const plat = platform === "win32" ? "win" : platform === "darwin" ? "darwin" : "linux";
52
+ const cpu = arch === "arm64" ? "arm64" : "x64";
53
+ const ext = platform === "win32" ? ".exe" : "";
54
+ return `muscat-pk-${plat}-${cpu}${ext}`;
55
+ }
56
+
57
+ async function resolveS3Version(tag) {
58
+ const pointer = tag === "dev" ? "dev-version.txt" : "latest-version.txt";
59
+ return fetchText(`${S3_BASE}/${pointer}`);
60
+ }
61
+
62
+ // ── self-update: binary ───────────────────────────────────────────────────────
63
+
64
+ async function updateBinary(tag) {
65
+ const version = await resolveS3Version(tag);
66
+ const current = kitVersion();
67
+ if (version === current) {
68
+ log.ok(`Already up to date (v${current})`);
69
+ return;
70
+ }
71
+
72
+ const binaryName = s3BinaryName();
73
+ const url = `${S3_BASE}/v${version}/${binaryName}`;
74
+ log.step(`Downloading binary: v${current} → v${version}`);
75
+
76
+ const data = await fetchBinary(url);
77
+ const execPath = process.execPath;
78
+
79
+ if (process.platform === "win32") {
80
+ // Windows: cannot overwrite a running .exe — write .new then schedule replacement
81
+ const newPath = execPath + ".new";
82
+ writeFileSync(newPath, data);
83
+
84
+ // PowerShell snippet that waits for this process to exit, then renames
85
+ const ps = `Start-Sleep -Milliseconds 500; Move-Item -Force '${newPath}' '${execPath}'`;
86
+ spawn("powershell", ["-NoProfile", "-Command", ps], {
87
+ detached: true,
88
+ stdio: "ignore",
89
+ }).unref();
90
+
91
+ log.ok(`v${version} will be applied on next run.`);
92
+ } else {
93
+ // macOS / Linux: 실행 중인 파일에 덮어쓰면 Linux 는 ETXTBSY. 같은 디렉토리에 쓰고 rename 으로 바꿔치기한다.
94
+ const newPath = execPath + ".new";
95
+ writeFileSync(newPath, data);
96
+ chmodSync(newPath, 0o755);
97
+ try {
98
+ renameSync(newPath, execPath);
99
+ } catch (err) {
100
+ if (err?.code !== "EACCES" && err?.code !== "EPERM") throw err;
101
+ log.info(" sudo is required...");
102
+ const r = spawnSync("sudo", ["mv", "-f", newPath, execPath], { stdio: "inherit" });
103
+ if (r.status !== 0) throw new Error(`sudo mv failed: ${execPath}`);
104
+ }
105
+ log.ok(`Updated to v${version}.`);
106
+ }
107
+ }
108
+
109
+ // ── self-update: npm global ───────────────────────────────────────────────────
110
+
111
+ function updateNpmGlobal(tag) {
112
+ log.step(`npm install -g ${KIT_PUBLISHED_NAME}@${tag}`);
113
+ execSync(`npm install -g ${KIT_PUBLISHED_NAME}@${tag}`, { stdio: "inherit" });
114
+ log.ok("Global CLI updated.");
115
+ }
116
+
117
+ // ── project structure migration ───────────────────────────────────────────────
118
+
119
+ function detectOldStructure(root) {
120
+ const issues = [];
121
+
122
+ // src/ exists (old UI dir) or vite.config references src/content.tsx
123
+ const hasSrcDir = existsSync(resolve(root, "src"));
124
+ const hasUiDir = existsSync(resolve(root, "ui"));
125
+ const viteRef = (() => {
126
+ try {
127
+ return readFileSync(resolve(root, "vite.config.ts"), "utf8").includes("src/content.tsx");
128
+ } catch { return false; }
129
+ })();
130
+ if ((hasSrcDir || viteRef) && !hasUiDir) {
131
+ issues.push({ kind: "src→ui", desc: "src/ → ui/ (+ vite.config.ts, tsconfig.json)" });
132
+ }
133
+
134
+ if (existsSync(resolve(root, "main.py")) && !existsSync(resolve(root, "py"))) {
135
+ issues.push({ kind: "main→py", desc: "main.py → py/main.py" });
136
+ }
137
+ if (existsSync(resolve(root, "backend"))) {
138
+ issues.push({ kind: "backend→py", desc: "backend/ → py/" });
139
+ }
140
+ return issues;
141
+ }
142
+
143
+ async function migrateStructure(root, issues) {
144
+ log.warn("This plugin project's structure is outdated:");
145
+ for (const issue of issues) log.info(" • " + issue.desc);
146
+ log.warn("Files will be moved and modified. Back up or commit your work before proceeding.");
147
+
148
+ const answer = await prompt("Migrate project structure? [y/N] ");
149
+ if (answer.toLowerCase() !== "y") {
150
+ log.info("Structure migration skipped.");
151
+ return;
152
+ }
153
+
154
+ for (const issue of issues) {
155
+ if (issue.kind === "src→ui") {
156
+ if (existsSync(resolve(root, "src"))) {
157
+ renameSync(resolve(root, "src"), resolve(root, "ui"));
158
+ log.ok("src/ → ui/");
159
+ }
160
+
161
+ const vitePath = resolve(root, "vite.config.ts");
162
+ if (existsSync(vitePath)) {
163
+ writeFileSync(vitePath, readFileSync(vitePath, "utf8").replace(/["']src\/content\.tsx["']/g, '"ui/content.tsx"'));
164
+ log.ok("vite.config.ts entry updated");
165
+ }
166
+ const tscPath = resolve(root, "tsconfig.json");
167
+ if (existsSync(tscPath)) {
168
+ writeFileSync(tscPath, readFileSync(tscPath, "utf8").replace(/"src"/g, '"ui"'));
169
+ log.ok("tsconfig.json include updated");
170
+ }
171
+ }
172
+
173
+ if (issue.kind === "main→py") {
174
+ mkdirSync(resolve(root, "py"), { recursive: true });
175
+ renameSync(resolve(root, "main.py"), resolve(root, "py", "main.py"));
176
+ log.ok("main.py → py/main.py");
177
+ }
178
+
179
+ if (issue.kind === "backend→py") {
180
+ renameSync(resolve(root, "backend"), resolve(root, "py"));
181
+ log.ok("backend/ → py/");
182
+ const pyReqs = resolve(root, "py", "requirements.txt");
183
+ const rootReqs = resolve(root, "requirements.txt");
184
+ if (existsSync(pyReqs) && !existsSync(rootReqs)) {
185
+ renameSync(pyReqs, rootReqs);
186
+ log.ok("py/requirements.txt → requirements.txt (project root)");
187
+ }
188
+ }
189
+ }
190
+
191
+ log.ok("Migration complete. Run `muscat-pk build` to verify.");
192
+ }
193
+
194
+ // ── plugin project local deps ─────────────────────────────────────────────────
195
+
196
+ function updateLocalDeps(root, tag) {
197
+ const pm = detectPm(root);
198
+ const list = [`${KIT_PUBLISHED_NAME}@${tag}`, `${PLUGIN_UI_PACKAGE}@${tag}`];
199
+ const cmd =
200
+ pm === "npm" ? `npm install ${list.join(" ")}`
201
+ : pm === "yarn" ? `yarn add ${list.join(" ")}`
202
+ : `${pm} add ${list.join(" ")}`;
203
+
204
+ log.step(`Updating plugin local deps (${pm}): ${list.join(", ")}`);
205
+ try {
206
+ execSync(cmd, { cwd: root, stdio: "inherit" });
207
+ log.ok("Local deps updated.");
208
+ } catch {
209
+ log.warn("Local deps update failed (offline?). Run manually:\n " + cmd);
210
+ }
211
+ }
212
+
213
+ // ── main ─────────────────────────────────────────────────────────────────────
214
+
215
+ async function resolveTag(explicitTag) {
216
+ if (explicitTag) return explicitTag;
217
+
218
+ const current = kitVersion();
219
+ const isDevVersion = current.includes("-dev.");
220
+
221
+ if (!isDevVersion) return "latest";
222
+
223
+ const answer = await prompt(
224
+ `You are on a dev version (v${current}).\n` +
225
+ ` [1] Stay on dev — update to latest dev build\n` +
226
+ ` [2] Switch to stable — update to latest stable release\n` +
227
+ `Choice [1/2, default: 1]: `
228
+ );
229
+ return answer.trim() === "2" ? "latest" : "dev";
230
+ }
231
+
232
+ export async function runUpdate(args = []) {
233
+ const opts = parseArgs(args, { booleans: ["global"] });
234
+ const explicitTag = opts._[0] || opts.version || opts.tag || "";
235
+ const tag = await resolveTag(explicitTag);
236
+
237
+ // 지금 도는 게 프로젝트의 로컬 설치면 그 프로젝트만 건드린다. 이 머신의 다른
238
+ // 프로젝트들이 쓰는 글로벌 CLI까지 같이 올리려면 --global을 명시해야 한다.
239
+ const runningLocal = isLocalKitInstall();
240
+
241
+ // 1. Update the CLI itself
242
+ if (!runningLocal || opts.global) {
243
+ if (isBinaryMode) {
244
+ log.step("Updating binary…");
245
+ try {
246
+ await updateBinary(tag);
247
+ } catch (err) {
248
+ log.warn(`Binary update failed: ${err.message}`);
249
+ }
250
+ } else {
251
+ const isGlobal = (() => {
252
+ try {
253
+ const prefix = execSync("npm prefix -g", { encoding: "utf8" }).trim();
254
+ return (
255
+ // Unix: {prefix}/bin/muscat-pk
256
+ existsSync(join(prefix, "bin", "muscat-pk")) ||
257
+ // Windows npm: {prefix}/muscat-pk.cmd (no bin/ subdir)
258
+ existsSync(join(prefix, "muscat-pk.cmd")) ||
259
+ existsSync(join(prefix, "bin", "muscat-pk.cmd"))
260
+ );
261
+ } catch { return false; }
262
+ })();
263
+
264
+ if (isGlobal) {
265
+ try {
266
+ updateNpmGlobal(tag);
267
+ } catch (err) {
268
+ log.warn(`Global CLI update failed: ${err.message}`);
269
+ }
270
+ }
271
+ }
272
+ } else {
273
+ log.info("Local install detected — updating this project only. Pass --global to also update the global CLI.");
274
+ }
275
+
276
+ // 2. Plugin project: structure migration + local deps
277
+ let root;
278
+ try {
279
+ root = findPluginRoot();
280
+ } catch {
281
+ // Not inside a plugin project — skip
282
+ return;
283
+ }
284
+
285
+ try {
286
+ const structureIssues = detectOldStructure(root);
287
+ if (structureIssues.length > 0) {
288
+ await migrateStructure(root, structureIssues);
289
+ }
290
+ } catch (err) {
291
+ log.warn(`Structure check failed: ${err.message}`);
292
+ }
293
+
294
+ updateLocalDeps(root, tag);
295
+ }