openspecui 0.2.0 → 0.4.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 (309) hide show
  1. package/dist/cli.mjs +17 -16
  2. package/dist/index.mjs +1 -1
  3. package/dist/{open-CmiqsyCQ.mjs → open-Cw_l4kh9.mjs} +11 -11
  4. package/dist/{src-Bmm_kaB8.mjs → src-BS3LYQ6T.mjs} +1012 -109
  5. package/package.json +6 -5
  6. package/web/assets/index-DU-ty3pA.css +1 -0
  7. package/web/assets/index-noSlrtA-.js +337 -0
  8. package/web/index.html +4 -4
  9. package/web/logo.svg +8 -0
  10. package/web/openspec_pixel_dark.svg +89 -0
  11. package/web/openspec_pixel_light.svg +89 -0
  12. package/web/assets/index-BhzIZ_eQ.js +0 -150
  13. package/web/assets/index-D6x5Q1lW.css +0 -1
  14. package/web/dist/assets/index-BNmb5C1F.css +0 -1
  15. package/web/dist/assets/index-BhzIZ_eQ.js +0 -150
  16. package/web/dist/assets/index-BxBbLX3V.js +0 -170
  17. package/web/dist/assets/index-C4c5ARU5.css +0 -1
  18. package/web/dist/assets/index-CqKOZnDE.css +0 -1
  19. package/web/dist/assets/index-D6x5Q1lW.css +0 -1
  20. package/web/dist/assets/index-Om_P2_SF.js +0 -155
  21. package/web/dist/assets/index-l4gcUD8y.js +0 -382
  22. package/web/dist/index.html +0 -14
  23. /package/web/{dist/assets → assets}/abap-BdImnpbu.js +0 -0
  24. /package/web/{dist/assets → assets}/actionscript-3-CfeIJUat.js +0 -0
  25. /package/web/{dist/assets → assets}/ada-bCR0ucgS.js +0 -0
  26. /package/web/{dist/assets → assets}/andromeeda-C-Jbm3Hp.js +0 -0
  27. /package/web/{dist/assets → assets}/angular-html-CU67Zn6k.js +0 -0
  28. /package/web/{dist/assets → assets}/angular-ts-BwZT4LLn.js +0 -0
  29. /package/web/{dist/assets → assets}/apache-Pmp26Uib.js +0 -0
  30. /package/web/{dist/assets → assets}/apex-DDbsPZ6N.js +0 -0
  31. /package/web/{dist/assets → assets}/apl-dKokRX4l.js +0 -0
  32. /package/web/{dist/assets → assets}/applescript-Co6uUVPk.js +0 -0
  33. /package/web/{dist/assets → assets}/ara-BRHolxvo.js +0 -0
  34. /package/web/{dist/assets → assets}/asciidoc-Dv7Oe6Be.js +0 -0
  35. /package/web/{dist/assets → assets}/asm-D_Q5rh1f.js +0 -0
  36. /package/web/{dist/assets → assets}/astro-CbQHKStN.js +0 -0
  37. /package/web/{dist/assets → assets}/aurora-x-D-2ljcwZ.js +0 -0
  38. /package/web/{dist/assets → assets}/awk-DMzUqQB5.js +0 -0
  39. /package/web/{dist/assets → assets}/ayu-dark-Cv9koXgw.js +0 -0
  40. /package/web/{dist/assets → assets}/ballerina-BFfxhgS-.js +0 -0
  41. /package/web/{dist/assets → assets}/bat-BkioyH1T.js +0 -0
  42. /package/web/{dist/assets → assets}/beancount-k_qm7-4y.js +0 -0
  43. /package/web/{dist/assets → assets}/berry-uYugtg8r.js +0 -0
  44. /package/web/{dist/assets → assets}/bibtex-CHM0blh-.js +0 -0
  45. /package/web/{dist/assets → assets}/bicep-Bmn6On1c.js +0 -0
  46. /package/web/{dist/assets → assets}/blade-DVc8C-J4.js +0 -0
  47. /package/web/{dist/assets → assets}/bsl-BO_Y6i37.js +0 -0
  48. /package/web/{dist/assets → assets}/c-BIGW1oBm.js +0 -0
  49. /package/web/{dist/assets → assets}/cadence-Bv_4Rxtq.js +0 -0
  50. /package/web/{dist/assets → assets}/cairo-KRGpt6FW.js +0 -0
  51. /package/web/{dist/assets → assets}/catppuccin-frappe-DFWUc33u.js +0 -0
  52. /package/web/{dist/assets → assets}/catppuccin-latte-C9dUb6Cb.js +0 -0
  53. /package/web/{dist/assets → assets}/catppuccin-macchiato-DQyhUUbL.js +0 -0
  54. /package/web/{dist/assets → assets}/catppuccin-mocha-D87Tk5Gz.js +0 -0
  55. /package/web/{dist/assets → assets}/clarity-D53aC0YG.js +0 -0
  56. /package/web/{dist/assets → assets}/clojure-P80f7IUj.js +0 -0
  57. /package/web/{dist/assets → assets}/cmake-D1j8_8rp.js +0 -0
  58. /package/web/{dist/assets → assets}/cobol-nwyudZeR.js +0 -0
  59. /package/web/{dist/assets → assets}/codeowners-Bp6g37R7.js +0 -0
  60. /package/web/{dist/assets → assets}/codeql-DsOJ9woJ.js +0 -0
  61. /package/web/{dist/assets → assets}/coffee-Ch7k5sss.js +0 -0
  62. /package/web/{dist/assets → assets}/common-lisp-Cg-RD9OK.js +0 -0
  63. /package/web/{dist/assets → assets}/coq-DkFqJrB1.js +0 -0
  64. /package/web/{dist/assets → assets}/cpp-CofmeUqb.js +0 -0
  65. /package/web/{dist/assets → assets}/crystal-tKQVLTB8.js +0 -0
  66. /package/web/{dist/assets → assets}/csharp-K5feNrxe.js +0 -0
  67. /package/web/{dist/assets → assets}/css-DPfMkruS.js +0 -0
  68. /package/web/{dist/assets → assets}/csv-fuZLfV_i.js +0 -0
  69. /package/web/{dist/assets → assets}/cue-D82EKSYY.js +0 -0
  70. /package/web/{dist/assets → assets}/cypher-COkxafJQ.js +0 -0
  71. /package/web/{dist/assets → assets}/d-85-TOEBH.js +0 -0
  72. /package/web/{dist/assets → assets}/dark-plus-C3mMm8J8.js +0 -0
  73. /package/web/{dist/assets → assets}/dart-CF10PKvl.js +0 -0
  74. /package/web/{dist/assets → assets}/dax-CEL-wOlO.js +0 -0
  75. /package/web/{dist/assets → assets}/desktop-BmXAJ9_W.js +0 -0
  76. /package/web/{dist/assets → assets}/diff-D97Zzqfu.js +0 -0
  77. /package/web/{dist/assets → assets}/docker-BcOcwvcX.js +0 -0
  78. /package/web/{dist/assets → assets}/dotenv-Da5cRb03.js +0 -0
  79. /package/web/{dist/assets → assets}/dracula-BzJJZx-M.js +0 -0
  80. /package/web/{dist/assets → assets}/dracula-soft-BXkSAIEj.js +0 -0
  81. /package/web/{dist/assets → assets}/dream-maker-BtqSS_iP.js +0 -0
  82. /package/web/{dist/assets → assets}/edge-BkV0erSs.js +0 -0
  83. /package/web/{dist/assets → assets}/elixir-CDX3lj18.js +0 -0
  84. /package/web/{dist/assets → assets}/elm-DbKCFpqz.js +0 -0
  85. /package/web/{dist/assets → assets}/emacs-lisp-C9XAeP06.js +0 -0
  86. /package/web/{dist/assets → assets}/erb-BOJIQeun.js +0 -0
  87. /package/web/{dist/assets → assets}/erlang-DsQrWhSR.js +0 -0
  88. /package/web/{dist/assets → assets}/everforest-dark-BgDCqdQA.js +0 -0
  89. /package/web/{dist/assets → assets}/everforest-light-C8M2exoo.js +0 -0
  90. /package/web/{dist/assets → assets}/fennel-BYunw83y.js +0 -0
  91. /package/web/{dist/assets → assets}/fish-BvzEVeQv.js +0 -0
  92. /package/web/{dist/assets → assets}/fluent-C4IJs8-o.js +0 -0
  93. /package/web/{dist/assets → assets}/fortran-fixed-form-BZjJHVRy.js +0 -0
  94. /package/web/{dist/assets → assets}/fortran-free-form-D22FLkUw.js +0 -0
  95. /package/web/{dist/assets → assets}/fsharp-CXgrBDvD.js +0 -0
  96. /package/web/{dist/assets → assets}/gdresource-B7Tvp0Sc.js +0 -0
  97. /package/web/{dist/assets → assets}/gdscript-DTMYz4Jt.js +0 -0
  98. /package/web/{dist/assets → assets}/gdshader-DkwncUOv.js +0 -0
  99. /package/web/{dist/assets → assets}/genie-D0YGMca9.js +0 -0
  100. /package/web/{dist/assets → assets}/gherkin-DyxjwDmM.js +0 -0
  101. /package/web/{dist/assets → assets}/git-commit-F4YmCXRG.js +0 -0
  102. /package/web/{dist/assets → assets}/git-rebase-r7XF79zn.js +0 -0
  103. /package/web/{dist/assets → assets}/github-dark-DHJKELXO.js +0 -0
  104. /package/web/{dist/assets → assets}/github-dark-default-Cuk6v7N8.js +0 -0
  105. /package/web/{dist/assets → assets}/github-dark-dimmed-DH5Ifo-i.js +0 -0
  106. /package/web/{dist/assets → assets}/github-dark-high-contrast-E3gJ1_iC.js +0 -0
  107. /package/web/{dist/assets → assets}/github-light-DAi9KRSo.js +0 -0
  108. /package/web/{dist/assets → assets}/github-light-default-D7oLnXFd.js +0 -0
  109. /package/web/{dist/assets → assets}/github-light-high-contrast-BfjtVDDH.js +0 -0
  110. /package/web/{dist/assets → assets}/gleam-BspZqrRM.js +0 -0
  111. /package/web/{dist/assets → assets}/glimmer-js-Rg0-pVw9.js +0 -0
  112. /package/web/{dist/assets → assets}/glimmer-ts-U6CK756n.js +0 -0
  113. /package/web/{dist/assets → assets}/glsl-DplSGwfg.js +0 -0
  114. /package/web/{dist/assets → assets}/gnuplot-DdkO51Og.js +0 -0
  115. /package/web/{dist/assets → assets}/go-Dn2_MT6a.js +0 -0
  116. /package/web/{dist/assets → assets}/graphql-ChdNCCLP.js +0 -0
  117. /package/web/{dist/assets → assets}/groovy-gcz8RCvz.js +0 -0
  118. /package/web/{dist/assets → assets}/gruvbox-dark-hard-CFHQjOhq.js +0 -0
  119. /package/web/{dist/assets → assets}/gruvbox-dark-medium-GsRaNv29.js +0 -0
  120. /package/web/{dist/assets → assets}/gruvbox-dark-soft-CVdnzihN.js +0 -0
  121. /package/web/{dist/assets → assets}/gruvbox-light-hard-CH1njM8p.js +0 -0
  122. /package/web/{dist/assets → assets}/gruvbox-light-medium-DRw_LuNl.js +0 -0
  123. /package/web/{dist/assets → assets}/gruvbox-light-soft-hJgmCMqR.js +0 -0
  124. /package/web/{dist/assets → assets}/hack-CaT9iCJl.js +0 -0
  125. /package/web/{dist/assets → assets}/haml-B8DHNrY2.js +0 -0
  126. /package/web/{dist/assets → assets}/handlebars-BL8al0AC.js +0 -0
  127. /package/web/{dist/assets → assets}/haskell-Df6bDoY_.js +0 -0
  128. /package/web/{dist/assets → assets}/haxe-CzTSHFRz.js +0 -0
  129. /package/web/{dist/assets → assets}/hcl-BWvSN4gD.js +0 -0
  130. /package/web/{dist/assets → assets}/hjson-D5-asLiD.js +0 -0
  131. /package/web/{dist/assets → assets}/hlsl-D3lLCCz7.js +0 -0
  132. /package/web/{dist/assets → assets}/houston-DnULxvSX.js +0 -0
  133. /package/web/{dist/assets → assets}/html-GMplVEZG.js +0 -0
  134. /package/web/{dist/assets → assets}/html-derivative-BFtXZ54Q.js +0 -0
  135. /package/web/{dist/assets → assets}/http-jrhK8wxY.js +0 -0
  136. /package/web/{dist/assets → assets}/hurl-irOxFIW8.js +0 -0
  137. /package/web/{dist/assets → assets}/hxml-Bvhsp5Yf.js +0 -0
  138. /package/web/{dist/assets → assets}/hy-DFXneXwc.js +0 -0
  139. /package/web/{dist/assets → assets}/imba-DGztddWO.js +0 -0
  140. /package/web/{dist/assets → assets}/ini-BEwlwnbL.js +0 -0
  141. /package/web/{dist/assets → assets}/java-CylS5w8V.js +0 -0
  142. /package/web/{dist/assets → assets}/javascript-wDzz0qaB.js +0 -0
  143. /package/web/{dist/assets → assets}/jinja-4LBKfQ-Z.js +0 -0
  144. /package/web/{dist/assets → assets}/jison-wvAkD_A8.js +0 -0
  145. /package/web/{dist/assets → assets}/json-Cp-IABpG.js +0 -0
  146. /package/web/{dist/assets → assets}/json5-C9tS-k6U.js +0 -0
  147. /package/web/{dist/assets → assets}/jsonc-Des-eS-w.js +0 -0
  148. /package/web/{dist/assets → assets}/jsonl-DcaNXYhu.js +0 -0
  149. /package/web/{dist/assets → assets}/jsonnet-DFQXde-d.js +0 -0
  150. /package/web/{dist/assets → assets}/jssm-C2t-YnRu.js +0 -0
  151. /package/web/{dist/assets → assets}/jsx-g9-lgVsj.js +0 -0
  152. /package/web/{dist/assets → assets}/julia-C8NyazO9.js +0 -0
  153. /package/web/{dist/assets → assets}/kanagawa-dragon-CkXjmgJE.js +0 -0
  154. /package/web/{dist/assets → assets}/kanagawa-lotus-CfQXZHmo.js +0 -0
  155. /package/web/{dist/assets → assets}/kanagawa-wave-DWedfzmr.js +0 -0
  156. /package/web/{dist/assets → assets}/kdl-DV7GczEv.js +0 -0
  157. /package/web/{dist/assets → assets}/kotlin-BdnUsdx6.js +0 -0
  158. /package/web/{dist/assets → assets}/kusto-BvAqAH-y.js +0 -0
  159. /package/web/{dist/assets → assets}/laserwave-DUszq2jm.js +0 -0
  160. /package/web/{dist/assets → assets}/latex-BdAV_C_H.js +0 -0
  161. /package/web/{dist/assets → assets}/lean-Bc6EcWN3.js +0 -0
  162. /package/web/{dist/assets → assets}/less-B1dDrJ26.js +0 -0
  163. /package/web/{dist/assets → assets}/light-plus-B7mTdjB0.js +0 -0
  164. /package/web/{dist/assets → assets}/liquid-DYVedYrR.js +0 -0
  165. /package/web/{dist/assets → assets}/llvm-BtvRca6l.js +0 -0
  166. /package/web/{dist/assets → assets}/log-2UxHyX5q.js +0 -0
  167. /package/web/{dist/assets → assets}/logo-BtOb2qkB.js +0 -0
  168. /package/web/{dist/assets → assets}/lua-BbnMAYS6.js +0 -0
  169. /package/web/{dist/assets → assets}/luau-CXu1NL6O.js +0 -0
  170. /package/web/{dist/assets → assets}/make-CHLpvVh8.js +0 -0
  171. /package/web/{dist/assets → assets}/markdown-Cvjx9yec.js +0 -0
  172. /package/web/{dist/assets → assets}/marko-CPi9NSCl.js +0 -0
  173. /package/web/{dist/assets → assets}/material-theme-D5KoaKCx.js +0 -0
  174. /package/web/{dist/assets → assets}/material-theme-darker-BfHTSMKl.js +0 -0
  175. /package/web/{dist/assets → assets}/material-theme-lighter-B0m2ddpp.js +0 -0
  176. /package/web/{dist/assets → assets}/material-theme-ocean-CyktbL80.js +0 -0
  177. /package/web/{dist/assets → assets}/material-theme-palenight-Csfq5Kiy.js +0 -0
  178. /package/web/{dist/assets → assets}/matlab-D7o27uSR.js +0 -0
  179. /package/web/{dist/assets → assets}/mdc-DUICxH0z.js +0 -0
  180. /package/web/{dist/assets → assets}/mdx-Cmh6b_Ma.js +0 -0
  181. /package/web/{dist/assets → assets}/mermaid-DKYwYmdq.js +0 -0
  182. /package/web/{dist/assets → assets}/min-dark-CafNBF8u.js +0 -0
  183. /package/web/{dist/assets → assets}/min-light-CTRr51gU.js +0 -0
  184. /package/web/{dist/assets → assets}/mipsasm-CKIfxQSi.js +0 -0
  185. /package/web/{dist/assets → assets}/mojo-1DNp92w6.js +0 -0
  186. /package/web/{dist/assets → assets}/monokai-D4h5O-jR.js +0 -0
  187. /package/web/{dist/assets → assets}/move-Bu9oaDYs.js +0 -0
  188. /package/web/{dist/assets → assets}/narrat-DRg8JJMk.js +0 -0
  189. /package/web/{dist/assets → assets}/nextflow-BrzmwbiE.js +0 -0
  190. /package/web/{dist/assets → assets}/nginx-DknmC5AR.js +0 -0
  191. /package/web/{dist/assets → assets}/night-owl-C39BiMTA.js +0 -0
  192. /package/web/{dist/assets → assets}/nim-CVrawwO9.js +0 -0
  193. /package/web/{dist/assets → assets}/nix-c8nO5XWb.js +0 -0
  194. /package/web/{dist/assets → assets}/nord-Ddv68eIx.js +0 -0
  195. /package/web/{dist/assets → assets}/nushell-C-sUppwS.js +0 -0
  196. /package/web/{dist/assets → assets}/objective-c-DXmwc3jG.js +0 -0
  197. /package/web/{dist/assets → assets}/objective-cpp-CLxacb5B.js +0 -0
  198. /package/web/{dist/assets → assets}/ocaml-C0hk2d4L.js +0 -0
  199. /package/web/{dist/assets → assets}/one-dark-pro-DVMEJ2y_.js +0 -0
  200. /package/web/{dist/assets → assets}/one-light-PoHY5YXO.js +0 -0
  201. /package/web/{dist/assets → assets}/openscad-C4EeE6gA.js +0 -0
  202. /package/web/{dist/assets → assets}/pascal-D93ZcfNL.js +0 -0
  203. /package/web/{dist/assets → assets}/perl-C0TMdlhV.js +0 -0
  204. /package/web/{dist/assets → assets}/php-CDn_0X-4.js +0 -0
  205. /package/web/{dist/assets → assets}/pkl-u5AG7uiY.js +0 -0
  206. /package/web/{dist/assets → assets}/plastic-3e1v2bzS.js +0 -0
  207. /package/web/{dist/assets → assets}/plsql-ChMvpjG-.js +0 -0
  208. /package/web/{dist/assets → assets}/po-BTJTHyun.js +0 -0
  209. /package/web/{dist/assets → assets}/poimandres-CS3Unz2-.js +0 -0
  210. /package/web/{dist/assets → assets}/polar-C0HS_06l.js +0 -0
  211. /package/web/{dist/assets → assets}/postcss-CXtECtnM.js +0 -0
  212. /package/web/{dist/assets → assets}/powerquery-CEu0bR-o.js +0 -0
  213. /package/web/{dist/assets → assets}/powershell-Dpen1YoG.js +0 -0
  214. /package/web/{dist/assets → assets}/prisma-Dd19v3D-.js +0 -0
  215. /package/web/{dist/assets → assets}/prolog-CbFg5uaA.js +0 -0
  216. /package/web/{dist/assets → assets}/proto-DyJlTyXw.js +0 -0
  217. /package/web/{dist/assets → assets}/pug-CGlum2m_.js +0 -0
  218. /package/web/{dist/assets → assets}/puppet-BMWR74SV.js +0 -0
  219. /package/web/{dist/assets → assets}/purescript-CklMAg4u.js +0 -0
  220. /package/web/{dist/assets → assets}/python-B6aJPvgy.js +0 -0
  221. /package/web/{dist/assets → assets}/qml-3beO22l8.js +0 -0
  222. /package/web/{dist/assets → assets}/qmldir-C8lEn-DE.js +0 -0
  223. /package/web/{dist/assets → assets}/qss-IeuSbFQv.js +0 -0
  224. /package/web/{dist/assets → assets}/r-DiinP2Uv.js +0 -0
  225. /package/web/{dist/assets → assets}/racket-BqYA7rlc.js +0 -0
  226. /package/web/{dist/assets → assets}/raku-DXvB9xmW.js +0 -0
  227. /package/web/{dist/assets → assets}/razor-CE9lU5zL.js +0 -0
  228. /package/web/{dist/assets → assets}/red-bN70gL4F.js +0 -0
  229. /package/web/{dist/assets → assets}/reg-C-SQnVFl.js +0 -0
  230. /package/web/{dist/assets → assets}/regexp-CDVJQ6XC.js +0 -0
  231. /package/web/{dist/assets → assets}/rel-C3B-1QV4.js +0 -0
  232. /package/web/{dist/assets → assets}/riscv-BM1_JUlF.js +0 -0
  233. /package/web/{dist/assets → assets}/rose-pine-dawn-DHQR4-dF.js +0 -0
  234. /package/web/{dist/assets → assets}/rose-pine-moon-D4_iv3hh.js +0 -0
  235. /package/web/{dist/assets → assets}/rose-pine-qdsjHGoJ.js +0 -0
  236. /package/web/{dist/assets → assets}/rosmsg-BJDFO7_C.js +0 -0
  237. /package/web/{dist/assets → assets}/rst-B0xPkSld.js +0 -0
  238. /package/web/{dist/assets → assets}/ruby-BvKwtOVI.js +0 -0
  239. /package/web/{dist/assets → assets}/rust-B1yitclQ.js +0 -0
  240. /package/web/{dist/assets → assets}/sas-cz2c8ADy.js +0 -0
  241. /package/web/{dist/assets → assets}/sass-Cj5Yp3dK.js +0 -0
  242. /package/web/{dist/assets → assets}/scala-C151Ov-r.js +0 -0
  243. /package/web/{dist/assets → assets}/scheme-C98Dy4si.js +0 -0
  244. /package/web/{dist/assets → assets}/scss-OYdSNvt2.js +0 -0
  245. /package/web/{dist/assets → assets}/sdbl-DVxCFoDh.js +0 -0
  246. /package/web/{dist/assets → assets}/shaderlab-Dg9Lc6iA.js +0 -0
  247. /package/web/{dist/assets → assets}/shellscript-Yzrsuije.js +0 -0
  248. /package/web/{dist/assets → assets}/shellsession-BADoaaVG.js +0 -0
  249. /package/web/{dist/assets → assets}/slack-dark-BthQWCQV.js +0 -0
  250. /package/web/{dist/assets → assets}/slack-ochin-DqwNpetd.js +0 -0
  251. /package/web/{dist/assets → assets}/smalltalk-BERRCDM3.js +0 -0
  252. /package/web/{dist/assets → assets}/snazzy-light-Bw305WKR.js +0 -0
  253. /package/web/{dist/assets → assets}/solarized-dark-DXbdFlpD.js +0 -0
  254. /package/web/{dist/assets → assets}/solarized-light-L9t79GZl.js +0 -0
  255. /package/web/{dist/assets → assets}/solidity-rGO070M0.js +0 -0
  256. /package/web/{dist/assets → assets}/soy-Brmx7dQM.js +0 -0
  257. /package/web/{dist/assets → assets}/sparql-rVzFXLq3.js +0 -0
  258. /package/web/{dist/assets → assets}/splunk-BtCnVYZw.js +0 -0
  259. /package/web/{dist/assets → assets}/sql-BLtJtn59.js +0 -0
  260. /package/web/{dist/assets → assets}/ssh-config-_ykCGR6B.js +0 -0
  261. /package/web/{dist/assets → assets}/stata-BH5u7GGu.js +0 -0
  262. /package/web/{dist/assets → assets}/stylus-BEDo0Tqx.js +0 -0
  263. /package/web/{dist/assets → assets}/svelte-3Dk4HxPD.js +0 -0
  264. /package/web/{dist/assets → assets}/swift-Dg5xB15N.js +0 -0
  265. /package/web/{dist/assets → assets}/synthwave-84-CbfX1IO0.js +0 -0
  266. /package/web/{dist/assets → assets}/system-verilog-CnnmHF94.js +0 -0
  267. /package/web/{dist/assets → assets}/systemd-4A_iFExJ.js +0 -0
  268. /package/web/{dist/assets → assets}/talonscript-CkByrt1z.js +0 -0
  269. /package/web/{dist/assets → assets}/tasl-QIJgUcNo.js +0 -0
  270. /package/web/{dist/assets → assets}/tcl-dwOrl1Do.js +0 -0
  271. /package/web/{dist/assets → assets}/templ-W15q3VgB.js +0 -0
  272. /package/web/{dist/assets → assets}/terraform-BETggiCN.js +0 -0
  273. /package/web/{dist/assets → assets}/tex-CxkMU7Pf.js +0 -0
  274. /package/web/{dist/assets → assets}/tokyo-night-hegEt444.js +0 -0
  275. /package/web/{dist/assets → assets}/toml-vGWfd6FD.js +0 -0
  276. /package/web/{dist/assets → assets}/ts-tags-zn1MmPIZ.js +0 -0
  277. /package/web/{dist/assets → assets}/tsv-B_m7g4N7.js +0 -0
  278. /package/web/{dist/assets → assets}/tsx-COt5Ahok.js +0 -0
  279. /package/web/{dist/assets → assets}/turtle-BsS91CYL.js +0 -0
  280. /package/web/{dist/assets → assets}/twig-CO9l9SDP.js +0 -0
  281. /package/web/{dist/assets → assets}/typescript-BPQ3VLAy.js +0 -0
  282. /package/web/{dist/assets → assets}/typespec-BGHnOYBU.js +0 -0
  283. /package/web/{dist/assets → assets}/typst-DHCkPAjA.js +0 -0
  284. /package/web/{dist/assets → assets}/v-BcVCzyr7.js +0 -0
  285. /package/web/{dist/assets → assets}/vala-CsfeWuGM.js +0 -0
  286. /package/web/{dist/assets → assets}/vb-D17OF-Vu.js +0 -0
  287. /package/web/{dist/assets → assets}/verilog-BQ8w6xss.js +0 -0
  288. /package/web/{dist/assets → assets}/vesper-DU1UobuO.js +0 -0
  289. /package/web/{dist/assets → assets}/vhdl-CeAyd5Ju.js +0 -0
  290. /package/web/{dist/assets → assets}/viml-CJc9bBzg.js +0 -0
  291. /package/web/{dist/assets → assets}/vitesse-black-Bkuqu6BP.js +0 -0
  292. /package/web/{dist/assets → assets}/vitesse-dark-D0r3Knsf.js +0 -0
  293. /package/web/{dist/assets → assets}/vitesse-light-CVO1_9PV.js +0 -0
  294. /package/web/{dist/assets → assets}/vue-DnHKYNfI.js +0 -0
  295. /package/web/{dist/assets → assets}/vue-html-CChd_i61.js +0 -0
  296. /package/web/{dist/assets → assets}/vue-vine-8moa0y9V.js +0 -0
  297. /package/web/{dist/assets → assets}/vyper-CDx5xZoG.js +0 -0
  298. /package/web/{dist/assets → assets}/wasm-CG6Dc4jp.js +0 -0
  299. /package/web/{dist/assets → assets}/wasm-MzD3tlZU.js +0 -0
  300. /package/web/{dist/assets → assets}/wenyan-BV7otONQ.js +0 -0
  301. /package/web/{dist/assets → assets}/wgsl-Dx-B1_4e.js +0 -0
  302. /package/web/{dist/assets → assets}/wikitext-BhOHFoWU.js +0 -0
  303. /package/web/{dist/assets → assets}/wit-5i3qLPDT.js +0 -0
  304. /package/web/{dist/assets → assets}/wolfram-lXgVvXCa.js +0 -0
  305. /package/web/{dist/assets → assets}/xml-sdJ4AIDG.js +0 -0
  306. /package/web/{dist/assets → assets}/xsl-CtQFsRM5.js +0 -0
  307. /package/web/{dist/assets → assets}/yaml-Buea-lGh.js +0 -0
  308. /package/web/{dist/assets → assets}/zenscript-DVFEvuxE.js +0 -0
  309. /package/web/{dist/assets → assets}/zig-VOosw3JB.js +0 -0
