pi-kot 0.1.20 → 0.1.21

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 (330) hide show
  1. package/README.md +253 -17
  2. package/package.json +8 -60
  3. package/bin/pi-kot.mjs +0 -148
  4. package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  5. package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  6. package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  7. package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  8. package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  9. package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  10. package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  11. package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  12. package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  13. package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  14. package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  15. package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  16. package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  17. package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  18. package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  19. package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  20. package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  21. package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  22. package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  23. package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  24. package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  25. package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  26. package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  27. package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  28. package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  29. package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  30. package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  31. package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  32. package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  33. package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  34. package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  35. package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  36. package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  37. package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  38. package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  39. package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  40. package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  41. package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  42. package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  43. package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  44. package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  45. package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  46. package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  47. package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  48. package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  49. package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  50. package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  51. package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  52. package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  53. package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  54. package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  55. package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  56. package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  57. package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  58. package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  59. package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  60. package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  61. package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  62. package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  63. package/dist/client/assets/index-C5quN7Km.js +0 -448
  64. package/dist/client/assets/index-C5quN7Km.js.map +0 -1
  65. package/dist/client/assets/index-CuQOwQs1.css +0 -1
  66. package/dist/client/index.html +0 -14
  67. package/dist/server/ask-user-question/envelope.d.ts +0 -11
  68. package/dist/server/ask-user-question/envelope.d.ts.map +0 -1
  69. package/dist/server/ask-user-question/envelope.js +0 -41
  70. package/dist/server/ask-user-question/envelope.js.map +0 -1
  71. package/dist/server/ask-user-question/plan-mode-question-tool.d.ts +0 -26
  72. package/dist/server/ask-user-question/plan-mode-question-tool.d.ts.map +0 -1
  73. package/dist/server/ask-user-question/plan-mode-question-tool.js +0 -273
  74. package/dist/server/ask-user-question/plan-mode-question-tool.js.map +0 -1
  75. package/dist/server/ask-user-question/prompt-strings.d.ts +0 -4
  76. package/dist/server/ask-user-question/prompt-strings.d.ts.map +0 -1
  77. package/dist/server/ask-user-question/prompt-strings.js +0 -18
  78. package/dist/server/ask-user-question/prompt-strings.js.map +0 -1
  79. package/dist/server/ask-user-question/registry.d.ts +0 -39
  80. package/dist/server/ask-user-question/registry.d.ts.map +0 -1
  81. package/dist/server/ask-user-question/registry.js +0 -124
  82. package/dist/server/ask-user-question/registry.js.map +0 -1
  83. package/dist/server/ask-user-question/tool.d.ts +0 -4
  84. package/dist/server/ask-user-question/tool.d.ts.map +0 -1
  85. package/dist/server/ask-user-question/tool.js +0 -68
  86. package/dist/server/ask-user-question/tool.js.map +0 -1
  87. package/dist/server/ask-user-question/types.d.ts +0 -50
  88. package/dist/server/ask-user-question/types.d.ts.map +0 -1
  89. package/dist/server/ask-user-question/types.js +0 -7
  90. package/dist/server/ask-user-question/types.js.map +0 -1
  91. package/dist/server/ask-user-question/validate.d.ts +0 -3
  92. package/dist/server/ask-user-question/validate.d.ts.map +0 -1
  93. package/dist/server/ask-user-question/validate.js +0 -67
  94. package/dist/server/ask-user-question/validate.js.map +0 -1
  95. package/dist/server/compaction-continuation.d.ts +0 -113
  96. package/dist/server/compaction-continuation.d.ts.map +0 -1
  97. package/dist/server/compaction-continuation.js +0 -65
  98. package/dist/server/compaction-continuation.js.map +0 -1
  99. package/dist/server/compaction-history.d.ts +0 -62
  100. package/dist/server/compaction-history.d.ts.map +0 -1
  101. package/dist/server/compaction-history.js +0 -180
  102. package/dist/server/compaction-history.js.map +0 -1
  103. package/dist/server/config-manager.d.ts +0 -97
  104. package/dist/server/config-manager.d.ts.map +0 -1
  105. package/dist/server/config-manager.js +0 -352
  106. package/dist/server/config-manager.js.map +0 -1
  107. package/dist/server/config.d.ts +0 -24
  108. package/dist/server/config.d.ts.map +0 -1
  109. package/dist/server/config.js +0 -67
  110. package/dist/server/config.js.map +0 -1
  111. package/dist/server/extension-manager.d.ts +0 -116
  112. package/dist/server/extension-manager.d.ts.map +0 -1
  113. package/dist/server/extension-manager.js +0 -586
  114. package/dist/server/extension-manager.js.map +0 -1
  115. package/dist/server/extension-ui-bridge.d.ts +0 -35
  116. package/dist/server/extension-ui-bridge.d.ts.map +0 -1
  117. package/dist/server/extension-ui-bridge.js +0 -228
  118. package/dist/server/extension-ui-bridge.js.map +0 -1
  119. package/dist/server/extensions-discovery.d.ts +0 -26
  120. package/dist/server/extensions-discovery.d.ts.map +0 -1
  121. package/dist/server/extensions-discovery.js +0 -107
  122. package/dist/server/extensions-discovery.js.map +0 -1
  123. package/dist/server/file-manager.d.ts +0 -79
  124. package/dist/server/file-manager.d.ts.map +0 -1
  125. package/dist/server/file-manager.js +0 -535
  126. package/dist/server/file-manager.js.map +0 -1
  127. package/dist/server/file-references.d.ts +0 -5
  128. package/dist/server/file-references.d.ts.map +0 -1
  129. package/dist/server/file-references.js +0 -249
  130. package/dist/server/file-references.js.map +0 -1
  131. package/dist/server/file-searcher.d.ts +0 -29
  132. package/dist/server/file-searcher.d.ts.map +0 -1
  133. package/dist/server/file-searcher.js +0 -318
  134. package/dist/server/file-searcher.js.map +0 -1
  135. package/dist/server/git-clone.d.ts +0 -56
  136. package/dist/server/git-clone.d.ts.map +0 -1
  137. package/dist/server/git-clone.js +0 -173
  138. package/dist/server/git-clone.js.map +0 -1
  139. package/dist/server/git-hunk-stager.d.ts +0 -56
  140. package/dist/server/git-hunk-stager.d.ts.map +0 -1
  141. package/dist/server/git-hunk-stager.js +0 -137
  142. package/dist/server/git-hunk-stager.js.map +0 -1
  143. package/dist/server/git-runner.d.ts +0 -346
  144. package/dist/server/git-runner.d.ts.map +0 -1
  145. package/dist/server/git-runner.js +0 -884
  146. package/dist/server/git-runner.js.map +0 -1
  147. package/dist/server/index.d.ts +0 -22
  148. package/dist/server/index.d.ts.map +0 -1
  149. package/dist/server/index.js +0 -262
  150. package/dist/server/index.js.map +0 -1
  151. package/dist/server/lifecycle-notifications.d.ts +0 -28
  152. package/dist/server/lifecycle-notifications.d.ts.map +0 -1
  153. package/dist/server/lifecycle-notifications.js +0 -38
  154. package/dist/server/lifecycle-notifications.js.map +0 -1
  155. package/dist/server/mcp/config.d.ts +0 -23
  156. package/dist/server/mcp/config.d.ts.map +0 -1
  157. package/dist/server/mcp/config.js +0 -139
  158. package/dist/server/mcp/config.js.map +0 -1
  159. package/dist/server/mcp/manager.d.ts +0 -40
  160. package/dist/server/mcp/manager.d.ts.map +0 -1
  161. package/dist/server/mcp/manager.js +0 -435
  162. package/dist/server/mcp/manager.js.map +0 -1
  163. package/dist/server/mcp/stdio-trust.d.ts +0 -5
  164. package/dist/server/mcp/stdio-trust.d.ts.map +0 -1
  165. package/dist/server/mcp/stdio-trust.js +0 -78
  166. package/dist/server/mcp/stdio-trust.js.map +0 -1
  167. package/dist/server/mcp/tool-bridge.d.ts +0 -33
  168. package/dist/server/mcp/tool-bridge.d.ts.map +0 -1
  169. package/dist/server/mcp/tool-bridge.js +0 -138
  170. package/dist/server/mcp/tool-bridge.js.map +0 -1
  171. package/dist/server/orchestration/config.d.ts +0 -5
  172. package/dist/server/orchestration/config.d.ts.map +0 -1
  173. package/dist/server/orchestration/config.js +0 -35
  174. package/dist/server/orchestration/config.js.map +0 -1
  175. package/dist/server/orchestration/event-bridge.d.ts +0 -32
  176. package/dist/server/orchestration/event-bridge.d.ts.map +0 -1
  177. package/dist/server/orchestration/event-bridge.js +0 -110
  178. package/dist/server/orchestration/event-bridge.js.map +0 -1
  179. package/dist/server/orchestration/inbox.d.ts +0 -9
  180. package/dist/server/orchestration/inbox.d.ts.map +0 -1
  181. package/dist/server/orchestration/inbox.js +0 -186
  182. package/dist/server/orchestration/inbox.js.map +0 -1
  183. package/dist/server/orchestration/init.d.ts +0 -2
  184. package/dist/server/orchestration/init.d.ts.map +0 -1
  185. package/dist/server/orchestration/init.js +0 -22
  186. package/dist/server/orchestration/init.js.map +0 -1
  187. package/dist/server/orchestration/store.d.ts +0 -33
  188. package/dist/server/orchestration/store.d.ts.map +0 -1
  189. package/dist/server/orchestration/store.js +0 -331
  190. package/dist/server/orchestration/store.js.map +0 -1
  191. package/dist/server/orchestration/tools.d.ts +0 -15
  192. package/dist/server/orchestration/tools.d.ts.map +0 -1
  193. package/dist/server/orchestration/tools.js +0 -621
  194. package/dist/server/orchestration/tools.js.map +0 -1
  195. package/dist/server/orchestration/types.d.ts +0 -68
  196. package/dist/server/orchestration/types.d.ts.map +0 -1
  197. package/dist/server/orchestration/types.js +0 -43
  198. package/dist/server/orchestration/types.js.map +0 -1
  199. package/dist/server/orchestration/worker-lifecycle.d.ts +0 -15
  200. package/dist/server/orchestration/worker-lifecycle.d.ts.map +0 -1
  201. package/dist/server/orchestration/worker-lifecycle.js +0 -45
  202. package/dist/server/orchestration/worker-lifecycle.js.map +0 -1
  203. package/dist/server/project-manager.d.ts +0 -25
  204. package/dist/server/project-manager.d.ts.map +0 -1
  205. package/dist/server/project-manager.js +0 -157
  206. package/dist/server/project-manager.js.map +0 -1
  207. package/dist/server/pty-manager.d.ts +0 -54
  208. package/dist/server/pty-manager.d.ts.map +0 -1
  209. package/dist/server/pty-manager.js +0 -220
  210. package/dist/server/pty-manager.js.map +0 -1
  211. package/dist/server/routes/_schemas.d.ts +0 -16
  212. package/dist/server/routes/_schemas.d.ts.map +0 -1
  213. package/dist/server/routes/_schemas.js +0 -12
  214. package/dist/server/routes/_schemas.js.map +0 -1
  215. package/dist/server/routes/ask-user-question.d.ts +0 -3
  216. package/dist/server/routes/ask-user-question.d.ts.map +0 -1
  217. package/dist/server/routes/ask-user-question.js +0 -105
  218. package/dist/server/routes/ask-user-question.js.map +0 -1
  219. package/dist/server/routes/auth.d.ts +0 -8
  220. package/dist/server/routes/auth.d.ts.map +0 -1
  221. package/dist/server/routes/auth.js +0 -225
  222. package/dist/server/routes/auth.js.map +0 -1
  223. package/dist/server/routes/config.d.ts +0 -3
  224. package/dist/server/routes/config.d.ts.map +0 -1
  225. package/dist/server/routes/config.js +0 -568
  226. package/dist/server/routes/config.js.map +0 -1
  227. package/dist/server/routes/control.d.ts +0 -3
  228. package/dist/server/routes/control.d.ts.map +0 -1
  229. package/dist/server/routes/control.js +0 -365
  230. package/dist/server/routes/control.js.map +0 -1
  231. package/dist/server/routes/exec.d.ts +0 -3
  232. package/dist/server/routes/exec.d.ts.map +0 -1
  233. package/dist/server/routes/exec.js +0 -426
  234. package/dist/server/routes/exec.js.map +0 -1
  235. package/dist/server/routes/extension-commands.d.ts +0 -22
  236. package/dist/server/routes/extension-commands.d.ts.map +0 -1
  237. package/dist/server/routes/extension-commands.js +0 -178
  238. package/dist/server/routes/extension-commands.js.map +0 -1
  239. package/dist/server/routes/extensions.d.ts +0 -8
  240. package/dist/server/routes/extensions.d.ts.map +0 -1
  241. package/dist/server/routes/extensions.js +0 -414
  242. package/dist/server/routes/extensions.js.map +0 -1
  243. package/dist/server/routes/files.d.ts +0 -3
  244. package/dist/server/routes/files.d.ts.map +0 -1
  245. package/dist/server/routes/files.js +0 -835
  246. package/dist/server/routes/files.js.map +0 -1
  247. package/dist/server/routes/git.d.ts +0 -3
  248. package/dist/server/routes/git.d.ts.map +0 -1
  249. package/dist/server/routes/git.js +0 -1058
  250. package/dist/server/routes/git.js.map +0 -1
  251. package/dist/server/routes/mcp.d.ts +0 -3
  252. package/dist/server/routes/mcp.d.ts.map +0 -1
  253. package/dist/server/routes/mcp.js +0 -403
  254. package/dist/server/routes/mcp.js.map +0 -1
  255. package/dist/server/routes/orchestration.d.ts +0 -3
  256. package/dist/server/routes/orchestration.d.ts.map +0 -1
  257. package/dist/server/routes/orchestration.js +0 -251
  258. package/dist/server/routes/orchestration.js.map +0 -1
  259. package/dist/server/routes/projects.d.ts +0 -3
  260. package/dist/server/routes/projects.d.ts.map +0 -1
  261. package/dist/server/routes/projects.js +0 -421
  262. package/dist/server/routes/projects.js.map +0 -1
  263. package/dist/server/routes/prompt.d.ts +0 -3
  264. package/dist/server/routes/prompt.d.ts.map +0 -1
  265. package/dist/server/routes/prompt.js +0 -311
  266. package/dist/server/routes/prompt.js.map +0 -1
  267. package/dist/server/routes/session-extensions.d.ts +0 -11
  268. package/dist/server/routes/session-extensions.d.ts.map +0 -1
  269. package/dist/server/routes/session-extensions.js +0 -171
  270. package/dist/server/routes/session-extensions.js.map +0 -1
  271. package/dist/server/routes/sessions.d.ts +0 -3
  272. package/dist/server/routes/sessions.d.ts.map +0 -1
  273. package/dist/server/routes/sessions.js +0 -706
  274. package/dist/server/routes/sessions.js.map +0 -1
  275. package/dist/server/routes/skills.d.ts +0 -14
  276. package/dist/server/routes/skills.d.ts.map +0 -1
  277. package/dist/server/routes/skills.js +0 -448
  278. package/dist/server/routes/skills.js.map +0 -1
  279. package/dist/server/routes/stream.d.ts +0 -3
  280. package/dist/server/routes/stream.d.ts.map +0 -1
  281. package/dist/server/routes/stream.js +0 -112
  282. package/dist/server/routes/stream.js.map +0 -1
  283. package/dist/server/routes/terminal.d.ts +0 -28
  284. package/dist/server/routes/terminal.d.ts.map +0 -1
  285. package/dist/server/routes/terminal.js +0 -211
  286. package/dist/server/routes/terminal.js.map +0 -1
  287. package/dist/server/routes/tunnel.d.ts +0 -13
  288. package/dist/server/routes/tunnel.d.ts.map +0 -1
  289. package/dist/server/routes/tunnel.js +0 -231
  290. package/dist/server/routes/tunnel.js.map +0 -1
  291. package/dist/server/session-registry.d.ts +0 -164
  292. package/dist/server/session-registry.d.ts.map +0 -1
  293. package/dist/server/session-registry.js +0 -754
  294. package/dist/server/session-registry.js.map +0 -1
  295. package/dist/server/skill-overrides.d.ts +0 -47
  296. package/dist/server/skill-overrides.d.ts.map +0 -1
  297. package/dist/server/skill-overrides.js +0 -178
  298. package/dist/server/skill-overrides.js.map +0 -1
  299. package/dist/server/sse-bridge.d.ts +0 -31
  300. package/dist/server/sse-bridge.d.ts.map +0 -1
  301. package/dist/server/sse-bridge.js +0 -158
  302. package/dist/server/sse-bridge.js.map +0 -1
  303. package/dist/server/tool-overrides.d.ts +0 -39
  304. package/dist/server/tool-overrides.d.ts.map +0 -1
  305. package/dist/server/tool-overrides.js +0 -121
  306. package/dist/server/tool-overrides.js.map +0 -1
  307. package/dist/server/tunnel/executable-search.d.ts +0 -45
  308. package/dist/server/tunnel/executable-search.d.ts.map +0 -1
  309. package/dist/server/tunnel/executable-search.js +0 -150
  310. package/dist/server/tunnel/executable-search.js.map +0 -1
  311. package/dist/server/tunnel/install-help.d.ts +0 -19
  312. package/dist/server/tunnel/install-help.d.ts.map +0 -1
  313. package/dist/server/tunnel/install-help.js +0 -47
  314. package/dist/server/tunnel/install-help.js.map +0 -1
  315. package/dist/server/tunnel/providers/ngrok.d.ts +0 -34
  316. package/dist/server/tunnel/providers/ngrok.d.ts.map +0 -1
  317. package/dist/server/tunnel/providers/ngrok.js +0 -464
  318. package/dist/server/tunnel/providers/ngrok.js.map +0 -1
  319. package/dist/server/tunnel/registry.d.ts +0 -15
  320. package/dist/server/tunnel/registry.d.ts.map +0 -1
  321. package/dist/server/tunnel/registry.js +0 -58
  322. package/dist/server/tunnel/registry.js.map +0 -1
  323. package/dist/server/tunnel/service.d.ts +0 -15
  324. package/dist/server/tunnel/service.d.ts.map +0 -1
  325. package/dist/server/tunnel/service.js +0 -158
  326. package/dist/server/tunnel/service.js.map +0 -1
  327. package/dist/server/tunnel/types.d.ts +0 -166
  328. package/dist/server/tunnel/types.d.ts.map +0 -1
  329. package/dist/server/tunnel/types.js +0 -217
  330. package/dist/server/tunnel/types.js.map +0 -1
