mycode-cli 0.1.0__py3-none-any.whl

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 (404) hide show
  1. mycode/__init__.py +1 -0
  2. mycode/cli/__init__.py +1 -0
  3. mycode/cli/chat.py +650 -0
  4. mycode/cli/main.py +245 -0
  5. mycode/cli/render.py +693 -0
  6. mycode/cli/runtime.py +271 -0
  7. mycode/cli/theme.py +109 -0
  8. mycode/core/__init__.py +30 -0
  9. mycode/core/agent.py +515 -0
  10. mycode/core/config.py +551 -0
  11. mycode/core/messages.py +166 -0
  12. mycode/core/models.py +144 -0
  13. mycode/core/models_catalog.json +2090 -0
  14. mycode/core/providers/__init__.py +86 -0
  15. mycode/core/providers/anthropic_like.py +366 -0
  16. mycode/core/providers/base.py +351 -0
  17. mycode/core/providers/gemini.py +321 -0
  18. mycode/core/providers/openai_chat.py +356 -0
  19. mycode/core/providers/openai_responses.py +326 -0
  20. mycode/core/session.py +537 -0
  21. mycode/core/system_prompt.md +10 -0
  22. mycode/core/system_prompt.py +319 -0
  23. mycode/core/tools.py +898 -0
  24. mycode/server/__init__.py +1 -0
  25. mycode/server/app.py +52 -0
  26. mycode/server/deps.py +29 -0
  27. mycode/server/routers/__init__.py +7 -0
  28. mycode/server/routers/chat.py +320 -0
  29. mycode/server/routers/sessions.py +77 -0
  30. mycode/server/routers/workspaces.py +92 -0
  31. mycode/server/run_manager.py +195 -0
  32. mycode/server/schemas.py +66 -0
  33. mycode/server/static/assets/EditDiff-C1ql7kft.js +12 -0
  34. mycode/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  35. mycode/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  36. mycode/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  37. mycode/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  38. mycode/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  39. mycode/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  40. mycode/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  41. mycode/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  42. mycode/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  43. mycode/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  44. mycode/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  45. mycode/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  46. mycode/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  47. mycode/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  48. mycode/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  49. mycode/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  50. mycode/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  51. mycode/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  52. mycode/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  53. mycode/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  54. mycode/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  55. mycode/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  56. mycode/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  57. mycode/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  58. mycode/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  59. mycode/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  60. mycode/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  61. mycode/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  62. mycode/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  63. mycode/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  64. mycode/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  65. mycode/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  66. mycode/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  67. mycode/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  68. mycode/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  69. mycode/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  70. mycode/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  71. mycode/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  72. mycode/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  73. mycode/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  74. mycode/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  75. mycode/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  76. mycode/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  77. mycode/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  78. mycode/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  79. mycode/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  80. mycode/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  81. mycode/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  82. mycode/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  83. mycode/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  84. mycode/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  85. mycode/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  86. mycode/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  87. mycode/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  88. mycode/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  89. mycode/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  90. mycode/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  91. mycode/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  92. mycode/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  93. mycode/server/static/assets/abap-BdImnpbu.js +1 -0
  94. mycode/server/static/assets/actionscript-3-CoDkCxhg.js +1 -0
  95. mycode/server/static/assets/ada-bCR0ucgS.js +1 -0
  96. mycode/server/static/assets/andromeeda-C4gqWexZ.js +1 -0
  97. mycode/server/static/assets/angular-html-DA-rfuFy.js +1 -0
  98. mycode/server/static/assets/angular-ts-BrjP3tb8.js +1 -0
  99. mycode/server/static/assets/apache-Pmp26Uib.js +1 -0
  100. mycode/server/static/assets/apex-D8_7TLub.js +1 -0
  101. mycode/server/static/assets/apl-CORt7UWP.js +1 -0
  102. mycode/server/static/assets/applescript-Co6uUVPk.js +1 -0
  103. mycode/server/static/assets/ara-BRHolxvo.js +1 -0
  104. mycode/server/static/assets/asciidoc-Ve4PFQV2.js +1 -0
  105. mycode/server/static/assets/asm-D_Q5rh1f.js +1 -0
  106. mycode/server/static/assets/astro-HNnZUWAn.js +1 -0
  107. mycode/server/static/assets/aurora-x-D-2ljcwZ.js +1 -0
  108. mycode/server/static/assets/auto-render-xntwXHOX.js +261 -0
  109. mycode/server/static/assets/awk-DMzUqQB5.js +1 -0
  110. mycode/server/static/assets/ayu-dark-DYE7WIF3.js +1 -0
  111. mycode/server/static/assets/ayu-light-BA47KaF1.js +1 -0
  112. mycode/server/static/assets/ayu-mirage-32ctXXKs.js +1 -0
  113. mycode/server/static/assets/ballerina-BFfxhgS-.js +1 -0
  114. mycode/server/static/assets/bat-BkioyH1T.js +1 -0
  115. mycode/server/static/assets/beancount-k_qm7-4y.js +1 -0
  116. mycode/server/static/assets/berry-uYugtg8r.js +1 -0
  117. mycode/server/static/assets/bibtex-CHM0blh-.js +1 -0
  118. mycode/server/static/assets/bicep-Bmn6On1c.js +1 -0
  119. mycode/server/static/assets/bird2-BIv1doCn.js +1 -0
  120. mycode/server/static/assets/blade-BjGOyj-B.js +1 -0
  121. mycode/server/static/assets/bsl-BO_Y6i37.js +1 -0
  122. mycode/server/static/assets/c-BIGW1oBm.js +1 -0
  123. mycode/server/static/assets/c3-eo99z4R2.js +1 -0
  124. mycode/server/static/assets/cadence-Bv_4Rxtq.js +1 -0
  125. mycode/server/static/assets/cairo-KRGpt6FW.js +1 -0
  126. mycode/server/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  127. mycode/server/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  128. mycode/server/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  129. mycode/server/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  130. mycode/server/static/assets/clarity-D53aC0YG.js +1 -0
  131. mycode/server/static/assets/clojure-P80f7IUj.js +1 -0
  132. mycode/server/static/assets/cmake-D1j8_8rp.js +1 -0
  133. mycode/server/static/assets/cobol-nBiQ_Alo.js +1 -0
  134. mycode/server/static/assets/codeowners-Bp6g37R7.js +1 -0
  135. mycode/server/static/assets/codeql-DsOJ9woJ.js +1 -0
  136. mycode/server/static/assets/coffee-Ch7k5sss.js +1 -0
  137. mycode/server/static/assets/common-lisp-Cg-RD9OK.js +1 -0
  138. mycode/server/static/assets/coq-DkFqJrB1.js +1 -0
  139. mycode/server/static/assets/cpp-CofmeUqb.js +1 -0
  140. mycode/server/static/assets/crystal-DNxU26gB.js +1 -0
  141. mycode/server/static/assets/csharp-COcwbKMJ.js +1 -0
  142. mycode/server/static/assets/css-CLj8gQPS.js +1 -0
  143. mycode/server/static/assets/csv-fuZLfV_i.js +1 -0
  144. mycode/server/static/assets/cue-D82EKSYY.js +1 -0
  145. mycode/server/static/assets/cypher-COkxafJQ.js +1 -0
  146. mycode/server/static/assets/d-85-TOEBH.js +1 -0
  147. mycode/server/static/assets/dark-plus-C3mMm8J8.js +1 -0
  148. mycode/server/static/assets/dart-bE4Kk8sk.js +1 -0
  149. mycode/server/static/assets/dax-CEL-wOlO.js +1 -0
  150. mycode/server/static/assets/desktop-BmXAJ9_W.js +1 -0
  151. mycode/server/static/assets/diff-D97Zzqfu.js +1 -0
  152. mycode/server/static/assets/docker-BcOcwvcX.js +1 -0
  153. mycode/server/static/assets/dotenv-Da5cRb03.js +1 -0
  154. mycode/server/static/assets/dracula-BzJJZx-M.js +1 -0
  155. mycode/server/static/assets/dracula-soft-BXkSAIEj.js +1 -0
  156. mycode/server/static/assets/dream-maker-BtqSS_iP.js +1 -0
  157. mycode/server/static/assets/edge-FbVlp4U3.js +1 -0
  158. mycode/server/static/assets/elixir-CkH2-t6x.js +1 -0
  159. mycode/server/static/assets/elm-DbKCFpqz.js +1 -0
  160. mycode/server/static/assets/emacs-lisp-CXvaQtF9.js +1 -0
  161. mycode/server/static/assets/erb-BYCe7drp.js +1 -0
  162. mycode/server/static/assets/erlang-DsQrWhSR.js +1 -0
  163. mycode/server/static/assets/everforest-dark-BgDCqdQA.js +1 -0
  164. mycode/server/static/assets/everforest-light-C8M2exoo.js +1 -0
  165. mycode/server/static/assets/fennel-BYunw83y.js +1 -0
  166. mycode/server/static/assets/fish-BvzEVeQv.js +1 -0
  167. mycode/server/static/assets/fluent-C4IJs8-o.js +1 -0
  168. mycode/server/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  169. mycode/server/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
  170. mycode/server/static/assets/fsharp-CXgrBDvD.js +1 -0
  171. mycode/server/static/assets/gdresource-BOOCDP_w.js +1 -0
  172. mycode/server/static/assets/gdscript-C5YyOfLZ.js +1 -0
  173. mycode/server/static/assets/gdshader-DkwncUOv.js +1 -0
  174. mycode/server/static/assets/genie-D0YGMca9.js +1 -0
  175. mycode/server/static/assets/gherkin-DyxjwDmM.js +1 -0
  176. mycode/server/static/assets/git-commit-F4YmCXRG.js +1 -0
  177. mycode/server/static/assets/git-rebase-r7XF79zn.js +1 -0
  178. mycode/server/static/assets/github-dark-DHJKELXO.js +1 -0
  179. mycode/server/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
  180. mycode/server/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  181. mycode/server/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  182. mycode/server/static/assets/github-light-DAi9KRSo.js +1 -0
  183. mycode/server/static/assets/github-light-default-D7oLnXFd.js +1 -0
  184. mycode/server/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  185. mycode/server/static/assets/gleam-BspZqrRM.js +1 -0
  186. mycode/server/static/assets/glimmer-js-ByusRIyA.js +1 -0
  187. mycode/server/static/assets/glimmer-ts-BfAWNZQY.js +1 -0
  188. mycode/server/static/assets/glsl-DplSGwfg.js +1 -0
  189. mycode/server/static/assets/gn-n2N0HUVH.js +1 -0
  190. mycode/server/static/assets/gnuplot-DdkO51Og.js +1 -0
  191. mycode/server/static/assets/go-C27-OAKa.js +1 -0
  192. mycode/server/static/assets/graphql-ChdNCCLP.js +1 -0
  193. mycode/server/static/assets/groovy-gcz8RCvz.js +1 -0
  194. mycode/server/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  195. mycode/server/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  196. mycode/server/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  197. mycode/server/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  198. mycode/server/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  199. mycode/server/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  200. mycode/server/static/assets/hack-i7_Ulhet.js +1 -0
  201. mycode/server/static/assets/haml-D5jkg6IW.js +1 -0
  202. mycode/server/static/assets/handlebars-BpdQsYii.js +1 -0
  203. mycode/server/static/assets/haskell-Df6bDoY_.js +1 -0
  204. mycode/server/static/assets/haxe-CzTSHFRz.js +1 -0
  205. mycode/server/static/assets/hcl-BWvSN4gD.js +1 -0
  206. mycode/server/static/assets/hjson-D5-asLiD.js +1 -0
  207. mycode/server/static/assets/hlsl-D3lLCCz7.js +1 -0
  208. mycode/server/static/assets/horizon-BUw7H-hv.js +1 -0
  209. mycode/server/static/assets/horizon-bright-CUuTKBJd.js +1 -0
  210. mycode/server/static/assets/houston-DnULxvSX.js +1 -0
  211. mycode/server/static/assets/html-derivative-DlHx6ybY.js +1 -0
  212. mycode/server/static/assets/html-pp8916En.js +1 -0
  213. mycode/server/static/assets/http-jrhK8wxY.js +1 -0
  214. mycode/server/static/assets/hurl-irOxFIW8.js +1 -0
  215. mycode/server/static/assets/hxml-Bvhsp5Yf.js +1 -0
  216. mycode/server/static/assets/hy-DFXneXwc.js +1 -0
  217. mycode/server/static/assets/imba-DGztddWO.js +1 -0
  218. mycode/server/static/assets/index-B4e4WQPq.css +1 -0
  219. mycode/server/static/assets/index-C2xTNJGd.js +203 -0
  220. mycode/server/static/assets/ini-BEwlwnbL.js +1 -0
  221. mycode/server/static/assets/java-CylS5w8V.js +1 -0
  222. mycode/server/static/assets/javascript-wDzz0qaB.js +1 -0
  223. mycode/server/static/assets/jinja-f2NsQr07.js +1 -0
  224. mycode/server/static/assets/jison-wvAkD_A8.js +1 -0
  225. mycode/server/static/assets/json-Cp-IABpG.js +1 -0
  226. mycode/server/static/assets/json5-C9tS-k6U.js +1 -0
  227. mycode/server/static/assets/jsonc-Des-eS-w.js +1 -0
  228. mycode/server/static/assets/jsonl-DcaNXYhu.js +1 -0
  229. mycode/server/static/assets/jsonnet-DFQXde-d.js +1 -0
  230. mycode/server/static/assets/jssm-C2t-YnRu.js +1 -0
  231. mycode/server/static/assets/jsx-g9-lgVsj.js +1 -0
  232. mycode/server/static/assets/julia-CxzCAyBv.js +1 -0
  233. mycode/server/static/assets/just-VxiPbLrw.js +1 -0
  234. mycode/server/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  235. mycode/server/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  236. mycode/server/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
  237. mycode/server/static/assets/katex-DnJR2-55.css +1 -0
  238. mycode/server/static/assets/kdl-DV7GczEv.js +1 -0
  239. mycode/server/static/assets/kotlin-BdnUsdx6.js +1 -0
  240. mycode/server/static/assets/kusto-wEQ09or8.js +1 -0
  241. mycode/server/static/assets/laserwave-DUszq2jm.js +1 -0
  242. mycode/server/static/assets/latex-CWtU0Tv5.js +1 -0
  243. mycode/server/static/assets/lean-BZvkOJ9d.js +1 -0
  244. mycode/server/static/assets/less-B1dDrJ26.js +1 -0
  245. mycode/server/static/assets/light-plus-B7mTdjB0.js +1 -0
  246. mycode/server/static/assets/liquid-C0sCDyMI.js +1 -0
  247. mycode/server/static/assets/llvm-DjAJT7YJ.js +1 -0
  248. mycode/server/static/assets/log-2UxHyX5q.js +1 -0
  249. mycode/server/static/assets/logo-BtOb2qkB.js +1 -0
  250. mycode/server/static/assets/lua-BaeVxFsk.js +1 -0
  251. mycode/server/static/assets/luau-C-HG3fhB.js +1 -0
  252. mycode/server/static/assets/make-CHLpvVh8.js +1 -0
  253. mycode/server/static/assets/markdown-Cvjx9yec.js +1 -0
  254. mycode/server/static/assets/marko-DjSrsDqO.js +1 -0
  255. mycode/server/static/assets/material-theme-D5KoaKCx.js +1 -0
  256. mycode/server/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
  257. mycode/server/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  258. mycode/server/static/assets/material-theme-ocean-CyktbL80.js +1 -0
  259. mycode/server/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  260. mycode/server/static/assets/matlab-D7o27uSR.js +1 -0
  261. mycode/server/static/assets/mdc-DTYItulj.js +1 -0
  262. mycode/server/static/assets/mdx-Cmh6b_Ma.js +1 -0
  263. mycode/server/static/assets/mermaid-mWjccvbQ.js +1 -0
  264. mycode/server/static/assets/min-dark-CafNBF8u.js +1 -0
  265. mycode/server/static/assets/min-light-CTRr51gU.js +1 -0
  266. mycode/server/static/assets/mipsasm-CKIfxQSi.js +1 -0
  267. mycode/server/static/assets/mojo-rZm6bMo-.js +1 -0
  268. mycode/server/static/assets/monokai-D4h5O-jR.js +1 -0
  269. mycode/server/static/assets/moonbit-_H4v1dQx.js +1 -0
  270. mycode/server/static/assets/move-IF9eRakj.js +1 -0
  271. mycode/server/static/assets/narrat-DRg8JJMk.js +1 -0
  272. mycode/server/static/assets/nextflow-C-mBbutL.js +1 -0
  273. mycode/server/static/assets/nextflow-groovy-vE_lwT2v.js +1 -0
  274. mycode/server/static/assets/nginx-BpAMiNFr.js +1 -0
  275. mycode/server/static/assets/night-owl-C39BiMTA.js +1 -0
  276. mycode/server/static/assets/night-owl-light-CMTm3GFP.js +1 -0
  277. mycode/server/static/assets/nim-BIad80T-.js +1 -0
  278. mycode/server/static/assets/nix-CwoSXNpI.js +1 -0
  279. mycode/server/static/assets/nord-Ddv68eIx.js +1 -0
  280. mycode/server/static/assets/nushell-Cz2AlsmD.js +1 -0
  281. mycode/server/static/assets/objective-c-DXmwc3jG.js +1 -0
  282. mycode/server/static/assets/objective-cpp-CLxacb5B.js +1 -0
  283. mycode/server/static/assets/ocaml-C0hk2d4L.js +1 -0
  284. mycode/server/static/assets/odin-BBf5iR-q.js +1 -0
  285. mycode/server/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  286. mycode/server/static/assets/one-light-C3Wv6jpd.js +1 -0
  287. mycode/server/static/assets/openscad-C4EeE6gA.js +1 -0
  288. mycode/server/static/assets/pascal-D93ZcfNL.js +1 -0
  289. mycode/server/static/assets/perl-NvoQZIq0.js +1 -0
  290. mycode/server/static/assets/php-R6g_5hLQ.js +1 -0
  291. mycode/server/static/assets/pkl-u5AG7uiY.js +1 -0
  292. mycode/server/static/assets/plastic-3e1v2bzS.js +1 -0
  293. mycode/server/static/assets/plsql-ChMvpjG-.js +1 -0
  294. mycode/server/static/assets/po-BTJTHyun.js +1 -0
  295. mycode/server/static/assets/poimandres-CS3Unz2-.js +1 -0
  296. mycode/server/static/assets/polar-C0HS_06l.js +1 -0
  297. mycode/server/static/assets/postcss-CXtECtnM.js +1 -0
  298. mycode/server/static/assets/powerquery-CEu0bR-o.js +1 -0
  299. mycode/server/static/assets/powershell-Dpen1YoG.js +1 -0
  300. mycode/server/static/assets/prisma-Dd19v3D-.js +1 -0
  301. mycode/server/static/assets/prolog-CbFg5uaA.js +1 -0
  302. mycode/server/static/assets/proto-C7zT0LnQ.js +1 -0
  303. mycode/server/static/assets/pug-DKIMFp6K.js +1 -0
  304. mycode/server/static/assets/puppet-BMWR74SV.js +1 -0
  305. mycode/server/static/assets/purescript-CklMAg4u.js +1 -0
  306. mycode/server/static/assets/python-B6aJPvgy.js +1 -0
  307. mycode/server/static/assets/qml-3beO22l8.js +1 -0
  308. mycode/server/static/assets/qmldir-C8lEn-DE.js +1 -0
  309. mycode/server/static/assets/qss-IeuSbFQv.js +1 -0
  310. mycode/server/static/assets/r-Dspwwk_N.js +1 -0
  311. mycode/server/static/assets/racket-BqYA7rlc.js +1 -0
  312. mycode/server/static/assets/raku-DXvB9xmW.js +1 -0
  313. mycode/server/static/assets/razor-BDqjjVU7.js +1 -0
  314. mycode/server/static/assets/red-bN70gL4F.js +1 -0
  315. mycode/server/static/assets/reg-C-SQnVFl.js +1 -0
  316. mycode/server/static/assets/regexp-CDVJQ6XC.js +1 -0
  317. mycode/server/static/assets/rel-C3B-1QV4.js +1 -0
  318. mycode/server/static/assets/riscv-BM1_JUlF.js +1 -0
  319. mycode/server/static/assets/ron-D8l8udqQ.js +1 -0
  320. mycode/server/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  321. mycode/server/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  322. mycode/server/static/assets/rose-pine-qdsjHGoJ.js +1 -0
  323. mycode/server/static/assets/rosmsg-BJDFO7_C.js +1 -0
  324. mycode/server/static/assets/rst-CRjBmOyv.js +1 -0
  325. mycode/server/static/assets/ruby-Wjq7vjNf.js +1 -0
  326. mycode/server/static/assets/rust-B1yitclQ.js +1 -0
  327. mycode/server/static/assets/sas-cz2c8ADy.js +1 -0
  328. mycode/server/static/assets/sass-Cj5Yp3dK.js +1 -0
  329. mycode/server/static/assets/scala-C151Ov-r.js +1 -0
  330. mycode/server/static/assets/scheme-C98Dy4si.js +1 -0
  331. mycode/server/static/assets/scss-D5BDwBP9.js +1 -0
  332. mycode/server/static/assets/sdbl-DVxCFoDh.js +1 -0
  333. mycode/server/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
  334. mycode/server/static/assets/shellscript-Yzrsuije.js +1 -0
  335. mycode/server/static/assets/shellsession-BADoaaVG.js +1 -0
  336. mycode/server/static/assets/slack-dark-BthQWCQV.js +1 -0
  337. mycode/server/static/assets/slack-ochin-DqwNpetd.js +1 -0
  338. mycode/server/static/assets/smalltalk-BERRCDM3.js +1 -0
  339. mycode/server/static/assets/snazzy-light-Bw305WKR.js +1 -0
  340. mycode/server/static/assets/solarized-dark-DXbdFlpD.js +1 -0
  341. mycode/server/static/assets/solarized-light-L9t79GZl.js +1 -0
  342. mycode/server/static/assets/solidity-rGO070M0.js +1 -0
  343. mycode/server/static/assets/soy-8wufbnw4.js +1 -0
  344. mycode/server/static/assets/sparql-rVzFXLq3.js +1 -0
  345. mycode/server/static/assets/splunk-BtCnVYZw.js +1 -0
  346. mycode/server/static/assets/sql-BLtJtn59.js +1 -0
  347. mycode/server/static/assets/ssh-config-_ykCGR6B.js +1 -0
  348. mycode/server/static/assets/stata-BH5u7GGu.js +1 -0
  349. mycode/server/static/assets/stylus-BEDo0Tqx.js +1 -0
  350. mycode/server/static/assets/surrealql-Bq5Q-fJD.js +1 -0
  351. mycode/server/static/assets/svelte-Cy7k_4gC.js +1 -0
  352. mycode/server/static/assets/swift-D82vCrfD.js +1 -0
  353. mycode/server/static/assets/synthwave-84-CbfX1IO0.js +1 -0
  354. mycode/server/static/assets/system-verilog-CnnmHF94.js +1 -0
  355. mycode/server/static/assets/systemd-4A_iFExJ.js +1 -0
  356. mycode/server/static/assets/talonscript-CkByrt1z.js +1 -0
  357. mycode/server/static/assets/tasl-QIJgUcNo.js +1 -0
  358. mycode/server/static/assets/tcl-dwOrl1Do.js +1 -0
  359. mycode/server/static/assets/templ-DhtptRzy.js +1 -0
  360. mycode/server/static/assets/terraform-BETggiCN.js +1 -0
  361. mycode/server/static/assets/tex-idrVyKtj.js +1 -0
  362. mycode/server/static/assets/tokyo-night-hegEt444.js +1 -0
  363. mycode/server/static/assets/toml-vGWfd6FD.js +1 -0
  364. mycode/server/static/assets/ts-tags-DQrlYJgV.js +1 -0
  365. mycode/server/static/assets/tsv-B_m7g4N7.js +1 -0
  366. mycode/server/static/assets/tsx-COt5Ahok.js +1 -0
  367. mycode/server/static/assets/turtle-BsS91CYL.js +1 -0
  368. mycode/server/static/assets/twig-xg9kU7Mw.js +1 -0
  369. mycode/server/static/assets/typescript-BPQ3VLAy.js +1 -0
  370. mycode/server/static/assets/typespec-CAFt9gP4.js +1 -0
  371. mycode/server/static/assets/typst-DHCkPAjA.js +1 -0
  372. mycode/server/static/assets/v-BcVCzyr7.js +1 -0
  373. mycode/server/static/assets/vala-CsfeWuGM.js +1 -0
  374. mycode/server/static/assets/vb-D17OF-Vu.js +1 -0
  375. mycode/server/static/assets/verilog-BQ8w6xss.js +1 -0
  376. mycode/server/static/assets/vesper-DU1UobuO.js +1 -0
  377. mycode/server/static/assets/vhdl-CeAyd5Ju.js +1 -0
  378. mycode/server/static/assets/viml-CJc9bBzg.js +1 -0
  379. mycode/server/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
  380. mycode/server/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
  381. mycode/server/static/assets/vitesse-light-CVO1_9PV.js +1 -0
  382. mycode/server/static/assets/vue-D2xRrEX4.js +1 -0
  383. mycode/server/static/assets/vue-html-AaS7Mt5G.js +1 -0
  384. mycode/server/static/assets/vue-vine-BoDAl6tE.js +1 -0
  385. mycode/server/static/assets/vyper-CDx5xZoG.js +1 -0
  386. mycode/server/static/assets/wasm-CG6Dc4jp.js +1 -0
  387. mycode/server/static/assets/wasm-MzD3tlZU.js +1 -0
  388. mycode/server/static/assets/wenyan-BV7otONQ.js +1 -0
  389. mycode/server/static/assets/wgsl-Dx-B1_4e.js +1 -0
  390. mycode/server/static/assets/wikitext-BhOHFoWU.js +1 -0
  391. mycode/server/static/assets/wit-5i3qLPDT.js +1 -0
  392. mycode/server/static/assets/wolfram-lXgVvXCa.js +1 -0
  393. mycode/server/static/assets/xml-sdJ4AIDG.js +1 -0
  394. mycode/server/static/assets/xsl-CtQFsRM5.js +1 -0
  395. mycode/server/static/assets/yaml-Buea-lGh.js +1 -0
  396. mycode/server/static/assets/zenscript-DVFEvuxE.js +1 -0
  397. mycode/server/static/assets/zig-VOosw3JB.js +1 -0
  398. mycode/server/static/favicon_slashes.svg +12 -0
  399. mycode/server/static/index.html +35 -0
  400. mycode_cli-0.1.0.dist-info/METADATA +186 -0
  401. mycode_cli-0.1.0.dist-info/RECORD +404 -0
  402. mycode_cli-0.1.0.dist-info/WHEEL +4 -0
  403. mycode_cli-0.1.0.dist-info/entry_points.txt +2 -0
  404. mycode_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
