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,759 @@
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.createProfilesRouter = createProfilesRouter;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const express_1 = require("express");
10
+ const agent_generator_1 = require("./agent-generator");
11
+ const agent_refine_db_1 = require("./agent-refine-db");
12
+ const agent_refine_manager_1 = require("./agent-refine-manager");
13
+ const providers_1 = require("./providers");
14
+ const profile_manager_1 = require("./profile-manager");
15
+ const AGENTS_SECTION_ENABLED = process.env.SPECRAILS_AGENTS_SECTION !== 'false';
16
+ function handleError(res, err) {
17
+ if (err instanceof profile_manager_1.ProfileValidationError) {
18
+ res.status(400).json({ error: err.message, details: err.errors });
19
+ return;
20
+ }
21
+ if (err instanceof profile_manager_1.ProfileConflictError) {
22
+ res.status(409).json({ error: err.message });
23
+ return;
24
+ }
25
+ if (err instanceof profile_manager_1.ProfileNotFoundError) {
26
+ res.status(404).json({ error: err.message });
27
+ return;
28
+ }
29
+ const message = err instanceof Error ? err.message : 'unknown error';
30
+ res.status(500).json({ error: message });
31
+ }
32
+ function createProfilesRouter() {
33
+ const router = (0, express_1.Router)({ mergeParams: true });
34
+ function ctx(req) {
35
+ return req.projectCtx;
36
+ }
37
+ // Feature-flag gate
38
+ router.use((_req, res, next) => {
39
+ if (!AGENTS_SECTION_ENABLED) {
40
+ res.status(404).json({ error: 'Agents section disabled on this server' });
41
+ return;
42
+ }
43
+ next();
44
+ });
45
+ // POST /api/projects/:projectId/profiles/migrate-from-settings
46
+ // Seed a `default` profile from the agent frontmatter + legacy routing.
47
+ // Intended for first-time onboarding of existing projects.
48
+ router.post('/migrate-from-settings', (req, res) => {
49
+ try {
50
+ const { project, broadcast } = ctx(req);
51
+ const agentsDir = path_1.default.join(project.path, '.claude', 'agents');
52
+ if (!fs_1.default.existsSync(agentsDir)) {
53
+ res.status(400).json({ error: 'no .claude/agents/ directory found' });
54
+ return;
55
+ }
56
+ // Gather installed sr-*.md with their declared models.
57
+ const agents = [];
58
+ for (const entry of fs_1.default.readdirSync(agentsDir)) {
59
+ if (!entry.endsWith('.md'))
60
+ continue;
61
+ if (!entry.startsWith('sr-'))
62
+ continue;
63
+ const id = entry.slice(0, -'.md'.length);
64
+ let model = 'sonnet';
65
+ try {
66
+ const content = fs_1.default.readFileSync(path_1.default.join(agentsDir, entry), 'utf8');
67
+ const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
68
+ if (fm) {
69
+ const m = fm[1].match(/^model:\s*(sonnet|opus|haiku)/m);
70
+ if (m)
71
+ model = m[1];
72
+ }
73
+ }
74
+ catch {
75
+ // skip unreadable files
76
+ }
77
+ agents.push({ id, model });
78
+ }
79
+ const baseline = ['sr-architect', 'sr-developer', 'sr-reviewer'];
80
+ const missing = baseline.filter((id) => !agents.some((a) => a.id === id));
81
+ if (missing.length > 0) {
82
+ res.status(400).json({
83
+ error: `missing baseline agents in this project: ${missing.join(', ')}. Run 'npx specrails-core@latest update' first.`,
84
+ });
85
+ return;
86
+ }
87
+ // Order: baseline trio first (architect, developer, reviewer), optional
88
+ // agents in the middle. sr-merge-resolver is no longer a baseline agent;
89
+ // it sorts among optional agents alphabetically when present.
90
+ const pinnedLast = new Set();
91
+ const baselineFirst = new Set(['sr-architect', 'sr-developer', 'sr-reviewer']);
92
+ const orderedAgents = [
93
+ ...agents.filter((a) => baselineFirst.has(a.id))
94
+ .sort((a, b) => {
95
+ const rank = ['sr-architect', 'sr-developer', 'sr-reviewer'];
96
+ return rank.indexOf(a.id) - rank.indexOf(b.id);
97
+ }),
98
+ ...agents.filter((a) => !baselineFirst.has(a.id) && !pinnedLast.has(a.id))
99
+ .sort((a, b) => a.id.localeCompare(b.id)),
100
+ ...agents.filter((a) => pinnedLast.has(a.id)),
101
+ ];
102
+ // Build the default profile mirroring legacy routing. The frontmatter
103
+ // model aliases parsed above are claude-specific; for any other provider
104
+ // they are not in the adapter's catalog, so fall back to that provider's
105
+ // default model and stamp the provider so the persisted profile validates
106
+ // against the right catalog on every future read.
107
+ const provider = project.provider ?? 'claude';
108
+ const isClaude = provider === 'claude';
109
+ const fallbackModel = isClaude ? 'sonnet' : (0, providers_1.getAdapter)(provider).defaultModel();
110
+ const profile = {
111
+ schemaVersion: 1,
112
+ name: 'default',
113
+ description: 'Baseline profile migrated from your current agent frontmatters.',
114
+ ...(isClaude ? {} : { provider }),
115
+ orchestrator: { model: isClaude ? 'sonnet' : fallbackModel },
116
+ agents: orderedAgents.map((a) => ({
117
+ id: a.id,
118
+ model: isClaude ? a.model : fallbackModel,
119
+ required: baseline.includes(a.id),
120
+ })),
121
+ routing: [
122
+ ...(agents.some((a) => a.id === 'sr-frontend-developer')
123
+ ? [{ tags: ['frontend'], agent: 'sr-frontend-developer' }]
124
+ : []),
125
+ ...(agents.some((a) => a.id === 'sr-backend-developer')
126
+ ? [{ tags: ['backend'], agent: 'sr-backend-developer' }]
127
+ : []),
128
+ { default: true, agent: 'sr-developer' },
129
+ ],
130
+ };
131
+ try {
132
+ (0, profile_manager_1.createProfile)(project.path, profile, provider);
133
+ }
134
+ catch (err) {
135
+ if (err instanceof profile_manager_1.ProfileConflictError) {
136
+ res.status(409).json({ error: "a profile named 'default' already exists; delete it first or edit it manually" });
137
+ return;
138
+ }
139
+ throw err;
140
+ }
141
+ broadcast({ type: 'profile.changed', projectId: project.id, name: 'default' });
142
+ res.status(201).json({ profile });
143
+ }
144
+ catch (err) {
145
+ handleError(res, err);
146
+ }
147
+ });
148
+ // GET /api/projects/:projectId/profiles/analytics?windowDays=30
149
+ // Per-profile aggregated metrics over the requested time window.
150
+ router.get('/analytics', (req, res) => {
151
+ try {
152
+ const { db } = ctx(req);
153
+ const windowDays = Math.max(1, Math.min(365, parseInt((req.query.windowDays ?? '30'), 10) || 30));
154
+ const since = Date.now() - windowDays * 24 * 60 * 60 * 1000;
155
+ const rows = db
156
+ .prepare(`SELECT
157
+ jp.profile_name AS profileName,
158
+ COUNT(*) AS jobs,
159
+ SUM(CASE WHEN j.status = 'completed' THEN 1 ELSE 0 END) AS succeeded,
160
+ AVG(j.duration_ms) AS avgDurationMs,
161
+ AVG(COALESCE(j.tokens_in, 0) + COALESCE(j.tokens_out, 0)
162
+ + COALESCE(j.tokens_cache_read, 0) + COALESCE(j.tokens_cache_create, 0)) AS avgTokens,
163
+ AVG(j.total_cost_usd) AS avgCostUsd
164
+ FROM job_profiles jp
165
+ JOIN jobs j ON j.id = jp.job_id
166
+ WHERE jp.created_at >= ?
167
+ GROUP BY jp.profile_name
168
+ ORDER BY jobs DESC`)
169
+ .all(since);
170
+ res.json({
171
+ windowDays,
172
+ rows: rows.map((r) => ({
173
+ profileName: r.profileName,
174
+ jobs: r.jobs,
175
+ succeeded: r.succeeded,
176
+ successRate: r.jobs > 0 ? r.succeeded / r.jobs : 0,
177
+ avgDurationMs: r.avgDurationMs,
178
+ avgTokens: r.avgTokens,
179
+ avgCostUsd: r.avgCostUsd,
180
+ })),
181
+ });
182
+ }
183
+ catch (err) {
184
+ handleError(res, err);
185
+ }
186
+ });
187
+ // GET /api/projects/:projectId/profiles/core-version
188
+ // Report the project's installed specrails-core version for the upgrade banner.
189
+ router.get('/core-version', (req, res) => {
190
+ try {
191
+ const { project } = ctx(req);
192
+ const candidates = [
193
+ path_1.default.join(project.path, '.specrails', 'specrails-version'),
194
+ path_1.default.join(project.path, '.specrails-version'),
195
+ ];
196
+ let version = null;
197
+ for (const p of candidates) {
198
+ if (fs_1.default.existsSync(p)) {
199
+ try {
200
+ version = fs_1.default.readFileSync(p, 'utf8').trim();
201
+ }
202
+ catch {
203
+ // ignore
204
+ }
205
+ if (version)
206
+ break;
207
+ }
208
+ }
209
+ // Minimum version required for profile-aware implement
210
+ const REQUIRED = '4.1.0';
211
+ let profileAware = false;
212
+ if (version) {
213
+ const [ma, mi, pa] = version.split('.').map((n) => parseInt(n, 10));
214
+ const [rma, rmi, rpa] = REQUIRED.split('.').map((n) => parseInt(n, 10));
215
+ if (!isNaN(ma) && !isNaN(mi) && !isNaN(pa)) {
216
+ profileAware =
217
+ ma > rma ||
218
+ (ma === rma && mi > rmi) ||
219
+ (ma === rma && mi === rmi && pa >= rpa);
220
+ }
221
+ }
222
+ res.json({ version, required: REQUIRED, profileAware });
223
+ }
224
+ catch (err) {
225
+ handleError(res, err);
226
+ }
227
+ });
228
+ // GET /api/projects/:projectId/profiles/catalog
229
+ // List all agents available in .claude/agents/ (upstream sr-* and custom custom-*)
230
+ router.get('/catalog', (req, res) => {
231
+ try {
232
+ const { project } = ctx(req);
233
+ const dir = path_1.default.join(project.path, '.claude', 'agents');
234
+ if (!fs_1.default.existsSync(dir)) {
235
+ res.json({ agents: [] });
236
+ return;
237
+ }
238
+ const agents = [];
239
+ for (const file of fs_1.default.readdirSync(dir)) {
240
+ if (!file.endsWith('.md'))
241
+ continue;
242
+ const id = file.slice(0, -'.md'.length);
243
+ const kind = id.startsWith('sr-')
244
+ ? 'upstream'
245
+ : id.startsWith('custom-')
246
+ ? 'custom'
247
+ : null;
248
+ if (!kind)
249
+ continue;
250
+ let description;
251
+ let model;
252
+ try {
253
+ const body = fs_1.default.readFileSync(path_1.default.join(dir, file), 'utf8');
254
+ const fm = body.match(/^---\r?\n([\s\S]*?)\r?\n---/);
255
+ if (fm) {
256
+ // description can be a long JSON-escaped string spanning multiple lines.
257
+ // Match from `description:` up to the next top-level YAML key or the end
258
+ // of the frontmatter block. Then unescape \n, \t, \" and strip surrounding
259
+ // quotes. Collapse whitespace so it fits the one-line header.
260
+ const descBlock = fm[1].match(/^description:\s*([\s\S]*?)(?=^[a-z_]+:\s|^---|\Z)/m);
261
+ if (descBlock) {
262
+ let raw = descBlock[1].trim();
263
+ // Strip surrounding quotes (YAML may use '...' or "...")
264
+ if ((raw.startsWith('"') && raw.endsWith('"')) ||
265
+ (raw.startsWith("'") && raw.endsWith("'"))) {
266
+ raw = raw.slice(1, -1);
267
+ }
268
+ // Decode common JSON-style escapes
269
+ raw = raw
270
+ .replace(/\\n/g, ' ')
271
+ .replace(/\\t/g, ' ')
272
+ .replace(/\\"/g, '"')
273
+ .replace(/\\'/g, "'")
274
+ .replace(/\\\\/g, '\\');
275
+ // Collapse any whitespace (incl. real newlines) and trim
276
+ description = raw.replace(/\s+/g, ' ').trim();
277
+ // Cap length for the one-line header preview
278
+ if (description.length > 280)
279
+ description = description.slice(0, 277) + '…';
280
+ }
281
+ const modelMatch = fm[1].match(/^model:\s*(\S+)/m);
282
+ if (modelMatch)
283
+ model = modelMatch[1];
284
+ }
285
+ }
286
+ catch {
287
+ // ignore unreadable files
288
+ }
289
+ agents.push({ id, kind, description, model });
290
+ }
291
+ agents.sort((a, b) => a.id.localeCompare(b.id));
292
+ res.json({ agents });
293
+ }
294
+ catch (err) {
295
+ handleError(res, err);
296
+ }
297
+ });
298
+ // GET /api/projects/:projectId/profiles/catalog/:agentId
299
+ // Return the full .md body of a single agent file (read-only for sr-*, editable for custom-*)
300
+ router.get('/catalog/:agentId', (req, res) => {
301
+ try {
302
+ const { project } = ctx(req);
303
+ const agentId = req.params.agentId;
304
+ if (!/^(sr|custom)-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
305
+ res.status(400).json({ error: 'invalid agent id' });
306
+ return;
307
+ }
308
+ const file = path_1.default.join(project.path, '.claude', 'agents', `${agentId}.md`);
309
+ if (!fs_1.default.existsSync(file)) {
310
+ res.status(404).json({ error: 'agent not found' });
311
+ return;
312
+ }
313
+ const body = fs_1.default.readFileSync(file, 'utf8');
314
+ res.json({ id: agentId, body });
315
+ }
316
+ catch (err) {
317
+ handleError(res, err);
318
+ }
319
+ });
320
+ // POST /api/projects/:projectId/profiles/catalog (create a custom agent)
321
+ // Body: { id: string, body: string }
322
+ // id must start with `custom-` and match ^custom-[a-z0-9][a-z0-9-]*$
323
+ router.post('/catalog', (req, res) => {
324
+ try {
325
+ const { project, db, broadcast } = ctx(req);
326
+ const id = (req.body?.id ?? '').toString().trim();
327
+ const body = (req.body?.body ?? '').toString();
328
+ if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(id)) {
329
+ res.status(400).json({ error: "id must match ^custom-[a-z0-9][a-z0-9-]*$ (the 'custom-' prefix is reserved for user-authored agents)" });
330
+ return;
331
+ }
332
+ if (!body || body.length === 0) {
333
+ res.status(400).json({ error: 'body is required' });
334
+ return;
335
+ }
336
+ const agentsDir = path_1.default.join(project.path, '.claude', 'agents');
337
+ fs_1.default.mkdirSync(agentsDir, { recursive: true });
338
+ const file = path_1.default.join(agentsDir, `${id}.md`);
339
+ if (fs_1.default.existsSync(file)) {
340
+ res.status(409).json({ error: `agent '${id}' already exists` });
341
+ return;
342
+ }
343
+ fs_1.default.writeFileSync(file, body, 'utf8');
344
+ // Record initial version
345
+ const nextVersion = 1;
346
+ db.prepare(`INSERT INTO agent_versions (agent_name, version, body, created_at) VALUES (?, ?, ?, ?)`).run(id, nextVersion, body, Date.now());
347
+ broadcast({ type: 'agent.changed', projectId: project.id, id });
348
+ res.status(201).json({ id, body, version: nextVersion });
349
+ }
350
+ catch (err) {
351
+ handleError(res, err);
352
+ }
353
+ });
354
+ // PATCH /api/projects/:projectId/profiles/catalog/:agentId
355
+ // Update a custom agent's body. sr-* agents are read-only (403).
356
+ router.patch('/catalog/:agentId', (req, res) => {
357
+ try {
358
+ const { project, db, broadcast } = ctx(req);
359
+ const agentId = req.params.agentId;
360
+ if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
361
+ res.status(403).json({ error: 'only custom-* agents can be edited from the app' });
362
+ return;
363
+ }
364
+ const body = (req.body?.body ?? '').toString();
365
+ if (!body || body.length === 0) {
366
+ res.status(400).json({ error: 'body is required' });
367
+ return;
368
+ }
369
+ const file = path_1.default.join(project.path, '.claude', 'agents', `${agentId}.md`);
370
+ if (!fs_1.default.existsSync(file)) {
371
+ res.status(404).json({ error: 'agent not found' });
372
+ return;
373
+ }
374
+ fs_1.default.writeFileSync(file, body, 'utf8');
375
+ const maxVersion = db
376
+ .prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM agent_versions WHERE agent_name = ?`)
377
+ .get(agentId).v;
378
+ const nextVersion = maxVersion + 1;
379
+ db.prepare(`INSERT INTO agent_versions (agent_name, version, body, created_at) VALUES (?, ?, ?, ?)`).run(agentId, nextVersion, body, Date.now());
380
+ broadcast({ type: 'agent.changed', projectId: project.id, id: agentId });
381
+ res.json({ id: agentId, body, version: nextVersion });
382
+ }
383
+ catch (err) {
384
+ handleError(res, err);
385
+ }
386
+ });
387
+ // DELETE /api/projects/:projectId/profiles/catalog/:agentId
388
+ // Only permitted for custom-* agents.
389
+ router.delete('/catalog/:agentId', (req, res) => {
390
+ try {
391
+ const { project, broadcast } = ctx(req);
392
+ const agentId = req.params.agentId;
393
+ if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
394
+ res.status(403).json({ error: 'only custom-* agents can be deleted' });
395
+ return;
396
+ }
397
+ const file = path_1.default.join(project.path, '.claude', 'agents', `${agentId}.md`);
398
+ if (!fs_1.default.existsSync(file)) {
399
+ res.status(404).json({ error: 'agent not found' });
400
+ return;
401
+ }
402
+ fs_1.default.unlinkSync(file);
403
+ broadcast({ type: 'agent.changed', projectId: project.id, id: agentId, deleted: true });
404
+ res.json({ ok: true });
405
+ }
406
+ catch (err) {
407
+ handleError(res, err);
408
+ }
409
+ });
410
+ // POST /api/projects/:projectId/profiles/catalog/test
411
+ // Smoke-test a draft body against a sample task without writing to disk.
412
+ // Body: { agentId?: string, draftBody: string, sampleTask: string }
413
+ // Persists the result to agent_tests; returns { output, tokens, durationMs }.
414
+ router.post('/catalog/test', async (req, res) => {
415
+ try {
416
+ const { project, db } = ctx(req);
417
+ const agentId = (req.body?.agentId ?? '').toString().trim() || 'draft';
418
+ const draftBody = (req.body?.draftBody ?? '').toString();
419
+ const sampleTask = (req.body?.sampleTask ?? '').toString().trim();
420
+ if (!draftBody) {
421
+ res.status(400).json({ error: 'draftBody is required' });
422
+ return;
423
+ }
424
+ if (!sampleTask) {
425
+ res.status(400).json({ error: 'sampleTask is required' });
426
+ return;
427
+ }
428
+ const result = await (0, agent_generator_1.testCustomAgent)(project.path, { draftBody, sampleTask });
429
+ db.prepare(`INSERT INTO agent_tests (agent_name, draft_hash, sample_task_id, tokens, duration_ms, output, created_at)
430
+ VALUES (?, ?, ?, ?, ?, ?, ?)`).run(agentId, result.draftHash, null, result.tokens, result.durationMs, result.output, Date.now());
431
+ res.json(result);
432
+ }
433
+ catch (err) {
434
+ handleError(res, err);
435
+ }
436
+ });
437
+ // POST /api/projects/:projectId/profiles/catalog/generate
438
+ // Generate a draft custom agent body via a one-shot claude spawn.
439
+ // Body: { name: string, description: string }
440
+ // Returns { draft: string } — caller (the Studio UI) previews and optionally saves.
441
+ router.post('/catalog/generate', async (req, res) => {
442
+ try {
443
+ const { project } = ctx(req);
444
+ const name = (req.body?.name ?? '').toString().trim();
445
+ const description = (req.body?.description ?? '').toString().trim();
446
+ if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(name)) {
447
+ res.status(400).json({ error: "name must match ^custom-[a-z0-9][a-z0-9-]*$" });
448
+ return;
449
+ }
450
+ if (!description) {
451
+ res.status(400).json({ error: 'description is required' });
452
+ return;
453
+ }
454
+ const draft = await (0, agent_generator_1.generateCustomAgent)(project.path, { name, description });
455
+ res.json({ draft });
456
+ }
457
+ catch (err) {
458
+ handleError(res, err);
459
+ }
460
+ });
461
+ // ── AI Refine: iterative AI editing for custom agents ───────────────────
462
+ // All routes scoped to /catalog/:agentId/refine[/:refineId][/...].
463
+ router.post('/catalog/:agentId/refine', async (req, res) => {
464
+ try {
465
+ const ctxObj = ctx(req);
466
+ const { agentRefineManager } = ctxObj;
467
+ const agentId = req.params.agentId;
468
+ if (!/^custom-[a-z0-9][a-z0-9-]*$/.test(agentId)) {
469
+ res.status(400).json({ error: 'not_a_custom_agent' });
470
+ return;
471
+ }
472
+ const instruction = (req.body?.instruction ?? '').toString().trim();
473
+ if (!instruction) {
474
+ res.status(400).json({ error: 'instruction is required' });
475
+ return;
476
+ }
477
+ const autoTest = req.body?.autoTest !== false;
478
+ try {
479
+ const result = await agentRefineManager.startRefine({ agentId, instruction, autoTest });
480
+ res.status(201).json({ refineId: result.refineId });
481
+ }
482
+ catch (err) {
483
+ const code = err.message;
484
+ if (code === 'not_a_custom_agent') {
485
+ res.status(400).json({ error: 'not_a_custom_agent' });
486
+ return;
487
+ }
488
+ if (code === 'agent_not_found') {
489
+ res.status(404).json({ error: 'agent not found' });
490
+ return;
491
+ }
492
+ throw err;
493
+ }
494
+ }
495
+ catch (err) {
496
+ handleError(res, err);
497
+ }
498
+ });
499
+ router.post('/catalog/:agentId/refine/:refineId/turn', async (req, res) => {
500
+ try {
501
+ const { agentRefineManager, db } = ctx(req);
502
+ const refineId = req.params.refineId;
503
+ const instruction = (req.body?.instruction ?? '').toString().trim();
504
+ if (!instruction) {
505
+ res.status(400).json({ error: 'instruction is required' });
506
+ return;
507
+ }
508
+ // Verify the session belongs to the :agentId in the path, matching the
509
+ // sibling GET/PATCH/DELETE/apply routes — otherwise the path segment is
510
+ // meaningless and the resource-scoping invariant is broken.
511
+ const session = (0, agent_refine_db_1.getRefineSession)(db, refineId);
512
+ if (!session || session.agent_id !== req.params.agentId) {
513
+ res.status(404).json({ error: 'refine session not found' });
514
+ return;
515
+ }
516
+ try {
517
+ await agentRefineManager.sendTurn({ refineId, instruction });
518
+ res.json({ ok: true });
519
+ }
520
+ catch (err) {
521
+ const code = err.message;
522
+ if (code === 'session_not_found') {
523
+ res.status(404).json({ error: 'refine session not found' });
524
+ return;
525
+ }
526
+ if (code === 'turn_in_progress') {
527
+ res.status(409).json({ error: 'a turn is already in progress for this session' });
528
+ return;
529
+ }
530
+ if (code === 'no_session_id') {
531
+ res.status(409).json({ error: 'first turn has not yet completed; cannot resume' });
532
+ return;
533
+ }
534
+ throw err;
535
+ }
536
+ }
537
+ catch (err) {
538
+ handleError(res, err);
539
+ }
540
+ });
541
+ router.get('/catalog/:agentId/refine', (req, res) => {
542
+ try {
543
+ const { db } = ctx(req);
544
+ const sessions = (0, agent_refine_db_1.listRefineSessionsForAgent)(db, req.params.agentId).map(agent_refine_manager_1.refineSessionToJson);
545
+ res.json({ sessions });
546
+ }
547
+ catch (err) {
548
+ handleError(res, err);
549
+ }
550
+ });
551
+ router.get('/catalog/:agentId/refine/:refineId', (req, res) => {
552
+ try {
553
+ const { db } = ctx(req);
554
+ const session = (0, agent_refine_db_1.getRefineSession)(db, req.params.refineId);
555
+ if (!session || session.agent_id !== req.params.agentId) {
556
+ res.status(404).json({ error: 'refine session not found' });
557
+ return;
558
+ }
559
+ res.json((0, agent_refine_manager_1.refineSessionToJson)(session));
560
+ }
561
+ catch (err) {
562
+ handleError(res, err);
563
+ }
564
+ });
565
+ router.patch('/catalog/:agentId/refine/:refineId', (req, res) => {
566
+ try {
567
+ const { agentRefineManager, db } = ctx(req);
568
+ const session = (0, agent_refine_db_1.getRefineSession)(db, req.params.refineId);
569
+ if (!session || session.agent_id !== req.params.agentId) {
570
+ res.status(404).json({ error: 'refine session not found' });
571
+ return;
572
+ }
573
+ if (typeof req.body?.autoTest === 'boolean') {
574
+ agentRefineManager.toggleAutoTest(req.params.refineId, req.body.autoTest);
575
+ }
576
+ const updated = (0, agent_refine_db_1.getRefineSession)(db, req.params.refineId);
577
+ res.json((0, agent_refine_manager_1.refineSessionToJson)(updated));
578
+ }
579
+ catch (err) {
580
+ handleError(res, err);
581
+ }
582
+ });
583
+ router.delete('/catalog/:agentId/refine/:refineId', (req, res) => {
584
+ try {
585
+ const { agentRefineManager, db } = ctx(req);
586
+ const session = (0, agent_refine_db_1.getRefineSession)(db, req.params.refineId);
587
+ if (!session || session.agent_id !== req.params.agentId) {
588
+ res.status(404).json({ error: 'refine session not found' });
589
+ return;
590
+ }
591
+ agentRefineManager.cancel(req.params.refineId);
592
+ res.json({ ok: true });
593
+ }
594
+ catch (err) {
595
+ handleError(res, err);
596
+ }
597
+ });
598
+ router.post('/catalog/:agentId/refine/:refineId/apply', (req, res) => {
599
+ try {
600
+ const { agentRefineManager, db, project, broadcast } = ctx(req);
601
+ const session = (0, agent_refine_db_1.getRefineSession)(db, req.params.refineId);
602
+ if (!session || session.agent_id !== req.params.agentId) {
603
+ res.status(404).json({ error: 'refine session not found' });
604
+ return;
605
+ }
606
+ const force = !!req.body?.force;
607
+ const result = agentRefineManager.apply({ refineId: req.params.refineId, force });
608
+ if (!result.ok) {
609
+ if (result.reason === 'disk_changed' || result.reason === 'name_changed') {
610
+ res.status(409).json({ error: result.reason, reason: result.reason });
611
+ return;
612
+ }
613
+ if (result.reason === 'agent_not_found') {
614
+ res.status(404).json({ error: 'agent not found' });
615
+ return;
616
+ }
617
+ res.status(400).json({ error: result.reason ?? 'apply_failed' });
618
+ return;
619
+ }
620
+ // Re-broadcast standard agent change with the proper projectId so the
621
+ // catalog UI updates (manager broadcasts an empty projectId; ProjectRegistry
622
+ // injects projectId via boundBroadcast, but the explicit emit below is
623
+ // belt-and-braces for any client filtering on `agent.changed`).
624
+ broadcast({ type: 'agent.changed', projectId: project.id, id: req.params.agentId });
625
+ res.json({ ok: true, version: result.version, body: result.body });
626
+ }
627
+ catch (err) {
628
+ handleError(res, err);
629
+ }
630
+ });
631
+ // GET /api/projects/:projectId/profiles/catalog/:agentId/versions
632
+ router.get('/catalog/:agentId/versions', (req, res) => {
633
+ try {
634
+ const { db } = ctx(req);
635
+ const agentId = req.params.agentId;
636
+ const rows = db
637
+ .prepare(`SELECT version, body, created_at AS createdAt FROM agent_versions
638
+ WHERE agent_name = ? ORDER BY version DESC`)
639
+ .all(agentId);
640
+ res.json({ versions: rows });
641
+ }
642
+ catch (err) {
643
+ handleError(res, err);
644
+ }
645
+ });
646
+ // GET /api/projects/:projectId/profiles
647
+ router.get('/', (req, res) => {
648
+ try {
649
+ const { project } = ctx(req);
650
+ res.json({ profiles: (0, profile_manager_1.listProfiles)(project.path) });
651
+ }
652
+ catch (err) {
653
+ handleError(res, err);
654
+ }
655
+ });
656
+ // GET /api/projects/:projectId/profiles/resolve?profile=<name>
657
+ router.get('/resolve', (req, res) => {
658
+ try {
659
+ const { project } = ctx(req);
660
+ const explicit = typeof req.query.profile === 'string' ? req.query.profile : undefined;
661
+ const resolved = (0, profile_manager_1.resolveProfile)(project.path, explicit, project.provider ?? 'claude');
662
+ if (!resolved) {
663
+ res.json({ resolved: null });
664
+ return;
665
+ }
666
+ res.json({ resolved: { name: resolved.name, profile: resolved.profile } });
667
+ }
668
+ catch (err) {
669
+ handleError(res, err);
670
+ }
671
+ });
672
+ // POST /api/projects/:projectId/profiles
673
+ router.post('/', (req, res) => {
674
+ try {
675
+ const { project, broadcast } = ctx(req);
676
+ const body = req.body;
677
+ (0, profile_manager_1.createProfile)(project.path, body, project.provider ?? 'claude');
678
+ broadcast({ type: 'profile.changed', projectId: project.id, name: body.name });
679
+ res.status(201).json({ profile: body });
680
+ }
681
+ catch (err) {
682
+ handleError(res, err);
683
+ }
684
+ });
685
+ // POST /api/projects/:projectId/profiles/:name/duplicate
686
+ router.post('/:name/duplicate', (req, res) => {
687
+ try {
688
+ const { project, broadcast } = ctx(req);
689
+ const newName = (req.body?.name ?? '').toString();
690
+ if (!newName) {
691
+ res.status(400).json({ error: "body field 'name' is required" });
692
+ return;
693
+ }
694
+ const copy = (0, profile_manager_1.duplicateProfile)(project.path, req.params.name, newName, project.provider ?? 'claude');
695
+ broadcast({ type: 'profile.changed', projectId: project.id, name: newName });
696
+ res.status(201).json({ profile: copy });
697
+ }
698
+ catch (err) {
699
+ handleError(res, err);
700
+ }
701
+ });
702
+ // POST /api/projects/:projectId/profiles/:name/rename
703
+ router.post('/:name/rename', (req, res) => {
704
+ try {
705
+ const { project, broadcast } = ctx(req);
706
+ const newName = (req.body?.name ?? '').toString();
707
+ if (!newName) {
708
+ res.status(400).json({ error: "body field 'name' is required" });
709
+ return;
710
+ }
711
+ const renamed = (0, profile_manager_1.renameProfile)(project.path, req.params.name, newName, project.provider ?? 'claude');
712
+ broadcast({ type: 'profile.changed', projectId: project.id, name: newName });
713
+ res.json({ profile: renamed });
714
+ }
715
+ catch (err) {
716
+ handleError(res, err);
717
+ }
718
+ });
719
+ // GET /api/projects/:projectId/profiles/:name
720
+ router.get('/:name', (req, res) => {
721
+ try {
722
+ const { project } = ctx(req);
723
+ res.json({ profile: (0, profile_manager_1.getProfile)(project.path, req.params.name, project.provider ?? 'claude') });
724
+ }
725
+ catch (err) {
726
+ handleError(res, err);
727
+ }
728
+ });
729
+ // PATCH /api/projects/:projectId/profiles/:name
730
+ router.patch('/:name', (req, res) => {
731
+ try {
732
+ const { project, broadcast } = ctx(req);
733
+ const body = req.body;
734
+ if (body.name !== req.params.name) {
735
+ res.status(400).json({ error: "body.name must match path parameter (use /rename to change name)" });
736
+ return;
737
+ }
738
+ (0, profile_manager_1.updateProfile)(project.path, body, project.provider ?? 'claude');
739
+ broadcast({ type: 'profile.changed', projectId: project.id, name: body.name });
740
+ res.json({ profile: body });
741
+ }
742
+ catch (err) {
743
+ handleError(res, err);
744
+ }
745
+ });
746
+ // DELETE /api/projects/:projectId/profiles/:name
747
+ router.delete('/:name', (req, res) => {
748
+ try {
749
+ const { project, broadcast } = ctx(req);
750
+ (0, profile_manager_1.deleteProfile)(project.path, req.params.name);
751
+ broadcast({ type: 'profile.changed', projectId: project.id, name: req.params.name, deleted: true });
752
+ res.json({ ok: true });
753
+ }
754
+ catch (err) {
755
+ handleError(res, err);
756
+ }
757
+ });
758
+ return router;
759
+ }