@@ -1,1058 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { GitCommandError, GitNotInstalledError, InvalidBranchNameError, addWorktree, checkoutBranch, commit, createBranch, deleteBranch, fetch, addRemote, getBranches, getCommitFiles, getCommitFileDiff, getRemotes, removeRemote, setRemoteInsecureTls, getDiff, getFileDiff, getLog, getStagedDiff, getStatus, getWorktrees, initRepo, isGitRepo, pull, push, removeWorktree, revertPaths, stagePaths, unstagePaths, } from "../git-runner.js";
3
- import { applyHunks, HunkStagingError } from "../git-hunk-stager.js";
4
- import { PathOutsideRootError } from "../file-manager.js";
5
- import { getProject } from "../project-manager.js";
6
- import { errorSchema } from "./_schemas.js";
7
- /* ----------------------------- schemas ----------------------------- */
8
- const fileStatusEntrySchema = {
9
- type: "object",
10
- required: ["path", "staged", "unstaged", "kind", "code"],
11
- properties: {
12
- path: { type: "string" },
13
- staged: { type: "boolean" },
14
- unstaged: { type: "boolean" },
15
- kind: {
16
- type: "string",
17
- enum: [
18
- "modified",
19
- "added",
20
- "deleted",
21
- "renamed",
22
- "copied",
23
- "untracked",
24
- "ignored",
25
- "conflicted",
26
- "unknown",
27
- ],
28
- },
29
- code: { type: "string" },
30
- originalPath: { type: "string" },
31
- },
32
- };
33
- const statusSchema = {
34
- type: "object",
35
- required: ["isGitRepo", "files"],
36
- properties: {
37
- isGitRepo: { type: "boolean" },
38
- branch: { type: "string" },
39
- files: { type: "array", items: fileStatusEntrySchema },
40
- },
41
- };
42
- const diffSchema = {
43
- type: "object",
44
- required: ["isGitRepo", "diff"],
45
- properties: {
46
- isGitRepo: { type: "boolean" },
47
- diff: { type: "string" },
48
- },
49
- };
50
- const logSchema = {
51
- type: "object",
52
- required: ["isGitRepo", "commits"],
53
- properties: {
54
- isGitRepo: { type: "boolean" },
55
- commits: {
56
- type: "array",
57
- items: {
58
- type: "object",
59
- required: ["hash", "message", "author", "date", "parents", "refs"],
60
- properties: {
61
- hash: { type: "string" },
62
- message: { type: "string" },
63
- author: { type: "string" },
64
- date: { type: "string" },
65
- parents: { type: "array", items: { type: "string" } },
66
- refs: { type: "array", items: { type: "string" } },
67
- },
68
- },
69
- },
70
- },
71
- };
72
- const branchesSchema = {
73
- type: "object",
74
- required: ["isGitRepo", "branches"],
75
- properties: {
76
- isGitRepo: { type: "boolean" },
77
- current: { type: "string" },
78
- branches: {
79
- type: "array",
80
- items: {
81
- type: "object",
82
- required: ["name", "current", "remote"],
83
- properties: {
84
- name: { type: "string" },
85
- current: { type: "boolean" },
86
- remote: { type: "boolean" },
87
- },
88
- },
89
- },
90
- },
91
- };
92
- const remotesSchema = {
93
- type: "object",
94
- required: ["isGitRepo", "remotes"],
95
- properties: {
96
- isGitRepo: { type: "boolean" },
97
- remotes: {
98
- type: "array",
99
- items: {
100
- type: "object",
101
- required: ["name", "fetchUrl", "pushUrl", "insecureTls"],
102
- properties: {
103
- name: { type: "string" },
104
- fetchUrl: { type: "string" },
105
- pushUrl: { type: "string" },
106
- insecureTls: { type: "boolean" },
107
- },
108
- },
109
- },
110
- },
111
- };
112
- const worktreesSchema = {
113
- type: "object",
114
- required: ["isGitRepo", "worktrees"],
115
- properties: {
116
- isGitRepo: { type: "boolean" },
117
- worktrees: {
118
- type: "array",
119
- items: {
120
- type: "object",
121
- required: ["path", "bare", "detached", "current"],
122
- properties: {
123
- path: { type: "string" },
124
- head: { type: "string" },
125
- branch: { type: "string" },
126
- bare: { type: "boolean" },
127
- detached: { type: "boolean" },
128
- current: { type: "boolean" },
129
- },
130
- },
131
- },
132
- },
133
- };
134
- class InvalidWorktreePathError extends Error {
135
- constructor(path) {
136
- super(`invalid worktree path: ${path}`);
137
- this.name = "InvalidWorktreePathError";
138
- }
139
- }
140
- /* ----------------------------- error mapping ----------------------------- */
141
- function mapError(reply, err) {
142
- if (err instanceof GitNotInstalledError) {
143
- return reply.code(500).send({
144
- error: "git_not_installed",
145
- message: "git binary is not on PATH on the server",
146
- });
147
- }
148
- if (err instanceof InvalidBranchNameError) {
149
- return reply.code(400).send({ error: "invalid_branch_name", message: err.message });
150
- }
151
- if (err instanceof InvalidWorktreePathError) {
152
- return reply.code(400).send({ error: "invalid_worktree_path", message: err.message });
153
- }
154
- if (err instanceof PathOutsideRootError) {
155
- return reply.code(403).send({ error: "path_not_allowed", message: "path outside workspace" });
156
- }
157
- if (err instanceof GitCommandError) {
158
- // Git "rejected" / "non-fast-forward" / commit hook failures /
159
- // missing upstream are user-actionable, not server bugs. 400
160
- // with a sanitized message lets the client surface the hint
161
- // verbatim. Auth failures get a stable error code so the UI can
162
- // distinguish "configure credentials" from ordinary git failures
163
- // without ever collecting or returning raw secrets.
164
- return reply.code(400).send({ error: err.errorCode, message: err.userMessage });
165
- }
166
- reply.log.error({ err }, "unmapped git-runner error");
167
- return reply.code(500).send({ error: "internal_error" });
168
- }
169
- /**
170
- * Resolve the project for a request. On miss, sends 404 + returns
171
- * undefined; caller MUST `return reply` immediately. Returning bare
172
- * `undefined` trips Fastify's `FST_ERR_REP_ALREADY_SENT` because the
173
- * route handler's resolved `undefined` is interpreted as "send this,"
174
- * which races the 404 the helper already sent. See
175
- * files.ts:resolveProject for the same contract.
176
- */
177
- async function resolveProject(projectId, reply) {
178
- const project = await getProject(projectId);
179
- if (project === undefined) {
180
- await reply.code(404).send({ error: "project_not_found", message: "no project with that id" });
181
- return undefined;
182
- }
183
- return { id: project.id, path: project.path };
184
- }
185
- /**
186
- * Resolve-then-run helper that collapses the project-not-found 404 +
187
- * runner-error 4xx mapping every git GET route shares. Without this
188
- * each route was 6 lines of identical boilerplate (resolveProject,
189
- * undefined-check, try, runner call, catch, mapError); routes shrink
190
- * to a one-liner.
191
- *
192
- * On project-not-found the resolveProject helper has already sent the
193
- * 404 reply; we return its `undefined` so the route handler short-
194
- * circuits without trying to return a value Fastify would re-send.
195
- * On runner success the result is returned verbatim (Fastify
196
- * serializes via the response schema). On runner throw we route to
197
- * mapError for the typed-error → wire-shape mapping.
198
- */
199
- async function withProject(projectId, reply, fn) {
200
- const project = await resolveProject(projectId, reply);
201
- // resolveProject already called reply.send for the 404 path —
202
- // returning the reply here tells Fastify the response was handled,
203
- // avoiding the FST_ERR_REP_ALREADY_SENT double-send error.
204
- if (project === undefined)
205
- return reply;
206
- try {
207
- return await fn(project);
208
- }
209
- catch (err) {
210
- return mapError(reply, err);
211
- }
212
- }
213
- async function resolveGitCwd(project, worktreePath) {
214
- if (worktreePath === undefined)
215
- return project.path;
216
- const listed = await getWorktrees(project.path);
217
- const match = listed.worktrees.find((w) => w.path === worktreePath);
218
- if (match === undefined)
219
- throw new InvalidWorktreePathError(worktreePath);
220
- return match.path;
221
- }
222
- async function withGitCwd(projectId, worktreePath, reply, fn) {
223
- const project = await resolveProject(projectId, reply);
224
- if (project === undefined)
225
- return reply;
226
- try {
227
- const cwd = await resolveGitCwd(project, worktreePath);
228
- return await fn(cwd);
229
- }
230
- catch (err) {
231
- return mapError(reply, err);
232
- }
233
- }
234
- const projectWorktreeQuerySchema = {
235
- type: "object",
236
- required: ["projectId"],
237
- properties: {
238
- projectId: { type: "string", minLength: 1 },
239
- worktreePath: { type: "string", minLength: 1 },
240
- },
241
- };
242
- const worktreeBodyProperty = { worktreePath: { type: "string", minLength: 1 } };
243
- /* ----------------------------- routes ----------------------------- */
244
- export const gitRoutes = async (fastify) => {
245
- fastify.post("/git/init", {
246
- schema: {
247
- description: "Initialize a fresh git repo at the project's path with `main` as the " +
248
- "initial branch. Idempotent: returns 200 with `{ alreadyInitialised: " +
249
- "true }` if the project is already a git working tree. Falls back to " +
250
- "plain `git init` (no `-b main`) on git versions < 2.28.",
251
- tags: ["git"],
252
- body: {
253
- type: "object",
254
- required: ["projectId"],
255
- additionalProperties: false,
256
- properties: { projectId: { type: "string", minLength: 1 } },
257
- },
258
- response: {
259
- 200: {
260
- type: "object",
261
- required: ["alreadyInitialised", "isGitRepo"],
262
- properties: {
263
- alreadyInitialised: { type: "boolean" },
264
- isGitRepo: { type: "boolean" },
265
- },
266
- },
267
- 400: errorSchema,
268
- 404: errorSchema,
269
- 500: errorSchema,
270
- },
271
- },
272
- }, async (req, reply) => {
273
- const project = await resolveProject(req.body.projectId, reply);
274
- if (project === undefined)
275
- return reply;
276
- try {
277
- if (await isGitRepo(project.path)) {
278
- return { alreadyInitialised: true, isGitRepo: true };
279
- }
280
- await initRepo(project.path);
281
- return { alreadyInitialised: false, isGitRepo: true };
282
- }
283
- catch (err) {
284
- return mapError(reply, err);
285
- }
286
- });
287
- fastify.get("/git/status", {
288
- // Polled by the client every ~15s while a project is open. We
289
- // silence the access logs for this route specifically so the
290
- // poll doesn't drown out interesting events; errors still log
291
- // at warn+.
292
- logLevel: "warn",
293
- schema: {
294
- description: "Parsed `git status --porcelain=v1 -uall` for the project. Files " +
295
- "include staged/unstaged flags, a coarse `kind` classification, and " +
296
- "the raw two-char porcelain code. Non-git directories return " +
297
- "`{ isGitRepo: false, files: [] }` (NOT 500) so the panel can sit " +
298
- "quiet on plain folders.",
299
- tags: ["git"],
300
- querystring: projectWorktreeQuerySchema,
301
- response: {
302
- 200: statusSchema,
303
- 400: errorSchema,
304
- 403: errorSchema,
305
- 404: errorSchema,
306
- 500: errorSchema,
307
- },
308
- },
309
- }, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getStatus(cwd)));
310
- fastify.get("/git/worktrees", {
311
- schema: {
312
- description: "List registered git worktrees for the project's repository. Returned absolute paths " +
313
- "can be passed back as `worktreePath` to git routes and are accepted only if still registered.",
314
- tags: ["git"],
315
- querystring: {
316
- type: "object",
317
- required: ["projectId"],
318
- properties: { projectId: { type: "string", minLength: 1 } },
319
- },
320
- response: {
321
- 200: worktreesSchema,
322
- 400: errorSchema,
323
- 403: errorSchema,
324
- 404: errorSchema,
325
- 500: errorSchema,
326
- },
327
- },
328
- }, async (req, reply) => withProject(req.query.projectId, reply, async (p) => {
329
- return getWorktrees(p.path);
330
- }));
331
- fastify.post("/git/worktree/add", {
332
- schema: {
333
- description: "Create a linked working tree at `worktreePath` (relative to the project root) " +
334
- "pinned to `commitHash`. If `worktreePath` is omitted, defaults to " +
335
- "`.git-worktrees/<first-7-of-commitHash>/`. The working tree can be dirty — " +
336
- "worktree creation doesn't require a clean index. Returns the absolute path " +
337
- "of the created worktree.",
338
- tags: ["git"],
339
- body: {
340
- type: "object",
341
- required: ["projectId", "commitHash"],
342
- additionalProperties: false,
343
- properties: {
344
- projectId: { type: "string", minLength: 1 },
345
- commitHash: { type: "string", minLength: 7, maxLength: 40 },
346
- worktreePath: { type: "string", minLength: 1 },
347
- },
348
- },
349
- response: {
350
- 200: {
351
- type: "object",
352
- required: ["path"],
353
- properties: { path: { type: "string" } },
354
- },
355
- 400: errorSchema,
356
- 404: errorSchema,
357
- 500: errorSchema,
358
- },
359
- },
360
- }, async (req, reply) => withProject(req.body.projectId, reply, async (p) => {
361
- const shortHash = req.body.commitHash.slice(0, 7);
362
- const relPath = req.body.worktreePath ?? `.git-worktrees/${shortHash}/`;
363
- const absPath = resolve(p.path, relPath);
364
- await addWorktree(p.path, absPath, req.body.commitHash);
365
- return { path: absPath };
366
- }));
367
- fastify.post("/git/worktree/remove", {
368
- schema: {
369
- description: "Remove a linked working tree at the given absolute path. Git refuses if the " +
370
- "worktree has uncommitted changes; the resulting error message is surfaced " +
371
- "so the user knows to commit or stash first.",
372
- tags: ["git"],
373
- body: {
374
- type: "object",
375
- required: ["projectId", "worktreePath"],
376
- additionalProperties: false,
377
- properties: {
378
- projectId: { type: "string", minLength: 1 },
379
- worktreePath: { type: "string", minLength: 1 },
380
- },
381
- },
382
- response: {
383
- 200: {
384
- type: "object",
385
- properties: { ok: { type: "boolean" } },
386
- required: ["ok"],
387
- },
388
- 400: errorSchema,
389
- 404: errorSchema,
390
- 500: errorSchema,
391
- },
392
- },
393
- }, async (req, reply) => withProject(req.body.projectId, reply, async (p) => {
394
- await removeWorktree(p.path, req.body.worktreePath);
395
- return { ok: true };
396
- }));
397
- fastify.get("/git/diff", {
398
- schema: {
399
- description: "Unstaged unified diff for the project (working tree vs index).",
400
- tags: ["git"],
401
- querystring: projectWorktreeQuerySchema,
402
- response: {
403
- 200: diffSchema,
404
- 400: errorSchema,
405
- 403: errorSchema,
406
- 404: errorSchema,
407
- 500: errorSchema,
408
- },
409
- },
410
- }, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getDiff(cwd)));
411
- fastify.get("/git/diff/staged", {
412
- schema: {
413
- description: "Staged unified diff (index vs HEAD).",
414
- tags: ["git"],
415
- querystring: projectWorktreeQuerySchema,
416
- response: {
417
- 200: diffSchema,
418
- 400: errorSchema,
419
- 403: errorSchema,
420
- 404: errorSchema,
421
- 500: errorSchema,
422
- },
423
- },
424
- }, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getStagedDiff(cwd)));
425
- fastify.get("/git/diff/file", {
426
- schema: {
427
- description: "Unified diff for a single file. `?staged=1` for the index↔HEAD diff; " +
428
- "default is working-tree↔index.",
429
- tags: ["git"],
430
- querystring: {
431
- type: "object",
432
- required: ["projectId", "path"],
433
- properties: {
434
- projectId: { type: "string", minLength: 1 },
435
- path: { type: "string", minLength: 1 },
436
- staged: { type: "string", enum: ["0", "1", "true", "false"] },
437
- worktreePath: { type: "string", minLength: 1 },
438
- },
439
- },
440
- response: {
441
- 200: diffSchema,
442
- 400: errorSchema,
443
- 403: errorSchema,
444
- 404: errorSchema,
445
- 500: errorSchema,
446
- },
447
- },
448
- }, async (req, reply) => {
449
- const staged = req.query.staged === "1" || req.query.staged === "true";
450
- return withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getFileDiff(cwd, req.query.path, staged));
451
- });
452
- fastify.get("/git/log", {
453
- schema: {
454
- description: "Recent commits as `{ hash, message, author, date }[]`. Default " + "limit 30; max 1000.",
455
- tags: ["git"],
456
- querystring: {
457
- type: "object",
458
- required: ["projectId"],
459
- properties: {
460
- projectId: { type: "string", minLength: 1 },
461
- limit: { type: "string", pattern: "^[0-9]+$" },
462
- worktreePath: { type: "string", minLength: 1 },
463
- },
464
- },
465
- response: {
466
- 200: logSchema,
467
- 400: errorSchema,
468
- 403: errorSchema,
469
- 404: errorSchema,
470
- 500: errorSchema,
471
- },
472
- },
473
- }, async (req, reply) => {
474
- const limit = req.query.limit !== undefined
475
- ? Math.min(1000, Math.max(1, Number.parseInt(req.query.limit, 10)))
476
- : 30;
477
- return withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getLog(cwd, limit));
478
- });
479
- /* ---------- commit file listing & diff ---------- */
480
- fastify.get("/git/commit/files", {
481
- schema: {
482
- description: "List files changed in a specific commit, with change " +
483
- "type (A/M/D/R). Uses git diff-tree under the hood.",
484
- tags: ["git"],
485
- querystring: {
486
- type: "object",
487
- required: ["projectId", "hash"],
488
- properties: {
489
- projectId: { type: "string", minLength: 1 },
490
- hash: { type: "string", minLength: 1 },
491
- worktreePath: { type: "string", minLength: 1 },
492
- },
493
- },
494
- response: {
495
- 200: {
496
- type: "object",
497
- required: ["files"],
498
- properties: {
499
- files: {
500
- type: "array",
501
- items: {
502
- type: "object",
503
- required: ["path", "changeType"],
504
- properties: {
505
- path: { type: "string" },
506
- changeType: { type: "string" },
507
- },
508
- },
509
- },
510
- },
511
- },
512
- 400: errorSchema,
513
- 403: errorSchema,
514
- 404: errorSchema,
515
- 500: errorSchema,
516
- },
517
- },
518
- }, async (req, reply) => {
519
- return withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getCommitFiles(cwd, req.query.hash).then((files) => ({ files })));
520
- });
521
- fastify.get("/git/commit/diff", {
522
- schema: {
523
- description: "Get the unified diff for a single file in a specific commit.",
524
- tags: ["git"],
525
- querystring: {
526
- type: "object",
527
- required: ["projectId", "hash", "path"],
528
- properties: {
529
- projectId: { type: "string", minLength: 1 },
530
- hash: { type: "string", minLength: 1 },
531
- path: { type: "string", minLength: 1 },
532
- worktreePath: { type: "string", minLength: 1 },
533
- },
534
- },
535
- response: {
536
- 200: {
537
- type: "object",
538
- required: ["diff"],
539
- properties: {
540
- diff: { type: "string" },
541
- },
542
- },
543
- 400: errorSchema,
544
- 403: errorSchema,
545
- 404: errorSchema,
546
- 500: errorSchema,
547
- },
548
- },
549
- }, async (req, reply) => {
550
- return withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getCommitFileDiff(cwd, req.query.hash, req.query.path).then((diff) => ({ diff })));
551
- });
552
- fastify.get("/git/branches", {
553
- schema: {
554
- description: "Local + remote branch list with `current` flag.",
555
- tags: ["git"],
556
- querystring: projectWorktreeQuerySchema,
557
- response: {
558
- 200: branchesSchema,
559
- 400: errorSchema,
560
- 403: errorSchema,
561
- 404: errorSchema,
562
- 500: errorSchema,
563
- },
564
- },
565
- }, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getBranches(cwd)));
566
- fastify.get("/git/remotes", {
567
- schema: {
568
- description: "Configured git remotes with their fetch + push URLs. " +
569
- "Empty array for non-git projects or repos with no remotes.",
570
- tags: ["git"],
571
- querystring: projectWorktreeQuerySchema,
572
- response: {
573
- 200: remotesSchema,
574
- 400: errorSchema,
575
- 403: errorSchema,
576
- 404: errorSchema,
577
- 500: errorSchema,
578
- },
579
- },
580
- }, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, (cwd) => getRemotes(cwd)));
581
- fastify.post("/git/remote/add", {
582
- schema: {
583
- description: "Add a git remote (`git remote add <name> <url>`). Name is " +
584
- "validated against the same character set as branch names. " +
585
- "URL accepts any string git itself accepts (https://, git@, " +
586
- "file://, etc.). Optional `insecureTls: true` persists a local, " +
587
- "URL-scoped `http.<url>.sslVerify=false` setting for this repo. " +
588
- "Duplicate name → 400 `git_failed`.",
589
- tags: ["git"],
590
- body: {
591
- type: "object",
592
- required: ["projectId", "name", "url"],
593
- additionalProperties: false,
594
- properties: {
595
- projectId: { type: "string", minLength: 1 },
596
- name: { type: "string", minLength: 1 },
597
- url: { type: "string", minLength: 1, maxLength: 1024 },
598
- insecureTls: { type: "boolean" },
599
- ...worktreeBodyProperty,
600
- },
601
- },
602
- response: {
603
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
604
- 400: errorSchema,
605
- 404: errorSchema,
606
- 500: errorSchema,
607
- },
608
- },
609
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
610
- await addRemote(cwd, req.body.name, req.body.url, {
611
- insecureTls: req.body.insecureTls === true,
612
- });
613
- return { ok: true };
614
- }));
615
- fastify.post("/git/remote/tls", {
616
- schema: {
617
- description: "Enable or disable TLS certificate verification for one remote in this repository. " +
618
- "The setting is persisted in local git config as URL-scoped `http.<url>.sslVerify=false`; " +
619
- "it does not change global git config or store credentials.",
620
- tags: ["git"],
621
- body: {
622
- type: "object",
623
- required: ["projectId", "name", "insecureTls"],
624
- additionalProperties: false,
625
- properties: {
626
- projectId: { type: "string", minLength: 1 },
627
- name: { type: "string", minLength: 1 },
628
- insecureTls: { type: "boolean" },
629
- ...worktreeBodyProperty,
630
- },
631
- },
632
- response: {
633
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
634
- 400: errorSchema,
635
- 404: errorSchema,
636
- 500: errorSchema,
637
- },
638
- },
639
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
640
- await setRemoteInsecureTls(cwd, req.body.name, req.body.insecureTls);
641
- return { ok: true };
642
- }));
643
- fastify.delete("/git/remote/:name", {
644
- schema: {
645
- description: "Remove a git remote (`git remote remove <name>`). 400 " +
646
- "`git_failed` if the name is unknown.",
647
- tags: ["git"],
648
- params: {
649
- type: "object",
650
- required: ["name"],
651
- properties: { name: { type: "string", minLength: 1 } },
652
- },
653
- querystring: projectWorktreeQuerySchema,
654
- response: {
655
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
656
- 400: errorSchema,
657
- 404: errorSchema,
658
- 500: errorSchema,
659
- },
660
- },
661
- }, async (req, reply) => withGitCwd(req.query.projectId, req.query.worktreePath, reply, async (cwd) => {
662
- await removeRemote(cwd, req.params.name);
663
- return { ok: true };
664
- }));
665
- fastify.post("/git/checkout", {
666
- schema: {
667
- description: "Switch the working tree to `branch`. Refuses on a dirty tree (git's " +
668
- "default) — caller surfaces the resulting `git_failed` message so the " +
669
- "user can stash or revert first. Pass `origin/feature` to start a " +
670
- "tracking branch from the remote ref.",
671
- tags: ["git"],
672
- body: {
673
- type: "object",
674
- required: ["projectId", "branch"],
675
- additionalProperties: false,
676
- properties: {
677
- projectId: { type: "string", minLength: 1 },
678
- branch: { type: "string", minLength: 1 },
679
- ...worktreeBodyProperty,
680
- },
681
- },
682
- response: {
683
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
684
- 400: errorSchema,
685
- 404: errorSchema,
686
- 500: errorSchema,
687
- },
688
- },
689
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
690
- await checkoutBranch(cwd, req.body.branch);
691
- return { ok: true };
692
- }));
693
- fastify.post("/git/branch/create", {
694
- schema: {
695
- description: "Create a local branch. `startPoint` (defaults to HEAD) accepts any ref " +
696
- "the user could pass to `git branch`. `checkout: true` creates and " +
697
- "switches in one step via `git checkout -b`.",
698
- tags: ["git"],
699
- body: {
700
- type: "object",
701
- required: ["projectId", "name"],
702
- additionalProperties: false,
703
- properties: {
704
- projectId: { type: "string", minLength: 1 },
705
- name: { type: "string", minLength: 1 },
706
- startPoint: { type: "string", minLength: 1 },
707
- checkout: { type: "boolean" },
708
- ...worktreeBodyProperty,
709
- },
710
- },
711
- response: {
712
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
713
- 400: errorSchema,
714
- 404: errorSchema,
715
- 500: errorSchema,
716
- },
717
- },
718
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
719
- const opts = {};
720
- if (req.body.startPoint !== undefined)
721
- opts.startPoint = req.body.startPoint;
722
- if (req.body.checkout !== undefined)
723
- opts.checkout = req.body.checkout;
724
- await createBranch(cwd, req.body.name, opts);
725
- return { ok: true };
726
- }));
727
- fastify.delete("/git/branch/:name", {
728
- schema: {
729
- description: "Delete a local branch via `git branch -d <name>`. `?force=1` switches " +
730
- "to `-D` for branches that haven't been merged. Refuses to delete the " +
731
- "currently-checked-out branch (git surfaces a `git_failed`).",
732
- tags: ["git"],
733
- params: {
734
- type: "object",
735
- required: ["name"],
736
- properties: { name: { type: "string", minLength: 1 } },
737
- },
738
- querystring: {
739
- type: "object",
740
- required: ["projectId"],
741
- properties: {
742
- projectId: { type: "string", minLength: 1 },
743
- force: { type: "string", enum: ["0", "1", "true", "false"] },
744
- worktreePath: { type: "string", minLength: 1 },
745
- },
746
- },
747
- response: {
748
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
749
- 400: errorSchema,
750
- 404: errorSchema,
751
- 500: errorSchema,
752
- },
753
- },
754
- }, async (req, reply) => {
755
- const force = req.query.force === "1" || req.query.force === "true";
756
- return withGitCwd(req.query.projectId, req.query.worktreePath, reply, async (cwd) => {
757
- await deleteBranch(cwd, req.params.name, { force });
758
- return { ok: true };
759
- });
760
- });
761
- fastify.post("/git/stage", {
762
- schema: {
763
- description: "Stage one or more files (`git add -- <paths>`).",
764
- tags: ["git"],
765
- body: {
766
- type: "object",
767
- required: ["projectId", "paths"],
768
- additionalProperties: false,
769
- properties: {
770
- projectId: { type: "string", minLength: 1 },
771
- paths: {
772
- type: "array",
773
- items: { type: "string", minLength: 1, maxLength: 4096 },
774
- minItems: 1,
775
- maxItems: 1000,
776
- },
777
- ...worktreeBodyProperty,
778
- },
779
- },
780
- response: {
781
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
782
- 400: errorSchema,
783
- 404: errorSchema,
784
- 500: errorSchema,
785
- },
786
- },
787
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
788
- await stagePaths(cwd, req.body.paths);
789
- return { ok: true };
790
- }));
791
- fastify.post("/git/unstage", {
792
- schema: {
793
- description: "Unstage one or more files (`git restore --staged -- <paths>`).",
794
- tags: ["git"],
795
- body: {
796
- type: "object",
797
- required: ["projectId", "paths"],
798
- additionalProperties: false,
799
- properties: {
800
- projectId: { type: "string", minLength: 1 },
801
- paths: {
802
- type: "array",
803
- items: { type: "string", minLength: 1, maxLength: 4096 },
804
- minItems: 1,
805
- maxItems: 1000,
806
- },
807
- ...worktreeBodyProperty,
808
- },
809
- },
810
- response: {
811
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
812
- 400: errorSchema,
813
- 404: errorSchema,
814
- 500: errorSchema,
815
- },
816
- },
817
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
818
- await unstagePaths(cwd, req.body.paths);
819
- return { ok: true };
820
- }));
821
- fastify.post("/git/apply-hunks", {
822
- schema: {
823
- description: "Stage or unstage selected hunks of a single file. Builds a " +
824
- "synthetic patch from the file's current diff containing only " +
825
- "the requested hunk indices, then runs " +
826
- "`git apply --cached --recount [--reverse]` against it. " +
827
- "Returns `{ ok: false, error }` for git-side failures (binary " +
828
- "file, no diff on the requested side, conflicting patch, etc.) " +
829
- "rather than 500 — these are user-visible events.",
830
- tags: ["git"],
831
- body: {
832
- type: "object",
833
- required: ["projectId", "path", "mode", "hunkIndices"],
834
- additionalProperties: false,
835
- properties: {
836
- projectId: { type: "string", minLength: 1 },
837
- path: { type: "string", minLength: 1, maxLength: 4096 },
838
- mode: { type: "string", enum: ["stage", "unstage"] },
839
- hunkIndices: {
840
- type: "array",
841
- items: { type: "integer", minimum: 0, maximum: 10_000 },
842
- minItems: 1,
843
- maxItems: 1_000,
844
- },
845
- ...worktreeBodyProperty,
846
- },
847
- },
848
- response: {
849
- 200: {
850
- type: "object",
851
- required: ["ok"],
852
- properties: {
853
- ok: { type: "boolean" },
854
- error: { type: "string" },
855
- totalHunks: { type: "integer", minimum: 0 },
856
- },
857
- },
858
- 400: errorSchema,
859
- 404: errorSchema,
860
- 500: errorSchema,
861
- },
862
- },
863
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
864
- try {
865
- const { totalHunks } = await applyHunks(cwd, req.body.path, req.body.hunkIndices, req.body.mode);
866
- return { ok: true, totalHunks };
867
- }
868
- catch (err) {
869
- if (err instanceof HunkStagingError) {
870
- return { ok: false, error: err.code, totalHunks: 0 };
871
- }
872
- throw err;
873
- }
874
- }));
875
- fastify.post("/git/revert", {
876
- schema: {
877
- description: "Discard local changes for the given files via `git restore " +
878
- "--staged --worktree --source=HEAD`. Restores both the index " +
879
- "and the working tree to HEAD — destructive, the caller is " +
880
- "expected to gate behind a confirmation. Untracked files " +
881
- "produce a 400 with git's stderr ('pathspec did not match'); " +
882
- "delete those via /files/delete instead.",
883
- tags: ["git"],
884
- body: {
885
- type: "object",
886
- required: ["projectId", "paths"],
887
- additionalProperties: false,
888
- properties: {
889
- projectId: { type: "string", minLength: 1 },
890
- paths: {
891
- type: "array",
892
- items: { type: "string", minLength: 1, maxLength: 4096 },
893
- minItems: 1,
894
- maxItems: 1000,
895
- },
896
- ...worktreeBodyProperty,
897
- },
898
- },
899
- response: {
900
- 200: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] },
901
- 400: errorSchema,
902
- 404: errorSchema,
903
- 500: errorSchema,
904
- },
905
- },
906
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
907
- await revertPaths(cwd, req.body.paths);
908
- return { ok: true };
909
- }));
910
- fastify.post("/git/commit", {
911
- schema: {
912
- description: "Commit the currently-staged changes. Pre-commit hooks fire as " +
913
- "normal — `--no-verify` is intentionally NOT used so browser " +
914
- "commits gate the same way terminal commits do.",
915
- tags: ["git"],
916
- body: {
917
- type: "object",
918
- required: ["projectId", "message"],
919
- additionalProperties: false,
920
- properties: {
921
- projectId: { type: "string", minLength: 1 },
922
- message: { type: "string", minLength: 1 },
923
- ...worktreeBodyProperty,
924
- },
925
- },
926
- response: {
927
- 200: {
928
- type: "object",
929
- required: ["hash"],
930
- properties: { hash: { type: "string" } },
931
- },
932
- 400: errorSchema,
933
- 404: errorSchema,
934
- 500: errorSchema,
935
- },
936
- },
937
- }, async (req, reply) => {
938
- const message = req.body.message.trim();
939
- if (message.length === 0) {
940
- return reply.code(400).send({ error: "empty_message" });
941
- }
942
- return withGitCwd(req.body.projectId, req.body.worktreePath, reply, (cwd) => commit(cwd, message));
943
- });
944
- fastify.post("/git/fetch", {
945
- schema: {
946
- description: "git fetch — never touches the working tree, safe regardless of " +
947
- "dirty state. `prune: true` adds --prune so deleted upstream " +
948
- "branches are removed locally. Returns the captured output.",
949
- tags: ["git"],
950
- body: {
951
- type: "object",
952
- required: ["projectId"],
953
- additionalProperties: false,
954
- properties: {
955
- projectId: { type: "string", minLength: 1 },
956
- remote: { type: "string", minLength: 1 },
957
- prune: { type: "boolean" },
958
- ...worktreeBodyProperty,
959
- },
960
- },
961
- response: {
962
- 200: { type: "object", required: ["output"], properties: { output: { type: "string" } } },
963
- 400: errorSchema,
964
- 404: errorSchema,
965
- 500: errorSchema,
966
- },
967
- },
968
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
969
- const opts = {};
970
- if (req.body.remote !== undefined)
971
- opts.remote = req.body.remote;
972
- if (req.body.prune !== undefined)
973
- opts.prune = req.body.prune;
974
- const { stdout } = await fetch(cwd, opts);
975
- return { output: stdout };
976
- }));
977
- fastify.post("/git/pull", {
978
- schema: {
979
- description: "git pull — fetches AND merges (or rebases with `rebase: true`). " +
980
- "Conflicts are surfaced verbatim in the 400 message; the user can " +
981
- "drop to the integrated terminal to resolve. No conflict-resolution " +
982
- "UI in v1.",
983
- tags: ["git"],
984
- body: {
985
- type: "object",
986
- required: ["projectId"],
987
- additionalProperties: false,
988
- properties: {
989
- projectId: { type: "string", minLength: 1 },
990
- remote: { type: "string", minLength: 1 },
991
- branch: { type: "string", minLength: 1 },
992
- rebase: { type: "boolean" },
993
- ...worktreeBodyProperty,
994
- },
995
- },
996
- response: {
997
- 200: { type: "object", required: ["output"], properties: { output: { type: "string" } } },
998
- 400: errorSchema,
999
- 404: errorSchema,
1000
- 500: errorSchema,
1001
- },
1002
- },
1003
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
1004
- const opts = {};
1005
- if (req.body.remote !== undefined)
1006
- opts.remote = req.body.remote;
1007
- if (req.body.branch !== undefined)
1008
- opts.branch = req.body.branch;
1009
- if (req.body.rebase !== undefined)
1010
- opts.rebase = req.body.rebase;
1011
- const { stdout } = await pull(cwd, opts);
1012
- return { output: stdout };
1013
- }));
1014
- fastify.post("/git/push", {
1015
- schema: {
1016
- description: "Push to a remote. With no `remote`/`branch` body fields, runs " +
1017
- "plain `git push` against the configured upstream. `setUpstream: " +
1018
- "true` adds `--set-upstream` so the remote/branch is recorded as " +
1019
- "the tracking ref (required on first push of a new local branch). " +
1020
- "Returns 400 with a sanitized git failure message. Auth failures use " +
1021
- "error=git_auth_required with guidance to configure credentials outside pi-kot.",
1022
- tags: ["git"],
1023
- body: {
1024
- type: "object",
1025
- required: ["projectId"],
1026
- additionalProperties: false,
1027
- properties: {
1028
- projectId: { type: "string", minLength: 1 },
1029
- remote: { type: "string", minLength: 1 },
1030
- branch: { type: "string", minLength: 1 },
1031
- setUpstream: { type: "boolean" },
1032
- ...worktreeBodyProperty,
1033
- },
1034
- },
1035
- response: {
1036
- 200: {
1037
- type: "object",
1038
- required: ["output"],
1039
- properties: { output: { type: "string" } },
1040
- },
1041
- 400: errorSchema,
1042
- 404: errorSchema,
1043
- 500: errorSchema,
1044
- },
1045
- },
1046
- }, async (req, reply) => withGitCwd(req.body.projectId, req.body.worktreePath, reply, async (cwd) => {
1047
- const opts = {};
1048
- if (req.body.remote !== undefined)
1049
- opts.remote = req.body.remote;
1050
- if (req.body.branch !== undefined)
1051
- opts.branch = req.body.branch;
1052
- if (req.body.setUpstream !== undefined)
1053
- opts.setUpstream = req.body.setUpstream;
1054
- const { stdout } = await push(cwd, opts);
1055
- return { output: stdout };
1056
- }));
1057
- };
1058
- //# sourceMappingURL=git.js.map