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.
- package/LICENSE +21 -0
- package/README.md +218 -2
- package/dist/agent.js +318 -0
- package/dist/cli.js +897 -0
- package/dist/client/assets/app.css +1 -0
- package/dist/client/assets/chunks/abap-EINPUMCJ.js +1 -0
- package/dist/client/assets/chunks/abnfDiagram-S7VZAUFO-W2CM2W6D.js +1 -0
- package/dist/client/assets/chunks/actionscript-3-O776AQUO.js +1 -0
- package/dist/client/assets/chunks/ada-WLOTXRTV.js +1 -0
- package/dist/client/assets/chunks/ahk-EF37A2DY.js +1 -0
- package/dist/client/assets/chunks/ahk2-FH4K3CKT.js +1 -0
- package/dist/client/assets/chunks/angular-html-ZALQFV2H.js +1 -0
- package/dist/client/assets/chunks/angular-ts-4IWMF7QO.js +1 -0
- package/dist/client/assets/chunks/apache-44AYOEWM.js +1 -0
- package/dist/client/assets/chunks/apex-XRYPID42.js +1 -0
- package/dist/client/assets/chunks/apl-5HBALFOI.js +1 -0
- package/dist/client/assets/chunks/applescript-2DZEHZEC.js +1 -0
- package/dist/client/assets/chunks/ara-ZGZQBLXL.js +1 -0
- package/dist/client/assets/chunks/architecture-WOLXFQ4H-EJGENNRL.js +1 -0
- package/dist/client/assets/chunks/architectureDiagram-YGSQEFNW-2VHWSKO6.js +36 -0
- package/dist/client/assets/chunks/asciidoc-GSQAKCPO.js +1 -0
- package/dist/client/assets/chunks/asm-BFURM5BK.js +1 -0
- package/dist/client/assets/chunks/astro-VJORWDZJ.js +1 -0
- package/dist/client/assets/chunks/awk-BSHTWXXX.js +1 -0
- package/dist/client/assets/chunks/ballerina-HAOQJ4B2.js +1 -0
- package/dist/client/assets/chunks/bat-OSH45EOF.js +1 -0
- package/dist/client/assets/chunks/beancount-RBATIHOV.js +1 -0
- package/dist/client/assets/chunks/berry-CZUIOUM4.js +1 -0
- package/dist/client/assets/chunks/bibtex-KCL2UWHE.js +1 -0
- package/dist/client/assets/chunks/bicep-SZJZB3SG.js +1 -0
- package/dist/client/assets/chunks/bird2-XBF5YQQV.js +1 -0
- package/dist/client/assets/chunks/blade-Q24LEVFC.js +1 -0
- package/dist/client/assets/chunks/blockDiagram-APKKGZFW-53LXSOOT.js +140 -0
- package/dist/client/assets/chunks/bpmnIcons-APHXDYXF-FMBRQNTY.js +1 -0
- package/dist/client/assets/chunks/bsl-TI44NWFB.js +1 -0
- package/dist/client/assets/chunks/c-NLHY4Y4N.js +1 -0
- package/dist/client/assets/chunks/c3-BUTEM6PO.js +1 -0
- package/dist/client/assets/chunks/c4Diagram-KJ2TOQU5-O53G4GQH.js +38 -0
- package/dist/client/assets/chunks/cadence-JIRTEU64.js +1 -0
- package/dist/client/assets/chunks/cairo-3W6NVDNW.js +1 -0
- package/dist/client/assets/chunks/chapel-BPBJ2E7K.js +1 -0
- package/dist/client/assets/chunks/chunk-25KVF5ZE.js +1 -0
- package/dist/client/assets/chunks/chunk-2CJKSQOI.js +1 -0
- package/dist/client/assets/chunks/chunk-2QIJRTWR.js +1 -0
- package/dist/client/assets/chunks/chunk-2ZCRJZGF.js +2 -0
- package/dist/client/assets/chunks/chunk-3ILNDZNY.js +213 -0
- package/dist/client/assets/chunks/chunk-3M2OOQ7S.js +1 -0
- package/dist/client/assets/chunks/chunk-3M57NTNK.js +1 -0
- package/dist/client/assets/chunks/chunk-3MR6DCZJ.js +1 -0
- package/dist/client/assets/chunks/chunk-3WGHS7F7.js +1 -0
- package/dist/client/assets/chunks/chunk-4JK75V33.js +1 -0
- package/dist/client/assets/chunks/chunk-4NXNVEWK.js +1 -0
- package/dist/client/assets/chunks/chunk-4RNNBDFQ.js +1 -0
- package/dist/client/assets/chunks/chunk-4T4NVH7V.js +1 -0
- package/dist/client/assets/chunks/chunk-4TFFBC35.js +1 -0
- package/dist/client/assets/chunks/chunk-4WYBE67P.js +1 -0
- package/dist/client/assets/chunks/chunk-56ITEUFA.js +1 -0
- package/dist/client/assets/chunks/chunk-5DXRE7IH.js +1 -0
- package/dist/client/assets/chunks/chunk-5IMADCNU.js +1 -0
- package/dist/client/assets/chunks/chunk-5XZPGKN4.js +1 -0
- package/dist/client/assets/chunks/chunk-6O32UKSP.js +1 -0
- package/dist/client/assets/chunks/chunk-7BAEB64Q.js +1 -0
- package/dist/client/assets/chunks/chunk-7FMWBHZO.js +1 -0
- package/dist/client/assets/chunks/chunk-7GFDITUT.js +1 -0
- package/dist/client/assets/chunks/chunk-7H4MLBHI.js +1 -0
- package/dist/client/assets/chunks/chunk-7OJ4MEEB.js +1 -0
- package/dist/client/assets/chunks/chunk-7RSEISFE.js +1 -0
- package/dist/client/assets/chunks/chunk-7YNZZQVT.js +1 -0
- package/dist/client/assets/chunks/chunk-AB7TIL2G.js +1 -0
- package/dist/client/assets/chunks/chunk-ADDF76FL.js +1 -0
- package/dist/client/assets/chunks/chunk-AF6UTCD4.js +321 -0
- package/dist/client/assets/chunks/chunk-AJ2K6ASU.js +1 -0
- package/dist/client/assets/chunks/chunk-AUBAENQZ.js +1 -0
- package/dist/client/assets/chunks/chunk-BJEYVZ65.js +1 -0
- package/dist/client/assets/chunks/chunk-BLRM6LXA.js +1 -0
- package/dist/client/assets/chunks/chunk-BOCTYCDZ.js +1 -0
- package/dist/client/assets/chunks/chunk-BU4VRYN7.js +1 -0
- package/dist/client/assets/chunks/chunk-CALYHV7D.js +1 -0
- package/dist/client/assets/chunks/chunk-CF4W3RZ4.js +1 -0
- package/dist/client/assets/chunks/chunk-DCZ2OBSL.js +1 -0
- package/dist/client/assets/chunks/chunk-DUFPBTHD.js +1 -0
- package/dist/client/assets/chunks/chunk-DUICIQBC.js +88 -0
- package/dist/client/assets/chunks/chunk-EH2N475V.js +1 -0
- package/dist/client/assets/chunks/chunk-EHLLNPZT.js +1 -0
- package/dist/client/assets/chunks/chunk-EIBFAW37.js +1 -0
- package/dist/client/assets/chunks/chunk-EO3ER3GN.js +1 -0
- package/dist/client/assets/chunks/chunk-FH24BIIA.js +1 -0
- package/dist/client/assets/chunks/chunk-FNOXGAL5.js +2 -0
- package/dist/client/assets/chunks/chunk-FTY6WLQF.js +3 -0
- package/dist/client/assets/chunks/chunk-FZFME6UK.js +1 -0
- package/dist/client/assets/chunks/chunk-FZJZZYYO.js +1 -0
- package/dist/client/assets/chunks/chunk-G4Z6L7OO.js +1 -0
- package/dist/client/assets/chunks/chunk-GNCEAN2B.js +1 -0
- package/dist/client/assets/chunks/chunk-GQJIKV3T.js +1 -0
- package/dist/client/assets/chunks/chunk-GUMNOKT2.js +1 -0
- package/dist/client/assets/chunks/chunk-GXRORAF3.js +1 -0
- package/dist/client/assets/chunks/chunk-H36ABDKY.js +1 -0
- package/dist/client/assets/chunks/chunk-HAR5NYVW.js +1 -0
- package/dist/client/assets/chunks/chunk-HIGPS4L2.js +1 -0
- package/dist/client/assets/chunks/chunk-HMOBGTME.js +1 -0
- package/dist/client/assets/chunks/chunk-HNO6SQ7M.js +1 -0
- package/dist/client/assets/chunks/chunk-HYVZ3GYN.js +1 -0
- package/dist/client/assets/chunks/chunk-I6TGFOQS.js +1 -0
- package/dist/client/assets/chunks/chunk-IHIOJK7I.js +1 -0
- package/dist/client/assets/chunks/chunk-IHJNUMSC.js +1 -0
- package/dist/client/assets/chunks/chunk-IP7PB2OC.js +1 -0
- package/dist/client/assets/chunks/chunk-ISDTMH2Y.js +1 -0
- package/dist/client/assets/chunks/chunk-J65M2LNT.js +1 -0
- package/dist/client/assets/chunks/chunk-JCWYZ7X2.js +62 -0
- package/dist/client/assets/chunks/chunk-JENWYTLI.js +1 -0
- package/dist/client/assets/chunks/chunk-JJ6YWJQS.js +1 -0
- package/dist/client/assets/chunks/chunk-JMUVGGX7.js +32 -0
- package/dist/client/assets/chunks/chunk-JNQUFMM4.js +1 -0
- package/dist/client/assets/chunks/chunk-JPTECOAL.js +1 -0
- package/dist/client/assets/chunks/chunk-JZNK4LKS.js +1 -0
- package/dist/client/assets/chunks/chunk-KPNXJBIW.js +1 -0
- package/dist/client/assets/chunks/chunk-KZO2GZL7.js +1 -0
- package/dist/client/assets/chunks/chunk-L2REESOQ.js +1 -0
- package/dist/client/assets/chunks/chunk-LJPJXR7R.js +1 -0
- package/dist/client/assets/chunks/chunk-LOEZMLCI.js +1 -0
- package/dist/client/assets/chunks/chunk-MLYC6FSE.js +1 -0
- package/dist/client/assets/chunks/chunk-MMDGEZYU.js +1 -0
- package/dist/client/assets/chunks/chunk-MYXE7CGE.js +1 -0
- package/dist/client/assets/chunks/chunk-NBH7YOBD.js +15 -0
- package/dist/client/assets/chunks/chunk-NCOVM3YF.js +1 -0
- package/dist/client/assets/chunks/chunk-NCWOBVU7.js +1 -0
- package/dist/client/assets/chunks/chunk-NLRIHHEH.js +1 -0
- package/dist/client/assets/chunks/chunk-NUPEOYK6.js +1 -0
- package/dist/client/assets/chunks/chunk-O4MVOA7X.js +1 -0
- package/dist/client/assets/chunks/chunk-OBHQQZ4H.js +1 -0
- package/dist/client/assets/chunks/chunk-OKPNJIIR.js +1 -0
- package/dist/client/assets/chunks/chunk-OLMPB57R.js +1 -0
- package/dist/client/assets/chunks/chunk-P2FYOAEV.js +1 -0
- package/dist/client/assets/chunks/chunk-PI3SNP4C.js +1 -0
- package/dist/client/assets/chunks/chunk-PRDTEROB.js +1 -0
- package/dist/client/assets/chunks/chunk-PZELHD2H.js +136 -0
- package/dist/client/assets/chunks/chunk-QR2Q34LM.js +1 -0
- package/dist/client/assets/chunks/chunk-R4GIXL6S.js +1 -0
- package/dist/client/assets/chunks/chunk-RORF4F5V.js +1 -0
- package/dist/client/assets/chunks/chunk-S73BYTLL.js +1 -0
- package/dist/client/assets/chunks/chunk-SFC5MRYR.js +1 -0
- package/dist/client/assets/chunks/chunk-SVM2H22K.js +2 -0
- package/dist/client/assets/chunks/chunk-SZ6UCGVD.js +1 -0
- package/dist/client/assets/chunks/chunk-SZYM6GIJ.js +127 -0
- package/dist/client/assets/chunks/chunk-TT5KJZKM.js +1 -0
- package/dist/client/assets/chunks/chunk-UFBKUYNH.js +1 -0
- package/dist/client/assets/chunks/chunk-UL57AD4T.js +1 -0
- package/dist/client/assets/chunks/chunk-UNEI7LMM.js +1 -0
- package/dist/client/assets/chunks/chunk-VNQBBFIY.js +1 -0
- package/dist/client/assets/chunks/chunk-WGVITPII.js +1 -0
- package/dist/client/assets/chunks/chunk-WRA6KJHX.js +10 -0
- package/dist/client/assets/chunks/chunk-WS5EHW43.js +1 -0
- package/dist/client/assets/chunks/chunk-XE6SLBBE.js +3 -0
- package/dist/client/assets/chunks/chunk-XF5T2P7U.js +72 -0
- package/dist/client/assets/chunks/chunk-XFRY5WKO.js +1 -0
- package/dist/client/assets/chunks/chunk-XWYV4OPA.js +1 -0
- package/dist/client/assets/chunks/chunk-YCE5ECKY.js +1 -0
- package/dist/client/assets/chunks/chunk-YIN5CSIF.js +1 -0
- package/dist/client/assets/chunks/chunk-YLFUX6XH.js +1 -0
- package/dist/client/assets/chunks/chunk-YNXJICZM.js +1 -0
- package/dist/client/assets/chunks/chunk-YOHF7NGD.js +1 -0
- package/dist/client/assets/chunks/chunk-YORIST7Z.js +1 -0
- package/dist/client/assets/chunks/chunk-YUL7CG4R.js +1 -0
- package/dist/client/assets/chunks/chunk-YUN7JSFJ.js +2 -0
- package/dist/client/assets/chunks/chunk-YZ7D2IT7.js +1 -0
- package/dist/client/assets/chunks/chunk-Z7LAPMFJ.js +1 -0
- package/dist/client/assets/chunks/chunk-ZE325LPC.js +1 -0
- package/dist/client/assets/chunks/chunk-ZN7TN6PO.js +1 -0
- package/dist/client/assets/chunks/chunk-ZST3EVPO.js +1 -0
- package/dist/client/assets/chunks/chunk-ZZHMDDZN.js +1 -0
- package/dist/client/assets/chunks/clarity-RKRYQ7WF.js +1 -0
- package/dist/client/assets/chunks/classDiagram-v2-L25IHNXF-Y67HVDZV.js +217 -0
- package/dist/client/assets/chunks/clojure-2X2DQECJ.js +1 -0
- package/dist/client/assets/chunks/cmake-LH5524XE.js +1 -0
- package/dist/client/assets/chunks/cobol-7DALQF3W.js +1 -0
- package/dist/client/assets/chunks/codeowners-52SZU5P5.js +1 -0
- package/dist/client/assets/chunks/codeql-EISH3KUP.js +1 -0
- package/dist/client/assets/chunks/coffee-VAWSGA7Y.js +1 -0
- package/dist/client/assets/chunks/common-lisp-4MVK2AN6.js +1 -0
- package/dist/client/assets/chunks/coq-4VRYZRZV.js +1 -0
- package/dist/client/assets/chunks/core-BRBMZSD2.js +12 -0
- package/dist/client/assets/chunks/cose-bilkent-JH36ORCC-BIOOHSAA.js +1 -0
- package/dist/client/assets/chunks/cpp-RFECMSKD.js +1 -0
- package/dist/client/assets/chunks/crystal-ZBGGFAOI.js +1 -0
- package/dist/client/assets/chunks/csharp-BZDPMKKW.js +1 -0
- package/dist/client/assets/chunks/css-3WWGM6UD.js +1 -0
- package/dist/client/assets/chunks/csv-AB4GNWSB.js +1 -0
- package/dist/client/assets/chunks/cue-2E46MZP2.js +1 -0
- package/dist/client/assets/chunks/cynefin-EF2NZ3EQ-TLFQLWUN.js +1 -0
- package/dist/client/assets/chunks/cynefinDiagram-L36ESFBY-FLPXCSSG.js +62 -0
- package/dist/client/assets/chunks/cypher-HR3ILFOL.js +1 -0
- package/dist/client/assets/chunks/d-TARSZPOL.js +1 -0
- package/dist/client/assets/chunks/dagre-SPEB5RXH-7EGEINJ2.js +4 -0
- package/dist/client/assets/chunks/dart-SALLJHVI.js +1 -0
- package/dist/client/assets/chunks/dax-WYMRGMXC.js +1 -0
- package/dist/client/assets/chunks/desktop-ND5CGLLJ.js +1 -0
- package/dist/client/assets/chunks/diagram-3B7GL73J-6USY4HZX.js +200 -0
- package/dist/client/assets/chunks/diagram-DNEMPZML-BNSOC4VW.js +24 -0
- package/dist/client/assets/chunks/diagram-L3MUGCDN-5V5FZYIG.js +41 -0
- package/dist/client/assets/chunks/diagram-LA4NIPWU-25VLY2CO.js +24 -0
- package/dist/client/assets/chunks/diagram-MHCOC4E2-G2LFP3JU.js +3 -0
- package/dist/client/assets/chunks/diagram-XPFHEONP-E6HBBIBV.js +30 -0
- package/dist/client/assets/chunks/diff-PWYGPM7B.js +1 -0
- package/dist/client/assets/chunks/docker-A4D7DT7W.js +1 -0
- package/dist/client/assets/chunks/dotenv-VWQWV2DY.js +1 -0
- package/dist/client/assets/chunks/dream-maker-M5YDQS35.js +1 -0
- package/dist/client/assets/chunks/ebnfDiagram-UZSYDL2S-BMXXZZPI.js +1 -0
- package/dist/client/assets/chunks/edge-3QXIDJHI.js +1 -0
- package/dist/client/assets/chunks/elixir-3OX2UHIZ.js +1 -0
- package/dist/client/assets/chunks/elk-XV6XMXQE-IPR65FON.js +27 -0
- package/dist/client/assets/chunks/elm-CTS65NCT.js +1 -0
- package/dist/client/assets/chunks/emacs-lisp-HHS2IDO7.js +1 -0
- package/dist/client/assets/chunks/engine-javascript-DNHSF67B.js +141 -0
- package/dist/client/assets/chunks/erDiagram-EYBAPDEH-3GEHFWHG.js +99 -0
- package/dist/client/assets/chunks/erb-ICYWK6HS.js +1 -0
- package/dist/client/assets/chunks/erlang-BDQPNCKX.js +1 -0
- package/dist/client/assets/chunks/eventmodeling-K75KTNOO-B27BJTZL.js +1 -0
- package/dist/client/assets/chunks/fennel-4ONBA2PH.js +1 -0
- package/dist/client/assets/chunks/fish-ATLPBZSD.js +1 -0
- package/dist/client/assets/chunks/flowDiagram-MALGAFMO-5PCLR4AE.js +1 -0
- package/dist/client/assets/chunks/fluent-GGMONBVW.js +1 -0
- package/dist/client/assets/chunks/fortran-fixed-form-5MSBDRWQ.js +1 -0
- package/dist/client/assets/chunks/fortran-free-form-TT3RBL75.js +1 -0
- package/dist/client/assets/chunks/fsharp-PAGHNW4O.js +1 -0
- package/dist/client/assets/chunks/ganttDiagram-23REXITY-VE5MVBTO.js +292 -0
- package/dist/client/assets/chunks/gdresource-5OTP326D.js +1 -0
- package/dist/client/assets/chunks/gdscript-VPWHSCLF.js +1 -0
- package/dist/client/assets/chunks/gdshader-FYLPXFE5.js +1 -0
- package/dist/client/assets/chunks/genie-UYMEEMA2.js +1 -0
- package/dist/client/assets/chunks/gherkin-SU7D5DMY.js +1 -0
- package/dist/client/assets/chunks/git-commit-5CF5QAG7.js +1 -0
- package/dist/client/assets/chunks/git-rebase-X72BAUZI.js +1 -0
- package/dist/client/assets/chunks/gitGraph-VSP46ZUC-J6CJ5YZF.js +1 -0
- package/dist/client/assets/chunks/gitGraphDiagram-STJD7YCW-7YJGT6ZP.js +106 -0
- package/dist/client/assets/chunks/gleam-LR42YRKO.js +1 -0
- package/dist/client/assets/chunks/glimmer-js-JPF67FJU.js +1 -0
- package/dist/client/assets/chunks/glimmer-ts-BYPAVIZT.js +1 -0
- package/dist/client/assets/chunks/glsl-GTGUULBN.js +1 -0
- package/dist/client/assets/chunks/gn-NMDQA4K3.js +1 -0
- package/dist/client/assets/chunks/gnuplot-UUAMYBCZ.js +1 -0
- package/dist/client/assets/chunks/go-4RERSTNP.js +1 -0
- package/dist/client/assets/chunks/graphql-5A6RK6YF.js +1 -0
- package/dist/client/assets/chunks/groovy-O7BJONCU.js +1 -0
- package/dist/client/assets/chunks/hack-7QGTNPDP.js +1 -0
- package/dist/client/assets/chunks/haml-ON5GAWN4.js +1 -0
- package/dist/client/assets/chunks/handlebars-6UOQSUBP.js +1 -0
- package/dist/client/assets/chunks/haskell-OIZXZ26Y.js +1 -0
- package/dist/client/assets/chunks/haxe-7FFVS4FO.js +1 -0
- package/dist/client/assets/chunks/hcl-XFUFQJHV.js +1 -0
- package/dist/client/assets/chunks/hjson-QDBHMTD5.js +1 -0
- package/dist/client/assets/chunks/hlsl-4LP25FFN.js +1 -0
- package/dist/client/assets/chunks/html-7RNQXAAV.js +1 -0
- package/dist/client/assets/chunks/html-derivative-ESOY72Q2.js +1 -0
- package/dist/client/assets/chunks/http-SAD4PMBD.js +1 -0
- package/dist/client/assets/chunks/hurl-ZSJJXPQH.js +1 -0
- package/dist/client/assets/chunks/hxml-HJCC37RK.js +1 -0
- package/dist/client/assets/chunks/hy-CLDE3P4K.js +1 -0
- package/dist/client/assets/chunks/imba-REB34U5V.js +1 -0
- package/dist/client/assets/chunks/info-OHQRW6UA-EQQKXR77.js +1 -0
- package/dist/client/assets/chunks/infoDiagram-KBXO4MBG-F6KGZCGS.js +2 -0
- package/dist/client/assets/chunks/ini-PDFEMGRA.js +1 -0
- package/dist/client/assets/chunks/ishikawaDiagram-M35NCXHK-5D6ZOZ34.js +70 -0
- package/dist/client/assets/chunks/java-KB6XENAD.js +1 -0
- package/dist/client/assets/chunks/javascript-3JX5T423.js +1 -0
- package/dist/client/assets/chunks/jinja-V4UNU6K4.js +1 -0
- package/dist/client/assets/chunks/jison-KH5XNLJP.js +1 -0
- package/dist/client/assets/chunks/journeyDiagram-OPLOXDXO-ANDOJZKW.js +139 -0
- package/dist/client/assets/chunks/json-HPXFRRPY.js +1 -0
- package/dist/client/assets/chunks/json5-LJM6TXWM.js +1 -0
- package/dist/client/assets/chunks/jsonc-UZWRU3JV.js +1 -0
- package/dist/client/assets/chunks/jsonl-RFZD5UN4.js +1 -0
- package/dist/client/assets/chunks/jsonnet-GFZABBL6.js +1 -0
- package/dist/client/assets/chunks/jssm-2YIGBISJ.js +1 -0
- package/dist/client/assets/chunks/jsx-KLV2ZWUT.js +1 -0
- package/dist/client/assets/chunks/julia-3TZDIPPO.js +1 -0
- package/dist/client/assets/chunks/just-UHCONBQS.js +1 -0
- package/dist/client/assets/chunks/kanban-definition-BZKZHSQY-GF5I5NVD.js +89 -0
- package/dist/client/assets/chunks/katex-PIN72PQZ.js +257 -0
- package/dist/client/assets/chunks/kdl-DARTFOCL.js +1 -0
- package/dist/client/assets/chunks/kotlin-7KT5DW6Y.js +1 -0
- package/dist/client/assets/chunks/kusto-EKYXZTC5.js +1 -0
- package/dist/client/assets/chunks/langs-GO3UQR4H.js +1 -0
- package/dist/client/assets/chunks/latex-T57MIHR5.js +1 -0
- package/dist/client/assets/chunks/lean-5QVPRMV6.js +1 -0
- package/dist/client/assets/chunks/less-K6QEH5TN.js +1 -0
- package/dist/client/assets/chunks/liquid-3LCNNEIV.js +1 -0
- package/dist/client/assets/chunks/llvm-O32ZIVRF.js +1 -0
- package/dist/client/assets/chunks/log-C4YPJ5BB.js +1 -0
- package/dist/client/assets/chunks/logo-JPMO43A6.js +1 -0
- package/dist/client/assets/chunks/lua-YKVSOG7K.js +1 -0
- package/dist/client/assets/chunks/luau-K4EECYSO.js +1 -0
- package/dist/client/assets/chunks/make-VHSYMHHW.js +1 -0
- package/dist/client/assets/chunks/markdown-GC5J46H4.js +1 -0
- package/dist/client/assets/chunks/marko-U45YFADC.js +1 -0
- package/dist/client/assets/chunks/matlab-AMOKC4LU.js +1 -0
- package/dist/client/assets/chunks/mdc-DIGA2UXP.js +1 -0
- package/dist/client/assets/chunks/mdx-BZUGCKQA.js +1 -0
- package/dist/client/assets/chunks/mermaid-WHDP6ZPA.js +1 -0
- package/dist/client/assets/chunks/mermaid.core-SQMPMUDN.js +10 -0
- package/dist/client/assets/chunks/mindmap-definition-I2QAS2VK-4NH5S56C.js +98 -0
- package/dist/client/assets/chunks/mipsasm-2S4CUGQV.js +1 -0
- package/dist/client/assets/chunks/mojo-ZOL2PNKT.js +1 -0
- package/dist/client/assets/chunks/moonbit-U3BDAIFE.js +1 -0
- package/dist/client/assets/chunks/move-YSXALBWA.js +1 -0
- package/dist/client/assets/chunks/narrat-DHFIAUJH.js +1 -0
- package/dist/client/assets/chunks/nextflow-ESAKNTI3.js +1 -0
- package/dist/client/assets/chunks/nextflow-groovy-FKDSPJUI.js +1 -0
- package/dist/client/assets/chunks/nginx-XYAYERLO.js +1 -0
- package/dist/client/assets/chunks/nim-XOWBVFDH.js +1 -0
- package/dist/client/assets/chunks/nix-RVSTQSHC.js +1 -0
- package/dist/client/assets/chunks/nsis-KMDUKQRL.js +1 -0
- package/dist/client/assets/chunks/nushell-SKJHHZ7U.js +1 -0
- package/dist/client/assets/chunks/objective-c-3B3KXQ7Q.js +1 -0
- package/dist/client/assets/chunks/objective-cpp-6JDULPJS.js +1 -0
- package/dist/client/assets/chunks/ocaml-TMRFXBVO.js +1 -0
- package/dist/client/assets/chunks/odin-IN3PLCK7.js +1 -0
- package/dist/client/assets/chunks/openscad-P52IKPNH.js +1 -0
- package/dist/client/assets/chunks/org-N3SJ2PIA.js +1 -0
- package/dist/client/assets/chunks/packet-JDAUHWVQ-4SWIKSKP.js +1 -0
- package/dist/client/assets/chunks/pascal-T7CUWCQ4.js +1 -0
- package/dist/client/assets/chunks/pegDiagram-FD4XPPHP-WDKZSEGN.js +1 -0
- package/dist/client/assets/chunks/perl-LCZ4KODH.js +1 -0
- package/dist/client/assets/chunks/php-4XHMBVR5.js +1 -0
- package/dist/client/assets/chunks/pie-XZMESJXO-PSSWOWGV.js +1 -0
- package/dist/client/assets/chunks/pieDiagram-J4AKIIQI-BDW5I5ON.js +39 -0
- package/dist/client/assets/chunks/pkl-OACG5B67.js +1 -0
- package/dist/client/assets/chunks/plsql-7E5C7CJX.js +1 -0
- package/dist/client/assets/chunks/po-ZSREFRRP.js +1 -0
- package/dist/client/assets/chunks/polar-4GBINVA7.js +1 -0
- package/dist/client/assets/chunks/postcss-B7RPVGFW.js +1 -0
- package/dist/client/assets/chunks/powerquery-6UFMTY4G.js +1 -0
- package/dist/client/assets/chunks/powershell-3STRPCNT.js +1 -0
- package/dist/client/assets/chunks/prisma-4TPSPIZ5.js +1 -0
- package/dist/client/assets/chunks/prolog-HEAYPKQW.js +1 -0
- package/dist/client/assets/chunks/proto-Y3Y3VKXM.js +1 -0
- package/dist/client/assets/chunks/pug-7PBPMG3M.js +1 -0
- package/dist/client/assets/chunks/puppet-FR35R4OI.js +1 -0
- package/dist/client/assets/chunks/purescript-OUHXQAOW.js +1 -0
- package/dist/client/assets/chunks/python-SNDG7FNU.js +1 -0
- package/dist/client/assets/chunks/qml-UVIOSWP4.js +1 -0
- package/dist/client/assets/chunks/qmldir-6JGUNYL5.js +1 -0
- package/dist/client/assets/chunks/qss-ZCC466IZ.js +1 -0
- package/dist/client/assets/chunks/quadrantDiagram-XTYBLID2-5VKXWVJ6.js +7 -0
- package/dist/client/assets/chunks/r-4XW4O3ZF.js +1 -0
- package/dist/client/assets/chunks/racket-YJAYXOOG.js +1 -0
- package/dist/client/assets/chunks/radar-ABXABTNO-3AXZCEY2.js +1 -0
- package/dist/client/assets/chunks/railroad-I3PHUGI6-2PLXYPOT.js +1 -0
- package/dist/client/assets/chunks/railroad-abnf-LNEFI6M7-M4DGICQX.js +1 -0
- package/dist/client/assets/chunks/railroad-ebnf-DWYD2UWJ-UEIHQMSZ.js +1 -0
- package/dist/client/assets/chunks/railroad-peg-JR7BVNR7-I7VLIBXM.js +1 -0
- package/dist/client/assets/chunks/railroadDiagram-MRQ6NCDL-Y6IYWEFL.js +1 -0
- package/dist/client/assets/chunks/raku-63NEL5JK.js +1 -0
- package/dist/client/assets/chunks/razor-J3CJP523.js +1 -0
- package/dist/client/assets/chunks/rbs-6GQLT7NW.js +1 -0
- package/dist/client/assets/chunks/reg-6Q3OEC2C.js +1 -0
- package/dist/client/assets/chunks/regexp-LPIMILT3.js +1 -0
- package/dist/client/assets/chunks/rel-TOQGUTHB.js +1 -0
- package/dist/client/assets/chunks/requirementDiagram-Y7LWUY3S-CIWXVVWJ.js +84 -0
- package/dist/client/assets/chunks/riscv-UELGBOXE.js +1 -0
- package/dist/client/assets/chunks/ron-FW7LCJJH.js +1 -0
- package/dist/client/assets/chunks/rosmsg-O63VHE3L.js +1 -0
- package/dist/client/assets/chunks/rst-3WJXGLA7.js +1 -0
- package/dist/client/assets/chunks/ruby-7S7L6JTH.js +1 -0
- package/dist/client/assets/chunks/rust-LIK4HRRI.js +1 -0
- package/dist/client/assets/chunks/sankeyDiagram-WGJFYUXA-OGNQPX5V.js +40 -0
- package/dist/client/assets/chunks/sas-FHDPZ2JH.js +1 -0
- package/dist/client/assets/chunks/sass-UI4HW2FN.js +1 -0
- package/dist/client/assets/chunks/scala-KN7RQSUJ.js +1 -0
- package/dist/client/assets/chunks/scheme-5R527YZ5.js +1 -0
- package/dist/client/assets/chunks/scss-KZ6DRE4A.js +1 -0
- package/dist/client/assets/chunks/sdbl-FHOCL5SL.js +1 -0
- package/dist/client/assets/chunks/sequenceDiagram-5NDRBG5M-QX2V24ZU.js +169 -0
- package/dist/client/assets/chunks/shaderlab-2TLHUS2H.js +1 -0
- package/dist/client/assets/chunks/shellscript-PIB6ZBDM.js +1 -0
- package/dist/client/assets/chunks/shellsession-5KL7NFNN.js +1 -0
- package/dist/client/assets/chunks/sizeCapture-INFHLROL-LCS2PIHM.js +1 -0
- package/dist/client/assets/chunks/smalltalk-JFLFU247.js +1 -0
- package/dist/client/assets/chunks/smithy-HONEXZAM.js +1 -0
- package/dist/client/assets/chunks/solidity-SJ3INJFZ.js +1 -0
- package/dist/client/assets/chunks/soy-ND4SWTM2.js +1 -0
- package/dist/client/assets/chunks/sparql-YB2JQBZD.js +1 -0
- package/dist/client/assets/chunks/splunk-UXIDDAWL.js +1 -0
- package/dist/client/assets/chunks/sql-K6OMG7SQ.js +1 -0
- package/dist/client/assets/chunks/ssh-config-I7KJY3KQ.js +1 -0
- package/dist/client/assets/chunks/stata-DNZ3DTFJ.js +1 -0
- package/dist/client/assets/chunks/stateDiagram-v2-FJ4FJNEA-LVAUWF3A.js +291 -0
- package/dist/client/assets/chunks/stylus-GWPR6IQY.js +1 -0
- package/dist/client/assets/chunks/surrealql-KQNHBWLC.js +1 -0
- package/dist/client/assets/chunks/svelte-YQKF25S4.js +1 -0
- package/dist/client/assets/chunks/swift-ZCIZUJXA.js +1 -0
- package/dist/client/assets/chunks/swimlanes-UMQ2AFYF-B3RQOARQ.js +1 -0
- package/dist/client/assets/chunks/swimlanesDiagram-OMPLPG4O-JOE33EOV.js +8 -0
- package/dist/client/assets/chunks/system-verilog-Y7E6FIO3.js +1 -0
- package/dist/client/assets/chunks/systemd-WHC7NBZP.js +1 -0
- package/dist/client/assets/chunks/talonscript-WC7SODAH.js +1 -0
- package/dist/client/assets/chunks/tasl-QHY2GTCB.js +1 -0
- package/dist/client/assets/chunks/tcl-VAMIL5PJ.js +1 -0
- package/dist/client/assets/chunks/temml-ISNSX3VV.js +96 -0
- package/dist/client/assets/chunks/templ-B3BHKWV4.js +1 -0
- package/dist/client/assets/chunks/terraform-Q4UEK6FK.js +1 -0
- package/dist/client/assets/chunks/tex-63OWCYAB.js +1 -0
- package/dist/client/assets/chunks/timeline-definition-SEUHKTQE-FDFTVAJG.js +120 -0
- package/dist/client/assets/chunks/toml-7Z4IPGES.js +1 -0
- package/dist/client/assets/chunks/treeView-D4JQ5SDB-ABSGRAGI.js +1 -0
- package/dist/client/assets/chunks/treemap-SAJKECNS-MYX2CBJN.js +1 -0
- package/dist/client/assets/chunks/ts-tags-DT62HE52.js +1 -0
- package/dist/client/assets/chunks/tsv-RTXHWADP.js +1 -0
- package/dist/client/assets/chunks/tsx-UANUBOC7.js +1 -0
- package/dist/client/assets/chunks/turtle-QVOUNUUL.js +1 -0
- package/dist/client/assets/chunks/twig-ZPC4BPDA.js +1 -0
- package/dist/client/assets/chunks/typescript-WGWKIMMW.js +1 -0
- package/dist/client/assets/chunks/typespec-TYCVR32J.js +1 -0
- package/dist/client/assets/chunks/typst-PIBFYUO6.js +1 -0
- package/dist/client/assets/chunks/usecaseDiagram-AFA6YXN4-KW7ADINR.js +370 -0
- package/dist/client/assets/chunks/v-3ERNWBSP.js +1 -0
- package/dist/client/assets/chunks/vala-UCBCT2GS.js +1 -0
- package/dist/client/assets/chunks/vb-DGI3VTPA.js +1 -0
- package/dist/client/assets/chunks/vennDiagram-NNMJR3RB-RDGLGOGM.js +34 -0
- package/dist/client/assets/chunks/verilog-G72NZROS.js +1 -0
- package/dist/client/assets/chunks/vhdl-PYGA77XE.js +1 -0
- package/dist/client/assets/chunks/viml-OOC5B5UU.js +1 -0
- package/dist/client/assets/chunks/vue-GTNHIOPG.js +1 -0
- package/dist/client/assets/chunks/vue-html-DKAMJV55.js +1 -0
- package/dist/client/assets/chunks/vue-vine-WTM5HREV.js +1 -0
- package/dist/client/assets/chunks/vyper-RHYEHOJM.js +1 -0
- package/dist/client/assets/chunks/wardley-7MLQ67FV-QJL2HSBT.js +1 -0
- package/dist/client/assets/chunks/wardleyDiagram-S7YFUSEH-A4XX3UJR.js +78 -0
- package/dist/client/assets/chunks/wasm-ZMM2Z5XV.js +1 -0
- package/dist/client/assets/chunks/wenyan-HF5UTKSJ.js +1 -0
- package/dist/client/assets/chunks/wgsl-O7D7OPO7.js +1 -0
- package/dist/client/assets/chunks/wikitext-T7BSWSKQ.js +1 -0
- package/dist/client/assets/chunks/wit-TKCHPPT6.js +1 -0
- package/dist/client/assets/chunks/wolfram-7MN66A7N.js +1 -0
- package/dist/client/assets/chunks/xml-NLTLAWOD.js +1 -0
- package/dist/client/assets/chunks/xsl-NZ4ECK3P.js +1 -0
- package/dist/client/assets/chunks/xychartDiagram-GGNKY7IX-OXJILWHR.js +7 -0
- package/dist/client/assets/chunks/yaml-HRJLEV3V.js +1 -0
- package/dist/client/assets/chunks/zenscript-4LLRSLPI.js +1 -0
- package/dist/client/assets/chunks/zig-Y7MUQFUO.js +1 -0
- package/dist/client/assets/icon.svg +1 -0
- package/dist/client/assets/main.js +254 -0
- package/dist/client/index.html +59 -0
- package/dist/diff.js +119 -0
- package/dist/fs/files.js +233 -0
- package/dist/fs/git.js +369 -0
- package/dist/fs/glob.js +89 -0
- package/dist/fs/notes.js +368 -0
- package/dist/fs/search.js +136 -0
- package/dist/fs/watch.js +69 -0
- package/dist/fs/zip.js +100 -0
- package/dist/hooks.js +65 -0
- package/dist/http.js +81 -0
- package/dist/hub/atomic.js +77 -0
- package/dist/hub/notify.js +239 -0
- package/dist/hub/registry.js +219 -0
- package/dist/mcp.js +500 -0
- package/dist/q/agent.js +215 -0
- package/dist/q/model.js +633 -0
- package/dist/q/schema.js +133 -0
- package/dist/q/store.js +413 -0
- package/dist/r/model.js +323 -0
- package/dist/r/schema.js +53 -0
- package/dist/r/store.js +489 -0
- package/dist/server.js +246 -0
- package/dist/settings.js +30 -0
- package/dist/workspace.js +639 -0
- package/package.json +73 -4
- package/schema/questions.schema.json +1670 -0
- package/schema/reviews.schema.json +151 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ClickerMonkey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,219 @@
|
|
|
1
|
-
#
|
|
1
|
+
# hitl — Human In The Loop
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**hitl** is where an AI agent and its human meet. The agent **asks** you questions, **shows** you things, asks you to
|
|
4
|
+
**review** its work, and waits at a **gate** for your OK. You answer, review, mark up pictures and browse the project's
|
|
5
|
+
files, on your phone or your computer. Everything you say lands in one feed the agent reads a little at a time.
|
|
6
|
+
|
|
7
|
+
The npm package is **`hitloop`**; once installed (`npm i -g hitloop`) the command is `hitl`.
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npx hitloop init # once per project: the .hitl folder, the MCP server for Claude Code, a note in CLAUDE.md
|
|
11
|
+
npx hitloop # the page, for every project, with links for your phone
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Site: https://clickermonkey.github.io/hitl/** (the commands, and a Claude skill to copy or download).
|
|
15
|
+
|
|
16
|
+
| | | | |
|
|
17
|
+
|:-:|:-:|:-:|:-:|
|
|
18
|
+
|  |  |  |  |
|
|
19
|
+
| **Inbox** | **Review** | **Before / after** | **Approve** |
|
|
20
|
+
|
|
21
|
+
"Human in the loop" means a person stays part of what an automated system does: it checks with them, they decide,
|
|
22
|
+
and it carries on. hitl gives that person somewhere to do it, instead of a chat scrolling past.
|
|
23
|
+
|
|
24
|
+
It grew from two smaller tools, and does everything both did:
|
|
25
|
+
|
|
26
|
+
- [questionme](https://github.com/ClickerMonkey/questionme): an agent's questions, answered on your phone.
|
|
27
|
+
- [folderview](https://github.com/ClickerMonkey/folderview): browse, search and comment on any folder, with git.
|
|
28
|
+
|
|
29
|
+
## What you can do
|
|
30
|
+
|
|
31
|
+
| | |
|
|
32
|
+
|---|---|
|
|
33
|
+
| **Inbox** | Everything waiting on you across every project, most urgent first: approvals (an agent is waiting), blocking questions, reviews, things to look at. What's done folds away. |
|
|
34
|
+
| **Answer questions** | Dozens at a time, with recommendations, why each one matters, and pictures, diffs and mockups to decide from. Take the recommendations and change the few you disagree with. Text answers grow and wrap as you type. |
|
|
35
|
+
| **Review** | Each thing the agent shows you gets **Looks good**, **Needs changes** or **No**. Comment on a line (tap its number; tap another for a range), on selected text, or on an **area of a picture** you draw around. Attach photos. Finish with **Approve** or **Request changes**. |
|
|
36
|
+
| **One at a time** | Swipe through a review: right for looks good, left for needs changes. Good for thirty screenshots. |
|
|
37
|
+
| **Before and after** | Pictures with a slider (or side by side); code and text as after, before, and the changes between them. |
|
|
38
|
+
| **Approve** | An agent asks "Deploy 2.4.0 to production?" and waits. You get a notification, read what it will do, and tap **Approve** or **Refuse**. |
|
|
39
|
+
| **Browse the files** | Everything folderview does: highlighted code, rendered markdown with Mermaid and math, images, CSV, JSON, notebooks, git status, diffs and history, search, downloads. Big files and big folders open quickly. |
|
|
40
|
+
| **Notes for the agent** | Write on any file, line or picture area, or about anything, with photos from your phone. The agent picks them up and resolves each with a reply. |
|
|
41
|
+
| **Decisions** | Everything you answered, approved or gave a verdict on, searchable. Agents check it before asking again. |
|
|
42
|
+
| **Who's asking** | Each request says which agent session or branch made it. |
|
|
43
|
+
| **Notifications** | Your phone buzzes when an agent asks, shows or waits (ntfy or a webhook). Approvals go out at once. |
|
|
44
|
+
|
|
45
|
+
It works on phones (sections along the bottom, big touch targets, the page scrolls), tablets and desktops (the file
|
|
46
|
+
tree, the folder and the file side by side).
|
|
47
|
+
|
|
48
|
+
## For agents
|
|
49
|
+
|
|
50
|
+
`npx hitloop init` adds the MCP server to Claude Code (`claude mcp add --scope user hitl -- npx -y hitloop@latest mcp`) and
|
|
51
|
+
a short section to the project's `CLAUDE.md` (or `AGENTS.md`) on when to use it.
|
|
52
|
+
|
|
53
|
+
### MCP tools
|
|
54
|
+
|
|
55
|
+
| Tool | |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `news` | Everything the human said since you last read, across every question set, review and note, one line each. Start here. |
|
|
58
|
+
| `wait` | Block until the human says something (`any`), finishes (`done`), answers everything (`all`), or answers the blocking questions. Up to 15 minutes; for longer, run `npx hitloop wait` in the background. |
|
|
59
|
+
| `show` | Put files, code, text, diffs, pictures, diagrams, mockups or links in front of the human. `mode: "review"` asks a verdict per item; `"show"` is just to look. Each call makes its own review. |
|
|
60
|
+
| `approve` | Ask yes or no before doing something, and wait for it. Returns APPROVED, REFUSED or "not yet" with their note. |
|
|
61
|
+
| `add_questions` | Questions in a set (questionme's format): options, a recommendation, why it matters, follow-ups that open on an answer. |
|
|
62
|
+
| `get_feedback`, `get_answers`, `notes` | One review's feedback, one set's answers, the notes. |
|
|
63
|
+
| `ack`, `resolve`, `reply` | Mark answers and verdicts as dealt with, resolve comments and notes saying what you did, write back. |
|
|
64
|
+
| `decisions` | What the human decided before. |
|
|
65
|
+
| `status`, `get_link` | What's open; the links to send. |
|
|
66
|
+
| `update_item`, `remove_items`, `update_review`, `update_question`, `remove_questions`, `update_set`, `update_section`, `show_set` | Change things. |
|
|
67
|
+
|
|
68
|
+
### The CLI does the same
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npx hitloop news # what's new (marks it read; --peek doesn't)
|
|
72
|
+
npx hitloop wait --any # exit once the human says something, printing it
|
|
73
|
+
npx hitloop wait --done <review> # exit once they've finished a review (or answered an approval)
|
|
74
|
+
npx hitloop show src/login.ts screens/*.png --title "Login, round 2" # a review of files
|
|
75
|
+
git diff | npx hitloop show - --title "The change" # stdin: a diff, code or text
|
|
76
|
+
npx hitloop approve "Deploy 2.4.0?" --details "Restarts the API" --timeout 30m && ./deploy.sh
|
|
77
|
+
npx hitloop ask <set> --file questions.json # questions (or write .hitl/questions/<set>.json)
|
|
78
|
+
npx hitloop feedback <review> npx hitloop answers <set> --new npx hitloop notes --open
|
|
79
|
+
npx hitloop ack <set|review> <id…> --note "Done in src/x.ts"
|
|
80
|
+
npx hitloop resolve [<review>] <id…> --note "Fixed: …"
|
|
81
|
+
npx hitloop reply <review> c12 "Because the API bills per call."
|
|
82
|
+
npx hitloop decisions auth # what was decided about auth
|
|
83
|
+
npx hitloop status # every request, with counts
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`hitl approve` exits 0 when approved, 1 when refused (or changes are asked for), 2 when there's no answer yet, so a
|
|
87
|
+
script can stop at a real yes or no.
|
|
88
|
+
|
|
89
|
+
`npx hitloop --help` lists every command and option; a mistyped option is refused rather than ignored.
|
|
90
|
+
|
|
91
|
+
### Claude Code hooks (optional)
|
|
92
|
+
|
|
93
|
+
Off unless you turn them on, per project (`hitl hooks on`, `hitl hooks on --stop`, or Settings on the page):
|
|
94
|
+
|
|
95
|
+
- **News**: whenever you send Claude a message, it's told if you've said something in hitl it hasn't read.
|
|
96
|
+
- **Stop**: Claude can't finish while an approval or a blocking question is waiting, or while there's news it
|
|
97
|
+
hasn't read. (It only holds Claude once, so it can't loop.)
|
|
98
|
+
|
|
99
|
+
## Where things live
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
<project>/.hitl/
|
|
103
|
+
questions/<id>.json question sets the agent writes
|
|
104
|
+
reviews/<id>.json reviews, showings and approvals the agent writes
|
|
105
|
+
answers/<id>.json, .md what you said about each one hitl writes
|
|
106
|
+
notes/notes.json, NOTES.md, images/, photos/ hitl writes
|
|
107
|
+
media/ pictures passed as data, snapshots of shown files, marked-up pictures, photos
|
|
108
|
+
config.json optional: exclude, hide, showSecrets, maxFileMb, git, watch for this project
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Every request is its own file, and a new review always gets a fresh id, so one agent's request never replaces
|
|
112
|
+
another's. Every write is locked and atomic, so the page, the MCP server and the CLI can all write at once.
|
|
113
|
+
Formats: `npx hitloop schema questions`, `npx hitloop schema reviews` (or `schema/` in the package).
|
|
114
|
+
|
|
115
|
+
`npx hitloop import` brings in a project's `.questionme/` (questions and answers) and `.review/` (folderview's comments,
|
|
116
|
+
as notes).
|
|
117
|
+
|
|
118
|
+
### A review
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"title": "Login screen, round 2",
|
|
123
|
+
"intro": "I moved the button and fixed the spacing. **The error text** is the part I'm least sure of.",
|
|
124
|
+
"mode": "review",
|
|
125
|
+
"from": "Claude · branch login-v2",
|
|
126
|
+
"items": [
|
|
127
|
+
{ "file": "src/login.tsx", "note": "The form", "ask": "Is the error text clear?" },
|
|
128
|
+
{ "file": "src/api.ts", "against": "main", "title": "What changed in the API" },
|
|
129
|
+
{ "image": "screens/login.png", "before": "screens/login-old.png" },
|
|
130
|
+
{ "code": "{ \"retries\": 3 }", "before": "{}", "lang": "json", "name": "config.json" },
|
|
131
|
+
{ "diff": "--- a/x\n+++ b/x\n@@ …" },
|
|
132
|
+
{ "md": "## Left out\n- Password reset" },
|
|
133
|
+
{ "mermaid": "flowchart LR\n A --> B" },
|
|
134
|
+
{ "html": "<button>Sign in</button>", "height": 120 },
|
|
135
|
+
{ "link": "https://example.com/preview" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
A `file` item keeps a copy of the file as it was shown; if it changes later, the page says so and shows what changed.
|
|
141
|
+
|
|
142
|
+
## One server for every project
|
|
143
|
+
|
|
144
|
+
`npx hitloop` starts one shared server (the hub) on port 4199 for every project you've used it in, so your phone needs
|
|
145
|
+
one bookmark. The MCP server starts it in the background when an agent needs a link.
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
npx hitloop # the hub (adds this folder); prints the links
|
|
149
|
+
npx hitloop url [review|set] # the links (starts the hub if needed)
|
|
150
|
+
npx hitloop stop # stop the hub
|
|
151
|
+
npx hitloop projects # what it serves; --forget <folder> to drop one
|
|
152
|
+
npx hitloop --solo [folder] # just this folder, on its own server
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Option | Environment | Default | |
|
|
156
|
+
|---|---|---|---|
|
|
157
|
+
| `-p, --port <n>` | `HITL_PORT` | `4199` | The port to try first; the next free one is used. |
|
|
158
|
+
| `-H, --host <addr>` | `HITL_HOST` | `0.0.0.0` | Where to listen. The default lets your phone reach it. |
|
|
159
|
+
| `--base <path>` | `HITL_BASE` | `/` | Serve under a URL path (behind a proxy). |
|
|
160
|
+
| `--local` | | | Only this computer (`127.0.0.1`), no token. |
|
|
161
|
+
| `--token <t>` | `HITL_TOKEN` | made once | Open a link with `?token=…` once on each device; it's kept as a cookie. |
|
|
162
|
+
| `--no-token` | | | Anyone on your network can open it. |
|
|
163
|
+
| `--allow-host <h>` | | | Accept this host name too (a tunnel's). |
|
|
164
|
+
| `-x, --exclude <globs>` | | | Never show, serve, search or zip these. Also `.hitlignore` in a project. |
|
|
165
|
+
| `--hide <names>`, `--show-secrets`, `--max-file-mb <n>`, `--no-git`, `--no-watch`, `--author <name>` | | | File browsing, as in folderview. |
|
|
166
|
+
| `-o, --open` | | | Open it in your browser. |
|
|
167
|
+
| `--read-only` | | | Look, don't answer. |
|
|
168
|
+
|
|
169
|
+
Settings can also go in `~/.config/hitl/config.json` (`port`, `host`, `base`, `token`, `allowHosts`, `notify`,
|
|
170
|
+
`exclude`, `hide`, `showSecrets`, `maxFileMb`, `git`, `watch`). `HITL_HOME` moves that folder.
|
|
171
|
+
|
|
172
|
+
The links include every address your phone might use: your Wi-Fi or LAN address, and a Tailscale address if you have
|
|
173
|
+
one. Add the page to your phone's home screen and it opens like an app.
|
|
174
|
+
|
|
175
|
+
## Notifications on your phone
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
npx hitloop notify ntfy # makes a private ntfy topic, saves it, and sends a test
|
|
179
|
+
npx hitloop notify webhook <url> # or POST JSON anywhere: Pushover, Discord, Home Assistant, …
|
|
180
|
+
npx hitloop notify link http://100.100.4.74:4199 # where the tap should open
|
|
181
|
+
npx hitloop notify test | off
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Install the [ntfy app](https://ntfy.sh) and subscribe to the topic it prints. Questions and reviews are batched (one
|
|
185
|
+
notification once the agent has been quiet for 20 seconds); approvals go out within a couple of seconds, at high
|
|
186
|
+
priority. Notifications say what's waiting, never your answers.
|
|
187
|
+
|
|
188
|
+
## Safety
|
|
189
|
+
|
|
190
|
+
- **A token by default**: the server listens on your network so your phone can reach it, so every request needs the
|
|
191
|
+
token (in the link once, then a cookie). `--local` listens on this computer only.
|
|
192
|
+
- **Project files** are served only from inside the project, never `.git` or secret-looking files (`.env`, `*.key`,
|
|
193
|
+
`*.pem`, `id_rsa`), never through a link that points outside, and never excluded ones.
|
|
194
|
+
- **What an agent writes is untrusted**: markdown is sanitised, HTML runs in a sandbox with no scripts, SVGs and files
|
|
195
|
+
are served so they can't run script, diagrams are drawn as images.
|
|
196
|
+
- **DNS rebinding** is refused (unknown host names), and so are writes from other sites.
|
|
197
|
+
- **It writes only** in `.hitl/` and its own config folder; `init` also writes `CLAUDE.md`, and the hooks
|
|
198
|
+
`.claude/settings.json`.
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```sh
|
|
203
|
+
npm install
|
|
204
|
+
npm test # builds, then runs the tests
|
|
205
|
+
node scripts/demo.mjs .demo/trailhead # a demo project with everything in it
|
|
206
|
+
HITL_HOME=.demo/home node dist/cli.js --solo .demo/trailhead --local
|
|
207
|
+
node scripts/shots.mjs http://localhost:4199/ .demo/shots # phone and desktop screenshots of every page
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- **The server** is TypeScript in `src/`, no runtime dependencies, Node 20 or newer.
|
|
211
|
+
- **The page** is plain JavaScript in `client/`, bundled by esbuild; Shiki's grammars, Mermaid and the math renderer
|
|
212
|
+
load only when needed.
|
|
213
|
+
|
|
214
|
+
Releasing: bump the version in `package.json`, commit, push to main. The Release workflow publishes a version that
|
|
215
|
+
isn't on npm yet (trusted publishing), tags it and makes a GitHub release.
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
MIT
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// What an agent reads: one feed of everything the human said since it last read (answers, verdicts, comments, notes),
|
|
2
|
+
// compact so hundreds of answers don't flood its context; waiting until the human has said something; and the
|
|
3
|
+
// decision log. Shared by the MCP tools and the CLI.
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { Questions, StoreError } from './q/store.js';
|
|
6
|
+
import { readAnswers, summaryLine as questionsLine } from './q/agent.js';
|
|
7
|
+
import { allQuestions, answerText, isDone } from './q/model.js';
|
|
8
|
+
import { Reviews } from './r/store.js';
|
|
9
|
+
import { VERDICT_TEXT, isStale, summarise, verdictText, whereText } from './r/model.js';
|
|
10
|
+
import { Notes } from './fs/notes.js';
|
|
11
|
+
export function openProject(root) {
|
|
12
|
+
const r = path.resolve(root);
|
|
13
|
+
return { root: r, questions: new Questions(r), reviews: new Reviews(r), notes: new Notes(path.join(r, '.hitl', 'notes'), r) };
|
|
14
|
+
}
|
|
15
|
+
// ── reviews ──
|
|
16
|
+
export function reviewLine(s) {
|
|
17
|
+
const bits = [];
|
|
18
|
+
if (s.mode === 'approve')
|
|
19
|
+
bits.push(s.overall ? (s.overall === 'ok' ? 'APPROVED' : s.overall === 'no' ? 'REFUSED' : 'changes asked for') : 'waiting for a yes or no');
|
|
20
|
+
else {
|
|
21
|
+
if (s.mode === 'review')
|
|
22
|
+
bits.push(`${s.reviewed} of ${s.items} items reviewed`);
|
|
23
|
+
else
|
|
24
|
+
bits.push(`${s.items} item${s.items === 1 ? '' : 's'} shown`);
|
|
25
|
+
if (s.ok)
|
|
26
|
+
bits.push(`${s.ok} good`);
|
|
27
|
+
if (s.changes)
|
|
28
|
+
bits.push(`${s.changes} need changes`);
|
|
29
|
+
if (s.no)
|
|
30
|
+
bits.push(`${s.no} no`);
|
|
31
|
+
if (s.open)
|
|
32
|
+
bits.push(`${s.open} open comment${s.open === 1 ? '' : 's'}`);
|
|
33
|
+
if (s.overall)
|
|
34
|
+
bits.push(`overall: ${VERDICT_TEXT[s.overall]}`);
|
|
35
|
+
}
|
|
36
|
+
if (s.stale)
|
|
37
|
+
bits.push(`${s.stale} verdict${s.stale === 1 ? '' : 's'} on an earlier version`);
|
|
38
|
+
if (s.unread)
|
|
39
|
+
bits.push(`${s.unread} new`);
|
|
40
|
+
if (s.status === 'closed')
|
|
41
|
+
bits.push('closed');
|
|
42
|
+
if (s.errors)
|
|
43
|
+
bits.push(`${s.errors} error${s.errors === 1 ? '' : 's'} in the file`);
|
|
44
|
+
return `${s.id} (${s.mode}, "${s.title}"${s.from ? `, from ${s.from}` : ''}): ${bits.join(', ')}`;
|
|
45
|
+
}
|
|
46
|
+
export const REVIEW_FILTERS = ['new', 'all', 'open'];
|
|
47
|
+
/** The human's feedback on a review, as text and data. "new" marks it read unless peek. */
|
|
48
|
+
export function readFeedback(reviews, id, opts = {}) {
|
|
49
|
+
const l = reviews.load(id);
|
|
50
|
+
if (!l)
|
|
51
|
+
throw new StoreError(`No review "${id}"${reviews.ids().length ? ` (there are: ${reviews.ids().join(', ')})` : ''}`);
|
|
52
|
+
const fb = reviews.feedback(id);
|
|
53
|
+
const r = l.review;
|
|
54
|
+
const filter = opts.filter ?? 'new';
|
|
55
|
+
const since = filter === 'new' ? fb.readSeq : -1;
|
|
56
|
+
const s = summarise(r, fb, l.errors.length);
|
|
57
|
+
const lines = [reviewLine(s)];
|
|
58
|
+
if (fb.overall && fb.overall.seq > since)
|
|
59
|
+
lines.push(`- OVERALL: ${verdictText(r, fb.overall.verdict).toUpperCase()}${fb.overall.note ? ` — "${fb.overall.note}"` : ''}`);
|
|
60
|
+
const label = (iid) => {
|
|
61
|
+
const it = r.items.find((i) => i.id === iid);
|
|
62
|
+
return iid === '' ? '(the whole review)' : it ? `${iid} (${it.title})` : `${iid} (removed)`;
|
|
63
|
+
};
|
|
64
|
+
for (const it of r.items) {
|
|
65
|
+
const v = fb.items[it.id];
|
|
66
|
+
if (v && v.seq > since)
|
|
67
|
+
lines.push(`- ${label(it.id)}: ${VERDICT_TEXT[v.verdict]}${v.note ? ` — "${v.note}"` : ''}${isStale(it, v) ? ' (given to an earlier version)' : ''}${v.ack && filter !== 'new' ? ` [done: ${v.ack.note}]` : ''}`);
|
|
68
|
+
}
|
|
69
|
+
const show = (c) => {
|
|
70
|
+
const fresh = c.seq > since || c.replies.some((x) => x.seq > since && x.from === 'owner');
|
|
71
|
+
if (filter === 'open')
|
|
72
|
+
return c.status === 'open' && c.from === 'owner';
|
|
73
|
+
return fresh;
|
|
74
|
+
};
|
|
75
|
+
for (const c of fb.comments.filter(show)) {
|
|
76
|
+
const w = whereText(c);
|
|
77
|
+
lines.push(`- comment ${c.id} on ${label(c.item)}${w ? `, ${w}` : ''}${c.status === 'resolved' ? ' [resolved]' : ''}: "${c.body}"`);
|
|
78
|
+
if (c.quote)
|
|
79
|
+
lines.push(` about: ${JSON.stringify(c.quote.length > 300 ? c.quote.slice(0, 297) + '…' : c.quote)}`);
|
|
80
|
+
if (c.marked)
|
|
81
|
+
lines.push(` marked-up picture: ${c.marked}`);
|
|
82
|
+
for (const p of c.photos ?? [])
|
|
83
|
+
lines.push(` photo: ${p}`);
|
|
84
|
+
for (const x of c.replies)
|
|
85
|
+
if (filter !== 'new' || (x.seq > since && x.from === 'owner'))
|
|
86
|
+
lines.push(` ${x.from === 'owner' ? 'human' : 'you'}: "${x.body}"`);
|
|
87
|
+
}
|
|
88
|
+
if (lines.length === 1)
|
|
89
|
+
lines.push(filter === 'new' ? 'Nothing new since you last read.' : filter === 'open' ? 'No open comments.' : 'No feedback yet.');
|
|
90
|
+
if (filter === 'new' && !opts.peek && fb.seq > fb.readSeq)
|
|
91
|
+
reviews.markRead(id, fb.seq);
|
|
92
|
+
return { text: lines.join('\n'), data: { summary: s, feedback: fb } };
|
|
93
|
+
}
|
|
94
|
+
// ── notes ──
|
|
95
|
+
export function readNotes(notes, opts = {}) {
|
|
96
|
+
const { seq, readSeq } = notes.marks();
|
|
97
|
+
const filter = opts.filter ?? 'new';
|
|
98
|
+
const list = notes.all().filter((c) => {
|
|
99
|
+
if (filter === 'open')
|
|
100
|
+
return c.status === 'open';
|
|
101
|
+
if (filter === 'all')
|
|
102
|
+
return true;
|
|
103
|
+
return (c.author !== 'agent' && (c.seq ?? 0) > readSeq) || (c.replies ?? []).some((r) => r.from !== 'agent' && (r.seq ?? 0) > readSeq);
|
|
104
|
+
});
|
|
105
|
+
const lines = [];
|
|
106
|
+
for (const c of list) {
|
|
107
|
+
const where = !c.path ? 'a note' : c.line ? `${c.path}:${c.line}${c.endLine ? '-' + c.endLine : ''}` : c.area ? `${c.path} (marked area ${c.area.w}×${c.area.h} at ${c.area.x},${c.area.y})` : c.path;
|
|
108
|
+
lines.push(`- note ${c.id} on ${where}${c.status === 'resolved' ? ' [resolved]' : ''}: "${c.body}"`);
|
|
109
|
+
if (c.quote)
|
|
110
|
+
lines.push(` lines: ${JSON.stringify(c.quote.length > 300 ? c.quote.slice(0, 297) + '…' : c.quote)}`);
|
|
111
|
+
if (c.image)
|
|
112
|
+
lines.push(` marked-up picture: .hitl/notes/${c.image}`);
|
|
113
|
+
for (const p of c.photos ?? [])
|
|
114
|
+
lines.push(` photo: .hitl/notes/${p}`);
|
|
115
|
+
for (const r of c.replies ?? [])
|
|
116
|
+
if (filter !== 'new' || ((r.seq ?? 0) > readSeq && r.from !== 'agent' && r.author !== 'agent'))
|
|
117
|
+
lines.push(` ${r.from === 'agent' || r.author === 'agent' ? 'you' : 'human'}: "${r.body}"`);
|
|
118
|
+
}
|
|
119
|
+
if (filter === 'new' && !opts.peek && seq > readSeq)
|
|
120
|
+
notes.markRead(seq);
|
|
121
|
+
return { text: lines.join('\n'), count: list.length };
|
|
122
|
+
}
|
|
123
|
+
/** How much is new for the agent, per kind (cheap: nothing is marked read). */
|
|
124
|
+
export function newsCounts(p) {
|
|
125
|
+
let questions = 0, reviews = 0;
|
|
126
|
+
for (const s of p.questions.summaries())
|
|
127
|
+
questions += s.unread + s.unreadMessages;
|
|
128
|
+
for (const s of p.reviews.summaries())
|
|
129
|
+
reviews += s.unread;
|
|
130
|
+
const notes = p.notes.unread();
|
|
131
|
+
return { questions, reviews, notes, total: questions + reviews + notes };
|
|
132
|
+
}
|
|
133
|
+
/** Everything the human said since the agent last read, across every question set, review and note. */
|
|
134
|
+
export function news(p, opts = {}) {
|
|
135
|
+
const counts = newsCounts(p);
|
|
136
|
+
const out = [];
|
|
137
|
+
const want = (id) => !opts.ids?.length || opts.ids.includes(id);
|
|
138
|
+
for (const s of p.reviews.summaries()) {
|
|
139
|
+
if (!s.unread || !want(s.id))
|
|
140
|
+
continue;
|
|
141
|
+
out.push(`## Review ${readFeedback(p.reviews, s.id, { filter: 'new', peek: opts.peek }).text}`, '');
|
|
142
|
+
}
|
|
143
|
+
for (const s of p.questions.summaries()) {
|
|
144
|
+
if (!(s.unread + s.unreadMessages) || !want(s.id))
|
|
145
|
+
continue;
|
|
146
|
+
out.push(`## Questions ${readAnswers(p.questions, s.id, { filter: 'new', peek: opts.peek }).text}`, '');
|
|
147
|
+
}
|
|
148
|
+
if (counts.notes && want('notes')) {
|
|
149
|
+
const n = readNotes(p.notes, { filter: 'new', peek: opts.peek });
|
|
150
|
+
if (n.count)
|
|
151
|
+
out.push(`## Notes from the human (${n.count}; resolve each with a reply saying what you did)`, n.text, '');
|
|
152
|
+
}
|
|
153
|
+
if (!out.length)
|
|
154
|
+
out.push('Nothing new from the human since you last read.');
|
|
155
|
+
return { text: out.join('\n').trimEnd(), counts };
|
|
156
|
+
}
|
|
157
|
+
/** What's open, briefly: for `hitl status` and the MCP list tool. */
|
|
158
|
+
export function overviewText(p) {
|
|
159
|
+
const lines = [];
|
|
160
|
+
const reviews = p.reviews.summaries();
|
|
161
|
+
const sets = p.questions.summaries();
|
|
162
|
+
const notes = p.notes.all();
|
|
163
|
+
if (reviews.length)
|
|
164
|
+
lines.push('Reviews:', ...reviews.map((s) => '- ' + reviewLine(s)));
|
|
165
|
+
if (sets.length)
|
|
166
|
+
lines.push('Question sets:', ...sets.map((s) => '- ' + questionsLine(s)));
|
|
167
|
+
const open = notes.filter((c) => c.status === 'open').length;
|
|
168
|
+
if (notes.length)
|
|
169
|
+
lines.push(`Notes from the human: ${open} open of ${notes.length}${p.notes.unread() ? `, ${p.notes.unread()} new` : ''}`);
|
|
170
|
+
if (!lines.length)
|
|
171
|
+
lines.push(`Nothing in ${p.root} yet: no questions, reviews or notes.`);
|
|
172
|
+
const c = newsCounts(p);
|
|
173
|
+
if (c.total)
|
|
174
|
+
lines.push('', `New since you last read: ${c.total} (npx hitloop news, or the news tool).`);
|
|
175
|
+
return lines.join('\n');
|
|
176
|
+
}
|
|
177
|
+
// ── waiting ──
|
|
178
|
+
export const WAITS = ['any', 'done', 'all', 'blocking'];
|
|
179
|
+
function met(p, w) {
|
|
180
|
+
const until = w.until ?? 'any';
|
|
181
|
+
const ids = w.ids?.length ? w.ids : undefined;
|
|
182
|
+
const stamp = [];
|
|
183
|
+
if (until === 'any') {
|
|
184
|
+
let n = 0;
|
|
185
|
+
for (const s of p.reviews.summaries())
|
|
186
|
+
if (!ids || ids.includes(s.id))
|
|
187
|
+
n += s.unread;
|
|
188
|
+
for (const s of p.questions.summaries())
|
|
189
|
+
if (!ids || ids.includes(s.id))
|
|
190
|
+
n += s.unread + s.unreadMessages;
|
|
191
|
+
if (!ids || ids.includes('notes'))
|
|
192
|
+
n += p.notes.unread();
|
|
193
|
+
return { ok: n > 0, stamp: String(n) + ':' + seqs(p, ids) };
|
|
194
|
+
}
|
|
195
|
+
const targets = ids ?? [...p.reviews.ids().filter((id) => p.reviews.summary(id)?.status === 'open'), ...p.questions.ids()];
|
|
196
|
+
if (!targets.length)
|
|
197
|
+
throw new StoreError('Nothing to wait for: name a review or question set');
|
|
198
|
+
let ok = true;
|
|
199
|
+
for (const id of targets) {
|
|
200
|
+
if (p.reviews.has(id)) {
|
|
201
|
+
const s = p.reviews.summary(id);
|
|
202
|
+
if (!s)
|
|
203
|
+
continue;
|
|
204
|
+
stamp.push(p.reviews.feedback(id).seq);
|
|
205
|
+
if (until === 'done' || until === 'blocking')
|
|
206
|
+
ok &&= s.done;
|
|
207
|
+
else
|
|
208
|
+
ok &&= s.done || (s.mode !== 'approve' && s.reviewed >= s.items && s.items > 0);
|
|
209
|
+
}
|
|
210
|
+
else if (p.questions.has(id)) {
|
|
211
|
+
const s = p.questions.summary(id);
|
|
212
|
+
if (!s)
|
|
213
|
+
continue;
|
|
214
|
+
stamp.push(p.questions.answers(id).seq);
|
|
215
|
+
if (until === 'blocking')
|
|
216
|
+
ok &&= s.blocking === 0;
|
|
217
|
+
else
|
|
218
|
+
ok &&= s.toAnswer === 0;
|
|
219
|
+
}
|
|
220
|
+
else
|
|
221
|
+
throw new StoreError(`No review or question set "${id}"`);
|
|
222
|
+
}
|
|
223
|
+
return { ok, stamp: stamp.join(',') };
|
|
224
|
+
}
|
|
225
|
+
function seqs(p, ids) {
|
|
226
|
+
const out = [];
|
|
227
|
+
for (const id of p.reviews.ids())
|
|
228
|
+
if (!ids || ids.includes(id))
|
|
229
|
+
out.push(p.reviews.feedback(id).seq);
|
|
230
|
+
for (const id of p.questions.ids())
|
|
231
|
+
if (!ids || ids.includes(id))
|
|
232
|
+
out.push(p.questions.answers(id).seq);
|
|
233
|
+
out.push(p.notes.marks().seq);
|
|
234
|
+
return out.join(',');
|
|
235
|
+
}
|
|
236
|
+
/** Wait until the condition holds (and the human has paused). Resolves true, or false on timeout or abort. */
|
|
237
|
+
export async function waitFor(p, w) {
|
|
238
|
+
const started = Date.now();
|
|
239
|
+
const poll = w.pollMs ?? 1000;
|
|
240
|
+
let metAt = 0;
|
|
241
|
+
let lastStamp = '';
|
|
242
|
+
for (;;) {
|
|
243
|
+
if (w.signal?.aborted)
|
|
244
|
+
return false;
|
|
245
|
+
const m = met(p, w);
|
|
246
|
+
if (m.ok) {
|
|
247
|
+
if (!w.settleMs)
|
|
248
|
+
return true;
|
|
249
|
+
if (m.stamp !== lastStamp) {
|
|
250
|
+
lastStamp = m.stamp;
|
|
251
|
+
metAt = Date.now();
|
|
252
|
+
}
|
|
253
|
+
else if (Date.now() - metAt >= w.settleMs)
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
else
|
|
257
|
+
lastStamp = '';
|
|
258
|
+
if (w.timeoutMs && Date.now() - started >= w.timeoutMs)
|
|
259
|
+
return false;
|
|
260
|
+
await new Promise((r) => setTimeout(r, poll));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/** What the human decided: every answer, verdict and approval, newest first. q narrows it (words, all must match). */
|
|
264
|
+
export function decisions(p, opts = {}) {
|
|
265
|
+
const out = [];
|
|
266
|
+
for (const id of p.questions.ids()) {
|
|
267
|
+
const l = p.questions.load(id);
|
|
268
|
+
if (!l)
|
|
269
|
+
continue;
|
|
270
|
+
const af = p.questions.answers(id);
|
|
271
|
+
for (const q of allQuestions(l.set)) {
|
|
272
|
+
const a = af.answers[q.id];
|
|
273
|
+
if (!a || !isDone(a) || a.status === 'skipped')
|
|
274
|
+
continue;
|
|
275
|
+
out.push({ at: a.at, kind: 'answer', request: id, requestTitle: l.set.title, id: q.id, subject: q.question, decision: answerText(q, a), ...(a.note ? { note: a.note } : {}) });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
for (const id of p.reviews.ids()) {
|
|
279
|
+
const l = p.reviews.load(id);
|
|
280
|
+
if (!l)
|
|
281
|
+
continue;
|
|
282
|
+
const fb = p.reviews.feedback(id);
|
|
283
|
+
const r = l.review;
|
|
284
|
+
if (fb.overall)
|
|
285
|
+
out.push({ at: fb.overall.at, kind: r.mode === 'approve' ? 'approval' : 'overall', request: id, requestTitle: r.title, id: '', subject: r.title, decision: verdictText(r, fb.overall.verdict), ...(fb.overall.note ? { note: fb.overall.note } : {}), ...(r.from ? { from: r.from } : {}) });
|
|
286
|
+
for (const it of r.items) {
|
|
287
|
+
const v = fb.items[it.id];
|
|
288
|
+
if (v)
|
|
289
|
+
out.push({ at: v.at, kind: 'verdict', request: id, requestTitle: r.title, id: it.id, subject: it.title, decision: VERDICT_TEXT[v.verdict], ...(v.note ? { note: v.note } : {}), ...(r.from ? { from: r.from } : {}) });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
out.sort((a, b) => b.at.localeCompare(a.at));
|
|
293
|
+
const words = (opts.q ?? '').toLowerCase().split(/\s+/).filter(Boolean);
|
|
294
|
+
const hits = words.length ? out.filter((d) => words.every((w) => `${d.requestTitle} ${d.subject} ${d.decision} ${d.note ?? ''} ${d.request} ${d.id}`.toLowerCase().includes(w))) : out;
|
|
295
|
+
return { decisions: hits.slice(0, Math.max(1, Math.min(2000, opts.limit ?? 200))), total: hits.length };
|
|
296
|
+
}
|
|
297
|
+
export function decisionsText(p, opts = {}) {
|
|
298
|
+
const { decisions: list, total } = decisions(p, opts);
|
|
299
|
+
if (!list.length)
|
|
300
|
+
return opts.q ? `No decisions match "${opts.q}".` : 'No decisions yet.';
|
|
301
|
+
const lines = [`${total} decision${total === 1 ? '' : 's'}${opts.q ? ` matching "${opts.q}"` : ''}${total > list.length ? ` (the newest ${list.length})` : ''}:`];
|
|
302
|
+
for (const d of list)
|
|
303
|
+
lines.push(`- ${d.at.slice(0, 10)} ${d.request}${d.id ? '/' + d.id : ''} — ${short(d.subject)}: ${d.decision}${d.note ? ` ("${d.note}")` : ''}`);
|
|
304
|
+
return lines.join('\n');
|
|
305
|
+
}
|
|
306
|
+
const short = (s) => {
|
|
307
|
+
const t = s.replace(/\s+/g, ' ').trim();
|
|
308
|
+
return t.length > 90 ? t.slice(0, 87) + '…' : t;
|
|
309
|
+
};
|
|
310
|
+
export function parseDuration(s, fallback) {
|
|
311
|
+
if (s === undefined || s === '')
|
|
312
|
+
return fallback;
|
|
313
|
+
const m = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/.exec(s.trim());
|
|
314
|
+
if (!m)
|
|
315
|
+
throw new StoreError(`Not a duration: "${s}" (like 90s, 10m, 2h)`);
|
|
316
|
+
const n = Number(m[1]);
|
|
317
|
+
return Math.round(n * ({ ms: 1, s: 1000, m: 60_000, h: 3_600_000 }[m[2] ?? 's'] ?? 1000));
|
|
318
|
+
}
|