specrails-desktop 2.0.0

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 (455) hide show
  1. package/.claude/commands/specrails/batch-implement.md +287 -0
  2. package/.claude/commands/specrails/compat-check.md +271 -0
  3. package/.claude/commands/specrails/doctor.md +62 -0
  4. package/.claude/commands/specrails/enrich.md +1635 -0
  5. package/.claude/commands/specrails/explore-spec.md +173 -0
  6. package/.claude/commands/specrails/health-check.md +527 -0
  7. package/.claude/commands/specrails/implement.md +1457 -0
  8. package/.claude/commands/specrails/memory-inspect.md +259 -0
  9. package/.claude/commands/specrails/opsx-diff.md +419 -0
  10. package/.claude/commands/specrails/propose-spec.md +102 -0
  11. package/.claude/commands/specrails/reconfig.md +89 -0
  12. package/.claude/commands/specrails/refactor-recommender.md +212 -0
  13. package/.claude/commands/specrails/retry.md +363 -0
  14. package/.claude/commands/specrails/telemetry.md +552 -0
  15. package/.claude/commands/specrails/why.md +96 -0
  16. package/LICENSE +21 -0
  17. package/README.md +290 -0
  18. package/cli/dist/specrails-desktop.js +1098 -0
  19. package/client/dist/assets/ActivityFeedPage-Gy4x8dBt.js +1 -0
  20. package/client/dist/assets/AgentsPage-CPgu--Fb.js +86 -0
  21. package/client/dist/assets/AnalyticsPage-B5sJEee2.js +1 -0
  22. package/client/dist/assets/BarChart-7IMQ8HY1.js +33 -0
  23. package/client/dist/assets/CodePage-CBdFvbwe.js +2 -0
  24. package/client/dist/assets/DesktopAnalyticsPage-w0rdTq4w.js +1 -0
  25. package/client/dist/assets/DocsDialog-BZUYM7wm.js +11 -0
  26. package/client/dist/assets/DocsPage-9QglWl46.js +11 -0
  27. package/client/dist/assets/ExportDropdown-BLZFXtNi.js +1 -0
  28. package/client/dist/assets/IntegrationsPage-BxBE4y99.js +3 -0
  29. package/client/dist/assets/JobDetailPage-DydWx_5S.js +16 -0
  30. package/client/dist/assets/JobsPage-20ibw0IO.js +1 -0
  31. package/client/dist/assets/abap-Bw6f2wDG.js +1 -0
  32. package/client/dist/assets/activity-BEIp_Y1A.js +1 -0
  33. package/client/dist/assets/activity-BdrPln96.js +1 -0
  34. package/client/dist/assets/activity-CpkRS8Sx.js +1 -0
  35. package/client/dist/assets/activity-DKCpESPt.js +1 -0
  36. package/client/dist/assets/activity-DOUVEjJi.js +1 -0
  37. package/client/dist/assets/activity-DRwkql_y.js +1 -0
  38. package/client/dist/assets/activity-DcDQ7tjw.js +1 -0
  39. package/client/dist/assets/activity-Dv6H7wEr.js +1 -0
  40. package/client/dist/assets/addon-image-3WCl5Vhd.js +1 -0
  41. package/client/dist/assets/addon-ligatures-C5OdliKs.js +2 -0
  42. package/client/dist/assets/addon-webgl-BbX6pSjl.js +44 -0
  43. package/client/dist/assets/addspec-B5yl4Loj.js +1 -0
  44. package/client/dist/assets/addspec-BEeF5-zc.js +1 -0
  45. package/client/dist/assets/addspec-D33ocMxf.js +1 -0
  46. package/client/dist/assets/addspec-DFswZ0jK.js +1 -0
  47. package/client/dist/assets/addspec-DRE-jZv7.js +1 -0
  48. package/client/dist/assets/addspec-DVZ15Jp8.js +1 -0
  49. package/client/dist/assets/addspec-Fkv91Opc.js +1 -0
  50. package/client/dist/assets/addspec-GWm4ffKl.js +1 -0
  51. package/client/dist/assets/agents-1nCDWRmP.js +1 -0
  52. package/client/dist/assets/agents-Bm9rPqnt.js +1 -0
  53. package/client/dist/assets/agents-CMxtJMLD.js +1 -0
  54. package/client/dist/assets/agents-DK-Dlc0i.js +1 -0
  55. package/client/dist/assets/agents-Q6Ldfpxx.js +1 -0
  56. package/client/dist/assets/agents-TeOSy-ax.js +1 -0
  57. package/client/dist/assets/agents-iTqjRajS.js +1 -0
  58. package/client/dist/assets/agents-s87sMGzL.js +1 -0
  59. package/client/dist/assets/agentstudio-B6Wb59E7.js +1 -0
  60. package/client/dist/assets/agentstudio-BADhZ41e.js +1 -0
  61. package/client/dist/assets/agentstudio-BSnWLR63.js +1 -0
  62. package/client/dist/assets/agentstudio-BdidyBzZ.js +1 -0
  63. package/client/dist/assets/agentstudio-CxlUllqI.js +1 -0
  64. package/client/dist/assets/agentstudio-D3I62TLJ.js +1 -0
  65. package/client/dist/assets/agentstudio-DuH9TogZ.js +1 -0
  66. package/client/dist/assets/agentstudio-Kw88_dUF.js +1 -0
  67. package/client/dist/assets/aiedit-BWxHGsYA.js +1 -0
  68. package/client/dist/assets/aiedit-D2ji6Qy0.js +1 -0
  69. package/client/dist/assets/aiedit-DAhZTvtk.js +1 -0
  70. package/client/dist/assets/aiedit-DJMny-D5.js +1 -0
  71. package/client/dist/assets/aiedit-DOcxERkU.js +1 -0
  72. package/client/dist/assets/aiedit-DvrcbwGv.js +1 -0
  73. package/client/dist/assets/aiedit-TTwzL1TS.js +1 -0
  74. package/client/dist/assets/aiedit-WBSjT_C1.js +1 -0
  75. package/client/dist/assets/analytics-BIdr0YfL.js +1 -0
  76. package/client/dist/assets/analytics-C6EzgtdE.js +1 -0
  77. package/client/dist/assets/analytics-C9Zc-rkM.js +1 -0
  78. package/client/dist/assets/analytics-CVx3YOc0.js +1 -0
  79. package/client/dist/assets/analytics-CYj0tfj7.js +1 -0
  80. package/client/dist/assets/analytics-CnY4kNG3.js +1 -0
  81. package/client/dist/assets/analytics-CrPCZRJ-.js +1 -0
  82. package/client/dist/assets/analytics-DMCto-TF.js +1 -0
  83. package/client/dist/assets/apex-Cw8_REBo.js +1 -0
  84. package/client/dist/assets/atom-one-dark-B-oHczHB.css +1 -0
  85. package/client/dist/assets/attachments-BIsSSnHJ.js +1 -0
  86. package/client/dist/assets/attachments-BW4L3l2L.js +1 -0
  87. package/client/dist/assets/attachments-Bcf6BG6V.js +1 -0
  88. package/client/dist/assets/attachments-Bke8sCU4.js +1 -0
  89. package/client/dist/assets/attachments-COcrGRFz.js +1 -0
  90. package/client/dist/assets/attachments-DYHGA2Dj.js +1 -0
  91. package/client/dist/assets/attachments-Dd92KpUH.js +1 -0
  92. package/client/dist/assets/attachments-DzdU6DV6.js +1 -0
  93. package/client/dist/assets/azcli-Cz6HAoOw.js +1 -0
  94. package/client/dist/assets/bat-CcJ-xyqL.js +1 -0
  95. package/client/dist/assets/bicep-z1WDCKYz.js +2 -0
  96. package/client/dist/assets/browser-5ErDlJoR.js +1 -0
  97. package/client/dist/assets/browser-Bc-YdlVg.js +1 -0
  98. package/client/dist/assets/browser-BlYF4OOq.js +1 -0
  99. package/client/dist/assets/browser-CT-ReZGt.js +1 -0
  100. package/client/dist/assets/browser-DGITz3fC.js +1 -0
  101. package/client/dist/assets/browser-JsAIGCEW.js +1 -0
  102. package/client/dist/assets/browser-M5-rbPlw.js +1 -0
  103. package/client/dist/assets/browser-Qya9cARy.js +1 -0
  104. package/client/dist/assets/cameligo-BRewOpfa.js +1 -0
  105. package/client/dist/assets/chat-BEGuC03z.js +1 -0
  106. package/client/dist/assets/chat-BEW60P_u.js +1 -0
  107. package/client/dist/assets/chat-BQNMD0PL.js +1 -0
  108. package/client/dist/assets/chat-BsbNGPW9.js +1 -0
  109. package/client/dist/assets/chat-CboQguCi.js +1 -0
  110. package/client/dist/assets/chat-DRCa9pOt.js +1 -0
  111. package/client/dist/assets/chat-DwUm6W9z.js +1 -0
  112. package/client/dist/assets/chat-yoXwguQu.js +1 -0
  113. package/client/dist/assets/chunk-CilyBKbf.js +1 -0
  114. package/client/dist/assets/clojure-DBjRWN6g.js +1 -0
  115. package/client/dist/assets/clsx-DnqN-uhr.js +1 -0
  116. package/client/dist/assets/code-AL1rVIMb.js +1 -0
  117. package/client/dist/assets/code-C0BKpkht.js +1 -0
  118. package/client/dist/assets/code-C0FTS3ew.js +1 -0
  119. package/client/dist/assets/code-CPcHxzxw.js +1 -0
  120. package/client/dist/assets/code-D3ryDniw.js +1 -0
  121. package/client/dist/assets/code-D3zVVQTj.js +1 -0
  122. package/client/dist/assets/code-PCmfS3dn.js +1 -0
  123. package/client/dist/assets/code-exI0G5Wd.js +1 -0
  124. package/client/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  125. package/client/dist/assets/coffee-Cfk_XHGR.js +1 -0
  126. package/client/dist/assets/commands-B772IyDa.js +1 -0
  127. package/client/dist/assets/commands-BDDp6xFG.js +1 -0
  128. package/client/dist/assets/commands-CJxCry-o.js +1 -0
  129. package/client/dist/assets/commands-CfgY-_of.js +1 -0
  130. package/client/dist/assets/commands-DLrvnPNg.js +1 -0
  131. package/client/dist/assets/commands-IXMOKBYt.js +1 -0
  132. package/client/dist/assets/commands-UD1NzmwX.js +1 -0
  133. package/client/dist/assets/commands-sqrqsxyE.js +1 -0
  134. package/client/dist/assets/common-DCr6VzJ7.js +1 -0
  135. package/client/dist/assets/common-Dard9UNH.js +1 -0
  136. package/client/dist/assets/common-DeDELLZJ.js +1 -0
  137. package/client/dist/assets/common-DltqHaAe.js +1 -0
  138. package/client/dist/assets/common-Dmm1GhdD.js +1 -0
  139. package/client/dist/assets/common-DnjcgkPH.js +1 -0
  140. package/client/dist/assets/common-GbpxfPG8.js +1 -0
  141. package/client/dist/assets/common-wA36jmj1.js +1 -0
  142. package/client/dist/assets/cpp-BVob6BaP.js +1 -0
  143. package/client/dist/assets/csharp-C4fbRuOu.js +1 -0
  144. package/client/dist/assets/csp-DthFP_vT.js +1 -0
  145. package/client/dist/assets/css-CGMH0hcW.js +3 -0
  146. package/client/dist/assets/css.worker-Wv5dxAWO.js +89 -0
  147. package/client/dist/assets/cssMode-Cc6ozl-J.js +1 -0
  148. package/client/dist/assets/cypher-Pnf68BRV.js +1 -0
  149. package/client/dist/assets/dart-PMMOtxZX.js +1 -0
  150. package/client/dist/assets/dashboard-B4ixDVk8.js +1 -0
  151. package/client/dist/assets/dashboard-BZBADHSj.js +1 -0
  152. package/client/dist/assets/dashboard-C1MfeUHs.js +1 -0
  153. package/client/dist/assets/dashboard-C7SK6xu5.js +1 -0
  154. package/client/dist/assets/dashboard-CB6Le1yN.js +1 -0
  155. package/client/dist/assets/dashboard-CoTpMOBM.js +1 -0
  156. package/client/dist/assets/dashboard-Duo4DDCW.js +1 -0
  157. package/client/dist/assets/dashboard-I19DXBxw.js +1 -0
  158. package/client/dist/assets/dist-js-BY-Fv_fg.js +1 -0
  159. package/client/dist/assets/dist-js-Bakc4uxT.js +1 -0
  160. package/client/dist/assets/dockerfile-di1nsJCc.js +1 -0
  161. package/client/dist/assets/ecl-D_WVcB5M.js +1 -0
  162. package/client/dist/assets/editor-Br_kD0ds.css +1 -0
  163. package/client/dist/assets/editor.api2-XLGzZfbc.js +872 -0
  164. package/client/dist/assets/editor.main-CfXxHimg.js +6 -0
  165. package/client/dist/assets/editor.worker-Bd9IXS8d.js +26 -0
  166. package/client/dist/assets/elixir-OAdJEMOn.js +1 -0
  167. package/client/dist/assets/explore-4mFpnrKU.js +1 -0
  168. package/client/dist/assets/explore-A8Ltoblq.js +1 -0
  169. package/client/dist/assets/explore-B9A3iN2W.js +1 -0
  170. package/client/dist/assets/explore-BV5Xxlsn.js +1 -0
  171. package/client/dist/assets/explore-BrBJvfjP.js +1 -0
  172. package/client/dist/assets/explore-C3FSE42C.js +1 -0
  173. package/client/dist/assets/explore-D2EFgt8J.js +1 -0
  174. package/client/dist/assets/explore-hFc3HFcp.js +1 -0
  175. package/client/dist/assets/flow9-D3QEZjgn.js +1 -0
  176. package/client/dist/assets/format-command-CwGuwzGA.js +1 -0
  177. package/client/dist/assets/freemarker2-DP7J1gG3.js +3 -0
  178. package/client/dist/assets/fsharp-BF0k_8N8.js +1 -0
  179. package/client/dist/assets/go-BAQO5Jsz.js +1 -0
  180. package/client/dist/assets/graphql-hdFVFkiV.js +1 -0
  181. package/client/dist/assets/handlebars-BjRlucw6.js +1 -0
  182. package/client/dist/assets/hcl-DWnl1o-X.js +1 -0
  183. package/client/dist/assets/html-OumBQJ-U.js +1 -0
  184. package/client/dist/assets/html.worker-CQP8QQsS.js +502 -0
  185. package/client/dist/assets/htmlMode-CStc3zXM.js +1 -0
  186. package/client/dist/assets/index-CimDRRi7.css +2 -0
  187. package/client/dist/assets/index-XGZaKl_u.js +142 -0
  188. package/client/dist/assets/ini-CB-6OVu3.js +1 -0
  189. package/client/dist/assets/integrations-C3p12Ms6.js +1 -0
  190. package/client/dist/assets/integrations-Cr6hH7XR.js +1 -0
  191. package/client/dist/assets/integrations-Cublz3m6.js +1 -0
  192. package/client/dist/assets/integrations-D28q1kF6.js +1 -0
  193. package/client/dist/assets/integrations-DRdbki5W.js +1 -0
  194. package/client/dist/assets/integrations-DaC4SzzL.js +1 -0
  195. package/client/dist/assets/integrations-DmQYCUvN.js +1 -0
  196. package/client/dist/assets/integrations-HIlUxXVs.js +1 -0
  197. package/client/dist/assets/java-d1CmfiHX.js +1 -0
  198. package/client/dist/assets/javascript-CMk--e7g.js +1 -0
  199. package/client/dist/assets/jobs-BE1siB0M.js +1 -0
  200. package/client/dist/assets/jobs-BHcQ_Faf.js +1 -0
  201. package/client/dist/assets/jobs-CFfc2dNX.js +1 -0
  202. package/client/dist/assets/jobs-CSi5n8X_.js +1 -0
  203. package/client/dist/assets/jobs-Dc3X86PY.js +1 -0
  204. package/client/dist/assets/jobs-De5tASex.js +1 -0
  205. package/client/dist/assets/jobs-DsoXEdo7.js +1 -0
  206. package/client/dist/assets/jobs-Wl-ApPMb.js +1 -0
  207. package/client/dist/assets/json.worker-DzV-CpCQ.js +58 -0
  208. package/client/dist/assets/jsonMode-C2h3ZcjZ.js +7 -0
  209. package/client/dist/assets/julia-Bgv08lKa.js +1 -0
  210. package/client/dist/assets/kotlin-u98kaVTf.js +1 -0
  211. package/client/dist/assets/less-CjYwpgg5.js +2 -0
  212. package/client/dist/assets/lexon-YTjaAFBB.js +1 -0
  213. package/client/dist/assets/lib-CPxTMOAq.js +7 -0
  214. package/client/dist/assets/liquid-mI3KJrBE.js +1 -0
  215. package/client/dist/assets/lspLanguageFeatures-DU09ggWi.js +4 -0
  216. package/client/dist/assets/lua-BzmkWv27.js +1 -0
  217. package/client/dist/assets/m3-CFwk9fw0.js +1 -0
  218. package/client/dist/assets/markdown-CR5iMpSZ.js +1 -0
  219. package/client/dist/assets/mdx-C41VDTR_.js +1 -0
  220. package/client/dist/assets/mips-CcEalc17.js +1 -0
  221. package/client/dist/assets/monaco.contribution-CPObAXMC.js +2 -0
  222. package/client/dist/assets/msdax-BQbkawnr.js +1 -0
  223. package/client/dist/assets/mysql-GTlaaW_P.js +1 -0
  224. package/client/dist/assets/nav-0fwkrgHt.js +1 -0
  225. package/client/dist/assets/nav-BEL3MTwK.js +1 -0
  226. package/client/dist/assets/nav-B_G-TJDW.js +1 -0
  227. package/client/dist/assets/nav-C2YXcbZS.js +1 -0
  228. package/client/dist/assets/nav-ClzOE4mA.js +1 -0
  229. package/client/dist/assets/nav-CtYwmMgu.js +1 -0
  230. package/client/dist/assets/nav-D2bOGSEg.js +1 -0
  231. package/client/dist/assets/nav-iH1V5j6o.js +1 -0
  232. package/client/dist/assets/objective-c-Byu1T5if.js +1 -0
  233. package/client/dist/assets/pascal-BrfzBfRm.js +1 -0
  234. package/client/dist/assets/pascaligo-BXXKFUeo.js +1 -0
  235. package/client/dist/assets/perl-B3OikKq-.js +1 -0
  236. package/client/dist/assets/pgsql-CTsa0Acc.js +1 -0
  237. package/client/dist/assets/php-DiQh3FUW.js +1 -0
  238. package/client/dist/assets/pla-92uH8Fzm.js +1 -0
  239. package/client/dist/assets/postiats-BbeWkKUr.js +1 -0
  240. package/client/dist/assets/powerquery-DgDMzpsm.js +1 -0
  241. package/client/dist/assets/powershell-BfdUUzaG.js +1 -0
  242. package/client/dist/assets/preload-helper-DSXbuxSR.js +1 -0
  243. package/client/dist/assets/protobuf-BojW2ftW.js +2 -0
  244. package/client/dist/assets/pug-BxqTg3IU.js +1 -0
  245. package/client/dist/assets/python-Y27rKQtk.js +1 -0
  246. package/client/dist/assets/qsharp-BX_A-MW9.js +1 -0
  247. package/client/dist/assets/r-D9BMnxvJ.js +1 -0
  248. package/client/dist/assets/razor-Cd5-q9Bp.js +1 -0
  249. package/client/dist/assets/redis-5cJqEQJJ.js +1 -0
  250. package/client/dist/assets/redshift-d8BBqiwb.js +1 -0
  251. package/client/dist/assets/restructuredtext-C8a6yIcZ.js +1 -0
  252. package/client/dist/assets/ruby-egeh-6KX.js +1 -0
  253. package/client/dist/assets/rust-a3r9IInB.js +1 -0
  254. package/client/dist/assets/sb-y8iRIDei.js +1 -0
  255. package/client/dist/assets/scala-BPDK2AmK.js +1 -0
  256. package/client/dist/assets/scheme-BIWUEoOs.js +1 -0
  257. package/client/dist/assets/scss-CA-PSzwg.js +3 -0
  258. package/client/dist/assets/settings-55oDcbSh.js +1 -0
  259. package/client/dist/assets/settings-Bd4Tq1RB.js +1 -0
  260. package/client/dist/assets/settings-CCSM-Fhn.js +1 -0
  261. package/client/dist/assets/settings-D3e_bDoW.js +1 -0
  262. package/client/dist/assets/settings-DKbTkbn7.js +1 -0
  263. package/client/dist/assets/settings-Dxpo6_w7.js +1 -0
  264. package/client/dist/assets/settings-bt84e3Aa.js +1 -0
  265. package/client/dist/assets/settings-nu68QukM.js +1 -0
  266. package/client/dist/assets/setup-BMqwfbW9.js +1 -0
  267. package/client/dist/assets/setup-Bb5LcG28.js +1 -0
  268. package/client/dist/assets/setup-BeEx2_da.js +1 -0
  269. package/client/dist/assets/setup-CCCrB53Q.js +1 -0
  270. package/client/dist/assets/setup-CJA0ATmd.js +1 -0
  271. package/client/dist/assets/setup-CeiDbZcb.js +1 -0
  272. package/client/dist/assets/setup-Cus7TApA.js +1 -0
  273. package/client/dist/assets/setup-D9qOs2Xo.js +1 -0
  274. package/client/dist/assets/shell--LiT1Bja.js +1 -0
  275. package/client/dist/assets/solidity-DdqZccZg.js +1 -0
  276. package/client/dist/assets/sophia-S6-YxNG_.js +1 -0
  277. package/client/dist/assets/sparql-BSf5kMp2.js +1 -0
  278. package/client/dist/assets/specs-BFfu3u-a.js +1 -0
  279. package/client/dist/assets/specs-B__C8-8a.js +1 -0
  280. package/client/dist/assets/specs-CZ1PsXsC.js +1 -0
  281. package/client/dist/assets/specs-D2FzlLn9.js +1 -0
  282. package/client/dist/assets/specs-DaUTrNF9.js +1 -0
  283. package/client/dist/assets/specs-Dyc5hYeE.js +1 -0
  284. package/client/dist/assets/specs-cKEh2LXt.js +1 -0
  285. package/client/dist/assets/specs-k0PyLDVt.js +1 -0
  286. package/client/dist/assets/sql-D7KgjR8G.js +1 -0
  287. package/client/dist/assets/st-BnoDa-Ml.js +1 -0
  288. package/client/dist/assets/swift-DEUHTkUX.js +1 -0
  289. package/client/dist/assets/systemverilog-Tqb_KPnW.js +1 -0
  290. package/client/dist/assets/tcl-BmBFS2qq.js +1 -0
  291. package/client/dist/assets/terminal-80yDMgMF.js +1 -0
  292. package/client/dist/assets/terminal-Bje4ziIa.js +1 -0
  293. package/client/dist/assets/terminal-C2WYcFHF.js +1 -0
  294. package/client/dist/assets/terminal-CSONJOex.js +1 -0
  295. package/client/dist/assets/terminal-DEqzGtcr.js +1 -0
  296. package/client/dist/assets/terminal-DeWzh6ys.js +1 -0
  297. package/client/dist/assets/terminal-YOlsJCQj.js +1 -0
  298. package/client/dist/assets/terminal-lkZYR4wJ.js +1 -0
  299. package/client/dist/assets/tickets-CB7N30gm.js +1 -0
  300. package/client/dist/assets/tickets-CF2PYelu.js +1 -0
  301. package/client/dist/assets/tickets-DNOANUXr.js +1 -0
  302. package/client/dist/assets/tickets-DU1aqsbr.js +1 -0
  303. package/client/dist/assets/tickets-DYvafSaY.js +1 -0
  304. package/client/dist/assets/tickets-DlpC_iTg.js +1 -0
  305. package/client/dist/assets/tickets-DucYgtdl.js +1 -0
  306. package/client/dist/assets/tickets-clefmXLv.js +1 -0
  307. package/client/dist/assets/ts.worker-METxwbDZ.js +67719 -0
  308. package/client/dist/assets/tsMode-B0y_xEci.js +11 -0
  309. package/client/dist/assets/twig-BQV8igWC.js +1 -0
  310. package/client/dist/assets/typescript-BzK0OgwW.js +1 -0
  311. package/client/dist/assets/typespec-DlFroUGY.js +1 -0
  312. package/client/dist/assets/useProjectCache-DSaiGFjV.js +1 -0
  313. package/client/dist/assets/vb-BlrJpIMX.js +1 -0
  314. package/client/dist/assets/wgsl-BWgIc6FZ.js +298 -0
  315. package/client/dist/assets/workers-rt--R2Qy.js +1 -0
  316. package/client/dist/assets/xml-eX9QXAmI.js +1 -0
  317. package/client/dist/assets/yaml-fcsNkpOt.js +1 -0
  318. package/client/dist/index.html +246 -0
  319. package/docs/README.md +54 -0
  320. package/docs/cli.md +198 -0
  321. package/docs/codex.md +210 -0
  322. package/docs/creating-specs.md +197 -0
  323. package/docs/customizing.md +197 -0
  324. package/docs/getting-started.md +140 -0
  325. package/docs/internals/README.md +25 -0
  326. package/docs/internals/adding-a-provider.md +238 -0
  327. package/docs/internals/api-reference.md +634 -0
  328. package/docs/internals/architecture.md +332 -0
  329. package/docs/internals/configuration.md +172 -0
  330. package/docs/internals/openspec-workflow.md +282 -0
  331. package/docs/internals/operations-runbook.md +198 -0
  332. package/docs/internals/profiles.md +152 -0
  333. package/docs/platforms/macos.md +130 -0
  334. package/docs/platforms/windows.md +81 -0
  335. package/docs/running-pipelines.md +240 -0
  336. package/docs/terminal.md +138 -0
  337. package/docs/tracking-cost.md +155 -0
  338. package/package.json +82 -0
  339. package/server/dist/agent-generator.js +232 -0
  340. package/server/dist/agent-refine-db.js +124 -0
  341. package/server/dist/agent-refine-manager.js +526 -0
  342. package/server/dist/ai-invocations.js +111 -0
  343. package/server/dist/attachment-manager.js +299 -0
  344. package/server/dist/auth.js +207 -0
  345. package/server/dist/binary-probe.js +35 -0
  346. package/server/dist/browser-capture-manager.js +576 -0
  347. package/server/dist/browser-capture-types.js +28 -0
  348. package/server/dist/browser-network.js +149 -0
  349. package/server/dist/browser-playwright.js +888 -0
  350. package/server/dist/build-dirs.js +44 -0
  351. package/server/dist/changes-reader.js +120 -0
  352. package/server/dist/chat-manager.js +1060 -0
  353. package/server/dist/chromium-resolver.js +311 -0
  354. package/server/dist/code-explorer-router.js +788 -0
  355. package/server/dist/codex-otel-bridge.js +235 -0
  356. package/server/dist/command-resolver.js +102 -0
  357. package/server/dist/config.js +306 -0
  358. package/server/dist/context-budget.js +113 -0
  359. package/server/dist/context-scope.js +279 -0
  360. package/server/dist/contract-refine-runner.js +521 -0
  361. package/server/dist/core-compat.js +207 -0
  362. package/server/dist/core-package.js +14 -0
  363. package/server/dist/db.js +1034 -0
  364. package/server/dist/desktop-analytics.js +156 -0
  365. package/server/dist/desktop-db.js +456 -0
  366. package/server/dist/desktop-router.js +735 -0
  367. package/server/dist/docs-router.js +207 -0
  368. package/server/dist/explore-contract-refine.js +421 -0
  369. package/server/dist/explore-cwd-manager.js +242 -0
  370. package/server/dist/explore-draft-title.js +47 -0
  371. package/server/dist/explore-smash.js +450 -0
  372. package/server/dist/feature-flags.js +17 -0
  373. package/server/dist/file-provenance.js +382 -0
  374. package/server/dist/file-summary-generator.js +221 -0
  375. package/server/dist/file-summary-manager.js +689 -0
  376. package/server/dist/hooks.js +102 -0
  377. package/server/dist/ids.js +7 -0
  378. package/server/dist/index.js +586 -0
  379. package/server/dist/metrics.js +136 -0
  380. package/server/dist/mobile/index.js +16 -0
  381. package/server/dist/mobile/mobile-admin-router.js +84 -0
  382. package/server/dist/mobile/mobile-auth.js +67 -0
  383. package/server/dist/mobile/mobile-devices.js +80 -0
  384. package/server/dist/mobile/mobile-event-bus.js +39 -0
  385. package/server/dist/mobile/mobile-gateway.js +285 -0
  386. package/server/dist/mobile/mobile-mdns.js +81 -0
  387. package/server/dist/mobile/mobile-pairing.js +179 -0
  388. package/server/dist/mobile/mobile-redact.js +53 -0
  389. package/server/dist/mobile/mobile-router.js +411 -0
  390. package/server/dist/mobile/mobile-tls.js +86 -0
  391. package/server/dist/mobile/mobile-types.js +9 -0
  392. package/server/dist/mobile/mobile-ws.js +275 -0
  393. package/server/dist/path-resolver.js +298 -0
  394. package/server/dist/plugin-manager.js +617 -0
  395. package/server/dist/plugins/claude-approval.js +179 -0
  396. package/server/dist/plugins/claude-md-mutation.js +146 -0
  397. package/server/dist/plugins/codex-mcp.js +108 -0
  398. package/server/dist/plugins/contributors.js +72 -0
  399. package/server/dist/plugins/drift.js +58 -0
  400. package/server/dist/plugins/index.js +14 -0
  401. package/server/dist/plugins/json-mutation.js +120 -0
  402. package/server/dist/plugins/manager.js +32 -0
  403. package/server/dist/plugins/ownership.js +86 -0
  404. package/server/dist/plugins/paths.js +37 -0
  405. package/server/dist/plugins/prereq-installer.js +104 -0
  406. package/server/dist/plugins/rail-integration.js +79 -0
  407. package/server/dist/plugins/serena/index.js +13 -0
  408. package/server/dist/plugins/serena/install.js +91 -0
  409. package/server/dist/plugins/serena/instructions-content.js +21 -0
  410. package/server/dist/plugins/serena/manifest.js +111 -0
  411. package/server/dist/plugins/serena/verify.js +78 -0
  412. package/server/dist/plugins-router.js +215 -0
  413. package/server/dist/pricing.js +89 -0
  414. package/server/dist/profile-manager.js +310 -0
  415. package/server/dist/profiles-router.js +759 -0
  416. package/server/dist/project-registry.js +443 -0
  417. package/server/dist/project-router.js +4016 -0
  418. package/server/dist/proposal-manager.js +291 -0
  419. package/server/dist/provider-selection.js +69 -0
  420. package/server/dist/providers/claude-adapter.js +281 -0
  421. package/server/dist/providers/codex-adapter.js +264 -0
  422. package/server/dist/providers/index.js +23 -0
  423. package/server/dist/providers/registry.js +37 -0
  424. package/server/dist/providers/types.js +22 -0
  425. package/server/dist/queue-manager.js +1511 -0
  426. package/server/dist/rails-router.js +362 -0
  427. package/server/dist/rails-store.js +116 -0
  428. package/server/dist/result-event.js +106 -0
  429. package/server/dist/schemas/profile.v1.json +151 -0
  430. package/server/dist/setup-manager.js +1165 -0
  431. package/server/dist/setup-prerequisites.js +372 -0
  432. package/server/dist/smash-runner.js +663 -0
  433. package/server/dist/spec-draft-parser.js +133 -0
  434. package/server/dist/spec-launcher-manager.js +174 -0
  435. package/server/dist/spec-models.js +32 -0
  436. package/server/dist/specrails-tech-client.js +82 -0
  437. package/server/dist/spending.js +448 -0
  438. package/server/dist/telemetry-compactor.js +180 -0
  439. package/server/dist/telemetry-export.js +317 -0
  440. package/server/dist/telemetry-receiver.js +224 -0
  441. package/server/dist/terminal-manager.js +633 -0
  442. package/server/dist/terminal-marks-store.js +117 -0
  443. package/server/dist/terminal-osc-parser.js +159 -0
  444. package/server/dist/terminal-settings.js +282 -0
  445. package/server/dist/terminal-shell-integration.js +196 -0
  446. package/server/dist/ticket-broadcast.js +47 -0
  447. package/server/dist/ticket-store.js +397 -0
  448. package/server/dist/ticket-watcher.js +117 -0
  449. package/server/dist/types.js +10 -0
  450. package/server/dist/user-mcp-config.js +117 -0
  451. package/server/dist/util/cli-prompt.js +181 -0
  452. package/server/dist/util/secure-fs.js +50 -0
  453. package/server/dist/util/win-spawn.js +43 -0
  454. package/server/dist/webhook-manager.js +89 -0
  455. package/server/dist/ws-routing.js +47 -0