@@ -4,12 +4,15 @@ import { Http2ServerRequest } from "http2";
4
4
  import { Readable } from "stream";
5
5
  import crypto from "crypto";
6
6
  import { EventEmitter } from "events";
7
- import { mkdir, readFile, readdir, rename, stat, writeFile } from "fs/promises";
7
+ import { mkdir, readFile, rename, writeFile } from "fs/promises";
8
8
  import { join } from "path";
9
- import { watch } from "fs";
10
- import { existsSync, readFileSync as readFileSync$1, statSync as statSync$1 } from "node:fs";
9
+ import { AsyncLocalStorage } from "node:async_hooks";
10
+ import { readFile as readFile$1, readdir, stat } from "node:fs/promises";
11
+ import { dirname as dirname$1, join as join$1, resolve as resolve$1 } from "node:path";
12
+ import { existsSync, readFileSync, statSync, watch } from "node:fs";
13
+ import { watch as watch$1 } from "fs";
14
+ import { spawn } from "child_process";
11
15
  import { createServer as createServer$1 } from "node:net";
12
- import { dirname as dirname$1, join as join$1 } from "node:path";
13
16
  import { fileURLToPath } from "node:url";
14
17
 
15
18
  //#region rolldown:runtime
@@ -376,7 +379,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
376
379
  });