mycode/core/session.py ADDED
@@ -0,0 +1,537 @@
1
+ """Session storage and timeline events (append-only JSONL).
2
+
3
+ Inspired by pi/mom design principles:
4
+ - append-only message log (JSONL)
5
+ - small metadata file per session
6
+ - no rewriting of full conversation on each turn
7
+
8
+ On disk:
9
+
10
+ ~/.mycode/sessions/<session_id>/
11
+ meta.json
12
+ messages.jsonl # Internal message/block dicts (excluding system prompt)
13
+ tool-output/ # large bash outputs (referenced by tool results)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+ import json
20
+ import os
21
+ from dataclasses import asdict, dataclass, field
22
+ from datetime import UTC, datetime
23
+ from pathlib import Path
24
+ from typing import Any
25
+ from uuid import uuid4
26
+
27
+ from mycode.core.config import resolve_sessions_dir
28
+ from mycode.core.messages import ConversationMessage, build_message, flatten_message_text, text_block, tool_result_block
29
+
30
+ # ---------------------------------------------------------------------
31
+ # Session format and compacting defaults
32
+ # ---------------------------------------------------------------------
33
+
34
+ MESSAGE_FORMAT_VERSION = 5
35
+ DEFAULT_SESSION_PROVIDER = "anthropic"
36
+ DEFAULT_SESSION_TITLE = "New chat"
37
+ DEFAULT_COMPACT_THRESHOLD = 0.8
38
+
39
+ COMPACT_SUMMARY_PROMPT = """\
40
+ Summarize this conversation to create a continuation document. \
41
+ This summary will replace the full conversation history, so it must \
42
+ capture everything needed to continue the work seamlessly.
43
+
44
+ Include:
45
+
46
+ 1. **User Requests**: Every distinct request or instruction the user gave, \
47
+ in chronological order. Preserve the user's original wording for ambiguous \
48
+ or nuanced requests.
49
+ 2. **Completed Work**: What was accomplished — files created, modified, or \
50
+ deleted; bugs fixed; features added. Include file paths and function names.
51
+ 3. **Current State**: The exact state of the work right now — what is working, \
52
+ what is broken, what is partially done.
53
+ 4. **Key Decisions**: Important decisions made, constraints discovered, \
54
+ approaches chosen or rejected, and why.
55
+ 5. **Next Steps**: What remains to be done, any work that was in progress \
56
+ when this summary was generated.
57
+
58
+ Rules:
59
+ - Be specific: include file paths, function names, error messages, and \
60
+ concrete details.
61
+ - Do not add suggestions or opinions — only summarize what happened.
62
+ - Keep it concise but complete.\
63
+ """
64
+
65
+ _COMPACT_ACK = "Understood. I have the context from the conversation summary and will continue the work."
66
+
67
+
68
+ # ---------------------------------------------------------------------
69
+ # Compact and rewind session events
70
+ # ---------------------------------------------------------------------
71
+
72
+
73
+ def _now() -> str:
74
+ return datetime.now(UTC).isoformat()
75
+
76
+
77
+ def should_compact(
78
+ last_usage: dict[str, Any] | None,
79
+ context_window: int | None,
80
+ threshold: float,
81
+ ) -> bool:
82
+ """Return True when the last response input tokens exceed the threshold."""
83
+
84
+ if not last_usage or not context_window or threshold <= 0:
85
+ return False
86
+
87
+ # Providers report prompt/input usage under slightly different field names.
88
+ input_tokens = int(
89
+ last_usage.get("input_tokens") or last_usage.get("prompt_tokens") or last_usage.get("prompt_token_count") or 0
90
+ )
91
+ return input_tokens >= context_window * threshold
92
+
93
+
94
+ def build_compact_event(
95
+ summary_text: str,
96
+ *,
97
+ provider: str,
98
+ model: str,
99
+ compacted_count: int,
100
+ usage: dict[str, Any] | None = None,
101
+ ) -> ConversationMessage:
102
+ """Build the compact event stored in session JSONL."""
103
+
104
+ meta: dict[str, Any] = {
105
+ "provider": provider,
106
+ "model": model,
107
+ "compacted_count": compacted_count,
108
+ }
109
+ if usage is not None:
110
+ meta["usage"] = usage
111
+ return build_message("compact", [text_block(summary_text)], meta=meta)
112
+
113
+
114
+ def apply_compact(messages: list[ConversationMessage]) -> list[ConversationMessage]:
115
+ """Replace the latest compact event with a summary + synthetic ack."""
116
+
117
+ # Only the newest compact event matters. Older history before it is no
118
+ # longer visible once the summary replaces that earlier conversation.
119
+ last_compact_index: int | None = None
120
+ for index, message in enumerate(messages):
121
+ if message.get("role") == "compact":
122
+ last_compact_index = index
123
+
124
+ if last_compact_index is None:
125
+ return messages
126
+
127
+ summary_text = ""
128
+ for block in messages[last_compact_index].get("content") or []:
129
+ if isinstance(block, dict) and block.get("type") == "text":
130
+ summary_text = str(block.get("text") or "")
131
+ break
132
+
133
+ return [
134
+ build_message(
135
+ "user",
136
+ [text_block(f"[Conversation Summary]\n\n{summary_text}")],
137
+ meta={"synthetic": True},
138
+ ),
139
+ build_message("assistant", [text_block(_COMPACT_ACK)], meta={"synthetic": True}),
140
+ *messages[last_compact_index + 1 :],
141
+ ]
142
+
143
+
144
+ def build_rewind_event(rewind_to: int) -> ConversationMessage:
145
+ """Build a rewind marker to append to session JSONL."""
146
+
147
+ return {
148
+ "role": "rewind",
149
+ "meta": {
150
+ "rewind_to": rewind_to,
151
+ "created_at": _now(),
152
+ },
153
+ }
154
+
155
+
156
+ def apply_rewind(messages: list[ConversationMessage]) -> list[ConversationMessage]:
157
+ """Apply rewind markers inline while replaying the raw message log."""
158
+
159
+ result: list[ConversationMessage] = []
160
+ for message in messages:
161
+ if message.get("role") == "rewind":
162
+ # Rewind indices refer to the visible message list at that moment,
163
+ # so replay truncates the accumulated result in place.
164
+ rewind_to = (message.get("meta") or {}).get("rewind_to", 0)
165
+ result = result[:rewind_to]
166
+ else:
167
+ result.append(message)
168
+ return result
169
+
170
+
171
+ # ---------------------------------------------------------------------
172
+ # Session metadata
173
+ # ---------------------------------------------------------------------
174
+
175
+
176
+ @dataclass
177
+ class SessionMeta:
178
+ id: str
179
+ title: str
180
+ provider: str
181
+ model: str
182
+ cwd: str
183
+ api_base: str | None
184
+ message_format_version: int
185
+ created_at: str
186
+ updated_at: str
187
+
188
+
189
+ # ---------------------------------------------------------------------
190
+ # Session store
191
+ # ---------------------------------------------------------------------
192
+
193
+
194
+ @dataclass
195
+ class SessionStore:
196
+ """File-based session store backed by append-only JSONL files."""
197
+
198
+ data_dir: Path = field(default_factory=resolve_sessions_dir)
199
+
200
+ def __post_init__(self) -> None:
201
+ self.data_dir.mkdir(parents=True, exist_ok=True)
202
+
203
+ # ---------------------------------------------------------------------
204
+ # Session paths and small JSON helpers
205
+ # ---------------------------------------------------------------------
206
+
207
+ def session_dir(self, session_id: str) -> Path:
208
+ return self.data_dir / session_id
209
+
210
+ def meta_path(self, session_id: str) -> Path:
211
+ return self.session_dir(session_id) / "meta.json"
212
+
213
+ def messages_path(self, session_id: str) -> Path:
214
+ return self.session_dir(session_id) / "messages.jsonl"
215
+
216
+ def _ensure_session_dir(self, session_id: str) -> None:
217
+ session_dir = self.session_dir(session_id)
218
+ session_dir.mkdir(parents=True, exist_ok=True)
219
+ (session_dir / "tool-output").mkdir(parents=True, exist_ok=True)
220
+
221
+ def _read_meta(self, session_id: str) -> dict | None:
222
+ path = self.meta_path(session_id)
223
+ if not path.exists():
224
+ return None
225
+ return json.loads(path.read_text(encoding="utf-8"))
226
+
227
+ def _write_meta(self, session_id: str, meta: dict) -> None:
228
+ self.meta_path(session_id).write_text(json.dumps(meta, indent=2), encoding="utf-8")
229
+
230
+ # ---------------------------------------------------------------------
231
+ # Session CRUD and loading
232
+ # ---------------------------------------------------------------------
233
+
234
+ def draft_session(
235
+ self,
236
+ title: str | None,
237
+ *,
238
+ provider: str = DEFAULT_SESSION_PROVIDER,
239
+ model: str,
240
+ cwd: str,
241
+ api_base: str | None,
242
+ ) -> dict:
243
+ session_id = uuid4().hex
244
+ now = _now()
245
+ meta = asdict(
246
+ SessionMeta(
247
+ id=session_id,
248
+ title=title or DEFAULT_SESSION_TITLE,
249
+ provider=provider,
250
+ model=model,
251
+ cwd=os.path.abspath(cwd),
252
+ api_base=api_base,
253
+ message_format_version=MESSAGE_FORMAT_VERSION,
254
+ created_at=now,
255
+ updated_at=now,
256
+ )
257
+ )
258
+ return {"session": meta, "messages": []}
259
+
260
+ async def create_session(
261
+ self,
262
+ title: str | None,
263
+ *,
264
+ session_id: str | None = None,
265
+ provider: str = DEFAULT_SESSION_PROVIDER,
266
+ model: str,
267
+ cwd: str,
268
+ api_base: str | None,
269
+ ) -> dict:
270
+ data = self.draft_session(
271
+ title,
272
+ provider=provider,
273
+ model=model,
274
+ cwd=cwd,
275
+ api_base=api_base,
276
+ )
277
+ session = data["session"]
278
+ if session_id:
279
+ session["id"] = session_id
280
+ session_id = str(session["id"])
281
+
282
+ def write_files() -> None:
283
+ self._ensure_session_dir(session_id)
284
+ self._write_meta(session_id, session)
285
+ self.messages_path(session_id).touch(exist_ok=True)
286
+
287
+ await asyncio.to_thread(write_files)
288
+ return data
289
+
290
+ async def list_sessions(self, *, cwd: str | None = None) -> list[dict]:
291
+ normalized = os.path.abspath(cwd) if cwd else None
292
+
293
+ def load_all() -> list[dict]:
294
+ out: list[dict] = []
295
+ for entry in self.data_dir.iterdir():
296
+ if not entry.is_dir():
297
+ continue
298
+ mp = entry / "meta.json"
299
+ if not mp.exists():
300
+ continue
301
+ try:
302
+ meta = json.loads(mp.read_text(encoding="utf-8"))
303
+ if normalized and os.path.abspath(meta.get("cwd") or "") != normalized:
304
+ continue
305
+ out.append(meta)
306
+ except Exception:
307
+ continue
308
+
309
+ out.sort(key=lambda m: m.get("updated_at") or "", reverse=True)
310
+ return out
311
+
312
+ return await asyncio.to_thread(load_all)
313
+
314
+ async def latest_session(self, *, cwd: str | None = None) -> dict | None:
315
+ sessions = await self.list_sessions(cwd=cwd)
316
+ return sessions[0] if sessions else None
317
+
318
+ async def load_session(self, session_id: str) -> dict | None:
319
+ def load() -> dict | None:
320
+ meta = self._read_meta(session_id)
321
+ if meta is None:
322
+ return None
323
+
324
+ # Read the raw append-only log first. Replay happens after that.
325
+ raw_messages: list[dict] = []
326
+ messages_path = self.messages_path(session_id)
327
+ try:
328
+ with messages_path.open("r", encoding="utf-8") as handle:
329
+ for line in handle:
330
+ line = line.strip()
331
+ if not line:
332
+ continue
333
+ try:
334
+ msg = json.loads(line)
335
+ if isinstance(msg, dict):
336
+ raw_messages.append(msg)
337
+ except Exception:
338
+ continue
339
+ except FileNotFoundError:
340
+ pass
341
+
342
+ # Replay order defines the visible conversation state.
343
+ # 1) compact rewrites older history into one summary view
344
+ # 2) rewind truncates that visible list by message index
345
+ # 3) interrupted tool repair patches the final visible state
346
+ visible_messages = apply_compact(raw_messages)
347
+ visible_messages = apply_rewind(visible_messages)
348
+ self._repair_interrupted_tool_loop(session_id, meta, visible_messages)
349
+
350
+ return {"session": meta, "messages": visible_messages}
351
+
352
+ return await asyncio.to_thread(load)
353
+
354
+ def _repair_interrupted_tool_loop(self, session_id: str, meta: dict, messages: list[dict]) -> None:
355
+ """Append a synthetic tool result when the latest tool loop was interrupted.
356
+
357
+ The runtime persists sessions as append-only JSONL. If a previous run was
358
+ interrupted after an assistant emitted `tool_use` blocks but before a
359
+ matching `tool_result` user message was written, repair the session by
360
+ appending one synthetic error result message.
361
+ """
362
+
363
+ pending_tool_use_ids: list[str] = []
364
+ pending_tool_call_index: int | None = None
365
+
366
+ # Find the latest assistant message that started a tool loop.
367
+ for index in range(len(messages) - 1, -1, -1):
368
+ message = messages[index]
369
+ if message.get("role") != "assistant":
370
+ continue
371
+
372
+ blocks = message.get("content")
373
+ if not isinstance(blocks, list):
374
+ continue
375
+
376
+ tool_use_ids = [
377
+ str(block.get("id") or "")
378
+ for block in blocks
379
+ if isinstance(block, dict) and block.get("type") == "tool_use" and block.get("id")
380
+ ]
381
+ if not tool_use_ids:
382
+ continue
383
+
384
+ pending_tool_use_ids = tool_use_ids
385
+ pending_tool_call_index = index
386
+ break
387
+
388
+ if pending_tool_call_index is None:
389
+ return
390
+
391
+ # Then collect tool results that were actually recorded after it.
392
+ completed_tool_use_ids: set[str] = set()
393
+ for message in messages[pending_tool_call_index + 1 :]:
394
+ if message.get("role") != "user":
395
+ continue
396
+
397
+ blocks = message.get("content")
398
+ if not isinstance(blocks, list):
399
+ continue
400
+
401
+ for block in blocks:
402
+ if not isinstance(block, dict) or block.get("type") != "tool_result":
403
+ continue
404
+ tool_use_id = str(block.get("tool_use_id") or "")
405
+ if tool_use_id:
406
+ completed_tool_use_ids.add(tool_use_id)
407
+
408
+ missing_tool_use_ids = [
409
+ tool_use_id for tool_use_id in pending_tool_use_ids if tool_use_id not in completed_tool_use_ids
410
+ ]
411
+ if not missing_tool_use_ids:
412
+ return
413
+
414
+ repair_message = build_message(
415
+ "user",
416
+ [
417
+ tool_result_block(
418
+ tool_use_id=tool_use_id,
419
+ model_text="error: tool call was interrupted",
420
+ display_text="Tool call was interrupted",
421
+ is_error=True,
422
+ )
423
+ for tool_use_id in missing_tool_use_ids
424
+ ],
425
+ )
426
+
427
+ with self.messages_path(session_id).open("a", encoding="utf-8") as handle:
428
+ handle.write(json.dumps(repair_message, ensure_ascii=False))
429
+ handle.write("\n")
430
+
431
+ meta["updated_at"] = _now()
432
+ self._write_meta(session_id, meta)
433
+ messages.append(repair_message)
434
+
435
+ async def delete_session(self, session_id: str) -> None:
436
+ def delete() -> None:
437
+ sdir = self.session_dir(session_id)
438
+ if not sdir.exists():
439
+ return
440
+ # small recursive delete (no shutil.rmtree to keep deps minimal)
441
+ for p in sorted(sdir.rglob("*"), reverse=True):
442
+ try:
443
+ if p.is_file() or p.is_symlink():
444
+ p.unlink(missing_ok=True)
445
+ elif p.is_dir():
446
+ p.rmdir()
447
+ except Exception:
448
+ pass
449
+ try:
450
+ sdir.rmdir()
451
+ except Exception:
452
+ pass
453
+
454
+ await asyncio.to_thread(delete)
455
+
456
+ async def clear_session(self, session_id: str) -> None:
457
+ def clear() -> None:
458
+ meta = self._read_meta(session_id)
459
+ if meta is None:
460
+ return
461
+ meta["updated_at"] = _now()
462
+ self._write_meta(session_id, meta)
463
+ self.messages_path(session_id).write_text("", encoding="utf-8")
464
+
465
+ await asyncio.to_thread(clear)
466
+
467
+ # ---------------------------------------------------------------------
468
+ # Append-only updates
469
+ # ---------------------------------------------------------------------
470
+
471
+ async def append_rewind(self, session_id: str, rewind_to: int) -> None:
472
+ """Append a rewind marker to the session JSONL."""
473
+
474
+ def append() -> None:
475
+ meta = self._read_meta(session_id)
476
+ if meta is None:
477
+ return
478
+
479
+ event = build_rewind_event(rewind_to)
480
+ messages_path = self.messages_path(session_id)
481
+ with messages_path.open("a", encoding="utf-8") as handle:
482
+ handle.write(json.dumps(event, ensure_ascii=False))
483
+ handle.write("\n")
484
+
485
+ meta["updated_at"] = _now()
486
+ self._write_meta(session_id, meta)
487
+
488
+ await asyncio.to_thread(append)
489
+
490
+ async def append_message(
491
+ self,
492
+ session_id: str,
493
+ message: dict,
494
+ *,
495
+ provider: str,
496
+ model: str,
497
+ cwd: str,
498
+ api_base: str | None,
499
+ ) -> None:
500
+ def append() -> None:
501
+ meta = self._read_meta(session_id)
502
+ if meta is None:
503
+ # Create the on-disk session only when the first message is persisted.
504
+ self._ensure_session_dir(session_id)
505
+ now = _now()
506
+ meta = asdict(
507
+ SessionMeta(
508
+ id=session_id,
509
+ title=DEFAULT_SESSION_TITLE,
510
+ provider=provider,
511
+ model=model,
512
+ cwd=os.path.abspath(cwd),
513
+ api_base=api_base,
514
+ message_format_version=MESSAGE_FORMAT_VERSION,
515
+ created_at=now,
516
+ updated_at=now,
517
+ )
518
+ )
519
+
520
+ messages_path = self.messages_path(session_id)
521
+ with messages_path.open("a", encoding="utf-8") as handle:
522
+ handle.write(json.dumps(message, ensure_ascii=False))
523
+ handle.write("\n")
524
+
525
+ meta["updated_at"] = _now()
526
+ meta.setdefault("message_format_version", MESSAGE_FORMAT_VERSION)
527
+
528
+ if meta.get("title") == DEFAULT_SESSION_TITLE and message.get("role") == "user":
529
+ # Keep the default title until we see the first real user text,
530
+ # then promote a short preview into the session title.
531
+ title_text = flatten_message_text(message, include_thinking=False).replace("\n", " ").strip()
532
+ if title_text:
533
+ meta["title"] = title_text[:48]
534
+
535
+ self._write_meta(session_id, meta)
536
+
537
+ await asyncio.to_thread(append)
@@ -0,0 +1,10 @@
1
+ You are mycode, an expert coding assistant.
2
+
3
+ You have four tools: read, write, edit, bash.
4
+
5
+ - Use bash for file operations and exploration like `ls`, `find`, `rg`, etc.
6
+ - Always set offset/limit when reading large files.
7
+ - Always read files before editing them.
8
+ - Use write only for new files or complete rewrites
9
+ - Your response should be concise and relevant.
10
+ - When available skills match the current task, prefer them over manual alternatives. To use a skill: read its `SKILL.md`, then follow the instructions inside.