@@ -0,0 +1,617 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PluginManager = exports.PluginInstallError = exports.PluginAlreadyInstalledError = exports.PluginNotInstalledError = exports.PluginNotFoundError = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const ownership_1 = require("./plugins/ownership");
10
+ const claude_approval_1 = require("./plugins/claude-approval");
11
+ const drift_1 = require("./plugins/drift");
12
+ const providers_1 = require("./providers");
13
+ const paths_1 = require("./plugins/paths");
14
+ function readMcpServersMap(projectPath) {
15
+ try {
16
+ if (!fs_1.default.existsSync((0, paths_1.mcpJsonPath)(projectPath)))
17
+ return {};
18
+ const raw = fs_1.default.readFileSync((0, paths_1.mcpJsonPath)(projectPath), 'utf8');
19
+ if (!raw.trim())
20
+ return {};
21
+ const parsed = JSON.parse(raw);
22
+ return parsed.mcpServers ?? {};
23
+ }
24
+ catch {
25
+ return {};
26
+ }
27
+ }
28
+ const json_mutation_1 = require("./plugins/json-mutation");
29
+ const contributors_1 = require("./plugins/contributors");
30
+ class PluginNotFoundError extends Error {
31
+ constructor(name) {
32
+ super(`plugin not found in registry: ${name}`);
33
+ this.name = 'PluginNotFoundError';
34
+ }
35
+ }
36
+ exports.PluginNotFoundError = PluginNotFoundError;
37
+ class PluginNotInstalledError extends Error {
38
+ constructor(name) {
39
+ super(`plugin is not installed: ${name}`);
40
+ this.name = 'PluginNotInstalledError';
41
+ }
42
+ }
43
+ exports.PluginNotInstalledError = PluginNotInstalledError;
44
+ class PluginAlreadyInstalledError extends Error {
45
+ constructor(name) {
46
+ super(`plugin is already installed: ${name}`);
47
+ this.name = 'PluginAlreadyInstalledError';
48
+ }
49
+ }
50
+ exports.PluginAlreadyInstalledError = PluginAlreadyInstalledError;
51
+ class PluginInstallError extends Error {
52
+ cause;
53
+ constructor(message, cause) {
54
+ super(message);
55
+ this.name = 'PluginInstallError';
56
+ this.cause = cause;
57
+ }
58
+ }
59
+ exports.PluginInstallError = PluginInstallError;
60
+ const DEFAULT_VERIFY_TIMEOUT_MS = 2000;
61
+ class PluginManager {
62
+ registry;
63
+ _options;
64
+ constructor(plugins, options = {}) {
65
+ this.registry = (0, ownership_1.buildOwnershipMap)(plugins);
66
+ this._options = {
67
+ defaultVerifyTimeoutMs: options.defaultVerifyTimeoutMs ?? DEFAULT_VERIFY_TIMEOUT_MS,
68
+ checkPrerequisite: options.checkPrerequisite,
69
+ claudeApprovalChecker: options.claudeApprovalChecker ?? claude_approval_1.getClaudeApprovalState,
70
+ };
71
+ }
72
+ // ─── State helpers ─────────────────────────────────────────────────────────
73
+ getProjectState(projectPath) {
74
+ return (0, json_mutation_1.readJsonOr)((0, paths_1.stateFilePath)(projectPath), {
75
+ schemaVersion: 1,
76
+ plugins: {},
77
+ });
78
+ }
79
+ async _writeProjectState(projectPath, state) {
80
+ fs_1.default.mkdirSync((0, paths_1.pluginsDir)(projectPath), { recursive: true });
81
+ await (0, json_mutation_1.withFileLock)((0, paths_1.stateFilePath)(projectPath), async () => {
82
+ (0, json_mutation_1.atomicWriteFileSync)((0, paths_1.stateFilePath)(projectPath), JSON.stringify(state, null, 2) + '\n');
83
+ });
84
+ }
85
+ // ─── Catalog ───────────────────────────────────────────────────────────────
86
+ async listAvailable(projectPath, providerId) {
87
+ const state = this.getProjectState(projectPath);
88
+ const entries = [];
89
+ // Bundled plugins, regardless of install state.
90
+ for (const plugin of this.registry.byName.values()) {
91
+ const m = plugin.manifest;
92
+ const stateEntry = state.plugins[m.name];
93
+ let status;
94
+ // Provider applicability: a plugin is `not-applicable` when the
95
+ // project's provider is registered, providerSupport is declared, and
96
+ // there's no entry for this provider. Plugins that don't declare
97
+ // providerSupport at all default to claude-compatible (preserves
98
+ // pre-§14 behaviour for unchanged manifests).
99
+ const supportsThisProvider = providerId === undefined
100
+ ? true
101
+ : m.providerSupport === undefined
102
+ ? providerId === 'claude'
103
+ : providerId in m.providerSupport;
104
+ if (!supportsThisProvider) {
105
+ status = 'not-applicable';
106
+ }
107
+ else if (!stateEntry) {
108
+ status = 'not-installed';
109
+ }
110
+ else if (stateEntry.health === 'degraded') {
111
+ status = 'degraded';
112
+ }
113
+ else {
114
+ // Plugin install lives in two files:
115
+ // (a) state.json — the app's record that the plugin is installed
116
+ // (b) .mcp.json — the actual contract with Claude (loaded blindly)
117
+ // Active = both present. Deactivated = (a) without the (b) keys
118
+ // (user toggled off; install survives). For codex projects the
119
+ // (b) check is skipped because the registration lives outside the
120
+ // project filesystem (CODEX_HOME).
121
+ let allKeysPresent = true;
122
+ if (providerId === 'codex') {
123
+ // For codex we trust state.json — `codex mcp list` against the
124
+ // per-project CODEX_HOME is the source of truth, but it requires a
125
+ // subprocess which is too expensive for a catalog listing call.
126
+ allKeysPresent = true;
127
+ }
128
+ else {
129
+ const mcpServers = readMcpServersMap(projectPath);
130
+ for (const server of m.owns.mcpServers ?? []) {
131
+ if (!(server in mcpServers)) {
132
+ allKeysPresent = false;
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ status = allKeysPresent ? 'installed' : 'deactivated';
138
+ }
139
+ // Surface marketplace conflicts so UI can offer a "Disable global"
140
+ // affordance when our project-scoped install is being shadowed.
141
+ const conflicts = [];
142
+ const cachedDisabled = [];
143
+ for (const server of m.owns.mcpServers ?? []) {
144
+ for (const key of (0, claude_approval_1.findEnabledMarketplaceKeys)(server)) {
145
+ if (!conflicts.includes(key))
146
+ conflicts.push(key);
147
+ }
148
+ for (const key of (0, claude_approval_1.findInstalledButNotEnabledMarketplaceKeys)(server)) {
149
+ if (!cachedDisabled.includes(key))
150
+ cachedDisabled.push(key);
151
+ }
152
+ }
153
+ // Drift detection: only meaningful when actually installed.
154
+ const updateAvailable = stateEntry ? (0, drift_1.detectMcpDrift)(projectPath, plugin) : false;
155
+ entries.push({
156
+ name: m.name,
157
+ version: m.version,
158
+ description: m.description,
159
+ whatItDoes: m.whatItDoes,
160
+ category: m.category,
161
+ requirements: m.requirements ?? [],
162
+ owns: m.owns,
163
+ status,
164
+ installedAt: stateEntry?.installedAt,
165
+ health: stateEntry?.health,
166
+ healthReason: stateEntry?.healthReason,
167
+ marketplaceConflicts: conflicts.length > 0 ? conflicts : undefined,
168
+ marketplaceCachedButDisabled: cachedDisabled.length > 0 ? cachedDisabled : undefined,
169
+ updateAvailable: updateAvailable || undefined,
170
+ });
171
+ }
172
+ // Orphan plugins: present in state.json but not in the bundled registry.
173
+ for (const [name, entry] of Object.entries(state.plugins)) {
174
+ if (this.registry.byName.has(name))
175
+ continue;
176
+ entries.push({
177
+ name,
178
+ version: entry.version,
179
+ description: '(plugin no longer bundled)',
180
+ whatItDoes: [],
181
+ requirements: [],
182
+ owns: {},
183
+ status: 'orphan',
184
+ installedAt: entry.installedAt,
185
+ health: entry.health,
186
+ healthReason: entry.healthReason,
187
+ });
188
+ }
189
+ return entries.sort((a, b) => a.name.localeCompare(b.name));
190
+ }
191
+ // ─── Preview install ───────────────────────────────────────────────────────
192
+ async previewInstall(projectPath, projectId, name) {
193
+ const plugin = this.registry.byName.get(name);
194
+ if (!plugin)
195
+ throw new PluginNotFoundError(name);
196
+ let files;
197
+ if (plugin.previewInstall) {
198
+ files = await plugin.previewInstall({ projectPath, projectId });
199
+ }
200
+ else {
201
+ files = this._derivePreviewFiles(projectPath, plugin);
202
+ }
203
+ const requirements = await Promise.all((plugin.manifest.requirements ?? []).map(async (req) => {
204
+ if (this._options.checkPrerequisite) {
205
+ const r = await this._options.checkPrerequisite(req);
206
+ return { name: req.name, ...r };
207
+ }
208
+ return { name: req.name, installed: true, executable: true, meetsMinimum: true };
209
+ }));
210
+ const hostKey = `${process.platform}-${process.arch}`;
211
+ const platformNote = plugin.manifest.platformNotes?.[hostKey];
212
+ return {
213
+ pluginName: name,
214
+ files,
215
+ requirements,
216
+ platformNote,
217
+ };
218
+ }
219
+ _derivePreviewFiles(projectPath, plugin) {
220
+ const out = [];
221
+ const m = plugin.manifest;
222
+ // .mcp.json
223
+ if ((m.owns.mcpServers ?? []).length > 0) {
224
+ const mcpExists = fs_1.default.existsSync((0, paths_1.mcpJsonPath)(projectPath));
225
+ out.push({
226
+ path: '.mcp.json',
227
+ op: mcpExists ? 'modify' : 'create',
228
+ summary: `+ mcpServers.${(m.owns.mcpServers ?? []).join(', mcpServers.')}`,
229
+ });
230
+ }
231
+ // Agent fragments
232
+ for (const frag of m.owns.agentFragments ?? []) {
233
+ const exists = fs_1.default.existsSync(path_1.default.join(projectPath, frag));
234
+ out.push({ path: frag, op: exists ? 'modify' : 'create' });
235
+ }
236
+ // Shared-file contributors (CLAUDE.md today, more in the future).
237
+ for (const rel of (0, contributors_1.contributorPaths)(plugin)) {
238
+ const exists = fs_1.default.existsSync(path_1.default.join(projectPath, rel));
239
+ out.push({
240
+ path: rel,
241
+ op: exists ? 'modify' : 'create',
242
+ summary: `+ <!-- specrails-desktop-managed:${m.name} --> block`,
243
+ });
244
+ }
245
+ // State file
246
+ const stateExists = fs_1.default.existsSync((0, paths_1.stateFilePath)(projectPath));
247
+ out.push({
248
+ path: '.specrails/plugins/state.json',
249
+ op: stateExists ? 'modify' : 'create',
250
+ summary: `+ plugins.${m.name}`,
251
+ });
252
+ return out;
253
+ }
254
+ // ─── Install ───────────────────────────────────────────────────────────────
255
+ async install(projectPath, projectId, name, broadcast, providerId) {
256
+ const plugin = this.registry.byName.get(name);
257
+ if (!plugin)
258
+ throw new PluginNotFoundError(name);
259
+ // Provider applicability gate: refuse to install a plugin that has no
260
+ // providerSupport entry for this project's provider. Plugins that omit
261
+ // providerSupport altogether default to claude-compatible.
262
+ if (providerId !== undefined && providerId !== 'claude') {
263
+ const declared = plugin.manifest.providerSupport;
264
+ if (declared !== undefined && !(providerId in declared)) {
265
+ throw new PluginInstallError(`plugin '${name}' is not applicable for provider '${providerId}'. Declared providers: ${Object.keys(declared).join(', ')}.`);
266
+ }
267
+ }
268
+ const state = this.getProjectState(projectPath);
269
+ if (state.plugins[name])
270
+ throw new PluginAlreadyInstalledError(name);
271
+ // Check for ownership conflicts with user-authored `.mcp.json` entries.
272
+ // Only meaningful for `project-json` MCP registration providers (claude
273
+ // today). Codex registers via `codex mcp add` against per-project
274
+ // CODEX_HOME, which the plugin's install path checks via `codex mcp list`.
275
+ const adapter = providerId !== undefined && (0, providers_1.hasAdapter)(providerId)
276
+ ? (0, providers_1.getAdapter)(providerId)
277
+ : null;
278
+ const usesProjectJsonMcp = adapter === null || adapter.mcpRegistration === 'project-json';
279
+ if (usesProjectJsonMcp) {
280
+ const mcpFile = (0, paths_1.mcpJsonPath)(projectPath);
281
+ if (fs_1.default.existsSync(mcpFile)) {
282
+ const raw = fs_1.default.readFileSync(mcpFile, 'utf8');
283
+ let parsed;
284
+ try {
285
+ parsed = raw.trim() ? JSON.parse(raw) : {};
286
+ }
287
+ catch {
288
+ // A hand-edited / broken `.mcp.json` should yield an actionable 409,
289
+ // not an opaque 500 with a raw "Unexpected token" SyntaxError.
290
+ throw new PluginInstallError(`cannot install '${name}': '${(0, paths_1.mcpJsonPath)(projectPath)}' is not valid JSON; fix it first.`);
291
+ }
292
+ const servers = parsed.mcpServers ?? {};
293
+ for (const key of plugin.manifest.owns.mcpServers ?? []) {
294
+ if (key in servers) {
295
+ throw new PluginInstallError(`cannot install '${name}': '${(0, paths_1.mcpJsonPath)(projectPath)}' already has a 'mcpServers.${key}' entry. Remove it first.`);
296
+ }
297
+ }
298
+ }
299
+ }
300
+ // Snapshot pre-install state of every file the plugin might touch — we
301
+ // need exact bytes to roll back if install/verify fails.
302
+ const targetPaths = [
303
+ (0, paths_1.mcpJsonPath)(projectPath),
304
+ (0, paths_1.stateFilePath)(projectPath),
305
+ ...(plugin.manifest.owns.agentFragments ?? []).map((f) => path_1.default.join(projectPath, f)),
306
+ // Include the shared instructions file (CLAUDE.md / AGENTS.md) so a failed
307
+ // install rolls it back too — otherwise an applyContributors write that
308
+ // survives a later failure leaves an orphaned managed block with no state
309
+ // entry, which uninstall can never remove (breaks byte-identical restore).
310
+ ...(0, contributors_1.contributorPaths)(plugin, providerId).map((rel) => path_1.default.join(projectPath, rel)),
311
+ ];
312
+ const preState = new Map();
313
+ for (const p of targetPaths) {
314
+ preState.set(p, fs_1.default.existsSync(p) ? fs_1.default.readFileSync(p) : null);
315
+ }
316
+ const installedFiles = [];
317
+ const onLog = (line) => {
318
+ const msg = {
319
+ type: 'plugin.install_progress',
320
+ projectId,
321
+ name,
322
+ line,
323
+ timestamp: new Date().toISOString(),
324
+ };
325
+ broadcast(msg);
326
+ };
327
+ const ctx = {
328
+ projectPath,
329
+ projectId,
330
+ providerId,
331
+ recordInstalledFile: (rel) => { installedFiles.push(rel); },
332
+ log: onLog,
333
+ };
334
+ try {
335
+ await plugin.install(ctx);
336
+ // Verify immediately. A degraded result also triggers rollback because
337
+ // the spec requires verify-pass before we commit state.
338
+ const verify = await this._runVerify(plugin, projectPath, projectId);
339
+ if (!verify.ok) {
340
+ throw new PluginInstallError(`verify failed after install: ${verify.reason ?? 'unknown'}`);
341
+ }
342
+ // Commit: write state.json with the install record.
343
+ const stateNow = this.getProjectState(projectPath);
344
+ stateNow.plugins[name] = {
345
+ version: plugin.manifest.version,
346
+ installedAt: new Date().toISOString(),
347
+ installedFiles,
348
+ health: 'ok',
349
+ };
350
+ await this._writeProjectState(projectPath, stateNow);
351
+ // No additional approval write needed: any server in `.mcp.json` loads
352
+ // automatically when Claude opens the project. Install IS active.
353
+ // Apply shared-file contributors (CLAUDE.md block today, more in the
354
+ // future). Each contributor is per-plugin and idempotent.
355
+ const sharedTouched = await (0, contributors_1.applyContributors)(plugin, projectPath, providerId);
356
+ if (sharedTouched.length > 0) {
357
+ for (const p of sharedTouched) {
358
+ if (!installedFiles.includes(p))
359
+ installedFiles.push(p);
360
+ }
361
+ const stateNow2 = this.getProjectState(projectPath);
362
+ if (stateNow2.plugins[name])
363
+ stateNow2.plugins[name].installedFiles = installedFiles;
364
+ await this._writeProjectState(projectPath, stateNow2);
365
+ }
366
+ }
367
+ catch (err) {
368
+ // Roll back every file we snapshotted. Byte-identical restore.
369
+ for (const [p, bytes] of preState.entries()) {
370
+ try {
371
+ if (bytes === null) {
372
+ if (fs_1.default.existsSync(p))
373
+ fs_1.default.unlinkSync(p);
374
+ }
375
+ else {
376
+ // Write the raw Buffer (not .toString('utf8')) so a snapshot with
377
+ // non-UTF8 bytes restores byte-for-byte.
378
+ (0, json_mutation_1.atomicWriteFileSync)(p, bytes);
379
+ }
380
+ }
381
+ catch {
382
+ // Best-effort rollback; any failure here will surface via verify on
383
+ // the next install attempt.
384
+ }
385
+ }
386
+ throw err instanceof PluginInstallError ? err : new PluginInstallError(`install of '${name}' failed: ${err?.message ?? String(err)}`, err);
387
+ }
388
+ const msg = {
389
+ type: 'plugin.installed',
390
+ projectId,
391
+ name,
392
+ version: plugin.manifest.version,
393
+ timestamp: new Date().toISOString(),
394
+ };
395
+ broadcast(msg);
396
+ }
397
+ // ─── Uninstall ─────────────────────────────────────────────────────────────
398
+ async uninstall(projectPath, projectId, name, broadcast, providerId) {
399
+ const state = this.getProjectState(projectPath);
400
+ const entry = state.plugins[name];
401
+ if (!entry)
402
+ throw new PluginNotInstalledError(name);
403
+ const plugin = this.registry.byName.get(name);
404
+ const onLog = (line) => {
405
+ broadcast({
406
+ type: 'plugin.install_progress',
407
+ projectId,
408
+ name,
409
+ line,
410
+ timestamp: new Date().toISOString(),
411
+ });
412
+ };
413
+ if (plugin) {
414
+ // Revert shared-file contributors first so a partial uninstall doesn't
415
+ // leave dangling instructions referencing missing tools.
416
+ await (0, contributors_1.revertContributors)(plugin, projectPath, providerId);
417
+ await plugin.uninstall({
418
+ projectPath,
419
+ projectId,
420
+ providerId,
421
+ recordInstalledFile: () => { },
422
+ log: onLog,
423
+ });
424
+ }
425
+ else {
426
+ // Orphan removal: no plugin code available. Best-effort cleanup of
427
+ // recorded installedFiles + drop the state entry. We cannot know which
428
+ // mcpServers keys belonged to this plugin, so we leave .mcp.json alone.
429
+ const root = path_1.default.resolve(projectPath);
430
+ for (const rel of entry.installedFiles ?? []) {
431
+ const abs = path_1.default.resolve(projectPath, rel);
432
+ // M5: installedFiles comes from state.json, which a hostile repo can
433
+ // ship. Without containment, `rel` of "../../../Users/victim/x" (or an
434
+ // absolute path) turns orphan removal into an arbitrary-file-deletion
435
+ // primitive. Skip anything that resolves outside the project root.
436
+ const within = path_1.default.relative(root, abs);
437
+ if (within === '' || within.startsWith('..') || path_1.default.isAbsolute(within)) {
438
+ console.warn(`[plugin-manager] skipping out-of-project installedFile during orphan removal: ${rel}`);
439
+ continue;
440
+ }
441
+ try {
442
+ if (fs_1.default.existsSync(abs))
443
+ fs_1.default.unlinkSync(abs);
444
+ }
445
+ catch { /* ignore */ }
446
+ }
447
+ }
448
+ const stateNow = this.getProjectState(projectPath);
449
+ delete stateNow.plugins[name];
450
+ await this._writeProjectState(projectPath, stateNow);
451
+ broadcast({
452
+ type: 'plugin.uninstalled',
453
+ projectId,
454
+ name,
455
+ timestamp: new Date().toISOString(),
456
+ });
457
+ }
458
+ /**
459
+ * Re-write the project's `.mcp.json` entries owned by this plugin to match
460
+ * the bundled manifest's canonical shape. Surgical: only the plugin's
461
+ * `owns.mcpServers` keys are touched; user entries are preserved. Used to
462
+ * resolve drift surfaced by `updateAvailable`.
463
+ */
464
+ async updateMcpEntry(projectPath, projectId, name, broadcast, providerId) {
465
+ const plugin = this.registry.byName.get(name);
466
+ if (!plugin)
467
+ throw new PluginNotFoundError(name);
468
+ const state = this.getProjectState(projectPath);
469
+ if (!state.plugins[name])
470
+ throw new PluginNotInstalledError(name);
471
+ const expected = plugin.expectedMcpEntry?.();
472
+ if (!expected) {
473
+ throw new PluginInstallError(`'${name}' does not declare expectedMcpEntry; cannot update`);
474
+ }
475
+ const owned = plugin.manifest.owns.mcpServers ?? [];
476
+ const entries = {};
477
+ for (const key of owned)
478
+ entries[key] = expected;
479
+ await PluginManager.mergeMcpServers(projectPath, entries);
480
+ // Refresh shared-file contributions too: a drift may exist in CLAUDE.md
481
+ // even when the .mcp.json entry matches.
482
+ await (0, contributors_1.applyContributors)(plugin, projectPath, providerId);
483
+ broadcast({
484
+ type: 'plugin.health_changed',
485
+ projectId,
486
+ name,
487
+ status: 'unknown',
488
+ reason: 'updated',
489
+ timestamp: new Date().toISOString(),
490
+ });
491
+ }
492
+ /**
493
+ * Toggle a plugin between active and deactivated. Reality of Claude's MCP
494
+ * loading: any server present in `<project>/.mcp.json` is loaded by Claude
495
+ * regardless of `enabledMcpjsonServers` flags. So:
496
+ *
497
+ * - active=true → re-write the canonical mcpServers entry (from the
498
+ * plugin's `expectedMcpEntry`) into `.mcp.json`
499
+ * - active=false → remove only the owned mcpServers keys; preserve
500
+ * state.json so `installed` memory survives, and
501
+ * preserve any user-authored sibling entries
502
+ *
503
+ * Plugin install state survives across toggles. Uninstall is the only
504
+ * action that clears state.json + custom-*.md fragments.
505
+ */
506
+ async setActive(projectPath, projectId, name, active, broadcast, providerId) {
507
+ const plugin = this.registry.byName.get(name);
508
+ if (!plugin)
509
+ throw new PluginNotFoundError(name);
510
+ const state = this.getProjectState(projectPath);
511
+ if (!state.plugins[name])
512
+ throw new PluginNotInstalledError(name);
513
+ const owned = plugin.manifest.owns.mcpServers ?? [];
514
+ if (owned.length === 0) {
515
+ throw new PluginInstallError(`'${name}' owns no mcpServers; cannot toggle activation`);
516
+ }
517
+ if (active) {
518
+ const expected = plugin.expectedMcpEntry?.();
519
+ if (!expected) {
520
+ throw new PluginInstallError(`'${name}' does not declare expectedMcpEntry; cannot activate`);
521
+ }
522
+ const entries = {};
523
+ for (const k of owned)
524
+ entries[k] = expected;
525
+ await PluginManager.mergeMcpServers(projectPath, entries);
526
+ await (0, contributors_1.applyContributors)(plugin, projectPath, providerId);
527
+ }
528
+ else {
529
+ await PluginManager.removeMcpServers(projectPath, owned);
530
+ await (0, contributors_1.revertContributors)(plugin, projectPath, providerId);
531
+ }
532
+ broadcast({
533
+ type: 'plugin.health_changed',
534
+ projectId,
535
+ name,
536
+ status: active ? 'ok' : 'unknown',
537
+ reason: active ? 'activated' : 'deactivated',
538
+ timestamp: new Date().toISOString(),
539
+ });
540
+ }
541
+ /** Drop a state.json entry for a plugin no longer in the registry. */
542
+ async removeOrphan(projectPath, projectId, name, broadcast) {
543
+ if (this.registry.byName.has(name)) {
544
+ throw new PluginInstallError(`'${name}' is not orphan; it is still bundled. Use uninstall instead.`);
545
+ }
546
+ return this.uninstall(projectPath, projectId, name, broadcast);
547
+ }
548
+ // ─── Verify ────────────────────────────────────────────────────────────────
549
+ async verify(projectPath, projectId, name, broadcast) {
550
+ const plugin = this.registry.byName.get(name);
551
+ if (!plugin)
552
+ throw new PluginNotFoundError(name);
553
+ const result = await this._runVerify(plugin, projectPath, projectId);
554
+ await this._cacheHealth(projectPath, projectId, name, result, broadcast);
555
+ return result;
556
+ }
557
+ async _runVerify(plugin, projectPath, projectId) {
558
+ const timeout = plugin.manifest.verifyTimeoutMs ?? this._options.defaultVerifyTimeoutMs;
559
+ const checkedAt = new Date().toISOString();
560
+ try {
561
+ const result = await Promise.race([
562
+ plugin.verify({ projectPath, projectId }),
563
+ new Promise((resolve) => setTimeout(() => resolve({ __timeout: true }), timeout).unref?.()),
564
+ ]);
565
+ if ('__timeout' in result) {
566
+ return { ok: false, reason: 'verify-timeout', checkedAt };
567
+ }
568
+ return { ok: result.ok, reason: result.reason, checkedAt: result.checkedAt ?? checkedAt };
569
+ }
570
+ catch (err) {
571
+ return { ok: false, reason: `verify-exception: ${err?.message ?? String(err)}`, checkedAt };
572
+ }
573
+ }
574
+ async _cacheHealth(projectPath, projectId, name, result, broadcast) {
575
+ const state = this.getProjectState(projectPath);
576
+ const entry = state.plugins[name];
577
+ if (!entry)
578
+ return;
579
+ const newHealth = result.ok ? 'ok' : 'degraded';
580
+ const changed = entry.health !== newHealth || entry.healthReason !== result.reason;
581
+ if (!changed)
582
+ return; // nothing to persist — avoids per-spawn write churn (verify runs on every rail spawn)
583
+ entry.health = newHealth;
584
+ entry.healthReason = result.reason;
585
+ await this._writeProjectState(projectPath, state);
586
+ if (broadcast) {
587
+ const msg = {
588
+ type: 'plugin.health_changed',
589
+ projectId,
590
+ name,
591
+ status: newHealth,
592
+ reason: result.reason,
593
+ timestamp: new Date().toISOString(),
594
+ };
595
+ broadcast(msg);
596
+ }
597
+ }
598
+ // ─── Surgical helpers exposed to plugins ───────────────────────────────────
599
+ /** Helper: surgically merge `mcpServers.<key>` entries into .mcp.json. */
600
+ static async mergeMcpServers(projectPath, entries) {
601
+ await (0, json_mutation_1.surgicalMergeJson)((0, paths_1.mcpJsonPath)(projectPath), (current) => {
602
+ const next = (current ?? {});
603
+ const servers = (next.mcpServers ?? {});
604
+ for (const [k, v] of Object.entries(entries))
605
+ servers[k] = v;
606
+ next.mcpServers = servers;
607
+ return next;
608
+ });
609
+ }
610
+ /** Helper: remove specific `mcpServers.<key>` entries from .mcp.json. */
611
+ static async removeMcpServers(projectPath, keys) {
612
+ if (keys.length === 0)
613
+ return;
614
+ await (0, json_mutation_1.surgicalRemoveKeys)((0, paths_1.mcpJsonPath)(projectPath), keys.map((k) => `mcpServers.${k}`));
615
+ }
616
+ }
617
+ exports.PluginManager = PluginManager;