hitloop 0.0.0-stage → 0.1.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 (469) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +218 -2
  3. package/dist/agent.js +318 -0
  4. package/dist/cli.js +897 -0
  5. package/dist/client/assets/app.css +1 -0
  6. package/dist/client/assets/chunks/abap-EINPUMCJ.js +1 -0
  7. package/dist/client/assets/chunks/abnfDiagram-S7VZAUFO-W2CM2W6D.js +1 -0
  8. package/dist/client/assets/chunks/actionscript-3-O776AQUO.js +1 -0
  9. package/dist/client/assets/chunks/ada-WLOTXRTV.js +1 -0
  10. package/dist/client/assets/chunks/ahk-EF37A2DY.js +1 -0
  11. package/dist/client/assets/chunks/ahk2-FH4K3CKT.js +1 -0
  12. package/dist/client/assets/chunks/angular-html-ZALQFV2H.js +1 -0
  13. package/dist/client/assets/chunks/angular-ts-4IWMF7QO.js +1 -0
  14. package/dist/client/assets/chunks/apache-44AYOEWM.js +1 -0
  15. package/dist/client/assets/chunks/apex-XRYPID42.js +1 -0
  16. package/dist/client/assets/chunks/apl-5HBALFOI.js +1 -0
  17. package/dist/client/assets/chunks/applescript-2DZEHZEC.js +1 -0
  18. package/dist/client/assets/chunks/ara-ZGZQBLXL.js +1 -0
  19. package/dist/client/assets/chunks/architecture-WOLXFQ4H-EJGENNRL.js +1 -0
  20. package/dist/client/assets/chunks/architectureDiagram-YGSQEFNW-2VHWSKO6.js +36 -0
  21. package/dist/client/assets/chunks/asciidoc-GSQAKCPO.js +1 -0
  22. package/dist/client/assets/chunks/asm-BFURM5BK.js +1 -0
  23. package/dist/client/assets/chunks/astro-VJORWDZJ.js +1 -0
  24. package/dist/client/assets/chunks/awk-BSHTWXXX.js +1 -0
  25. package/dist/client/assets/chunks/ballerina-HAOQJ4B2.js +1 -0
  26. package/dist/client/assets/chunks/bat-OSH45EOF.js +1 -0
  27. package/dist/client/assets/chunks/beancount-RBATIHOV.js +1 -0
  28. package/dist/client/assets/chunks/berry-CZUIOUM4.js +1 -0
  29. package/dist/client/assets/chunks/bibtex-KCL2UWHE.js +1 -0
  30. package/dist/client/assets/chunks/bicep-SZJZB3SG.js +1 -0
  31. package/dist/client/assets/chunks/bird2-XBF5YQQV.js +1 -0
  32. package/dist/client/assets/chunks/blade-Q24LEVFC.js +1 -0
  33. package/dist/client/assets/chunks/blockDiagram-APKKGZFW-53LXSOOT.js +140 -0
  34. package/dist/client/assets/chunks/bpmnIcons-APHXDYXF-FMBRQNTY.js +1 -0
  35. package/dist/client/assets/chunks/bsl-TI44NWFB.js +1 -0
  36. package/dist/client/assets/chunks/c-NLHY4Y4N.js +1 -0
  37. package/dist/client/assets/chunks/c3-BUTEM6PO.js +1 -0
  38. package/dist/client/assets/chunks/c4Diagram-KJ2TOQU5-O53G4GQH.js +38 -0
  39. package/dist/client/assets/chunks/cadence-JIRTEU64.js +1 -0
  40. package/dist/client/assets/chunks/cairo-3W6NVDNW.js +1 -0
  41. package/dist/client/assets/chunks/chapel-BPBJ2E7K.js +1 -0
  42. package/dist/client/assets/chunks/chunk-25KVF5ZE.js +1 -0
  43. package/dist/client/assets/chunks/chunk-2CJKSQOI.js +1 -0
  44. package/dist/client/assets/chunks/chunk-2QIJRTWR.js +1 -0
  45. package/dist/client/assets/chunks/chunk-2ZCRJZGF.js +2 -0
  46. package/dist/client/assets/chunks/chunk-3ILNDZNY.js +213 -0
  47. package/dist/client/assets/chunks/chunk-3M2OOQ7S.js +1 -0
  48. package/dist/client/assets/chunks/chunk-3M57NTNK.js +1 -0
  49. package/dist/client/assets/chunks/chunk-3MR6DCZJ.js +1 -0
  50. package/dist/client/assets/chunks/chunk-3WGHS7F7.js +1 -0
  51. package/dist/client/assets/chunks/chunk-4JK75V33.js +1 -0
  52. package/dist/client/assets/chunks/chunk-4NXNVEWK.js +1 -0
  53. package/dist/client/assets/chunks/chunk-4RNNBDFQ.js +1 -0
  54. package/dist/client/assets/chunks/chunk-4T4NVH7V.js +1 -0
  55. package/dist/client/assets/chunks/chunk-4TFFBC35.js +1 -0
  56. package/dist/client/assets/chunks/chunk-4WYBE67P.js +1 -0
  57. package/dist/client/assets/chunks/chunk-56ITEUFA.js +1 -0
  58. package/dist/client/assets/chunks/chunk-5DXRE7IH.js +1 -0
  59. package/dist/client/assets/chunks/chunk-5IMADCNU.js +1 -0
  60. package/dist/client/assets/chunks/chunk-5XZPGKN4.js +1 -0
  61. package/dist/client/assets/chunks/chunk-6O32UKSP.js +1 -0
  62. package/dist/client/assets/chunks/chunk-7BAEB64Q.js +1 -0
  63. package/dist/client/assets/chunks/chunk-7FMWBHZO.js +1 -0
  64. package/dist/client/assets/chunks/chunk-7GFDITUT.js +1 -0
  65. package/dist/client/assets/chunks/chunk-7H4MLBHI.js +1 -0
  66. package/dist/client/assets/chunks/chunk-7OJ4MEEB.js +1 -0
  67. package/dist/client/assets/chunks/chunk-7RSEISFE.js +1 -0
  68. package/dist/client/assets/chunks/chunk-7YNZZQVT.js +1 -0
  69. package/dist/client/assets/chunks/chunk-AB7TIL2G.js +1 -0
  70. package/dist/client/assets/chunks/chunk-ADDF76FL.js +1 -0
  71. package/dist/client/assets/chunks/chunk-AF6UTCD4.js +321 -0
  72. package/dist/client/assets/chunks/chunk-AJ2K6ASU.js +1 -0
  73. package/dist/client/assets/chunks/chunk-AUBAENQZ.js +1 -0
  74. package/dist/client/assets/chunks/chunk-BJEYVZ65.js +1 -0
  75. package/dist/client/assets/chunks/chunk-BLRM6LXA.js +1 -0
  76. package/dist/client/assets/chunks/chunk-BOCTYCDZ.js +1 -0
  77. package/dist/client/assets/chunks/chunk-BU4VRYN7.js +1 -0
  78. package/dist/client/assets/chunks/chunk-CALYHV7D.js +1 -0
  79. package/dist/client/assets/chunks/chunk-CF4W3RZ4.js +1 -0
  80. package/dist/client/assets/chunks/chunk-DCZ2OBSL.js +1 -0
  81. package/dist/client/assets/chunks/chunk-DUFPBTHD.js +1 -0
  82. package/dist/client/assets/chunks/chunk-DUICIQBC.js +88 -0
  83. package/dist/client/assets/chunks/chunk-EH2N475V.js +1 -0
  84. package/dist/client/assets/chunks/chunk-EHLLNPZT.js +1 -0
  85. package/dist/client/assets/chunks/chunk-EIBFAW37.js +1 -0
  86. package/dist/client/assets/chunks/chunk-EO3ER3GN.js +1 -0
  87. package/dist/client/assets/chunks/chunk-FH24BIIA.js +1 -0
  88. package/dist/client/assets/chunks/chunk-FNOXGAL5.js +2 -0
  89. package/dist/client/assets/chunks/chunk-FTY6WLQF.js +3 -0
  90. package/dist/client/assets/chunks/chunk-FZFME6UK.js +1 -0
  91. package/dist/client/assets/chunks/chunk-FZJZZYYO.js +1 -0
  92. package/dist/client/assets/chunks/chunk-G4Z6L7OO.js +1 -0
  93. package/dist/client/assets/chunks/chunk-GNCEAN2B.js +1 -0
  94. package/dist/client/assets/chunks/chunk-GQJIKV3T.js +1 -0
  95. package/dist/client/assets/chunks/chunk-GUMNOKT2.js +1 -0
  96. package/dist/client/assets/chunks/chunk-GXRORAF3.js +1 -0
  97. package/dist/client/assets/chunks/chunk-H36ABDKY.js +1 -0
  98. package/dist/client/assets/chunks/chunk-HAR5NYVW.js +1 -0
  99. package/dist/client/assets/chunks/chunk-HIGPS4L2.js +1 -0
  100. package/dist/client/assets/chunks/chunk-HMOBGTME.js +1 -0
  101. package/dist/client/assets/chunks/chunk-HNO6SQ7M.js +1 -0
  102. package/dist/client/assets/chunks/chunk-HYVZ3GYN.js +1 -0
  103. package/dist/client/assets/chunks/chunk-I6TGFOQS.js +1 -0
  104. package/dist/client/assets/chunks/chunk-IHIOJK7I.js +1 -0
  105. package/dist/client/assets/chunks/chunk-IHJNUMSC.js +1 -0
  106. package/dist/client/assets/chunks/chunk-IP7PB2OC.js +1 -0
  107. package/dist/client/assets/chunks/chunk-ISDTMH2Y.js +1 -0
  108. package/dist/client/assets/chunks/chunk-J65M2LNT.js +1 -0
  109. package/dist/client/assets/chunks/chunk-JCWYZ7X2.js +62 -0
  110. package/dist/client/assets/chunks/chunk-JENWYTLI.js +1 -0
  111. package/dist/client/assets/chunks/chunk-JJ6YWJQS.js +1 -0
  112. package/dist/client/assets/chunks/chunk-JMUVGGX7.js +32 -0
  113. package/dist/client/assets/chunks/chunk-JNQUFMM4.js +1 -0
  114. package/dist/client/assets/chunks/chunk-JPTECOAL.js +1 -0
  115. package/dist/client/assets/chunks/chunk-JZNK4LKS.js +1 -0
  116. package/dist/client/assets/chunks/chunk-KPNXJBIW.js +1 -0
  117. package/dist/client/assets/chunks/chunk-KZO2GZL7.js +1 -0
  118. package/dist/client/assets/chunks/chunk-L2REESOQ.js +1 -0
  119. package/dist/client/assets/chunks/chunk-LJPJXR7R.js +1 -0
  120. package/dist/client/assets/chunks/chunk-LOEZMLCI.js +1 -0
  121. package/dist/client/assets/chunks/chunk-MLYC6FSE.js +1 -0
  122. package/dist/client/assets/chunks/chunk-MMDGEZYU.js +1 -0
  123. package/dist/client/assets/chunks/chunk-MYXE7CGE.js +1 -0
  124. package/dist/client/assets/chunks/chunk-NBH7YOBD.js +15 -0
  125. package/dist/client/assets/chunks/chunk-NCOVM3YF.js +1 -0
  126. package/dist/client/assets/chunks/chunk-NCWOBVU7.js +1 -0
  127. package/dist/client/assets/chunks/chunk-NLRIHHEH.js +1 -0
  128. package/dist/client/assets/chunks/chunk-NUPEOYK6.js +1 -0
  129. package/dist/client/assets/chunks/chunk-O4MVOA7X.js +1 -0
  130. package/dist/client/assets/chunks/chunk-OBHQQZ4H.js +1 -0
  131. package/dist/client/assets/chunks/chunk-OKPNJIIR.js +1 -0
  132. package/dist/client/assets/chunks/chunk-OLMPB57R.js +1 -0
  133. package/dist/client/assets/chunks/chunk-P2FYOAEV.js +1 -0
  134. package/dist/client/assets/chunks/chunk-PI3SNP4C.js +1 -0
  135. package/dist/client/assets/chunks/chunk-PRDTEROB.js +1 -0
  136. package/dist/client/assets/chunks/chunk-PZELHD2H.js +136 -0
  137. package/dist/client/assets/chunks/chunk-QR2Q34LM.js +1 -0
  138. package/dist/client/assets/chunks/chunk-R4GIXL6S.js +1 -0
  139. package/dist/client/assets/chunks/chunk-RORF4F5V.js +1 -0
  140. package/dist/client/assets/chunks/chunk-S73BYTLL.js +1 -0
  141. package/dist/client/assets/chunks/chunk-SFC5MRYR.js +1 -0
  142. package/dist/client/assets/chunks/chunk-SVM2H22K.js +2 -0
  143. package/dist/client/assets/chunks/chunk-SZ6UCGVD.js +1 -0
  144. package/dist/client/assets/chunks/chunk-SZYM6GIJ.js +127 -0
  145. package/dist/client/assets/chunks/chunk-TT5KJZKM.js +1 -0
  146. package/dist/client/assets/chunks/chunk-UFBKUYNH.js +1 -0
  147. package/dist/client/assets/chunks/chunk-UL57AD4T.js +1 -0
  148. package/dist/client/assets/chunks/chunk-UNEI7LMM.js +1 -0
  149. package/dist/client/assets/chunks/chunk-VNQBBFIY.js +1 -0
  150. package/dist/client/assets/chunks/chunk-WGVITPII.js +1 -0
  151. package/dist/client/assets/chunks/chunk-WRA6KJHX.js +10 -0
  152. package/dist/client/assets/chunks/chunk-WS5EHW43.js +1 -0
  153. package/dist/client/assets/chunks/chunk-XE6SLBBE.js +3 -0
  154. package/dist/client/assets/chunks/chunk-XF5T2P7U.js +72 -0
  155. package/dist/client/assets/chunks/chunk-XFRY5WKO.js +1 -0
  156. package/dist/client/assets/chunks/chunk-XWYV4OPA.js +1 -0
  157. package/dist/client/assets/chunks/chunk-YCE5ECKY.js +1 -0
  158. package/dist/client/assets/chunks/chunk-YIN5CSIF.js +1 -0
  159. package/dist/client/assets/chunks/chunk-YLFUX6XH.js +1 -0
  160. package/dist/client/assets/chunks/chunk-YNXJICZM.js +1 -0
  161. package/dist/client/assets/chunks/chunk-YOHF7NGD.js +1 -0
  162. package/dist/client/assets/chunks/chunk-YORIST7Z.js +1 -0
  163. package/dist/client/assets/chunks/chunk-YUL7CG4R.js +1 -0
  164. package/dist/client/assets/chunks/chunk-YUN7JSFJ.js +2 -0
  165. package/dist/client/assets/chunks/chunk-YZ7D2IT7.js +1 -0
  166. package/dist/client/assets/chunks/chunk-Z7LAPMFJ.js +1 -0
  167. package/dist/client/assets/chunks/chunk-ZE325LPC.js +1 -0
  168. package/dist/client/assets/chunks/chunk-ZN7TN6PO.js +1 -0
  169. package/dist/client/assets/chunks/chunk-ZST3EVPO.js +1 -0
  170. package/dist/client/assets/chunks/chunk-ZZHMDDZN.js +1 -0
  171. package/dist/client/assets/chunks/clarity-RKRYQ7WF.js +1 -0
  172. package/dist/client/assets/chunks/classDiagram-v2-L25IHNXF-Y67HVDZV.js +217 -0
  173. package/dist/client/assets/chunks/clojure-2X2DQECJ.js +1 -0
  174. package/dist/client/assets/chunks/cmake-LH5524XE.js +1 -0
  175. package/dist/client/assets/chunks/cobol-7DALQF3W.js +1 -0
  176. package/dist/client/assets/chunks/codeowners-52SZU5P5.js +1 -0
  177. package/dist/client/assets/chunks/codeql-EISH3KUP.js +1 -0
  178. package/dist/client/assets/chunks/coffee-VAWSGA7Y.js +1 -0
  179. package/dist/client/assets/chunks/common-lisp-4MVK2AN6.js +1 -0
  180. package/dist/client/assets/chunks/coq-4VRYZRZV.js +1 -0
  181. package/dist/client/assets/chunks/core-BRBMZSD2.js +12 -0
  182. package/dist/client/assets/chunks/cose-bilkent-JH36ORCC-BIOOHSAA.js +1 -0
  183. package/dist/client/assets/chunks/cpp-RFECMSKD.js +1 -0
  184. package/dist/client/assets/chunks/crystal-ZBGGFAOI.js +1 -0
  185. package/dist/client/assets/chunks/csharp-BZDPMKKW.js +1 -0
  186. package/dist/client/assets/chunks/css-3WWGM6UD.js +1 -0
  187. package/dist/client/assets/chunks/csv-AB4GNWSB.js +1 -0
  188. package/dist/client/assets/chunks/cue-2E46MZP2.js +1 -0
  189. package/dist/client/assets/chunks/cynefin-EF2NZ3EQ-TLFQLWUN.js +1 -0
  190. package/dist/client/assets/chunks/cynefinDiagram-L36ESFBY-FLPXCSSG.js +62 -0
  191. package/dist/client/assets/chunks/cypher-HR3ILFOL.js +1 -0
  192. package/dist/client/assets/chunks/d-TARSZPOL.js +1 -0
  193. package/dist/client/assets/chunks/dagre-SPEB5RXH-7EGEINJ2.js +4 -0
  194. package/dist/client/assets/chunks/dart-SALLJHVI.js +1 -0
  195. package/dist/client/assets/chunks/dax-WYMRGMXC.js +1 -0
  196. package/dist/client/assets/chunks/desktop-ND5CGLLJ.js +1 -0
  197. package/dist/client/assets/chunks/diagram-3B7GL73J-6USY4HZX.js +200 -0
  198. package/dist/client/assets/chunks/diagram-DNEMPZML-BNSOC4VW.js +24 -0
  199. package/dist/client/assets/chunks/diagram-L3MUGCDN-5V5FZYIG.js +41 -0
  200. package/dist/client/assets/chunks/diagram-LA4NIPWU-25VLY2CO.js +24 -0
  201. package/dist/client/assets/chunks/diagram-MHCOC4E2-G2LFP3JU.js +3 -0
  202. package/dist/client/assets/chunks/diagram-XPFHEONP-E6HBBIBV.js +30 -0
  203. package/dist/client/assets/chunks/diff-PWYGPM7B.js +1 -0
  204. package/dist/client/assets/chunks/docker-A4D7DT7W.js +1 -0
  205. package/dist/client/assets/chunks/dotenv-VWQWV2DY.js +1 -0
  206. package/dist/client/assets/chunks/dream-maker-M5YDQS35.js +1 -0
  207. package/dist/client/assets/chunks/ebnfDiagram-UZSYDL2S-BMXXZZPI.js +1 -0
  208. package/dist/client/assets/chunks/edge-3QXIDJHI.js +1 -0
  209. package/dist/client/assets/chunks/elixir-3OX2UHIZ.js +1 -0
  210. package/dist/client/assets/chunks/elk-XV6XMXQE-IPR65FON.js +27 -0
  211. package/dist/client/assets/chunks/elm-CTS65NCT.js +1 -0
  212. package/dist/client/assets/chunks/emacs-lisp-HHS2IDO7.js +1 -0
  213. package/dist/client/assets/chunks/engine-javascript-DNHSF67B.js +141 -0
  214. package/dist/client/assets/chunks/erDiagram-EYBAPDEH-3GEHFWHG.js +99 -0
  215. package/dist/client/assets/chunks/erb-ICYWK6HS.js +1 -0
  216. package/dist/client/assets/chunks/erlang-BDQPNCKX.js +1 -0
  217. package/dist/client/assets/chunks/eventmodeling-K75KTNOO-B27BJTZL.js +1 -0
  218. package/dist/client/assets/chunks/fennel-4ONBA2PH.js +1 -0
  219. package/dist/client/assets/chunks/fish-ATLPBZSD.js +1 -0
  220. package/dist/client/assets/chunks/flowDiagram-MALGAFMO-5PCLR4AE.js +1 -0
  221. package/dist/client/assets/chunks/fluent-GGMONBVW.js +1 -0
  222. package/dist/client/assets/chunks/fortran-fixed-form-5MSBDRWQ.js +1 -0
  223. package/dist/client/assets/chunks/fortran-free-form-TT3RBL75.js +1 -0
  224. package/dist/client/assets/chunks/fsharp-PAGHNW4O.js +1 -0
  225. package/dist/client/assets/chunks/ganttDiagram-23REXITY-VE5MVBTO.js +292 -0
  226. package/dist/client/assets/chunks/gdresource-5OTP326D.js +1 -0
  227. package/dist/client/assets/chunks/gdscript-VPWHSCLF.js +1 -0
  228. package/dist/client/assets/chunks/gdshader-FYLPXFE5.js +1 -0
  229. package/dist/client/assets/chunks/genie-UYMEEMA2.js +1 -0
  230. package/dist/client/assets/chunks/gherkin-SU7D5DMY.js +1 -0
  231. package/dist/client/assets/chunks/git-commit-5CF5QAG7.js +1 -0
  232. package/dist/client/assets/chunks/git-rebase-X72BAUZI.js +1 -0
  233. package/dist/client/assets/chunks/gitGraph-VSP46ZUC-J6CJ5YZF.js +1 -0
  234. package/dist/client/assets/chunks/gitGraphDiagram-STJD7YCW-7YJGT6ZP.js +106 -0
  235. package/dist/client/assets/chunks/gleam-LR42YRKO.js +1 -0
  236. package/dist/client/assets/chunks/glimmer-js-JPF67FJU.js +1 -0
  237. package/dist/client/assets/chunks/glimmer-ts-BYPAVIZT.js +1 -0
  238. package/dist/client/assets/chunks/glsl-GTGUULBN.js +1 -0
  239. package/dist/client/assets/chunks/gn-NMDQA4K3.js +1 -0
  240. package/dist/client/assets/chunks/gnuplot-UUAMYBCZ.js +1 -0
  241. package/dist/client/assets/chunks/go-4RERSTNP.js +1 -0
  242. package/dist/client/assets/chunks/graphql-5A6RK6YF.js +1 -0
  243. package/dist/client/assets/chunks/groovy-O7BJONCU.js +1 -0
  244. package/dist/client/assets/chunks/hack-7QGTNPDP.js +1 -0
  245. package/dist/client/assets/chunks/haml-ON5GAWN4.js +1 -0
  246. package/dist/client/assets/chunks/handlebars-6UOQSUBP.js +1 -0
  247. package/dist/client/assets/chunks/haskell-OIZXZ26Y.js +1 -0
  248. package/dist/client/assets/chunks/haxe-7FFVS4FO.js +1 -0
  249. package/dist/client/assets/chunks/hcl-XFUFQJHV.js +1 -0
  250. package/dist/client/assets/chunks/hjson-QDBHMTD5.js +1 -0
  251. package/dist/client/assets/chunks/hlsl-4LP25FFN.js +1 -0
  252. package/dist/client/assets/chunks/html-7RNQXAAV.js +1 -0
  253. package/dist/client/assets/chunks/html-derivative-ESOY72Q2.js +1 -0
  254. package/dist/client/assets/chunks/http-SAD4PMBD.js +1 -0
  255. package/dist/client/assets/chunks/hurl-ZSJJXPQH.js +1 -0
  256. package/dist/client/assets/chunks/hxml-HJCC37RK.js +1 -0
  257. package/dist/client/assets/chunks/hy-CLDE3P4K.js +1 -0
  258. package/dist/client/assets/chunks/imba-REB34U5V.js +1 -0
  259. package/dist/client/assets/chunks/info-OHQRW6UA-EQQKXR77.js +1 -0
  260. package/dist/client/assets/chunks/infoDiagram-KBXO4MBG-F6KGZCGS.js +2 -0
  261. package/dist/client/assets/chunks/ini-PDFEMGRA.js +1 -0
  262. package/dist/client/assets/chunks/ishikawaDiagram-M35NCXHK-5D6ZOZ34.js +70 -0
  263. package/dist/client/assets/chunks/java-KB6XENAD.js +1 -0
  264. package/dist/client/assets/chunks/javascript-3JX5T423.js +1 -0
  265. package/dist/client/assets/chunks/jinja-V4UNU6K4.js +1 -0
  266. package/dist/client/assets/chunks/jison-KH5XNLJP.js +1 -0
  267. package/dist/client/assets/chunks/journeyDiagram-OPLOXDXO-ANDOJZKW.js +139 -0
  268. package/dist/client/assets/chunks/json-HPXFRRPY.js +1 -0
  269. package/dist/client/assets/chunks/json5-LJM6TXWM.js +1 -0
  270. package/dist/client/assets/chunks/jsonc-UZWRU3JV.js +1 -0
  271. package/dist/client/assets/chunks/jsonl-RFZD5UN4.js +1 -0
  272. package/dist/client/assets/chunks/jsonnet-GFZABBL6.js +1 -0
  273. package/dist/client/assets/chunks/jssm-2YIGBISJ.js +1 -0
  274. package/dist/client/assets/chunks/jsx-KLV2ZWUT.js +1 -0
  275. package/dist/client/assets/chunks/julia-3TZDIPPO.js +1 -0
  276. package/dist/client/assets/chunks/just-UHCONBQS.js +1 -0
  277. package/dist/client/assets/chunks/kanban-definition-BZKZHSQY-GF5I5NVD.js +89 -0
  278. package/dist/client/assets/chunks/katex-PIN72PQZ.js +257 -0
  279. package/dist/client/assets/chunks/kdl-DARTFOCL.js +1 -0
  280. package/dist/client/assets/chunks/kotlin-7KT5DW6Y.js +1 -0
  281. package/dist/client/assets/chunks/kusto-EKYXZTC5.js +1 -0
  282. package/dist/client/assets/chunks/langs-GO3UQR4H.js +1 -0
  283. package/dist/client/assets/chunks/latex-T57MIHR5.js +1 -0
  284. package/dist/client/assets/chunks/lean-5QVPRMV6.js +1 -0
  285. package/dist/client/assets/chunks/less-K6QEH5TN.js +1 -0
  286. package/dist/client/assets/chunks/liquid-3LCNNEIV.js +1 -0
  287. package/dist/client/assets/chunks/llvm-O32ZIVRF.js +1 -0
  288. package/dist/client/assets/chunks/log-C4YPJ5BB.js +1 -0
  289. package/dist/client/assets/chunks/logo-JPMO43A6.js +1 -0
  290. package/dist/client/assets/chunks/lua-YKVSOG7K.js +1 -0
  291. package/dist/client/assets/chunks/luau-K4EECYSO.js +1 -0
  292. package/dist/client/assets/chunks/make-VHSYMHHW.js +1 -0
  293. package/dist/client/assets/chunks/markdown-GC5J46H4.js +1 -0
  294. package/dist/client/assets/chunks/marko-U45YFADC.js +1 -0
  295. package/dist/client/assets/chunks/matlab-AMOKC4LU.js +1 -0
  296. package/dist/client/assets/chunks/mdc-DIGA2UXP.js +1 -0
  297. package/dist/client/assets/chunks/mdx-BZUGCKQA.js +1 -0
  298. package/dist/client/assets/chunks/mermaid-WHDP6ZPA.js +1 -0
  299. package/dist/client/assets/chunks/mermaid.core-SQMPMUDN.js +10 -0
  300. package/dist/client/assets/chunks/mindmap-definition-I2QAS2VK-4NH5S56C.js +98 -0
  301. package/dist/client/assets/chunks/mipsasm-2S4CUGQV.js +1 -0
  302. package/dist/client/assets/chunks/mojo-ZOL2PNKT.js +1 -0
  303. package/dist/client/assets/chunks/moonbit-U3BDAIFE.js +1 -0
  304. package/dist/client/assets/chunks/move-YSXALBWA.js +1 -0
  305. package/dist/client/assets/chunks/narrat-DHFIAUJH.js +1 -0
  306. package/dist/client/assets/chunks/nextflow-ESAKNTI3.js +1 -0
  307. package/dist/client/assets/chunks/nextflow-groovy-FKDSPJUI.js +1 -0
  308. package/dist/client/assets/chunks/nginx-XYAYERLO.js +1 -0
  309. package/dist/client/assets/chunks/nim-XOWBVFDH.js +1 -0
  310. package/dist/client/assets/chunks/nix-RVSTQSHC.js +1 -0
  311. package/dist/client/assets/chunks/nsis-KMDUKQRL.js +1 -0
  312. package/dist/client/assets/chunks/nushell-SKJHHZ7U.js +1 -0
  313. package/dist/client/assets/chunks/objective-c-3B3KXQ7Q.js +1 -0
  314. package/dist/client/assets/chunks/objective-cpp-6JDULPJS.js +1 -0
  315. package/dist/client/assets/chunks/ocaml-TMRFXBVO.js +1 -0
  316. package/dist/client/assets/chunks/odin-IN3PLCK7.js +1 -0
  317. package/dist/client/assets/chunks/openscad-P52IKPNH.js +1 -0
  318. package/dist/client/assets/chunks/org-N3SJ2PIA.js +1 -0
  319. package/dist/client/assets/chunks/packet-JDAUHWVQ-4SWIKSKP.js +1 -0
  320. package/dist/client/assets/chunks/pascal-T7CUWCQ4.js +1 -0
  321. package/dist/client/assets/chunks/pegDiagram-FD4XPPHP-WDKZSEGN.js +1 -0
  322. package/dist/client/assets/chunks/perl-LCZ4KODH.js +1 -0
  323. package/dist/client/assets/chunks/php-4XHMBVR5.js +1 -0
  324. package/dist/client/assets/chunks/pie-XZMESJXO-PSSWOWGV.js +1 -0
  325. package/dist/client/assets/chunks/pieDiagram-J4AKIIQI-BDW5I5ON.js +39 -0
  326. package/dist/client/assets/chunks/pkl-OACG5B67.js +1 -0
  327. package/dist/client/assets/chunks/plsql-7E5C7CJX.js +1 -0
  328. package/dist/client/assets/chunks/po-ZSREFRRP.js +1 -0
  329. package/dist/client/assets/chunks/polar-4GBINVA7.js +1 -0
  330. package/dist/client/assets/chunks/postcss-B7RPVGFW.js +1 -0
  331. package/dist/client/assets/chunks/powerquery-6UFMTY4G.js +1 -0
  332. package/dist/client/assets/chunks/powershell-3STRPCNT.js +1 -0
  333. package/dist/client/assets/chunks/prisma-4TPSPIZ5.js +1 -0
  334. package/dist/client/assets/chunks/prolog-HEAYPKQW.js +1 -0
  335. package/dist/client/assets/chunks/proto-Y3Y3VKXM.js +1 -0
  336. package/dist/client/assets/chunks/pug-7PBPMG3M.js +1 -0
  337. package/dist/client/assets/chunks/puppet-FR35R4OI.js +1 -0
  338. package/dist/client/assets/chunks/purescript-OUHXQAOW.js +1 -0
  339. package/dist/client/assets/chunks/python-SNDG7FNU.js +1 -0
  340. package/dist/client/assets/chunks/qml-UVIOSWP4.js +1 -0
  341. package/dist/client/assets/chunks/qmldir-6JGUNYL5.js +1 -0
  342. package/dist/client/assets/chunks/qss-ZCC466IZ.js +1 -0
  343. package/dist/client/assets/chunks/quadrantDiagram-XTYBLID2-5VKXWVJ6.js +7 -0
  344. package/dist/client/assets/chunks/r-4XW4O3ZF.js +1 -0
  345. package/dist/client/assets/chunks/racket-YJAYXOOG.js +1 -0
  346. package/dist/client/assets/chunks/radar-ABXABTNO-3AXZCEY2.js +1 -0
  347. package/dist/client/assets/chunks/railroad-I3PHUGI6-2PLXYPOT.js +1 -0
  348. package/dist/client/assets/chunks/railroad-abnf-LNEFI6M7-M4DGICQX.js +1 -0
  349. package/dist/client/assets/chunks/railroad-ebnf-DWYD2UWJ-UEIHQMSZ.js +1 -0
  350. package/dist/client/assets/chunks/railroad-peg-JR7BVNR7-I7VLIBXM.js +1 -0
  351. package/dist/client/assets/chunks/railroadDiagram-MRQ6NCDL-Y6IYWEFL.js +1 -0
  352. package/dist/client/assets/chunks/raku-63NEL5JK.js +1 -0
  353. package/dist/client/assets/chunks/razor-J3CJP523.js +1 -0
  354. package/dist/client/assets/chunks/rbs-6GQLT7NW.js +1 -0
  355. package/dist/client/assets/chunks/reg-6Q3OEC2C.js +1 -0
  356. package/dist/client/assets/chunks/regexp-LPIMILT3.js +1 -0
  357. package/dist/client/assets/chunks/rel-TOQGUTHB.js +1 -0
  358. package/dist/client/assets/chunks/requirementDiagram-Y7LWUY3S-CIWXVVWJ.js +84 -0
  359. package/dist/client/assets/chunks/riscv-UELGBOXE.js +1 -0
  360. package/dist/client/assets/chunks/ron-FW7LCJJH.js +1 -0
  361. package/dist/client/assets/chunks/rosmsg-O63VHE3L.js +1 -0
  362. package/dist/client/assets/chunks/rst-3WJXGLA7.js +1 -0
  363. package/dist/client/assets/chunks/ruby-7S7L6JTH.js +1 -0
  364. package/dist/client/assets/chunks/rust-LIK4HRRI.js +1 -0
  365. package/dist/client/assets/chunks/sankeyDiagram-WGJFYUXA-OGNQPX5V.js +40 -0
  366. package/dist/client/assets/chunks/sas-FHDPZ2JH.js +1 -0
  367. package/dist/client/assets/chunks/sass-UI4HW2FN.js +1 -0
  368. package/dist/client/assets/chunks/scala-KN7RQSUJ.js +1 -0
  369. package/dist/client/assets/chunks/scheme-5R527YZ5.js +1 -0
  370. package/dist/client/assets/chunks/scss-KZ6DRE4A.js +1 -0
  371. package/dist/client/assets/chunks/sdbl-FHOCL5SL.js +1 -0
  372. package/dist/client/assets/chunks/sequenceDiagram-5NDRBG5M-QX2V24ZU.js +169 -0
  373. package/dist/client/assets/chunks/shaderlab-2TLHUS2H.js +1 -0
  374. package/dist/client/assets/chunks/shellscript-PIB6ZBDM.js +1 -0
  375. package/dist/client/assets/chunks/shellsession-5KL7NFNN.js +1 -0
  376. package/dist/client/assets/chunks/sizeCapture-INFHLROL-LCS2PIHM.js +1 -0
  377. package/dist/client/assets/chunks/smalltalk-JFLFU247.js +1 -0
  378. package/dist/client/assets/chunks/smithy-HONEXZAM.js +1 -0
  379. package/dist/client/assets/chunks/solidity-SJ3INJFZ.js +1 -0
  380. package/dist/client/assets/chunks/soy-ND4SWTM2.js +1 -0
  381. package/dist/client/assets/chunks/sparql-YB2JQBZD.js +1 -0
  382. package/dist/client/assets/chunks/splunk-UXIDDAWL.js +1 -0
  383. package/dist/client/assets/chunks/sql-K6OMG7SQ.js +1 -0
  384. package/dist/client/assets/chunks/ssh-config-I7KJY3KQ.js +1 -0
  385. package/dist/client/assets/chunks/stata-DNZ3DTFJ.js +1 -0
  386. package/dist/client/assets/chunks/stateDiagram-v2-FJ4FJNEA-LVAUWF3A.js +291 -0
  387. package/dist/client/assets/chunks/stylus-GWPR6IQY.js +1 -0
  388. package/dist/client/assets/chunks/surrealql-KQNHBWLC.js +1 -0
  389. package/dist/client/assets/chunks/svelte-YQKF25S4.js +1 -0
  390. package/dist/client/assets/chunks/swift-ZCIZUJXA.js +1 -0
  391. package/dist/client/assets/chunks/swimlanes-UMQ2AFYF-B3RQOARQ.js +1 -0
  392. package/dist/client/assets/chunks/swimlanesDiagram-OMPLPG4O-JOE33EOV.js +8 -0
  393. package/dist/client/assets/chunks/system-verilog-Y7E6FIO3.js +1 -0
  394. package/dist/client/assets/chunks/systemd-WHC7NBZP.js +1 -0
  395. package/dist/client/assets/chunks/talonscript-WC7SODAH.js +1 -0
  396. package/dist/client/assets/chunks/tasl-QHY2GTCB.js +1 -0
  397. package/dist/client/assets/chunks/tcl-VAMIL5PJ.js +1 -0
  398. package/dist/client/assets/chunks/temml-ISNSX3VV.js +96 -0
  399. package/dist/client/assets/chunks/templ-B3BHKWV4.js +1 -0
  400. package/dist/client/assets/chunks/terraform-Q4UEK6FK.js +1 -0
  401. package/dist/client/assets/chunks/tex-63OWCYAB.js +1 -0
  402. package/dist/client/assets/chunks/timeline-definition-SEUHKTQE-FDFTVAJG.js +120 -0
  403. package/dist/client/assets/chunks/toml-7Z4IPGES.js +1 -0
  404. package/dist/client/assets/chunks/treeView-D4JQ5SDB-ABSGRAGI.js +1 -0
  405. package/dist/client/assets/chunks/treemap-SAJKECNS-MYX2CBJN.js +1 -0
  406. package/dist/client/assets/chunks/ts-tags-DT62HE52.js +1 -0
  407. package/dist/client/assets/chunks/tsv-RTXHWADP.js +1 -0
  408. package/dist/client/assets/chunks/tsx-UANUBOC7.js +1 -0
  409. package/dist/client/assets/chunks/turtle-QVOUNUUL.js +1 -0
  410. package/dist/client/assets/chunks/twig-ZPC4BPDA.js +1 -0
  411. package/dist/client/assets/chunks/typescript-WGWKIMMW.js +1 -0
  412. package/dist/client/assets/chunks/typespec-TYCVR32J.js +1 -0
  413. package/dist/client/assets/chunks/typst-PIBFYUO6.js +1 -0
  414. package/dist/client/assets/chunks/usecaseDiagram-AFA6YXN4-KW7ADINR.js +370 -0
  415. package/dist/client/assets/chunks/v-3ERNWBSP.js +1 -0
  416. package/dist/client/assets/chunks/vala-UCBCT2GS.js +1 -0
  417. package/dist/client/assets/chunks/vb-DGI3VTPA.js +1 -0
  418. package/dist/client/assets/chunks/vennDiagram-NNMJR3RB-RDGLGOGM.js +34 -0
  419. package/dist/client/assets/chunks/verilog-G72NZROS.js +1 -0
  420. package/dist/client/assets/chunks/vhdl-PYGA77XE.js +1 -0
  421. package/dist/client/assets/chunks/viml-OOC5B5UU.js +1 -0
  422. package/dist/client/assets/chunks/vue-GTNHIOPG.js +1 -0
  423. package/dist/client/assets/chunks/vue-html-DKAMJV55.js +1 -0
  424. package/dist/client/assets/chunks/vue-vine-WTM5HREV.js +1 -0
  425. package/dist/client/assets/chunks/vyper-RHYEHOJM.js +1 -0
  426. package/dist/client/assets/chunks/wardley-7MLQ67FV-QJL2HSBT.js +1 -0
  427. package/dist/client/assets/chunks/wardleyDiagram-S7YFUSEH-A4XX3UJR.js +78 -0
  428. package/dist/client/assets/chunks/wasm-ZMM2Z5XV.js +1 -0
  429. package/dist/client/assets/chunks/wenyan-HF5UTKSJ.js +1 -0
  430. package/dist/client/assets/chunks/wgsl-O7D7OPO7.js +1 -0
  431. package/dist/client/assets/chunks/wikitext-T7BSWSKQ.js +1 -0
  432. package/dist/client/assets/chunks/wit-TKCHPPT6.js +1 -0
  433. package/dist/client/assets/chunks/wolfram-7MN66A7N.js +1 -0
  434. package/dist/client/assets/chunks/xml-NLTLAWOD.js +1 -0
  435. package/dist/client/assets/chunks/xsl-NZ4ECK3P.js +1 -0
  436. package/dist/client/assets/chunks/xychartDiagram-GGNKY7IX-OXJILWHR.js +7 -0
  437. package/dist/client/assets/chunks/yaml-HRJLEV3V.js +1 -0
  438. package/dist/client/assets/chunks/zenscript-4LLRSLPI.js +1 -0
  439. package/dist/client/assets/chunks/zig-Y7MUQFUO.js +1 -0
  440. package/dist/client/assets/icon.svg +1 -0
  441. package/dist/client/assets/main.js +254 -0
  442. package/dist/client/index.html +59 -0
  443. package/dist/diff.js +119 -0
  444. package/dist/fs/files.js +233 -0
  445. package/dist/fs/git.js +369 -0
  446. package/dist/fs/glob.js +89 -0
  447. package/dist/fs/notes.js +368 -0
  448. package/dist/fs/search.js +136 -0
  449. package/dist/fs/watch.js +69 -0
  450. package/dist/fs/zip.js +100 -0
  451. package/dist/hooks.js +65 -0
  452. package/dist/http.js +81 -0
  453. package/dist/hub/atomic.js +77 -0
  454. package/dist/hub/notify.js +239 -0
  455. package/dist/hub/registry.js +219 -0
  456. package/dist/mcp.js +500 -0
  457. package/dist/q/agent.js +215 -0
  458. package/dist/q/model.js +633 -0
  459. package/dist/q/schema.js +133 -0
  460. package/dist/q/store.js +413 -0
  461. package/dist/r/model.js +323 -0
  462. package/dist/r/schema.js +53 -0
  463. package/dist/r/store.js +489 -0
  464. package/dist/server.js +246 -0
  465. package/dist/settings.js +30 -0
  466. package/dist/workspace.js +639 -0
  467. package/package.json +73 -4
  468. package/schema/questions.schema.json +1670 -0
  469. package/schema/reviews.schema.json +151 -0