377
380
  if (!chunk) {
378
381
  if (i === 1) {
379
- await new Promise((resolve$1) => setTimeout(resolve$1));
382
+ await new Promise((resolve$2) => setTimeout(resolve$2));
380
383
  maxReadCount = 3;
381
384
  continue;
382
385
  }
@@ -6653,8 +6656,8 @@ var Unpromise = class Unpromise$1 {
6653
6656
  status: "fulfilled",
6654
6657
  value
6655
6658
  };
6656
- subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve$1 }) => {
6657
- resolve$1(value);
6659
+ subscribers === null || subscribers === void 0 || subscribers.forEach(({ resolve: resolve$2 }) => {
6660
+ resolve$2(value);
6658
6661
  });
6659
6662
  });
6660
6663
  if ("catch" in thenReturn) thenReturn.catch((reason) => {
@@ -6801,14 +6804,14 @@ function resolveSelfTuple(promise) {
6801
6804
  /** VENDORED (Future) PROMISE UTILITIES */
6802
6805
  /** Reference implementation of https://github.com/tc39/proposal-promise-with-resolvers */
6803
6806
  function withResolvers() {
6804
- let resolve$1;
6807
+ let resolve$2;
6805
6808
  let reject;
6806
6809
  return {
6807
6810
  promise: new Promise((_resolve, _reject) => {
6808
- resolve$1 = _resolve;
6811
+ resolve$2 = _resolve;
6809
6812
  reject = _reject;
6810
6813
  }),
6811
- resolve: resolve$1,
6814
+ resolve: resolve$2,
6812
6815
  reject
6813
6816
  };
6814
6817
  }
@@ -6864,8 +6867,8 @@ function timerResource(ms) {
6864
6867
  let timer = null;
6865
6868
  return makeResource({ start() {
6866
6869
  if (timer) throw new Error("Timer already started");
6867
- return new Promise((resolve$1) => {
6868
- timer = setTimeout(() => resolve$1(disposablePromiseTimerResult), ms);
6870
+ return new Promise((resolve$2) => {
6871
+ timer = setTimeout(() => resolve$2(disposablePromiseTimerResult), ms);
6869
6872
  });
6870
6873
  } }, () => {
6871
6874
  if (timer) clearTimeout(timer);
@@ -7082,14 +7085,14 @@ function _takeWithGrace() {
7082
7085
  return _takeWithGrace.apply(this, arguments);
7083
7086
  }
7084
7087
  function createDeferred() {
7085
- let resolve$1;
7088
+ let resolve$2;
7086
7089
  let reject;
7087
7090
  return {
7088
7091
  promise: new Promise((res, rej) => {
7089
- resolve$1 = res;
7092
+ resolve$2 = res;
7090
7093
  reject = rej;
7091
7094
  }),
7092
- resolve: resolve$1,
7095
+ resolve: resolve$2,
7093
7096
  reject
7094
7097
  };
7095
7098
  }
@@ -8703,8 +8706,8 @@ function getWSConnectionHandler(opts) {
8703
8706
  try {
8704
8707
  var _usingCtx = (0, import_usingCtx.default)();
8705
8708
  const iterator = _usingCtx.a(iteratorResource(iterable));
8706
- const abortPromise = new Promise((resolve$1) => {
8707
- abortController$1.signal.onabort = () => resolve$1("abort");
8709
+ const abortPromise = new Promise((resolve$2) => {
8710
+ abortController$1.signal.onabort = () => resolve$2("abort");
8708
8711
  });
8709
8712
  let next;
8710
8713
  let result$1;
@@ -12716,6 +12719,390 @@ var Validator = class {
12716
12719
  }
12717
12720
  };
12718
12721
 
12722
+ //#endregion
12723
+ //#region ../core/src/reactive-fs/reactive-state.ts
12724
+ /**
12725
+ * 全局的 AsyncLocalStorage,用于在异步调用链中传递 ReactiveContext
12726
+ * 这是实现依赖收集的核心机制
12727
+ */
12728
+ const contextStorage = new AsyncLocalStorage();
12729
+ /**
12730
+ * 响应式状态类,类似 Signal.State
12731
+ *
12732
+ * 核心机制:
12733
+ * - get() 时自动注册到当前 ReactiveContext 的依赖列表
12734
+ * - set() 时如果值变化,通知所有依赖的 Context
12735
+ */
12736
+ var ReactiveState = class {
12737
+ currentValue;
12738
+ equals;
12739
+ /** 所有依赖此状态的 Context */
12740
+ subscribers = /* @__PURE__ */ new Set();
12741
+ constructor(initialValue, options) {
12742
+ this.currentValue = initialValue;
12743
+ this.equals = options?.equals ?? ((a, b) => a === b);
12744
+ }
12745
+ /**
12746
+ * 获取当前值
12747
+ * 如果在 ReactiveContext 中调用,会自动注册依赖
12748
+ */
12749
+ get() {
12750
+ const context = contextStorage.getStore();
12751
+ if (context) {
12752
+ context.track(this);
12753
+ this.subscribers.add(context);
12754
+ }
12755
+ return this.currentValue;
12756
+ }
12757
+ /**
12758
+ * 设置新值
12759
+ * 如果值变化,通知所有订阅者
12760
+ * @returns 是否发生了变化
12761
+ */
12762
+ set(newValue) {
12763
+ if (this.equals(this.currentValue, newValue)) return false;
12764
+ this.currentValue = newValue;
12765
+ for (const context of this.subscribers) context.notifyChange();
12766
+ return true;
12767
+ }
12768
+ /**
12769
+ * 取消订阅
12770
+ * 当 Context 销毁时调用
12771
+ */
12772
+ unsubscribe(context) {
12773
+ this.subscribers.delete(context);
12774
+ }
12775
+ /**
12776
+ * 获取当前订阅者数量(用于调试)
12777
+ */
12778
+ get subscriberCount() {
12779
+ return this.subscribers.size;
12780
+ }
12781
+ };
12782
+
12783
+ //#endregion
12784
+ //#region ../core/src/reactive-fs/reactive-context.ts
12785
+ function createPromiseWithResolvers() {
12786
+ let resolve$2;
12787
+ let reject;
12788
+ return {
12789
+ promise: new Promise((res, rej) => {
12790
+ resolve$2 = res;
12791
+ reject = rej;
12792
+ }),
12793
+ resolve: resolve$2,
12794
+ reject
12795
+ };
12796
+ }
12797
+ /**
12798
+ * 响应式上下文,管理依赖收集和变更通知
12799
+ *
12800
+ * 核心机制:
12801
+ * - 在 stream() 中执行任务时,通过 AsyncLocalStorage 传递 this
12802
+ * - 任务中的所有 ReactiveState.get() 调用都会自动注册依赖
12803
+ * - 当任何依赖变更时,重新执行任务并 yield 新结果
12804
+ */
12805
+ var ReactiveContext = class {
12806
+ /** 当前追踪的依赖 */
12807
+ dependencies = /* @__PURE__ */ new Set();
12808
+ /** 等待变更的 Promise */
12809
+ changePromise;
12810
+ /** 是否已销毁 */
12811
+ destroyed = false;
12812
+ /**
12813
+ * 追踪依赖
12814
+ * 由 ReactiveState.get() 调用
12815
+ */
12816
+ track(state) {
12817
+ if (!this.destroyed) this.dependencies.add(state);
12818
+ }
12819
+ /**
12820
+ * 通知变更
12821
+ * 由 ReactiveState.set() 调用
12822
+ */
12823
+ notifyChange() {
12824
+ if (!this.destroyed && this.changePromise) this.changePromise.resolve();
12825
+ }
12826
+ /**
12827
+ * 运行响应式任务流
12828
+ * 每次依赖变更时重新执行任务并 yield 结果
12829
+ *
12830
+ * @param task 要执行的异步任务
12831
+ * @param signal 用于取消的 AbortSignal
12832
+ */
12833
+ async *stream(task, signal) {
12834
+ try {
12835
+ while (!signal?.aborted && !this.destroyed) {
12836
+ this.clearDependencies();
12837
+ this.changePromise = createPromiseWithResolvers();
12838
+ yield await contextStorage.run(this, task);
12839
+ if (this.dependencies.size === 0) break;
12840
+ await Promise.race([this.changePromise.promise, signal ? this.waitForAbort(signal) : new Promise(() => {})]);
12841
+ if (signal?.aborted) break;
12842
+ }
12843
+ } finally {
12844
+ this.destroy();
12845
+ }
12846
+ }
12847
+ /**
12848
+ * 执行一次任务(非响应式)
12849
+ * 用于初始数据获取
12850
+ */
12851
+ async runOnce(task) {
12852
+ return contextStorage.run(this, task);
12853
+ }
12854
+ /**
12855
+ * 清理依赖
12856
+ */
12857
+ clearDependencies() {
12858
+ for (const state of this.dependencies) state.unsubscribe(this);
12859
+ this.dependencies.clear();
12860
+ }
12861
+ /**
12862
+ * 销毁上下文
12863
+ * @param reason 可选的销毁原因,如果提供则 reject changePromise
12864
+ */
12865
+ destroy(reason) {
12866
+ this.destroyed = true;
12867
+ this.clearDependencies();
12868
+ if (reason && this.changePromise) this.changePromise.reject(reason);
12869
+ this.changePromise = void 0;
12870
+ }
12871
+ /**
12872
+ * 等待 AbortSignal
12873
+ */
12874
+ waitForAbort(signal) {
12875
+ return new Promise((_, reject) => {
12876
+ if (signal.aborted) {
12877
+ reject(new DOMException("Aborted", "AbortError"));
12878
+ return;
12879
+ }
12880
+ signal.addEventListener("abort", () => {
12881
+ reject(new DOMException("Aborted", "AbortError"));
12882
+ });
12883
+ });
12884
+ }
12885
+ };
12886
+
12887
+ //#endregion
12888
+ //#region ../core/src/reactive-fs/watcher-pool.ts
12889
+ /** 全局监听器池,共享同一路径的监听器 */
12890
+ const watcherPool = /* @__PURE__ */ new Map();
12891
+ /** 防抖定时器 */
12892
+ const debounceTimers = /* @__PURE__ */ new Map();
12893
+ /** 默认防抖时间 (ms) */
12894
+ const DEBOUNCE_MS = 100;
12895
+ /**
12896
+ * 获取或创建文件/目录监听器
12897
+ *
12898
+ * 特性:
12899
+ * - 同一路径共享监听器
12900
+ * - 引用计数管理生命周期
12901
+ * - 内置防抖机制
12902
+ *
12903
+ * @param path 要监听的路径
12904
+ * @param onChange 变更回调
12905
+ * @param options 监听选项
12906
+ * @returns 释放函数,调用后取消订阅
12907
+ */
12908
+ function acquireWatcher(path$1, onChange, options = {}) {
12909
+ const normalizedPath = resolve$1(path$1);
12910
+ const debounceMs = options.debounceMs ?? DEBOUNCE_MS;
12911
+ let entry = watcherPool.get(normalizedPath);
12912
+ if (!entry) {
12913
+ const watcher = watch(normalizedPath, {
12914
+ recursive: options.recursive ?? false,
12915
+ persistent: false
12916
+ }, () => {
12917
+ const existingTimer = debounceTimers.get(normalizedPath);
12918
+ if (existingTimer) clearTimeout(existingTimer);
12919
+ const timer = setTimeout(() => {
12920
+ debounceTimers.delete(normalizedPath);
12921
+ const currentEntry = watcherPool.get(normalizedPath);
12922
+ if (currentEntry) for (const cb of currentEntry.callbacks) try {
12923
+ cb();
12924
+ } catch (err) {
12925
+ console.error(`Watcher callback error for ${normalizedPath}:`, err);
12926
+ }
12927
+ }, debounceMs);
12928
+ debounceTimers.set(normalizedPath, timer);
12929
+ });
12930
+ watcher.on("error", (err) => {
12931
+ console.error(`Watcher error for ${normalizedPath}:`, err);
12932
+ });
12933
+ entry = {
12934
+ watcher,
12935
+ refCount: 0,
12936
+ callbacks: /* @__PURE__ */ new Set()
12937
+ };
12938
+ watcherPool.set(normalizedPath, entry);
12939
+ }
12940
+ entry.refCount++;
12941
+ entry.callbacks.add(onChange);
12942
+ return () => {
12943
+ const currentEntry = watcherPool.get(normalizedPath);
12944
+ if (!currentEntry) return;
12945
+ currentEntry.callbacks.delete(onChange);
12946
+ currentEntry.refCount--;
12947
+ if (currentEntry.refCount === 0) {
12948
+ currentEntry.watcher.close();
12949
+ watcherPool.delete(normalizedPath);
12950
+ const timer = debounceTimers.get(normalizedPath);
12951
+ if (timer) {
12952
+ clearTimeout(timer);
12953
+ debounceTimers.delete(normalizedPath);
12954
+ }
12955
+ }
12956
+ };
12957
+ }
12958
+
12959
+ //#endregion
12960
+ //#region ../core/src/reactive-fs/reactive-fs.ts
12961
+ /** 状态缓存:路径 -> ReactiveState */
12962
+ const stateCache = /* @__PURE__ */ new Map();
12963
+ /** 监听器释放函数缓存 */
12964
+ const releaseCache = /* @__PURE__ */ new Map();
12965
+ /**
12966
+ * 响应式读取文件内容
12967
+ *
12968
+ * 特性:
12969
+ * - 自动注册文件监听
12970
+ * - 文件变更时自动更新状态
12971
+ * - 在 ReactiveContext 中调用时自动追踪依赖
12972
+ *
12973
+ * @param filepath 文件路径
12974
+ * @returns 文件内容,文件不存在时返回 null
12975
+ */
12976
+ async function reactiveReadFile(filepath) {
12977
+ const normalizedPath = resolve$1(filepath);
12978
+ const key = `file:${normalizedPath}`;
12979
+ const getValue = async () => {
12980
+ try {
12981
+ return await readFile$1(normalizedPath, "utf-8");
12982
+ } catch {
12983
+ return null;
12984
+ }
12985
+ };
12986
+ let state = stateCache.get(key);
12987
+ if (!state) {
12988
+ state = new ReactiveState(await getValue());
12989
+ stateCache.set(key, state);
12990
+ const release = acquireWatcher(dirname$1(normalizedPath), async () => {
12991
+ const newValue = await getValue();
12992
+ state.set(newValue);
12993
+ });
12994
+ releaseCache.set(key, release);
12995
+ }
12996
+ return state.get();
12997
+ }
12998
+ /**
12999
+ * 响应式读取目录内容
13000
+ *
13001
+ * 特性:
13002
+ * - 自动注册目录监听
13003
+ * - 目录变更时自动更新状态
13004
+ * - 在 ReactiveContext 中调用时自动追踪依赖
13005
+ *
13006
+ * @param dirpath 目录路径
13007
+ * @param options 选项
13008
+ * @returns 目录项名称数组
13009
+ */
13010
+ async function reactiveReadDir(dirpath, options = {}) {
13011
+ const normalizedPath = resolve$1(dirpath);
13012
+ const key = `dir:${normalizedPath}:${JSON.stringify(options)}`;
13013
+ const getValue = async () => {
13014
+ try {
13015
+ return (await readdir(normalizedPath, { withFileTypes: true })).filter((entry) => {
13016
+ if (!options.includeHidden && entry.name.startsWith(".")) return false;
13017
+ if (options.exclude?.includes(entry.name)) return false;
13018
+ if (options.directoriesOnly && !entry.isDirectory()) return false;
13019
+ if (options.filesOnly && !entry.isFile()) return false;
13020
+ return true;
13021
+ }).map((entry) => entry.name);
13022
+ } catch {
13023
+ return [];
13024
+ }
13025
+ };
13026
+ let state = stateCache.get(key);
13027
+ if (!state) {
13028
+ state = new ReactiveState(await getValue(), { equals: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]) });
13029
+ stateCache.set(key, state);
13030
+ const release = acquireWatcher(normalizedPath, async () => {
13031
+ const newValue = await getValue();
13032
+ state.set(newValue);
13033
+ }, { recursive: true });
13034
+ releaseCache.set(key, release);
13035
+ }
13036
+ return state.get();
13037
+ }
13038
+ /**
13039
+ * 响应式检查路径是否存在
13040
+ *
13041
+ * @param path 路径
13042
+ * @returns 是否存在
13043
+ */
13044
+ async function reactiveExists(path$1) {
13045
+ const normalizedPath = resolve$1(path$1);
13046
+ const key = `exists:${normalizedPath}`;
13047
+ const getValue = async () => {
13048
+ try {
13049
+ await stat(normalizedPath);
13050
+ return true;
13051
+ } catch {
13052
+ return false;
13053
+ }
13054
+ };
13055
+ let state = stateCache.get(key);
13056
+ if (!state) {
13057
+ state = new ReactiveState(await getValue());
13058
+ stateCache.set(key, state);
13059
+ const release = acquireWatcher(dirname$1(normalizedPath), async () => {
13060
+ const newValue = await getValue();
13061
+ state.set(newValue);
13062
+ });
13063
+ releaseCache.set(key, release);
13064
+ }
13065
+ return state.get();
13066
+ }
13067
+ /**
13068
+ * 响应式获取文件/目录的 stat 信息
13069
+ *
13070
+ * @param path 路径
13071
+ * @returns stat 信息,不存在时返回 null
13072
+ */
13073
+ async function reactiveStat(path$1) {
13074
+ const normalizedPath = resolve$1(path$1);
13075
+ const key = `stat:${normalizedPath}`;
13076
+ const getValue = async () => {
13077
+ try {
13078
+ const s = await stat(normalizedPath);
13079
+ return {
13080
+ isDirectory: s.isDirectory(),
13081
+ isFile: s.isFile(),
13082
+ mtime: s.mtime.getTime(),
13083
+ birthtime: s.birthtime.getTime()
13084
+ };
13085
+ } catch {
13086
+ return null;
13087
+ }
13088
+ };
13089
+ let state = stateCache.get(key);
13090
+ if (!state) {
13091
+ state = new ReactiveState(await getValue(), { equals: (a, b) => {
13092
+ if (a === null && b === null) return true;
13093
+ if (a === null || b === null) return false;
13094
+ return a.isDirectory === b.isDirectory && a.isFile === b.isFile && a.mtime === b.mtime && a.birthtime === b.birthtime;
13095
+ } });
13096
+ stateCache.set(key, state);
13097
+ const release = acquireWatcher(dirname$1(normalizedPath), async () => {
13098
+ const newValue = await getValue();
13099
+ state.set(newValue);
13100
+ });
13101
+ releaseCache.set(key, release);
13102
+ }
13103
+ return state.get();
13104
+ }
13105
+
12719
13106
  //#endregion
12720
13107
  //#region ../core/src/adapter.ts
12721
13108
  /**
@@ -12741,93 +13128,101 @@ var OpenSpecAdapter = class {
12741
13128
  return join(this.changesDir, "archive");
12742
13129
  }
12743
13130
  async isInitialized() {
12744
- try {
12745
- return (await stat(this.openspecDir)).isDirectory();
12746
- } catch {
12747
- return false;
12748
- }
13131
+ return (await reactiveStat(this.openspecDir))?.isDirectory ?? false;
13132
+ }
13133
+ /** File time info derived from filesystem (reactive) */
13134
+ async getFileTimeInfo(filePath) {
13135
+ const statInfo = await reactiveStat(filePath);
13136
+ if (!statInfo) return null;
13137
+ return {
13138
+ createdAt: statInfo.birthtime,
13139
+ updatedAt: statInfo.mtime
13140
+ };
12749
13141
  }
12750
13142
  async listSpecs() {
12751
- try {
12752
- return (await readdir(this.specsDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
12753
- } catch {
12754
- return [];
12755
- }
13143
+ return reactiveReadDir(this.specsDir, { directoriesOnly: true });
12756
13144
  }
12757
13145
  /**
12758
- * List specs with metadata (id and name)
13146
+ * List specs with metadata (id, name, and time info)
13147
+ * Only returns specs that have valid spec.md
13148
+ * Sorted by updatedAt descending (most recent first)
12759
13149
  */
12760
13150
  async listSpecsWithMeta() {
12761
13151
  const ids = await this.listSpecs();
12762
- return await Promise.all(ids.map(async (id) => {
13152
+ return (await Promise.all(ids.map(async (id) => {
13153
+ const spec = await this.readSpec(id);
13154
+ if (!spec) return null;
13155
+ const specPath = join(this.specsDir, id, "spec.md");
13156
+ const timeInfo = await this.getFileTimeInfo(specPath);
12763
13157
  return {
12764
13158
  id,
12765
- name: (await this.readSpec(id))?.name ?? id
13159
+ name: spec.name,
13160
+ createdAt: timeInfo?.createdAt ?? 0,
13161
+ updatedAt: timeInfo?.updatedAt ?? 0
12766
13162
  };
12767
- }));
13163
+ }))).filter((r) => r !== null).sort((a, b) => b.updatedAt - a.updatedAt);
12768
13164
  }
12769
13165
  async listChanges() {
12770
- try {
12771
- return (await readdir(this.changesDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "archive").map((e) => e.name);
12772
- } catch {
12773
- return [];
12774
- }
13166
+ return reactiveReadDir(this.changesDir, {
13167
+ directoriesOnly: true,
13168
+ exclude: ["archive"]
13169
+ });
12775
13170
  }
12776
13171
  /**
12777
- * List changes with metadata (id, name, and progress)
13172
+ * List changes with metadata (id, name, progress, and time info)
13173
+ * Only returns changes that have valid proposal.md
13174
+ * Sorted by updatedAt descending (most recent first)
12778
13175
  */
12779
13176
  async listChangesWithMeta() {
12780
13177
  const ids = await this.listChanges();
12781
- return await Promise.all(ids.map(async (id) => {
13178
+ return (await Promise.all(ids.map(async (id) => {
12782
13179
  const change = await this.readChange(id);
13180
+ if (!change) return null;
13181
+ const proposalPath = join(this.changesDir, id, "proposal.md");
13182
+ const timeInfo = await this.getFileTimeInfo(proposalPath);
12783
13183
  return {
12784
13184
  id,
12785
- name: change?.name ?? id,
12786
- progress: change?.progress ?? {
12787
- total: 0,
12788
- completed: 0
12789
- }
13185
+ name: change.name,
13186
+ progress: change.progress,
13187
+ createdAt: timeInfo?.createdAt ?? 0,
13188
+ updatedAt: timeInfo?.updatedAt ?? 0
12790
13189
  };
12791
- }));
13190
+ }))).filter((r) => r !== null).sort((a, b) => b.updatedAt - a.updatedAt);
12792
13191
  }
12793
13192
  async listArchivedChanges() {
12794
- try {
12795
- return (await readdir(this.archiveDir, { withFileTypes: true })).filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
12796
- } catch {
12797
- return [];
12798
- }
13193
+ return reactiveReadDir(this.archiveDir, { directoriesOnly: true });
12799
13194
  }
12800
13195
  /**
12801
- * List archived changes with metadata
13196
+ * List archived changes with metadata and time info
13197
+ * Only returns archives that have valid proposal.md
13198
+ * Sorted by updatedAt descending (most recent first)
12802
13199
  */
12803
13200
  async listArchivedChangesWithMeta() {
12804
13201
  const ids = await this.listArchivedChanges();
12805
- return await Promise.all(ids.map(async (id) => {
13202
+ return (await Promise.all(ids.map(async (id) => {
13203
+ const change = await this.readArchivedChange(id);
13204
+ if (!change) return null;
13205
+ const proposalPath = join(this.archiveDir, id, "proposal.md");
13206
+ const timeInfo = await this.getFileTimeInfo(proposalPath);
12806
13207
  return {
12807
13208
  id,
12808
- name: (await this.readArchivedChange(id))?.name ?? id
13209
+ name: change.name,
13210
+ createdAt: timeInfo?.createdAt ?? 0,
13211
+ updatedAt: timeInfo?.updatedAt ?? 0
12809
13212
  };
12810
- }));
13213
+ }))).filter((r) => r !== null).sort((a, b) => b.updatedAt - a.updatedAt);
12811
13214
  }
12812
13215
  /**
12813
- * Read project.md content
13216
+ * Read project.md content (reactive)
12814
13217
  */
12815
13218
  async readProjectMd() {
12816
- try {
12817
- return await readFile(join(this.openspecDir, "project.md"), "utf-8");
12818
- } catch {
12819
- return null;
12820
- }
13219
+ return reactiveReadFile(join(this.openspecDir, "project.md"));
12821
13220
  }
12822
13221
  /**
12823
- * Read AGENTS.md content
13222
+ * Read AGENTS.md content (reactive)
12824
13223
  */
12825
13224
  async readAgentsMd() {
12826
- try {
12827
- return await readFile(join(this.openspecDir, "AGENTS.md"), "utf-8");
12828
- } catch {
12829
- return null;
12830
- }
13225
+ return reactiveReadFile(join(this.openspecDir, "AGENTS.md"));
12831
13226
  }
12832
13227
  /**
12833
13228
  * Write project.md content
@@ -12851,11 +13246,7 @@ var OpenSpecAdapter = class {
12851
13246
  }
12852
13247
  }
12853
13248
  async readSpecRaw(specId) {
12854
- try {
12855
- return await readFile(join(this.specsDir, specId, "spec.md"), "utf-8");
12856
- } catch {
12857
- return null;
12858
- }
13249
+ return reactiveReadFile(join(this.specsDir, specId, "spec.md"));
12859
13250
  }
12860
13251
  async readChange(changeId) {
12861
13252
  try {
@@ -12867,17 +13258,14 @@ var OpenSpecAdapter = class {
12867
13258
  }
12868
13259
  }
12869
13260
  async readChangeRaw(changeId) {
12870
- try {
12871
- const proposalPath = join(this.changesDir, changeId, "proposal.md");
12872
- const tasksPath = join(this.changesDir, changeId, "tasks.md");
12873
- const [proposal, tasks] = await Promise.all([readFile(proposalPath, "utf-8"), readFile(tasksPath, "utf-8").catch(() => "")]);
12874
- return {
12875
- proposal,
12876
- tasks
12877
- };
12878
- } catch {
12879
- return null;
12880
- }
13261
+ const proposalPath = join(this.changesDir, changeId, "proposal.md");
13262
+ const tasksPath = join(this.changesDir, changeId, "tasks.md");
13263
+ const [proposal, tasks] = await Promise.all([reactiveReadFile(proposalPath), reactiveReadFile(tasksPath)]);
13264
+ if (!proposal) return null;
13265
+ return {
13266
+ proposal,
13267
+ tasks: tasks ?? ""
13268
+ };
12881
13269
  }
12882
13270
  /**
12883
13271
  * Read an archived change
@@ -12892,20 +13280,17 @@ var OpenSpecAdapter = class {
12892
13280
  }
12893
13281
  }
12894
13282
  /**
12895
- * Read raw archived change files
13283
+ * Read raw archived change files (reactive)
12896
13284
  */
12897
13285
  async readArchivedChangeRaw(changeId) {
12898
- try {
12899
- const proposalPath = join(this.archiveDir, changeId, "proposal.md");
12900
- const tasksPath = join(this.archiveDir, changeId, "tasks.md");
12901
- const [proposal, tasks] = await Promise.all([readFile(proposalPath, "utf-8"), readFile(tasksPath, "utf-8").catch(() => "")]);
12902
- return {
12903
- proposal,
12904
- tasks
12905
- };
12906
- } catch {
12907
- return null;
12908
- }
13286
+ const proposalPath = join(this.archiveDir, changeId, "proposal.md");
13287
+ const tasksPath = join(this.archiveDir, changeId, "tasks.md");
13288
+ const [proposal, tasks] = await Promise.all([reactiveReadFile(proposalPath), reactiveReadFile(tasksPath)]);
13289
+ if (!proposal) return null;
13290
+ return {
13291
+ proposal,
13292
+ tasks: tasks ?? ""
13293
+ };
12909
13294
  }
12910
13295
  async writeSpec(specId, content) {
12911
13296
  const specDir = join(this.specsDir, specId);
@@ -13222,7 +13607,7 @@ var OpenSpecWatcher = class extends EventEmitter {
13222
13607
  */
13223
13608
  watchDir(dir, callback) {
13224
13609
  try {
13225
- const watcher = watch(dir, { recursive: true }, (eventType, filename) => {
13610
+ const watcher = watch$1(dir, { recursive: true }, (eventType, filename) => {
13226
13611
  if (filename) callback(filename, eventType);
13227
13612
  });
13228
13613
  watcher.on("error", (error) => {
@@ -13247,6 +13632,397 @@ var OpenSpecWatcher = class extends EventEmitter {
13247
13632
  }
13248
13633
  };
13249
13634
 
13635
+ //#endregion
13636
+ //#region ../core/src/config.ts
13637
+ /**
13638
+ * OpenSpecUI 配置 Schema
13639
+ *
13640
+ * 存储在 openspec/.openspecui.json 中,利用文件监听实现响应式更新
13641
+ */
13642
+ const OpenSpecUIConfigSchema = objectType({
13643
+ cli: objectType({ command: stringType().default("npx @fission-ai/openspec") }).default({}),
13644
+ ui: objectType({ theme: enumType([
13645
+ "light",
13646
+ "dark",
13647
+ "system"
13648
+ ]).default("system") }).default({})
13649
+ });
13650
+ /** 默认配置 */
13651
+ const DEFAULT_CONFIG = {
13652
+ cli: { command: "npx @fission-ai/openspec" },
13653
+ ui: { theme: "system" }
13654
+ };
13655
+ /**
13656
+ * 配置管理器
13657
+ *
13658
+ * 负责读写 openspec/.openspecui.json 配置文件。
13659
+ * 读取操作使用 reactiveReadFile,支持响应式更新。
13660
+ */
13661
+ var ConfigManager = class {
13662
+ configPath;
13663
+ constructor(projectDir) {
13664
+ this.configPath = join(projectDir, "openspec", ".openspecui.json");
13665
+ }
13666
+ /**
13667
+ * 读取配置(响应式)
13668
+ *
13669
+ * 如果配置文件不存在,返回默认配置。
13670
+ * 如果配置文件格式错误,返回默认配置并打印警告。
13671
+ */
13672
+ async readConfig() {
13673
+ const content = await reactiveReadFile(this.configPath);
13674
+ if (!content) return DEFAULT_CONFIG;
13675
+ try {
13676
+ const parsed = JSON.parse(content);
13677
+ const result = OpenSpecUIConfigSchema.safeParse(parsed);
13678
+ if (result.success) return result.data;
13679
+ console.warn("Invalid config format, using defaults:", result.error.message);
13680
+ return DEFAULT_CONFIG;
13681
+ } catch (err) {
13682
+ console.warn("Failed to parse config, using defaults:", err);
13683
+ return DEFAULT_CONFIG;
13684
+ }
13685
+ }
13686
+ /**
13687
+ * 写入配置
13688
+ *
13689
+ * 会触发文件监听,自动更新订阅者。
13690
+ */
13691
+ async writeConfig(config) {
13692
+ const current = await this.readConfig();
13693
+ const merged = {
13694
+ ...current,
13695
+ ...config,
13696
+ cli: {
13697
+ ...current.cli,
13698
+ ...config.cli
13699
+ },
13700
+ ui: {
13701
+ ...current.ui,
13702
+ ...config.ui
13703
+ }
13704
+ };
13705
+ await writeFile(this.configPath, JSON.stringify(merged, null, 2), "utf-8");
13706
+ }
13707
+ /**
13708
+ * 获取 CLI 命令
13709
+ */
13710
+ async getCliCommand() {
13711
+ return (await this.readConfig()).cli.command;
13712
+ }
13713
+ /**
13714
+ * 设置 CLI 命令
13715
+ */
13716
+ async setCliCommand(command) {
13717
+ await this.writeConfig({ cli: { command } });
13718
+ }
13719
+ };
13720
+
13721
+ //#endregion
13722
+ //#region ../core/src/cli-executor.ts
13723
+ /**
13724
+ * CLI 执行器
13725
+ *
13726
+ * 负责调用外部 openspec CLI 命令。
13727
+ * 命令前缀从 ConfigManager 获取,支持:
13728
+ * - npx @fission-ai/openspec (默认)
13729
+ * - bunx openspec
13730
+ * - openspec (本地安装)
13731
+ * - 自定义命令 (如 xspec)
13732
+ */
13733
+ var CliExecutor = class {
13734
+ constructor(configManager, projectDir) {
13735
+ this.configManager = configManager;
13736
+ this.projectDir = projectDir;
13737
+ }
13738
+ /**
13739
+ * 创建干净的环境变量,移除 pnpm 特有的配置
13740
+ * 避免 pnpm 环境变量污染 npx/npm 执行
13741
+ */
13742
+ getCleanEnv() {
13743
+ const env = { ...process.env };
13744
+ for (const key of Object.keys(env)) if (key.startsWith("npm_config_") || key.startsWith("npm_package_") || key === "npm_execpath" || key === "npm_lifecycle_event" || key === "npm_lifecycle_script") delete env[key];
13745
+ return env;
13746
+ }
13747
+ /**
13748
+ * 执行 CLI 命令
13749
+ *
13750
+ * @param args CLI 参数,如 ['init'] 或 ['archive', 'change-id']
13751
+ * @returns 执行结果
13752
+ */
13753
+ async execute(args) {
13754
+ const parts = (await this.configManager.getCliCommand()).split(/\s+/);
13755
+ const cmd = parts[0];
13756
+ const cmdArgs = [...parts.slice(1), ...args];
13757
+ return new Promise((resolve$2) => {
13758
+ const child = spawn(cmd, cmdArgs, {
13759
+ cwd: this.projectDir,
13760
+ shell: true,
13761
+ env: this.getCleanEnv()
13762
+ });
13763
+ let stdout = "";
13764
+ let stderr = "";
13765
+ child.stdout?.on("data", (data) => {
13766
+ stdout += data.toString();
13767
+ });
13768
+ child.stderr?.on("data", (data) => {
13769
+ stderr += data.toString();
13770
+ });
13771
+ child.on("close", (exitCode) => {
13772
+ resolve$2({
13773
+ success: exitCode === 0,
13774
+ stdout,
13775
+ stderr,
13776
+ exitCode
13777
+ });
13778
+ });
13779
+ child.on("error", (err) => {
13780
+ resolve$2({
13781
+ success: false,
13782
+ stdout,
13783
+ stderr: stderr + "\n" + err.message,
13784
+ exitCode: null
13785
+ });
13786
+ });
13787
+ });
13788
+ }
13789
+ /**
13790
+ * 执行 openspec init(非交互式)
13791
+ *
13792
+ * @param tools 工具列表,如 ['claude', 'cursor'] 或 'all' 或 'none'
13793
+ */
13794
+ async init(tools = "all") {
13795
+ const toolsArg = Array.isArray(tools) ? tools.join(",") : tools;
13796
+ return this.execute(["init", `--tools=${toolsArg}`]);
13797
+ }
13798
+ /**
13799
+ * 执行 openspec archive <changeId>(非交互式)
13800
+ *
13801
+ * @param changeId 要归档的 change ID
13802
+ * @param options 选项
13803
+ */
13804
+ async archive(changeId, options = {}) {
13805
+ const args = [
13806
+ "archive",
13807
+ "-y",
13808
+ changeId
13809
+ ];
13810
+ if (options.skipSpecs) args.push("--skip-specs");
13811
+ if (options.noValidate) args.push("--no-validate");
13812
+ return this.execute(args);
13813
+ }
13814
+ /**
13815
+ * 执行 openspec validate [type] [id]
13816
+ */
13817
+ async validate(type, id) {
13818
+ const args = ["validate"];
13819
+ if (type) args.push(type);
13820
+ if (id) args.push(id);
13821
+ return this.execute(args);
13822
+ }
13823
+ /**
13824
+ * 检查 CLI 是否可用
13825
+ */
13826
+ async checkAvailability() {
13827
+ try {
13828
+ const result = await this.execute(["--version"]);
13829
+ if (result.success) return {
13830
+ available: true,
13831
+ version: result.stdout.trim()
13832
+ };
13833
+ return {
13834
+ available: false,
13835
+ error: result.stderr || "Unknown error"
13836
+ };
13837
+ } catch (err) {
13838
+ return {
13839
+ available: false,
13840
+ error: err instanceof Error ? err.message : "Unknown error"
13841
+ };
13842
+ }
13843
+ }
13844
+ };
13845
+
13846
+ //#endregion
13847
+ //#region ../core/src/tool-config.ts
13848
+ /**
13849
+ * 工具配置检测模块
13850
+ *
13851
+ * 基于 @fission-ai/openspec 的 configurators 实现
13852
+ * 用于检测项目中已配置的 AI 工具
13853
+ *
13854
+ * 重要:使用响应式文件系统 (reactiveExists) 实现,
13855
+ * 当配置文件变化时会自动触发更新。
13856
+ *
13857
+ * @see references/openspec/src/core/configurators/slash/
13858
+ */
13859
+ /**
13860
+ * 所有支持的工具配置
13861
+ *
13862
+ * 检测路径使用 proposal 命令文件,因为这是 openspec init 创建的第一个文件
13863
+ * 如果该文件存在,说明工具已配置
13864
+ */
13865
+ const TOOL_CONFIGS = [
13866
+ {
13867
+ toolId: "claude",
13868
+ detectionPath: ".claude/commands/openspec/proposal.md"
13869
+ },
13870
+ {
13871
+ toolId: "cursor",
13872
+ detectionPath: ".cursor/commands/openspec-proposal.md"
13873
+ },
13874
+ {
13875
+ toolId: "windsurf",
13876
+ detectionPath: ".windsurf/workflows/openspec-proposal.md"
13877
+ },
13878
+ {
13879
+ toolId: "cline",
13880
+ detectionPath: ".clinerules/workflows/openspec-proposal.md"
13881
+ },
13882
+ {
13883
+ toolId: "github-copilot",
13884
+ detectionPath: ".github/prompts/openspec-proposal.prompt.md"
13885
+ },
13886
+ {
13887
+ toolId: "amazon-q",
13888
+ detectionPath: ".amazonq/prompts/openspec-proposal.md"
13889
+ },
13890
+ {
13891
+ toolId: "codex",
13892
+ detectionPath: ".codex/prompts/openspec-proposal.md"
13893
+ },
13894
+ {
13895
+ toolId: "gemini",
13896
+ detectionPath: ".gemini/commands/openspec/proposal.toml"
13897
+ },
13898
+ {
13899
+ toolId: "auggie",
13900
+ detectionPath: ".augment/commands/openspec-proposal.md"
13901
+ },
13902
+ {
13903
+ toolId: "codebuddy",
13904
+ detectionPath: ".codebuddy/commands/openspec/proposal.md"
13905
+ },
13906
+ {
13907
+ toolId: "qoder",
13908
+ detectionPath: ".qoder/commands/openspec/proposal.md"
13909
+ },
13910
+ {
13911
+ toolId: "roocode",
13912
+ detectionPath: ".roo/commands/openspec-proposal.md"
13913
+ },
13914
+ {
13915
+ toolId: "kilocode",
13916
+ detectionPath: ".kilocode/workflows/openspec-proposal.md"
13917
+ },
13918
+ {
13919
+ toolId: "opencode",
13920
+ detectionPath: ".opencode/command/openspec-proposal.md"
13921
+ },
13922
+ {
13923
+ toolId: "factory",
13924
+ detectionPath: ".factory/commands/openspec-proposal.md"
13925
+ },
13926
+ {
13927
+ toolId: "crush",
13928
+ detectionPath: ".crush/commands/openspec/proposal.md"
13929
+ },
13930
+ {
13931
+ toolId: "costrict",
13932
+ detectionPath: ".cospec/openspec/commands/openspec-proposal.md"
13933
+ },
13934
+ {
13935
+ toolId: "qwen",
13936
+ detectionPath: ".qwen/commands/openspec-proposal.toml"
13937
+ },
13938
+ {
13939
+ toolId: "iflow",
13940
+ detectionPath: ".iflow/commands/openspec-proposal.md"
13941
+ },
13942
+ {
13943
+ toolId: "antigravity",
13944
+ detectionPath: ".agent/workflows/openspec-proposal.md"
13945
+ }
13946
+ ];
13947
+ /**
13948
+ * 获取所有可用的工具 ID 列表
13949
+ */
13950
+ function getAvailableToolIds() {
13951
+ return TOOL_CONFIGS.map((config) => config.toolId);
13952
+ }
13953
+ /**
13954
+ * 检测项目中已配置的工具(响应式)
13955
+ *
13956
+ * 使用 reactiveExists 检测文件,当文件变化时会自动触发更新。
13957
+ * 必须在 ReactiveContext 中调用才能获得响应式能力。
13958
+ *
13959
+ * @param projectDir 项目根目录
13960
+ * @returns 已配置的工具 ID 列表
13961
+ */
13962
+ async function getConfiguredTools(projectDir) {
13963
+ const configured = [];
13964
+ for (const config of TOOL_CONFIGS) if (await reactiveExists(join(projectDir, config.detectionPath))) configured.push(config.toolId);
13965
+ return configured;
13966
+ }
13967
+
13968
+ //#endregion
13969
+ //#region ../server/src/reactive-subscription.ts
13970
+ /**
13971
+ * 创建响应式订阅
13972
+ *
13973
+ * 自动追踪 task 中的文件依赖,当依赖变更时自动重新执行并推送新数据。
13974
+ *
13975
+ * @param task 要执行的异步任务,内部的文件读取会被自动追踪
13976
+ * @returns tRPC observable
13977
+ *
13978
+ * @example
13979
+ * ```typescript
13980
+ * // 在 router 中使用
13981
+ * subscribe: publicProcedure.subscription(({ ctx }) => {
13982
+ * return createReactiveSubscription(() => ctx.adapter.listSpecsWithMeta())
13983
+ * })
13984
+ * ```
13985
+ */
13986
+ function createReactiveSubscription(task) {
13987
+ return observable((emit) => {
13988
+ const context = new ReactiveContext();
13989
+ const controller = new AbortController();
13990
+ (async () => {
13991
+ try {
13992
+ for await (const data of context.stream(task, controller.signal)) emit.next(data);
13993
+ } catch (err) {
13994
+ if (!controller.signal.aborted) emit.error(err);
13995
+ }
13996
+ })();
13997
+ return () => {
13998
+ controller.abort();
13999
+ };
14000
+ });
14001
+ }
14002
+ /**
14003
+ * 创建带输入参数的响应式订阅
14004
+ *
14005
+ * @param task 接收输入参数的异步任务
14006
+ * @returns 返回一个函数,接收输入参数并返回 tRPC observable
14007
+ *
14008
+ * @example
14009
+ * ```typescript
14010
+ * // 在 router 中使用
14011
+ * subscribeOne: publicProcedure
14012
+ * .input(z.object({ id: z.string() }))
14013
+ * .subscription(({ ctx, input }) => {
14014
+ * return createReactiveSubscriptionWithInput(
14015
+ * (id: string) => ctx.adapter.readSpec(id)
14016
+ * )(input.id)
14017
+ * })
14018
+ * ```
14019
+ */
14020
+ function createReactiveSubscriptionWithInput(task) {
14021
+ return (input) => {
14022
+ return createReactiveSubscription(() => task(input));
14023
+ };
14024
+ }
14025
+
13250
14026
  //#endregion
13251
14027
  //#region ../server/src/router.ts
13252
14028
  const t = initTRPC.context().create();
@@ -13261,6 +14037,12 @@ const dashboardRouter = router({
13261
14037
  }),
13262
14038
  isInitialized: publicProcedure.query(async ({ ctx }) => {
13263
14039
  return ctx.adapter.isInitialized();
14040
+ }),
14041
+ subscribe: publicProcedure.subscription(({ ctx }) => {
14042
+ return createReactiveSubscription(() => ctx.adapter.getDashboardData());
14043
+ }),
14044
+ subscribeInitialized: publicProcedure.subscription(({ ctx }) => {
14045
+ return createReactiveSubscription(() => ctx.adapter.isInitialized());
13264
14046
  })
13265
14047
  });
13266
14048
  /**
@@ -13288,6 +14070,15 @@ const specRouter = router({
13288
14070
  }),
13289
14071
  validate: publicProcedure.input(objectType({ id: stringType() })).query(async ({ ctx, input }) => {
13290
14072
  return ctx.adapter.validateSpec(input.id);
14073
+ }),
14074
+ subscribe: publicProcedure.subscription(({ ctx }) => {
14075
+ return createReactiveSubscription(() => ctx.adapter.listSpecsWithMeta());
14076
+ }),
14077
+ subscribeOne: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
14078
+ return createReactiveSubscriptionWithInput((id) => ctx.adapter.readSpec(id))(input.id);
14079
+ }),
14080
+ subscribeRaw: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
14081
+ return createReactiveSubscriptionWithInput((id) => ctx.adapter.readSpecRaw(id))(input.id);
13291
14082
  })
13292
14083
  });
13293
14084
  /**
@@ -13330,6 +14121,15 @@ const changeRouter = router({
13330
14121
  })).mutation(async ({ ctx, input }) => {
13331
14122
  if (!await ctx.adapter.toggleTask(input.changeId, input.taskIndex, input.completed)) throw new Error(`Failed to toggle task ${input.taskIndex} in change ${input.changeId}`);
13332
14123
  return { success: true };
14124
+ }),
14125
+ subscribe: publicProcedure.subscription(({ ctx }) => {
14126
+ return createReactiveSubscription(() => ctx.adapter.listChangesWithMeta());
14127
+ }),
14128
+ subscribeOne: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
14129
+ return createReactiveSubscriptionWithInput((id) => ctx.adapter.readChange(id))(input.id);
14130
+ }),
14131
+ subscribeRaw: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
14132
+ return createReactiveSubscriptionWithInput((id) => ctx.adapter.readChangeRaw(id))(input.id);
13333
14133
  })
13334
14134
  });
13335
14135
  /**
@@ -13427,6 +14227,12 @@ const projectRouter = router({
13427
14227
  saveAgentsMd: publicProcedure.input(objectType({ content: stringType() })).mutation(async ({ ctx, input }) => {
13428
14228
  await ctx.adapter.writeAgentsMd(input.content);
13429
14229
  return { success: true };
14230
+ }),
14231
+ subscribeProjectMd: publicProcedure.subscription(({ ctx }) => {
14232
+ return createReactiveSubscription(() => ctx.adapter.readProjectMd());
14233
+ }),
14234
+ subscribeAgentsMd: publicProcedure.subscription(({ ctx }) => {
14235
+ return createReactiveSubscription(() => ctx.adapter.readAgentsMd());
13430
14236
  })
13431
14237
  });
13432
14238
  /**
@@ -13444,6 +14250,12 @@ const archiveRouter = router({
13444
14250
  }),
13445
14251
  getRaw: publicProcedure.input(objectType({ id: stringType() })).query(async ({ ctx, input }) => {
13446
14252
  return ctx.adapter.readArchivedChangeRaw(input.id);
14253
+ }),
14254
+ subscribe: publicProcedure.subscription(({ ctx }) => {
14255
+ return createReactiveSubscription(() => ctx.adapter.listArchivedChangesWithMeta());
14256
+ }),
14257
+ subscribeOne: publicProcedure.input(objectType({ id: stringType() })).subscription(({ ctx, input }) => {
14258
+ return createReactiveSubscriptionWithInput((id) => ctx.adapter.readArchivedChange(id))(input.id);
13447
14259
  })
13448
14260
  });
13449
14261
  objectType({
@@ -13517,6 +14329,75 @@ const realtimeRouter = router({
13517
14329
  })
13518
14330
  });
13519
14331
  /**
14332
+ * Config router - configuration management
14333
+ */
14334
+ const configRouter = router({
14335
+ get: publicProcedure.query(async ({ ctx }) => {
14336
+ return ctx.configManager.readConfig();
14337
+ }),
14338
+ update: publicProcedure.input(objectType({
14339
+ cli: objectType({ command: stringType() }).optional(),
14340
+ ui: objectType({ theme: enumType([
14341
+ "light",
14342
+ "dark",
14343
+ "system"
14344
+ ]) }).optional()
14345
+ })).mutation(async ({ ctx, input }) => {
14346
+ await ctx.configManager.writeConfig(input);
14347
+ return { success: true };
14348
+ }),
14349
+ setCliCommand: publicProcedure.input(objectType({ command: stringType() })).mutation(async ({ ctx, input }) => {
14350
+ await ctx.configManager.setCliCommand(input.command);
14351
+ return { success: true };
14352
+ }),
14353
+ subscribe: publicProcedure.subscription(({ ctx }) => {
14354
+ return createReactiveSubscription(() => ctx.configManager.readConfig());
14355
+ })
14356
+ });
14357
+ /**
14358
+ * CLI router - execute external openspec CLI commands
14359
+ */
14360
+ const cliRouter = router({
14361
+ checkAvailability: publicProcedure.query(async ({ ctx }) => {
14362
+ return ctx.cliExecutor.checkAvailability();
14363
+ }),
14364
+ getAvailableTools: publicProcedure.query(() => {
14365
+ return getAvailableToolIds();
14366
+ }),
14367
+ getConfiguredTools: publicProcedure.query(async ({ ctx }) => {
14368
+ return getConfiguredTools(ctx.projectDir);
14369
+ }),
14370
+ subscribeConfiguredTools: publicProcedure.subscription(({ ctx }) => {
14371
+ return createReactiveSubscription(() => getConfiguredTools(ctx.projectDir));
14372
+ }),
14373
+ init: publicProcedure.input(objectType({ tools: unionType([
14374
+ arrayType(stringType()),
14375
+ literalType("all"),
14376
+ literalType("none")
14377
+ ]).optional() }).optional()).mutation(async ({ ctx, input }) => {
14378
+ return ctx.cliExecutor.init(input?.tools ?? "all");
14379
+ }),
14380
+ archive: publicProcedure.input(objectType({
14381
+ changeId: stringType(),
14382
+ skipSpecs: booleanType().optional(),
14383
+ noValidate: booleanType().optional()
14384
+ })).mutation(async ({ ctx, input }) => {
14385
+ return ctx.cliExecutor.archive(input.changeId, {
14386
+ skipSpecs: input.skipSpecs,
14387
+ noValidate: input.noValidate
14388
+ });
14389
+ }),
14390
+ validate: publicProcedure.input(objectType({
14391
+ type: enumType(["spec", "change"]).optional(),
14392
+ id: stringType().optional()
14393
+ })).mutation(async ({ ctx, input }) => {
14394
+ return ctx.cliExecutor.validate(input.type, input.id);
14395
+ }),
14396
+ execute: publicProcedure.input(objectType({ args: arrayType(stringType()) })).mutation(async ({ ctx, input }) => {
14397
+ return ctx.cliExecutor.execute(input.args);
14398
+ })
14399
+ });
14400
+ /**
13520
14401
  * Main app router
13521
14402
  */
13522
14403
  const appRouter = router({
@@ -13527,7 +14408,9 @@ const appRouter = router({
13527
14408
  project: projectRouter,
13528
14409
  ai: aiRouter,
13529
14410
  init: initRouter,
13530
- realtime: realtimeRouter
14411
+ realtime: realtimeRouter,
14412
+ config: configRouter,
14413
+ cli: cliRouter
13531
14414
  });
13532
14415
 
13533
14416
  //#endregion
@@ -13550,6 +14433,8 @@ const appRouter = router({
13550
14433
  function createServer$2(config) {
13551
14434
  const adapter = new OpenSpecAdapter(config.projectDir);
13552
14435
  const providerManager = new ProviderManager(config.providers);
14436
+ const configManager = new ConfigManager(config.projectDir);
14437
+ const cliExecutor = new CliExecutor(configManager, config.projectDir);
13553
14438
  const watcher = config.enableWatcher !== false ? new OpenSpecWatcher(config.projectDir) : void 0;
13554
14439
  const app = new Hono();
13555
14440
  const corsOrigins = config.corsOrigins ?? ["http://localhost:5173", "http://localhost:3000"];
@@ -13572,19 +14457,27 @@ function createServer$2(config) {
13572
14457
  createContext: () => ({
13573
14458
  adapter,
13574
14459
  providerManager,
13575
- watcher
14460
+ configManager,
14461
+ cliExecutor,
14462
+ watcher,
14463
+ projectDir: config.projectDir
13576
14464
  })
13577
14465
  });
13578
14466
  });
13579
14467
  const createContext = () => ({
13580
14468
  adapter,
13581
14469
  providerManager,
13582
- watcher
14470
+ configManager,
14471
+ cliExecutor,
14472
+ watcher,
14473
+ projectDir: config.projectDir
13583
14474
  });
13584
14475
  return {
13585
14476
  app,
13586
14477
  adapter,
13587
14478
  providerManager,
14479
+ configManager,
14480
+ cliExecutor,
13588
14481
  watcher,
13589
14482
  createContext,
13590
14483
  port: config.port ?? 3100
@@ -13622,18 +14515,28 @@ function createWebSocketServer(server, httpServer) {
13622
14515
  //#region src/index.ts
13623
14516
  const __dirname = dirname$1(fileURLToPath(import.meta.url));
13624
14517
  /**
13625
- * Check if a port is available by trying to listen on it
14518
+ * Check if a port is available by trying to listen on it.
14519
+ * Tests both 127.0.0.1 and 0.0.0.0 to ensure the port is truly available.
13626
14520
  */
13627
14521
  function isPortAvailable(port) {
13628
- return new Promise((resolve$1) => {
13629
- const server = createServer$1();
13630
- server.once("error", () => {
13631
- resolve$1(false);
13632
- });
13633
- server.once("listening", () => {
13634
- server.close(() => resolve$1(true));
14522
+ return new Promise((resolve$2) => {
14523
+ const server1 = createServer$1();
14524
+ server1.once("error", () => {
14525
+ resolve$2(false);
14526
+ });
14527
+ server1.once("listening", () => {
14528
+ server1.close(() => {
14529
+ const server2 = createServer$1();
14530
+ server2.once("error", () => {
14531
+ resolve$2(false);
14532
+ });
14533
+ server2.once("listening", () => {
14534
+ server2.close(() => resolve$2(true));
14535
+ });
14536
+ server2.listen(port, "0.0.0.0");
14537
+ });
13635
14538
  });
13636
- server.listen(port, "0.0.0.0");
14539
+ server1.listen(port, "127.0.0.1");
13637
14540
  });
13638
14541
  }
13639
14542
  /**
@@ -13702,8 +14605,8 @@ async function startServer(options = {}) {
13702
14605
  const path$1 = c.req.path === "/" ? "/index.html" : c.req.path;
13703
14606
  if (path$1.startsWith("/trpc")) return next();
13704
14607
  const filePath = join$1(webDir, path$1);
13705
- if (existsSync(filePath) && statSync$1(filePath).isFile()) {
13706
- const content = readFileSync$1(filePath);
14608
+ if (existsSync(filePath) && statSync(filePath).isFile()) {
14609
+ const content = readFileSync(filePath);
13707
14610
  const contentType = {
13708
14611
  html: "text/html",
13709
14612
  js: "application/javascript",
@@ -13724,7 +14627,7 @@ async function startServer(options = {}) {
13724
14627
  if (!path$1.includes(".")) {
13725
14628
  const indexPath = join$1(webDir, "index.html");
13726
14629
  if (existsSync(indexPath)) {
13727
- const content = readFileSync$1(indexPath, "utf-8");
14630
+ const content = readFileSync(indexPath, "utf-8");
13728
14631
  return c.html(content);
13729
14632
  }
13730
14633
  }