package/dist/cli.js ADDED
@@ -0,0 +1,897 @@
1
+ #!/usr/bin/env node
2
+ // hitl — Human In The Loop: an agent asks, shows and asks for approval; you answer, review and browse on your phone.
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import crypto from 'node:crypto';
7
+ import { spawn, spawnSync } from 'node:child_process';
8
+ import { StoreError, ensureReadme } from './hub/atomic.js';
9
+ import { start, version } from './server.js';
10
+ import { describeSet, readAnswers, FILTERS } from './q/agent.js';
11
+ import { decisionsText, news, newsCounts, openProject, overviewText, parseDuration, readFeedback, readNotes, waitFor } from './agent.js';
12
+ import { clearHubState, configDir, ensureHub, forget, hubToken, keyed, links, projects, projectsPath, register, runningHub, normaliseBase, userConfig, userConfigPath, updateUserConfig, writeHubState } from './hub/registry.js';
13
+ import { setSchema } from './q/schema.js';
14
+ import { reviewSchema } from './r/schema.js';
15
+ import { serveMcp, defaultFrom } from './mcp.js';
16
+ import { normalise as normaliseSet } from './q/model.js';
17
+ import { normalise as normaliseReview } from './r/model.js';
18
+ import { Notifier, linkBase, notifyConfig, notifyEnabled, send } from './hub/notify.js';
19
+ import { projectSettings } from './settings.js';
20
+ import { hookStatus, launcher, setHooks } from './hooks.js';
21
+ const PORT = 4199;
22
+ const HELP = `hitl ${version} — Human In The Loop: an agent asks, shows and asks for approval; you answer, review
23
+ and browse the project on your phone or computer, and it reads what you said.
24
+
25
+ Serve the page:
26
+ hitl [options] one server for every project (the hub); adds this folder
27
+ hitl --solo [folder] just this folder, on its own server
28
+ -p, --port <n> port to try first (default ${PORT})
29
+ -H, --host <addr> address to listen on (default 0.0.0.0: your phone can reach it, with a token)
30
+ --local only this computer (127.0.0.1, no token)
31
+ --base <path> serve under a URL path, e.g. hitl or team/hitl (behind a proxy; in Git Bash leave
32
+ off the leading /, or it becomes a Windows path)
33
+ --token <t> use this token (default: one made once, kept in ${tildeOf(configDir())})
34
+ --no-token no token (anyone on your network can open it)
35
+ --allow-host <h> accept this host name too (repeatable; * for any)
36
+ -o, --open open it in your browser
37
+ --read-only look, don't answer or comment
38
+ File browsing (every project; a project's .hitl/config.json and .hitlignore can add to these):
39
+ -x, --exclude <globs> never show, serve, search or zip these (comma-separated, repeatable)
40
+ --hide <names> exact names to hide everywhere (.git always is)
41
+ --show-secrets show .env*, *.key, *.pem and id_rsa-style files
42
+ --max-file-mb <n> larger files aren't shown as text or searched (default 5)
43
+ --no-git don't use git --no-watch no live updates
44
+ --author <name> the name on your notes (default: git user.name)
45
+
46
+ For agents (and you), in a project folder (or -C <folder>):
47
+ hitl status every review, question set and note, with counts
48
+ hitl news [--peek] [--ids a,b] everything the human said since you last read (marks it read)
49
+ hitl wait [--any|--done|--all|--blocking] [ids…] [--timeout 30m] [--settle 30s]
50
+ exit once the human has said something, printing the news
51
+ hitl show <path…> [--title t] [--intro md] [--note md] [--mode review|show] [--to <review>]
52
+ [--lines 10-40] [--against HEAD] [--lang ts --name x.ts] ("-" reads stdin as code or text)
53
+ put files (or stdin) in front of the human; prints the link
54
+ hitl approve "<question>" [path…] [--details md] [--timeout 10m]
55
+ ask yes or no; exits 0 approved, 1 refused or changes asked, 2 no answer yet
56
+ hitl feedback <review> [--new|--all|--open] [--peek] [--json]
57
+ hitl ask <set> --file <questions.json> add questions to a set (or write .hitl/questions/<set>.json)
58
+ hitl show-set <set> [--full] a question set, one line per question
59
+ hitl answers <set> [--new|--all|--unacked|--changed|--blocking|--open] [--ids a,b] [--peek] [--json]
60
+ hitl notes [--new|--open|--all] [--peek] notes the human wrote on files, lines, pictures
61
+ hitl reply <set> <question> <message…> write on a question's thread
62
+ hitl reply <review> <comment> <message…> reply to a review comment (c12)
63
+ hitl reply --note <id> <message…> reply to a note
64
+ hitl ack <set|review> <id…> --note "…" answers / verdicts dealt with (the human sees the note)
65
+ hitl resolve [<review>] <id…> --note "…" resolve review comments, or notes (no review), saying what you did
66
+ hitl close <review|set> close it (it drops to the bottom of the list)
67
+ hitl decisions [words…] [--limit n] what the human decided before, newest first
68
+ hitl validate [id|file…] check question sets and reviews against their formats
69
+ hitl schema [questions|reviews] the formats, as JSON Schema
70
+ hitl url [review|set] links to the page (starts the hub if needed)
71
+
72
+ Setup:
73
+ hitl init [--hook] [--stop-hook] [--no-mcp] [--no-claude-md]
74
+ set up the folder, the MCP server for Claude Code, and a note in CLAUDE.md
75
+ --hook: tell Claude about news whenever you send it a message
76
+ --stop-hook: don't let Claude finish while a blocking question or approval is waiting
77
+ hitl hooks [on|off] [--stop] turn those hooks on or off later
78
+ hitl import [folder] bring in .questionme/ (questions, answers) and .review/ (notes)
79
+ hitl stop stop the hub
80
+ hitl projects [--forget <folder>] the folders the hub serves
81
+ hitl notify [ntfy [url]|webhook <url>|link <url>|test|off]
82
+ push notifications to your phone (the ntfy app, or a webhook)
83
+ hitl hook [prompt|stop] the Claude Code hooks themselves (prompt: news; stop: the gate)
84
+ hitl mcp the MCP server, on stdio
85
+
86
+ Everything lives in <project>/.hitl/: questions/, reviews/, answers/, notes/, media/.`;
87
+ function tildeOf(p) {
88
+ return p.startsWith(os.homedir()) ? '~' + p.slice(os.homedir().length) : p;
89
+ }
90
+ function fail(msg, code = 1) {
91
+ console.error(`hitl: ${msg}`);
92
+ process.exit(code);
93
+ }
94
+ const VALUED = new Set([
95
+ '--base', '-p', '--port', '-H', '--host', '--token', '--allow-host', '-C', '--folder', '--timeout', '--settle', '--ids', '--section', '--limit', '--offset', '--note', '--forget', '--questions',
96
+ '-x', '--exclude', '--hide', '--max-file-mb', '--author', '--title', '--intro', '--mode', '--to', '--lines', '--against', '--lang', '--name', '--details', '--file', '--from',
97
+ ]);
98
+ const BOOLEAN = new Set([
99
+ '-h', '--help', '-v', '--version', '--solo', '--background', '--local', '--no-token', '-o', '--open', '--read-only', '--json', '--full', '--peek', '--all', '--new', '--unacked', '--changed',
100
+ '--blocking', '--any', '--done', '--quiet', '--hook', '--stop-hook', '--stop', '--no-mcp', '--no-claude-md', '--show-secrets', '--no-git', '--no-watch', '--open-only',
101
+ ]);
102
+ const REPEAT = new Set(['--allow-host', '--ids', '-x', '--exclude', '--hide']);
103
+ const COMMANDS = new Set([
104
+ 'serve', 'init', 'status', 'news', 'wait', 'show', 'review', 'approve', 'feedback', 'ask', 'show-set', 'answers', 'notes', 'reply', 'ack', 'resolve', 'close', 'decisions', 'validate', 'schema', 'url', 'stop',
105
+ 'projects', 'notify', 'hook', 'hooks', 'import', 'mcp', 'help', 'list',
106
+ ]);
107
+ function parse(argv) {
108
+ const flags = new Map();
109
+ const pos = [];
110
+ let cmd = '';
111
+ for (let i = 0; i < argv.length; i++) {
112
+ const a = argv[i] ?? '';
113
+ if (a === '--') {
114
+ pos.push(...argv.slice(i + 1));
115
+ break;
116
+ }
117
+ if (a.startsWith('-') && a !== '-') {
118
+ const eq = a.startsWith('--') ? a.indexOf('=') : -1;
119
+ const name = eq > 0 ? a.slice(0, eq) : a;
120
+ if (VALUED.has(name)) {
121
+ const v = eq > 0 ? a.slice(eq + 1) : argv[++i];
122
+ if (v === undefined)
123
+ fail(`${name} needs a value`);
124
+ const prev = flags.get(name);
125
+ flags.set(name, typeof prev === 'string' && REPEAT.has(name) ? `${prev},${v}` : v);
126
+ }
127
+ else if (BOOLEAN.has(name))
128
+ flags.set(name, true);
129
+ else
130
+ fail(`unknown option ${name} (hitl --help lists every command and option)`);
131
+ continue;
132
+ }
133
+ if (!cmd && !pos.length && COMMANDS.has(a))
134
+ cmd = a;
135
+ else
136
+ pos.push(a);
137
+ }
138
+ return { cmd: cmd || 'serve', pos, flags };
139
+ }
140
+ const args = parse(process.argv.slice(2));
141
+ const flag = (...names) => names.some((n) => args.flags.has(n));
142
+ const value = (...names) => {
143
+ for (const n of names) {
144
+ const v = args.flags.get(n);
145
+ if (typeof v === 'string')
146
+ return v;
147
+ }
148
+ return undefined;
149
+ };
150
+ const folder = () => path.resolve(value('-C', '--folder') ?? process.cwd());
151
+ const project = () => openProject(folder());
152
+ const need = (v, what) => v || fail(`which ${what}? (see hitl --help)`);
153
+ if (flag('-h', '--help') || args.cmd === 'help') {
154
+ console.log(HELP);
155
+ process.exit(0);
156
+ }
157
+ if (flag('-v', '--version')) {
158
+ console.log(version);
159
+ process.exit(0);
160
+ }
161
+ async function run() {
162
+ switch (args.cmd) {
163
+ case 'serve':
164
+ return serve();
165
+ case 'mcp':
166
+ return serveMcp();
167
+ case 'init':
168
+ return init();
169
+ case 'status':
170
+ case 'list':
171
+ if (flag('--json')) {
172
+ const p = project();
173
+ return void console.log(JSON.stringify({ folder: p.root, reviews: p.reviews.summaries(), questions: p.questions.summaries(), notes: { open: p.notes.all().filter((c) => c.status === 'open').length, total: p.notes.all().length }, news: newsCounts(p) }, null, 2));
174
+ }
175
+ return void console.log(overviewText(project()));
176
+ case 'news':
177
+ return void console.log(news(project(), { peek: flag('--peek'), ids: list(value('--ids')) }).text);
178
+ case 'wait':
179
+ return wait();
180
+ case 'show':
181
+ case 'review':
182
+ return show();
183
+ case 'approve':
184
+ return approve();
185
+ case 'feedback': {
186
+ const filter = flag('--all') ? 'all' : flag('--open') ? 'open' : 'new';
187
+ const r = readFeedback(project().reviews, need(args.pos[0], 'review'), { filter, peek: flag('--peek') });
188
+ return void console.log(flag('--json') ? JSON.stringify(r.data, null, 2) : r.text);
189
+ }
190
+ case 'ask':
191
+ return ask();
192
+ case 'show-set':
193
+ return void console.log(describeSet(project().questions, need(args.pos[0], 'set'), { full: flag('--full'), section: value('--section'), limit: num(value('--limit')), offset: num(value('--offset')), ids: list(value('--ids')) }));
194
+ case 'answers': {
195
+ const filter = (FILTERS.find((f) => flag('--' + f)) ?? 'new');
196
+ const r = readAnswers(project().questions, need(args.pos[0], 'set'), {
197
+ filter,
198
+ ids: list(value('--ids')),
199
+ section: value('--section'),
200
+ limit: num(value('--limit')),
201
+ offset: num(value('--offset')),
202
+ peek: flag('--peek'),
203
+ questions: (['none', 'short', 'full'].includes(value('--questions') ?? '') ? value('--questions') : undefined),
204
+ });
205
+ return void console.log(flag('--json') ? JSON.stringify(r.data, null, 2) : r.text);
206
+ }
207
+ case 'notes': {
208
+ const f = flag('--open') ? 'open' : flag('--all') ? 'all' : 'new';
209
+ const r = readNotes(project().notes, { filter: f, peek: flag('--peek') });
210
+ return void console.log(r.count ? r.text : f === 'new' ? 'No new notes.' : `No ${f} notes.`);
211
+ }
212
+ case 'reply':
213
+ return reply();
214
+ case 'ack': {
215
+ const [id, ...ids] = args.pos;
216
+ const p = project();
217
+ const target = need(id, 'set or review');
218
+ const done = p.reviews.has(target) ? p.reviews.ack(target, ids, value('--note') ?? '') : p.questions.ack(target, ids, value('--note') ?? '');
219
+ return void console.log(done.length ? `Marked ${done.length} done: ${done.join(', ')}` : 'None of those have answers or verdicts.');
220
+ }
221
+ case 'resolve': {
222
+ const p = project();
223
+ const [first, ...rest] = args.pos;
224
+ const status = flag('--open') ? 'open' : 'resolved';
225
+ const done = first && p.reviews.has(first) ? p.reviews.setStatus(first, rest, status, 'agent', value('--note') ?? '') : p.notes.resolve(args.pos, value('--note') ?? '', status);
226
+ return void console.log(done.length ? `${status === 'open' ? 'Reopened' : 'Resolved'} ${done.join(', ')}.` : 'None of those ids were found.');
227
+ }
228
+ case 'close': {
229
+ const p = project();
230
+ const id = need(args.pos[0], 'review or set');
231
+ if (p.reviews.has(id))
232
+ p.reviews.update(id, { status: 'closed' });
233
+ else if (p.questions.has(id))
234
+ p.questions.updateSet(id, { status: 'closed' });
235
+ else
236
+ fail(`no review or question set "${id}"`);
237
+ return void console.log(`Closed ${id}.`);
238
+ }
239
+ case 'decisions':
240
+ return void console.log(decisionsText(project(), { q: args.pos.join(' '), limit: num(value('--limit')) ?? 100 }));
241
+ case 'validate':
242
+ return validate();
243
+ case 'schema': {
244
+ const which = args.pos[0] ?? 'questions';
245
+ if (which !== 'questions' && which !== 'reviews')
246
+ fail('hitl schema questions | reviews');
247
+ return void console.log(JSON.stringify(which === 'reviews' ? reviewSchema : setSchema, null, 2));
248
+ }
249
+ case 'url': {
250
+ const root = folder();
251
+ if (fs.existsSync(path.join(root, '.hitl')))
252
+ register(root);
253
+ const hub = await ensureHub();
254
+ const key = keyed(projects()).find((k) => same(k.root, root))?.key;
255
+ const id = args.pos[0];
256
+ const p = project();
257
+ const where = id ? (p.reviews.has(id) ? `/r/${encodeURIComponent(id)}` : `/q/${encodeURIComponent(id)}`) : '';
258
+ const hash = key ? `#/p/${encodeURIComponent(key)}${where}` : '';
259
+ for (const l of links(hub.port, hub.host, hub.token, hash, hub.base))
260
+ console.log(l);
261
+ return;
262
+ }
263
+ case 'stop': {
264
+ const hub = await runningHub();
265
+ if (!hub)
266
+ return void console.log('The hitl hub is not running.');
267
+ try {
268
+ process.kill(hub.pid);
269
+ console.log(`Stopped the hitl hub (pid ${hub.pid}, port ${hub.port}).`);
270
+ }
271
+ catch (e) {
272
+ fail(`couldn't stop pid ${hub.pid}: ${e instanceof Error ? e.message : String(e)}`);
273
+ }
274
+ return;
275
+ }
276
+ case 'projects': {
277
+ const f = value('--forget');
278
+ if (f)
279
+ return void console.log(forget(path.resolve(f)) ? `Forgot ${path.resolve(f)}.` : `${path.resolve(f)} wasn't in the list.`);
280
+ const all = keyed(projects());
281
+ if (!all.length)
282
+ return void console.log(`No projects yet (${tildeOf(projectsPath())}).`);
283
+ for (const p of all) {
284
+ const v = fs.existsSync(p.root) ? openProject(p.root) : undefined;
285
+ const n = v ? `${v.reviews.ids().length} reviews, ${v.questions.ids().length} question sets` : '(missing)';
286
+ console.log(`${p.key.padEnd(20)} ${tildeOf(p.root)} ${n}`);
287
+ }
288
+ return;
289
+ }
290
+ case 'hook':
291
+ return hook();
292
+ case 'hooks':
293
+ return hooks();
294
+ case 'notify':
295
+ return notify();
296
+ case 'import':
297
+ return importOld();
298
+ }
299
+ }
300
+ // ── serve ──
301
+ async function serve() {
302
+ const cfg = userConfig();
303
+ const solo = flag('--solo');
304
+ const background = flag('--background');
305
+ const local = flag('--local');
306
+ const port = num(value('-p', '--port') ?? process.env['HITL_PORT']) ?? cfg.port ?? PORT;
307
+ const host = value('-H', '--host') ?? process.env['HITL_HOST'] ?? (local ? '127.0.0.1' : (cfg.host ?? '0.0.0.0'));
308
+ const token = flag('--no-token') || (local && !value('--token')) ? '' : (value('--token') ?? process.env['HITL_TOKEN'] ?? (cfg.token === false ? '' : (cfg.token ?? hubToken())));
309
+ const allowHosts = [...(cfg.allowHosts ?? []), ...list(value('--allow-host'))];
310
+ const base = normaliseBase(value('--base') ?? process.env['HITL_BASE'] ?? cfg.base ?? '/', value('--base') ? 'command line' : process.env['HITL_BASE'] ? 'HITL_BASE' : 'config.json');
311
+ const root = path.resolve(args.pos[0] ?? value('-C', '--folder') ?? process.cwd());
312
+ const overrides = {};
313
+ if (value('-x', '--exclude'))
314
+ overrides.exclude = list(value('-x', '--exclude'));
315
+ if (value('--hide'))
316
+ overrides.hide = list(value('--hide'));
317
+ if (flag('--show-secrets'))
318
+ overrides.showSecrets = true;
319
+ if (value('--max-file-mb'))
320
+ overrides.maxFileMb = num(value('--max-file-mb')) ?? 5;
321
+ if (flag('--no-git'))
322
+ overrides.git = false;
323
+ if (flag('--no-watch'))
324
+ overrides.watch = false;
325
+ if (value('--author'))
326
+ overrides.author = value('--author') ?? '';
327
+ if (flag('--read-only'))
328
+ overrides.readOnly = true;
329
+ if (!solo) {
330
+ if (args.pos[0] || !isHome(root))
331
+ register(root);
332
+ const running = await runningHub();
333
+ if (running) {
334
+ if (background)
335
+ return;
336
+ const key = keyed(projects()).find((k) => same(k.root, root))?.key;
337
+ console.log(`hitl is already running (pid ${running.pid}):`);
338
+ for (const l of links(running.port, running.host, running.token, key ? `#/p/${encodeURIComponent(key)}` : '', running.base))
339
+ console.log(' ' + l);
340
+ if ((running.base ?? '/') !== base)
341
+ console.log(` (it serves under ${running.base ?? '/'}, not ${base}: hitl stop, then start it again to change that)`);
342
+ console.log(' (hitl stop to stop it)');
343
+ return;
344
+ }
345
+ }
346
+ const time = () => new Date().toLocaleTimeString();
347
+ const served = solo ? () => [{ key: keyFor(root), root }] : () => keyed(projects()).filter((p) => fs.existsSync(p.root));
348
+ let listening = port;
349
+ const views = new Map();
350
+ const viewOf = (key) => {
351
+ const p = served().find((x) => x.key === key);
352
+ if (!p)
353
+ return undefined;
354
+ let v = views.get(key);
355
+ if (!v || v.root !== path.resolve(p.root))
356
+ views.set(key, (v = openProject(p.root)));
357
+ return v;
358
+ };
359
+ const notifier = new Notifier(viewOf, () => listening, token, base, (line) => console.log(`[${time()}] ${line}`));
360
+ for (const p of served())
361
+ notifier.baseline(p.key);
362
+ const running = await start({
363
+ mode: solo ? 'solo' : 'hub',
364
+ projects: served,
365
+ ...(solo ? {} : { watchList: projectsPath() }),
366
+ port,
367
+ host,
368
+ base,
369
+ token,
370
+ allowHosts,
371
+ settings: (r) => projectSettings(r, overrides),
372
+ onRequest: (key, c) => {
373
+ const v = viewOf(key);
374
+ if (c.side === 'human' && v) {
375
+ const n = newsCounts(v);
376
+ if (n.total)
377
+ console.log(`[${time()}] ${key}: ${n.total} new from the human (${[n.reviews && `${n.reviews} on reviews`, n.questions && `${n.questions} answers`, n.notes && `${n.notes} notes`].filter(Boolean).join(', ')}) → npx hitloop news`);
378
+ }
379
+ else if (c.side === 'agent')
380
+ console.log(`[${time()}] ${key}: ${c.kind === 'review' ? 'review' : 'questions'} ${c.id} changed`);
381
+ notifier.observe(key, c.kind, c.id);
382
+ },
383
+ onProject: (key) => notifier.baseline(key),
384
+ });
385
+ listening = running.port;
386
+ if (!solo)
387
+ writeHubState({ pid: process.pid, port: running.port, host, token, base, version, started: new Date().toISOString() });
388
+ const key = solo ? keyFor(root) : keyed(projects()).find((k) => same(k.root, root))?.key;
389
+ const hash = key ? `#/p/${encodeURIComponent(key)}` : '';
390
+ console.log(`hitl ${version} (Human In The Loop) — ${solo ? root : `${projects().length} project${projects().length === 1 ? '' : 's'} (${tildeOf(projectsPath())})`}`);
391
+ for (const l of links(running.port, host, token, hash, base))
392
+ console.log(' ' + l);
393
+ if (host === '127.0.0.1')
394
+ console.log(' (only this computer: drop --local to use it from your phone)');
395
+ if (!token && host !== '127.0.0.1')
396
+ console.log(' (no token: anyone on your network can open it)');
397
+ if (!background && fs.existsSync(path.join(root, '.hitl')))
398
+ console.log(' ' + overviewText(openProject(root)).split('\n').join('\n '));
399
+ const n = notifyConfig(cfg);
400
+ console.log(notifyEnabled(n) ? ` notifications: ${[n.ntfy ? 'ntfy' : '', n.webhook ? 'webhook' : ''].filter(Boolean).join(' + ')} (hitl notify test)` : ' notifications: off (hitl notify ntfy puts them on your phone)');
401
+ console.log(' every command and option: npx hitloop --help');
402
+ if (flag('-o', '--open'))
403
+ openBrowser(links(running.port, host, token, hash, base)[0] ?? '');
404
+ const stop = () => {
405
+ if (!solo)
406
+ clearHubState(process.pid);
407
+ void running.close().finally(() => process.exit(0));
408
+ setTimeout(() => process.exit(0), 1500).unref();
409
+ };
410
+ process.on('SIGINT', stop);
411
+ process.on('SIGTERM', stop);
412
+ process.on('exit', () => !solo && clearHubState(process.pid));
413
+ }
414
+ function keyFor(root) {
415
+ return (path
416
+ .basename(root)
417
+ .toLowerCase()
418
+ .replace(/[^a-z0-9._-]+/g, '-')
419
+ .replace(/^-+|-+$/g, '') || 'project');
420
+ }
421
+ const isHome = (p) => same(p, os.homedir());
422
+ function same(a, b) {
423
+ const n = (p) => (process.platform === 'win32' ? path.resolve(p).toLowerCase() : path.resolve(p));
424
+ return n(a) === n(b);
425
+ }
426
+ function openBrowser(url) {
427
+ const cmd = process.platform === 'win32' ? 'cmd' : process.platform === 'darwin' ? 'open' : 'xdg-open';
428
+ const a = process.platform === 'win32' ? ['/c', 'start', '', url.replace(/&/g, '^&')] : [url];
429
+ spawn(cmd, a, { stdio: 'ignore', detached: true, windowsHide: true }).on('error', () => undefined).unref();
430
+ }
431
+ async function linkLines(root, where) {
432
+ register(root);
433
+ try {
434
+ const hub = await ensureHub();
435
+ const key = keyed(projects()).find((k) => same(k.root, root))?.key ?? '';
436
+ return links(hub.port, hub.host, hub.token, key ? `#/p/${encodeURIComponent(key)}${where}` : '', hub.base);
437
+ }
438
+ catch (e) {
439
+ return [`(couldn't start the hitl server: ${e instanceof Error ? e.message : String(e)})`];
440
+ }
441
+ }
442
+ // ── show / approve / ask ──
443
+ function readStdin() {
444
+ try {
445
+ return fs.readFileSync(0, 'utf8');
446
+ }
447
+ catch {
448
+ return '';
449
+ }
450
+ }
451
+ function itemsFromArgs() {
452
+ const note = value('--note');
453
+ const items = [];
454
+ for (const a of args.pos.slice(args.cmd === 'approve' ? 1 : 0)) {
455
+ if (a === '-') {
456
+ const text = readStdin();
457
+ const lang = value('--lang');
458
+ const name = value('--name');
459
+ items.push(lang || name ? { code: text, ...(lang ? { lang } : {}), ...(name ? { name } : {}) } : /^(---|diff --git) /m.test(text) && /^@@ /m.test(text) ? { diff: text } : { text });
460
+ continue;
461
+ }
462
+ if (/^https?:\/\//.test(a)) {
463
+ items.push(/\.(png|jpe?g|gif|webp|avif|svg)(\?|$)/i.test(a) ? { image: a } : { link: a });
464
+ continue;
465
+ }
466
+ const abs = path.resolve(a);
467
+ if (!fs.existsSync(abs))
468
+ fail(`no such file: ${a}`);
469
+ const rel = path.relative(folder(), abs).split(path.sep).join('/');
470
+ if (rel.startsWith('..'))
471
+ fail(`${a} is outside the project (${folder()})`);
472
+ if (fs.statSync(abs).isDirectory())
473
+ fail(`${a} is a folder: name the files (or use a glob your shell expands)`);
474
+ items.push({ file: rel, ...(value('--lines') ? { lines: value('--lines') } : {}), ...(value('--against') ? { against: value('--against') } : {}) });
475
+ }
476
+ if (note && items[0])
477
+ items[0].note = note;
478
+ return items;
479
+ }
480
+ async function show() {
481
+ const p = project();
482
+ const items = itemsFromArgs();
483
+ if (!items.length)
484
+ fail('what should it show? hitl show <path…> (or - for stdin)');
485
+ const to = value('--to');
486
+ const from = value('--from') ?? defaultFrom(p.root);
487
+ let id;
488
+ if (to) {
489
+ const r = p.reviews.addItems(to, items);
490
+ id = to;
491
+ console.log(`Added ${r.ids.length} to "${to}": ${r.ids.join(', ')}`);
492
+ }
493
+ else {
494
+ const mode = value('--mode') === 'show' || (args.cmd === 'show' && value('--mode') !== 'review') ? 'show' : 'review';
495
+ const r = p.reviews.create({ title: value('--title') ?? (items.length === 1 ? (items[0]?.file ?? 'Review') : `${items.length} things to review`), ...(value('--intro') ? { intro: value('--intro') } : {}), mode, ...(from ? { from } : {}) }, items);
496
+ id = r.id;
497
+ console.log(`Made ${mode === 'show' ? 'a showing' : 'a review'} "${r.id}": ${r.ids.join(', ')}`);
498
+ for (const e of [...r.normalised.errors, ...r.normalised.warnings])
499
+ console.log(' ' + e);
500
+ }
501
+ for (const l of await linkLines(p.root, `/r/${encodeURIComponent(id)}`))
502
+ console.log(' ' + l);
503
+ console.log(`Read what they say: npx hitloop feedback ${id} (wait: npx hitloop wait --done ${id})`);
504
+ }
505
+ async function approve() {
506
+ const p = project();
507
+ const question = need(args.pos[0], 'question');
508
+ const from = value('--from') ?? defaultFrom(p.root);
509
+ const r = p.reviews.create({ title: question, ...(value('--details') ? { intro: value('--details') } : {}), mode: 'approve', ...(from ? { from } : {}) }, itemsFromArgs());
510
+ console.log(`Asked "${r.id}": ${question}`);
511
+ for (const l of await linkLines(p.root, `/r/${encodeURIComponent(r.id)}`))
512
+ console.log(' ' + l);
513
+ const ctl = new AbortController();
514
+ process.on('SIGINT', () => ctl.abort());
515
+ const ok = await waitFor(p, { until: 'done', ids: [r.id], timeoutMs: parseDuration(value('--timeout'), 0), signal: ctl.signal });
516
+ const fb = p.reviews.feedback(r.id);
517
+ if (!ok || !fb.overall) {
518
+ console.log(`No answer yet. Wait again: npx hitloop wait --done ${r.id}`);
519
+ process.exit(2);
520
+ }
521
+ p.reviews.markRead(r.id);
522
+ const v = fb.overall.verdict;
523
+ console.log(`${v === 'ok' ? 'APPROVED' : v === 'no' ? 'REFUSED' : 'NOT YET: changes asked for'}${fb.overall.note ? ` — "${fb.overall.note}"` : ''}`);
524
+ process.exit(v === 'ok' ? 0 : 1);
525
+ }
526
+ async function ask() {
527
+ const p = project();
528
+ const set = need(args.pos[0], 'set');
529
+ if (p.reviews.has(set))
530
+ fail(`"${set}" is a review's id; pick another set id`);
531
+ const file = value('--file');
532
+ const raw = file === '-' || !file ? readStdin() : fs.readFileSync(file, 'utf8');
533
+ let data;
534
+ try {
535
+ data = JSON.parse(raw);
536
+ }
537
+ catch (e) {
538
+ fail(`not JSON: ${e instanceof Error ? e.message : String(e)}`);
539
+ }
540
+ const qs = Array.isArray(data) ? data : data && typeof data === 'object' && Array.isArray(data.questions) ? data.questions : [data];
541
+ const from = value('--from') ?? defaultFrom(p.root);
542
+ const { ids, normalised } = p.questions.addQuestions(set, qs, value('--section'), { title: value('--title'), from });
543
+ console.log(`Added ${ids.length} question${ids.length === 1 ? '' : 's'} to "${set}": ${ids.join(', ')}`);
544
+ for (const e of [...normalised.errors, ...normalised.warnings])
545
+ console.log(' ' + e);
546
+ for (const l of await linkLines(p.root, `/q/${encodeURIComponent(set)}`))
547
+ console.log(' ' + l);
548
+ }
549
+ function reply() {
550
+ const p = project();
551
+ const note = value('--note');
552
+ if (note) {
553
+ const c = p.notes.reply(note, 'agent', args.pos.join(' '), 'agent');
554
+ return void console.log(c ? `Replied on note ${note}.` : `No note "${note}".`);
555
+ }
556
+ const [target, id, ...words] = args.pos;
557
+ const t = need(target, 'set or review');
558
+ const msg = words.join(' ');
559
+ if (!msg)
560
+ fail('what should the reply say?');
561
+ if (p.reviews.has(t)) {
562
+ if (id && /^c\d+$/.test(id))
563
+ p.reviews.reply(t, id, 'agent', msg);
564
+ else
565
+ p.reviews.comment(t, { item: id ?? '', body: msg }, 'agent');
566
+ }
567
+ else
568
+ p.questions.message(t, need(id, 'question'), 'agent', msg);
569
+ console.log(`Replied on ${id}.`);
570
+ }
571
+ // ── wait ──
572
+ async function wait() {
573
+ const p = project();
574
+ const until = flag('--done') ? 'done' : flag('--all') ? 'all' : flag('--blocking') ? 'blocking' : 'any';
575
+ const ids = [...args.pos, ...list(value('--ids'))];
576
+ const timeoutMs = parseDuration(value('--timeout'), 0);
577
+ const settleMs = parseDuration(value('--settle'), until === 'any' ? 30_000 : 0);
578
+ const ctl = new AbortController();
579
+ process.on('SIGINT', () => ctl.abort());
580
+ process.on('SIGTERM', () => ctl.abort());
581
+ if (!flag('--quiet'))
582
+ console.log(`Waiting for ${until === 'any' ? 'anything new' : until === 'done' ? 'the human to finish' : until === 'all' ? 'every item and question' : 'the blocking questions'}${ids.length ? ` on ${ids.join(', ')}` : ' in ' + p.root}…`);
583
+ const ok = await waitFor(p, { until, ids, timeoutMs, settleMs, signal: ctl.signal });
584
+ if (!ok) {
585
+ console.log(ctl.signal.aborted ? 'Stopped waiting.' : `Still waiting after ${value('--timeout')}.`);
586
+ console.log(news(p, { peek: true, ids }).text);
587
+ process.exit(2);
588
+ }
589
+ console.log('The human has spoken. New since you last read:');
590
+ console.log(news(p, { peek: flag('--peek'), ids }).text);
591
+ }
592
+ // ── validate ──
593
+ function validate() {
594
+ const p = project();
595
+ const targets = args.pos.length ? args.pos : [...p.questions.ids(), ...p.reviews.ids()];
596
+ if (!targets.length)
597
+ return void console.log(`Nothing in ${path.join(p.root, '.hitl')} to check.`);
598
+ let bad = 0;
599
+ for (const t of targets) {
600
+ const isFile = t.endsWith('.json') && fs.existsSync(t);
601
+ const id = isFile ? path.basename(t, '.json') : t;
602
+ let errors = [], warnings = [], what = '';
603
+ const asReview = isFile ? /[\\/]reviews[\\/]/.test(path.resolve(t)) || (() => { try {
604
+ return 'items' in JSON.parse(fs.readFileSync(t, 'utf8'));
605
+ }
606
+ catch {
607
+ return false;
608
+ } })() : p.reviews.has(id);
609
+ try {
610
+ if (asReview) {
611
+ const n = isFile ? normaliseReview(id, JSON.parse(fs.readFileSync(t, 'utf8'))) : p.reviews.load(id);
612
+ if (!n)
613
+ throw new Error('no such review');
614
+ ({ errors, warnings } = n);
615
+ what = `review, ${n.review.items.length} items`;
616
+ }
617
+ else {
618
+ const n = isFile ? normaliseSet(id, JSON.parse(fs.readFileSync(t, 'utf8'))) : p.questions.load(id);
619
+ if (!n)
620
+ throw new Error('no such question set or review');
621
+ ({ errors, warnings } = n);
622
+ what = `questions, ${n.set.sections.reduce((s, x) => s + x.questions.length, 0)} of them`;
623
+ }
624
+ }
625
+ catch (e) {
626
+ errors = [e instanceof Error ? e.message : String(e)];
627
+ }
628
+ console.log(`${errors.length ? '✗' : '✓'} ${id}${what ? ` (${what})` : ''}: ${errors.length} errors, ${warnings.length} warnings`);
629
+ for (const e of errors)
630
+ console.log(' error: ' + e);
631
+ for (const w of warnings)
632
+ console.log(' warning: ' + w);
633
+ if (errors.length)
634
+ bad++;
635
+ }
636
+ if (bad)
637
+ process.exit(1);
638
+ }
639
+ // ── notify ──
640
+ async function notify() {
641
+ const [what, arg] = args.pos;
642
+ const hubPort = () => readHub()?.port ?? PORT;
643
+ const show = () => {
644
+ const n = notifyConfig();
645
+ console.log(`Notifications (${tildeOf(userConfigPath())}):`);
646
+ console.log(` ntfy: ${n.ntfy ?? '(off)'}`);
647
+ console.log(` webhook: ${n.webhook ?? '(off)'}`);
648
+ console.log(` links: ${linkBase(n, hubPort(), '', readHub()?.base)} (hitl notify link <url> to change)`);
649
+ console.log(` batched: sent once agents have been quiet for ${n.quietSeconds ?? 20} s (approvals: at once)`);
650
+ if (!notifyEnabled(n))
651
+ console.log('\nTurn them on: hitl notify ntfy (then subscribe in the ntfy app)');
652
+ };
653
+ const test = async () => {
654
+ const n = notifyConfig();
655
+ if (!notifyEnabled(n))
656
+ fail('notifications are off: hitl notify ntfy (or webhook <url>) first');
657
+ await send(n, { title: 'hitl: notifications work', message: "You'll get one when an agent asks you something, shows you something, or waits for your OK.", url: linkBase(n, hubPort(), '', readHub()?.base), project: '', request: '', kind: 'test', newQuestions: 0, newItems: 0, approval: false, blocking: 0, replies: 0 });
658
+ console.log('Sent a test notification.');
659
+ };
660
+ switch (what) {
661
+ case undefined:
662
+ return show();
663
+ case 'ntfy': {
664
+ const url = arg ?? `https://ntfy.sh/hitl-${crypto.randomBytes(9).toString('base64url').toLowerCase().replace(/[^a-z0-9]/g, '')}`;
665
+ if (!/^https?:\/\/[^/]+\/[^/]+/.test(url))
666
+ fail('an ntfy topic URL looks like https://ntfy.sh/your-topic');
667
+ updateUserConfig((c) => (c.notify = { ...c.notify, ntfy: url }));
668
+ console.log(`✓ ntfy topic: ${url}`);
669
+ console.log(' On your phone: install the ntfy app (Android or iOS), tap +, and subscribe to');
670
+ console.log(` the topic "${new URL(url).pathname.slice(1)}"${new URL(url).host === 'ntfy.sh' ? '' : ` on the server ${new URL(url).origin}`}.`);
671
+ console.log(' Anyone who knows the topic can read it: notifications say what is waiting, never your answers.');
672
+ return test();
673
+ }
674
+ case 'webhook':
675
+ if (!arg || !/^https?:\/\//.test(arg))
676
+ fail('hitl notify webhook <https://…>');
677
+ updateUserConfig((c) => (c.notify = { ...c.notify, webhook: arg }));
678
+ console.log(`✓ webhook: ${arg}`);
679
+ return test();
680
+ case 'link':
681
+ if (!arg || !/^https?:\/\//.test(arg))
682
+ fail('hitl notify link <http://host:port> (where your phone opens hitl)');
683
+ updateUserConfig((c) => (c.notify = { ...c.notify, link: arg }));
684
+ return show();
685
+ case 'test':
686
+ return test();
687
+ case 'off':
688
+ updateUserConfig((c) => {
689
+ if (c.notify) {
690
+ delete c.notify.ntfy;
691
+ delete c.notify.webhook;
692
+ }
693
+ });
694
+ return void console.log('Notifications are off.');
695
+ }
696
+ fail('hitl notify [ntfy [url]|webhook <url>|link <url>|test|off]');
697
+ }
698
+ function readHub() {
699
+ try {
700
+ return JSON.parse(fs.readFileSync(path.join(configDir(), 'hub.json'), 'utf8'));
701
+ }
702
+ catch {
703
+ return undefined;
704
+ }
705
+ }
706
+ // ── hooks for Claude Code (opt-in) ──
707
+ /** `hitl hook` (or `hook prompt`): news for Claude when you send a message. `hook stop`: the gate before it finishes. */
708
+ function hook() {
709
+ const p = project();
710
+ if (!fs.existsSync(path.join(p.root, '.hitl')))
711
+ return;
712
+ if (args.pos[0] === 'stop')
713
+ return stopHook(p);
714
+ const c = newsCounts(p);
715
+ if (!c.total)
716
+ return;
717
+ const parts = [c.reviews && `${c.reviews} on reviews`, c.questions && `${c.questions} answers`, c.notes && `${c.notes} notes`].filter(Boolean);
718
+ console.log(`hitl: the human has said ${c.total} new thing${c.total === 1 ? '' : 's'} (${parts.join(', ')}). Read them with the news tool or \`npx hitloop news\`.`);
719
+ }
720
+ function stopHook(p) {
721
+ let input = {};
722
+ try {
723
+ input = JSON.parse(readStdin() || '{}');
724
+ }
725
+ catch {
726
+ input = {};
727
+ }
728
+ // already kept going once because of this hook: let it stop, so it can't loop
729
+ if (input.stop_hook_active)
730
+ return;
731
+ const reasons = [];
732
+ const c = newsCounts(p);
733
+ if (c.total)
734
+ reasons.push(`the human said ${c.total} new thing${c.total === 1 ? '' : 's'} you haven't read (npx hitloop news)`);
735
+ const gates = p.reviews.summaries().filter((s) => s.mode === 'approve' && s.status === 'open' && !s.done);
736
+ if (gates.length)
737
+ reasons.push(`${gates.length} approval${gates.length === 1 ? ' is' : 's are'} still waiting (${gates.map((g) => g.id).join(', ')}): wait for ${gates.length === 1 ? 'it' : 'them'} with \`npx hitloop wait --done ${gates.map((g) => g.id).join(' ')}\` before doing what needs the OK`);
738
+ const blocking = p.questions.summaries().filter((s) => s.status === 'open' && s.blocking > 0);
739
+ if (blocking.length)
740
+ reasons.push(`blocking questions are unanswered in ${blocking.map((s) => s.id).join(', ')}: wait with \`npx hitloop wait --blocking ${blocking.map((s) => s.id).join(' ')}\``);
741
+ if (!reasons.length)
742
+ return;
743
+ console.log(JSON.stringify({ decision: 'block', reason: `hitl: before you finish, ${reasons.join('; and ')}.` }));
744
+ }
745
+ function hooks() {
746
+ const root = folder();
747
+ const what = args.pos[0];
748
+ if (what !== 'on' && what !== 'off') {
749
+ const h = hookStatus(root);
750
+ console.log(`News hook: ${h.prompt ? 'on' : 'off'}`);
751
+ console.log(`Stop hook: ${h.stop ? 'on' : 'off'}`);
752
+ console.log('Turn on: hitl hooks on [--stop] off: hitl hooks off [--stop]');
753
+ return;
754
+ }
755
+ const on = what === 'on';
756
+ for (const l of setHooks(root, flag('--stop') ? { stop: on } : { prompt: on }))
757
+ console.log(l);
758
+ }
759
+ // ── init ──
760
+ const MARK_START = '<!-- hitl -->';
761
+ const MARK_END = '<!-- /hitl -->';
762
+ const SNIPPET = `${MARK_START}
763
+ ## The human in the loop: hitl
764
+
765
+ Use hitl (Human In The Loop: the hitl MCP tools, or \`npx hitloop\`) to put things in front of the user on their phone
766
+ or computer instead of asking in chat:
767
+
768
+ - **More than about 4 questions**, or ones that need thought or pictures: \`add_questions\` (each with options, one
769
+ recommended, and \`why\`; mark what you can't go on without as \`blocking\`).
770
+ - **Something to look at or review** (files, diffs, screenshots, mockups): \`show\` (mode "review" asks a verdict per
771
+ item). Each call makes its own review.
772
+ - **A yes or no before something risky** (deploy, delete, spend): \`approve\`; it waits for the answer.
773
+ - Give the user the link the tool returns. Keep working on what doesn't depend on the answer; to wait, run
774
+ \`npx hitloop wait --any\` (or \`--done <id>\`) in the background.
775
+ - Read what they said with \`news\` (or \`npx hitloop news\`): answers, verdicts, comments on lines and pictures, and notes
776
+ they wrote on files. Then \`ack\` / \`resolve\` with a short note. Put their words into docs word for word.
777
+ - Check \`decisions\` before asking something they may have decided already. Say who's asking with \`from\`.
778
+ - \`npx hitloop --help\` lists every command and option; check it rather than guessing at flags.
779
+ ${MARK_END}`;
780
+ function init() {
781
+ const root = path.resolve(args.pos[0] ?? value('-C', '--folder') ?? process.cwd());
782
+ const dir = path.join(root, '.hitl');
783
+ for (const d of ['questions', 'reviews', 'answers'])
784
+ fs.mkdirSync(path.join(dir, d), { recursive: true });
785
+ ensureReadme(dir);
786
+ register(root);
787
+ console.log(`✓ ${tildeOf(dir)}: questions/, reviews/, answers/ (the hub serves this folder now)`);
788
+ if (!flag('--no-claude-md')) {
789
+ const candidates = ['CLAUDE.md', 'AGENTS.md'].map((f) => path.join(root, f));
790
+ const file = candidates.find((f) => fs.existsSync(f)) ?? path.join(root, 'CLAUDE.md');
791
+ const text = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
792
+ const re = new RegExp(`${MARK_START}[\\s\\S]*?${MARK_END}`);
793
+ const next = re.test(text) ? text.replace(re, SNIPPET) : (text.trimEnd() ? text.trimEnd() + '\n\n' : '') + SNIPPET + '\n';
794
+ if (next !== text) {
795
+ fs.writeFileSync(file, next);
796
+ console.log(`✓ ${path.basename(file)}: how to use hitl ${re.test(text) ? 'updated' : 'added'}`);
797
+ }
798
+ else
799
+ console.log(`✓ ${path.basename(file)}: already says how to use hitl`);
800
+ }
801
+ const l = launcher();
802
+ if (!flag('--no-mcp')) {
803
+ const shell = process.platform === 'win32';
804
+ const has = spawnSync('claude', ['mcp', 'get', 'hitl'], { stdio: 'pipe', shell, timeout: 30_000 });
805
+ if (has.error || has.status === null)
806
+ console.log(`- Claude Code not found. Add the MCP server yourself: claude mcp add --scope user hitl -- ${l.line} mcp`);
807
+ else if (has.status === 0)
808
+ console.log('✓ MCP server: hitl is already set up in Claude Code');
809
+ else {
810
+ const add = spawnSync('claude', ['mcp', 'add', '--scope', 'user', 'hitl', '--', ...[l.command, ...l.args, 'mcp'].map((a) => (shell && /\s/.test(a) ? `"${a}"` : a))], { stdio: 'pipe', shell, timeout: 30_000 });
811
+ if (add.status === 0)
812
+ console.log('✓ MCP server: added to Claude Code for all your projects (restart Claude Code to load it)');
813
+ else
814
+ console.log(`✗ MCP server: claude mcp add failed (${String(add.stderr || add.stdout).trim()}). Run: claude mcp add --scope user hitl -- ${l.line} mcp`);
815
+ }
816
+ }
817
+ if (flag('--hook') || flag('--stop-hook'))
818
+ for (const line of setHooks(root, { ...(flag('--hook') ? { prompt: true } : {}), ...(flag('--stop-hook') ? { stop: true } : {}) }))
819
+ console.log(line);
820
+ else
821
+ console.log('- Hooks: off (hitl hooks on: news when you send a message; hitl hooks on --stop: a gate before Claude finishes)');
822
+ if (fs.existsSync(path.join(root, '.questionme')) || fs.existsSync(path.join(root, '.review')))
823
+ console.log('- Found .questionme/ or .review/: hitl import brings them in');
824
+ console.log('\nOpen it on your phone: npx hitloop (or: hitl url)');
825
+ }
826
+ // ── import: questionme and folderview folders ──
827
+ function importOld() {
828
+ const root = path.resolve(args.pos[0] ?? value('-C', '--folder') ?? process.cwd());
829
+ const dir = path.join(root, '.hitl');
830
+ let n = 0;
831
+ const copyDir = (from, to, filter) => {
832
+ if (!fs.existsSync(from))
833
+ return;
834
+ fs.mkdirSync(to, { recursive: true });
835
+ for (const f of fs.readdirSync(from)) {
836
+ if (!filter(f))
837
+ continue;
838
+ const dest = path.join(to, f);
839
+ if (fs.existsSync(dest)) {
840
+ console.log(` skipped ${path.relative(root, dest)} (already there)`);
841
+ continue;
842
+ }
843
+ fs.copyFileSync(path.join(from, f), dest);
844
+ n++;
845
+ }
846
+ };
847
+ const qm = path.join(root, '.questionme');
848
+ copyDir(path.join(qm, 'sets'), path.join(dir, 'questions'), (f) => f.endsWith('.json'));
849
+ copyDir(path.join(qm, 'answers'), path.join(dir, 'answers'), (f) => f.endsWith('.json') || f.endsWith('.md'));
850
+ const rv = path.join(root, '.review');
851
+ const old = path.join(rv, 'comments.json');
852
+ if (fs.existsSync(old)) {
853
+ const notesDir = path.join(dir, 'notes');
854
+ const target = path.join(notesDir, 'notes.json');
855
+ let mine = {};
856
+ try {
857
+ mine = JSON.parse(fs.readFileSync(target, 'utf8'));
858
+ }
859
+ catch {
860
+ mine = {};
861
+ }
862
+ const theirs = JSON.parse(fs.readFileSync(old, 'utf8')).comments ?? [];
863
+ const have = new Set((mine.comments ?? []).map((c) => c.id));
864
+ const add = theirs.filter((c) => !have.has(c.id));
865
+ fs.mkdirSync(notesDir, { recursive: true });
866
+ const comments = [...(mine.comments ?? []), ...add];
867
+ fs.writeFileSync(target, JSON.stringify({ version: 1, seq: (mine.seq ?? 0) + add.length, readSeq: mine.readSeq ?? 0, comments }, null, 2) + '\n');
868
+ copyDir(path.join(rv, 'images'), path.join(notesDir, 'images'), () => true);
869
+ n += add.length;
870
+ console.log(` ${add.length} note${add.length === 1 ? '' : 's'} from .review/comments.json`);
871
+ }
872
+ ensureReadme(dir);
873
+ register(root);
874
+ console.log(n ? `✓ Imported ${n} file${n === 1 ? '' : 's'} and notes into ${tildeOf(dir)}. The old folders are untouched: delete them when you're happy.` : 'Nothing to import (no .questionme/ or .review/ here).');
875
+ }
876
+ // ── small helpers ──
877
+ function num(v) {
878
+ if (v === undefined)
879
+ return undefined;
880
+ const n = Number(v);
881
+ if (!Number.isFinite(n))
882
+ fail(`not a number: ${v}`);
883
+ return n;
884
+ }
885
+ function list(v) {
886
+ return (v ?? '')
887
+ .split(',')
888
+ .map((s) => s.trim())
889
+ .filter(Boolean);
890
+ }
891
+ // run last, once every helper above is defined
892
+ try {
893
+ await run();
894
+ }
895
+ catch (e) {
896
+ fail(e instanceof StoreError || e instanceof Error ? e.message : String(e));
897
+ }