@jait/gateway 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/bin/jait.mjs +144 -0
  2. package/dist/config.d.ts +24 -0
  3. package/dist/config.d.ts.map +1 -0
  4. package/dist/config.js +73 -0
  5. package/dist/config.js.map +1 -0
  6. package/dist/db/connection.d.ts +37 -0
  7. package/dist/db/connection.d.ts.map +1 -0
  8. package/dist/db/connection.js +85 -0
  9. package/dist/db/connection.js.map +1 -0
  10. package/dist/db/index.d.ts +4 -0
  11. package/dist/db/index.d.ts.map +1 -0
  12. package/dist/db/index.js +4 -0
  13. package/dist/db/index.js.map +1 -0
  14. package/dist/db/migrations.d.ts +24 -0
  15. package/dist/db/migrations.d.ts.map +1 -0
  16. package/dist/db/migrations.js +312 -0
  17. package/dist/db/migrations.js.map +1 -0
  18. package/dist/db/schema.d.ts +2253 -0
  19. package/dist/db/schema.d.ts.map +1 -0
  20. package/dist/db/schema.js +195 -0
  21. package/dist/db/schema.js.map +1 -0
  22. package/dist/foundation.d.ts +26 -0
  23. package/dist/foundation.d.ts.map +1 -0
  24. package/dist/foundation.js +15 -0
  25. package/dist/foundation.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +413 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/uuidv7.d.ts +10 -0
  31. package/dist/lib/uuidv7.d.ts.map +1 -0
  32. package/dist/lib/uuidv7.js +33 -0
  33. package/dist/lib/uuidv7.js.map +1 -0
  34. package/dist/memory/contracts.d.ts +42 -0
  35. package/dist/memory/contracts.d.ts.map +1 -0
  36. package/dist/memory/contracts.js +2 -0
  37. package/dist/memory/contracts.js.map +1 -0
  38. package/dist/memory/embeddings.d.ts +4 -0
  39. package/dist/memory/embeddings.d.ts.map +1 -0
  40. package/dist/memory/embeddings.js +26 -0
  41. package/dist/memory/embeddings.js.map +1 -0
  42. package/dist/memory/service.d.ts +17 -0
  43. package/dist/memory/service.d.ts.map +1 -0
  44. package/dist/memory/service.js +82 -0
  45. package/dist/memory/service.js.map +1 -0
  46. package/dist/memory/sqlite-backend.d.ts +11 -0
  47. package/dist/memory/sqlite-backend.d.ts.map +1 -0
  48. package/dist/memory/sqlite-backend.js +68 -0
  49. package/dist/memory/sqlite-backend.js.map +1 -0
  50. package/dist/plugins/contracts.d.ts +11 -0
  51. package/dist/plugins/contracts.d.ts.map +1 -0
  52. package/dist/plugins/contracts.js +2 -0
  53. package/dist/plugins/contracts.js.map +1 -0
  54. package/dist/providers/claude-code-provider.d.ts +39 -0
  55. package/dist/providers/claude-code-provider.d.ts.map +1 -0
  56. package/dist/providers/claude-code-provider.js +322 -0
  57. package/dist/providers/claude-code-provider.js.map +1 -0
  58. package/dist/providers/codex-provider.d.ts +51 -0
  59. package/dist/providers/codex-provider.d.ts.map +1 -0
  60. package/dist/providers/codex-provider.js +826 -0
  61. package/dist/providers/codex-provider.js.map +1 -0
  62. package/dist/providers/contracts.d.ts +167 -0
  63. package/dist/providers/contracts.d.ts.map +1 -0
  64. package/dist/providers/contracts.js +13 -0
  65. package/dist/providers/contracts.js.map +1 -0
  66. package/dist/providers/index.d.ts +6 -0
  67. package/dist/providers/index.d.ts.map +1 -0
  68. package/dist/providers/index.js +5 -0
  69. package/dist/providers/index.js.map +1 -0
  70. package/dist/providers/jait-provider.d.ts +23 -0
  71. package/dist/providers/jait-provider.d.ts.map +1 -0
  72. package/dist/providers/jait-provider.js +67 -0
  73. package/dist/providers/jait-provider.js.map +1 -0
  74. package/dist/providers/registry.d.ts +39 -0
  75. package/dist/providers/registry.d.ts.map +1 -0
  76. package/dist/providers/registry.js +64 -0
  77. package/dist/providers/registry.js.map +1 -0
  78. package/dist/pty-broker-client.d.ts +46 -0
  79. package/dist/pty-broker-client.d.ts.map +1 -0
  80. package/dist/pty-broker-client.js +142 -0
  81. package/dist/pty-broker-client.js.map +1 -0
  82. package/dist/routes/auth.d.ts +6 -0
  83. package/dist/routes/auth.d.ts.map +1 -0
  84. package/dist/routes/auth.js +236 -0
  85. package/dist/routes/auth.js.map +1 -0
  86. package/dist/routes/chat.d.ts +32 -0
  87. package/dist/routes/chat.d.ts.map +1 -0
  88. package/dist/routes/chat.js +1503 -0
  89. package/dist/routes/chat.js.map +1 -0
  90. package/dist/routes/consent.d.ts +10 -0
  91. package/dist/routes/consent.d.ts.map +1 -0
  92. package/dist/routes/consent.js +127 -0
  93. package/dist/routes/consent.js.map +1 -0
  94. package/dist/routes/filesystem.d.ts +14 -0
  95. package/dist/routes/filesystem.d.ts.map +1 -0
  96. package/dist/routes/filesystem.js +152 -0
  97. package/dist/routes/filesystem.js.map +1 -0
  98. package/dist/routes/git.d.ts +17 -0
  99. package/dist/routes/git.d.ts.map +1 -0
  100. package/dist/routes/git.js +213 -0
  101. package/dist/routes/git.js.map +1 -0
  102. package/dist/routes/health.d.ts +7 -0
  103. package/dist/routes/health.d.ts.map +1 -0
  104. package/dist/routes/health.js +21 -0
  105. package/dist/routes/health.js.map +1 -0
  106. package/dist/routes/hooks.d.ts +9 -0
  107. package/dist/routes/hooks.d.ts.map +1 -0
  108. package/dist/routes/hooks.js +22 -0
  109. package/dist/routes/hooks.js.map +1 -0
  110. package/dist/routes/jobs.d.ts +5 -0
  111. package/dist/routes/jobs.d.ts.map +1 -0
  112. package/dist/routes/jobs.js +333 -0
  113. package/dist/routes/jobs.js.map +1 -0
  114. package/dist/routes/mcp-server.d.ts +23 -0
  115. package/dist/routes/mcp-server.d.ts.map +1 -0
  116. package/dist/routes/mcp-server.js +177 -0
  117. package/dist/routes/mcp-server.js.map +1 -0
  118. package/dist/routes/mobile.d.ts +12 -0
  119. package/dist/routes/mobile.d.ts.map +1 -0
  120. package/dist/routes/mobile.js +64 -0
  121. package/dist/routes/mobile.js.map +1 -0
  122. package/dist/routes/network.d.ts +3 -0
  123. package/dist/routes/network.d.ts.map +1 -0
  124. package/dist/routes/network.js +367 -0
  125. package/dist/routes/network.js.map +1 -0
  126. package/dist/routes/repositories.d.ts +18 -0
  127. package/dist/routes/repositories.d.ts.map +1 -0
  128. package/dist/routes/repositories.js +90 -0
  129. package/dist/routes/repositories.js.map +1 -0
  130. package/dist/routes/screen-share.d.ts +17 -0
  131. package/dist/routes/screen-share.d.ts.map +1 -0
  132. package/dist/routes/screen-share.js +92 -0
  133. package/dist/routes/screen-share.js.map +1 -0
  134. package/dist/routes/sessions.d.ts +18 -0
  135. package/dist/routes/sessions.d.ts.map +1 -0
  136. package/dist/routes/sessions.js +169 -0
  137. package/dist/routes/sessions.js.map +1 -0
  138. package/dist/routes/terminals.d.ts +15 -0
  139. package/dist/routes/terminals.d.ts.map +1 -0
  140. package/dist/routes/terminals.js +326 -0
  141. package/dist/routes/terminals.js.map +1 -0
  142. package/dist/routes/threads.d.ts +38 -0
  143. package/dist/routes/threads.d.ts.map +1 -0
  144. package/dist/routes/threads.js +488 -0
  145. package/dist/routes/threads.js.map +1 -0
  146. package/dist/routes/trust.d.ts +9 -0
  147. package/dist/routes/trust.d.ts.map +1 -0
  148. package/dist/routes/trust.js +25 -0
  149. package/dist/routes/trust.js.map +1 -0
  150. package/dist/routes/voice.d.ts +5 -0
  151. package/dist/routes/voice.d.ts.map +1 -0
  152. package/dist/routes/voice.js +37 -0
  153. package/dist/routes/voice.js.map +1 -0
  154. package/dist/routes/workspace.d.ts +13 -0
  155. package/dist/routes/workspace.d.ts.map +1 -0
  156. package/dist/routes/workspace.js +275 -0
  157. package/dist/routes/workspace.js.map +1 -0
  158. package/dist/scheduler/contracts.d.ts +15 -0
  159. package/dist/scheduler/contracts.d.ts.map +1 -0
  160. package/dist/scheduler/contracts.js +2 -0
  161. package/dist/scheduler/contracts.js.map +1 -0
  162. package/dist/scheduler/hooks.d.ts +20 -0
  163. package/dist/scheduler/hooks.d.ts.map +1 -0
  164. package/dist/scheduler/hooks.js +78 -0
  165. package/dist/scheduler/hooks.js.map +1 -0
  166. package/dist/scheduler/service.d.ts +65 -0
  167. package/dist/scheduler/service.d.ts.map +1 -0
  168. package/dist/scheduler/service.js +188 -0
  169. package/dist/scheduler/service.js.map +1 -0
  170. package/dist/security/consent-executor.d.ts +48 -0
  171. package/dist/security/consent-executor.d.ts.map +1 -0
  172. package/dist/security/consent-executor.js +158 -0
  173. package/dist/security/consent-executor.js.map +1 -0
  174. package/dist/security/consent-manager.d.ts +105 -0
  175. package/dist/security/consent-manager.d.ts.map +1 -0
  176. package/dist/security/consent-manager.js +227 -0
  177. package/dist/security/consent-manager.js.map +1 -0
  178. package/dist/security/contracts.d.ts +31 -0
  179. package/dist/security/contracts.d.ts.map +1 -0
  180. package/dist/security/contracts.js +2 -0
  181. package/dist/security/contracts.js.map +1 -0
  182. package/dist/security/http-auth.d.ts +10 -0
  183. package/dist/security/http-auth.d.ts.map +1 -0
  184. package/dist/security/http-auth.js +48 -0
  185. package/dist/security/http-auth.js.map +1 -0
  186. package/dist/security/index.d.ts +10 -0
  187. package/dist/security/index.d.ts.map +1 -0
  188. package/dist/security/index.js +9 -0
  189. package/dist/security/index.js.map +1 -0
  190. package/dist/security/path-guard.d.ts +40 -0
  191. package/dist/security/path-guard.d.ts.map +1 -0
  192. package/dist/security/path-guard.js +125 -0
  193. package/dist/security/path-guard.js.map +1 -0
  194. package/dist/security/sandbox-manager.d.ts +43 -0
  195. package/dist/security/sandbox-manager.d.ts.map +1 -0
  196. package/dist/security/sandbox-manager.js +110 -0
  197. package/dist/security/sandbox-manager.js.map +1 -0
  198. package/dist/security/ssrf-guard.d.ts +11 -0
  199. package/dist/security/ssrf-guard.d.ts.map +1 -0
  200. package/dist/security/ssrf-guard.js +59 -0
  201. package/dist/security/ssrf-guard.js.map +1 -0
  202. package/dist/security/tool-permissions.d.ts +61 -0
  203. package/dist/security/tool-permissions.d.ts.map +1 -0
  204. package/dist/security/tool-permissions.js +105 -0
  205. package/dist/security/tool-permissions.js.map +1 -0
  206. package/dist/security/tool-profiles.d.ts +23 -0
  207. package/dist/security/tool-profiles.d.ts.map +1 -0
  208. package/dist/security/tool-profiles.js +106 -0
  209. package/dist/security/tool-profiles.js.map +1 -0
  210. package/dist/security/trust-engine.d.ts +61 -0
  211. package/dist/security/trust-engine.d.ts.map +1 -0
  212. package/dist/security/trust-engine.js +192 -0
  213. package/dist/security/trust-engine.js.map +1 -0
  214. package/dist/server.d.ts +54 -0
  215. package/dist/server.d.ts.map +1 -0
  216. package/dist/server.js +188 -0
  217. package/dist/server.js.map +1 -0
  218. package/dist/services/audit.d.ts +60 -0
  219. package/dist/services/audit.d.ts.map +1 -0
  220. package/dist/services/audit.js +58 -0
  221. package/dist/services/audit.js.map +1 -0
  222. package/dist/services/device-registry.d.ts +15 -0
  223. package/dist/services/device-registry.d.ts.map +1 -0
  224. package/dist/services/device-registry.js +32 -0
  225. package/dist/services/device-registry.js.map +1 -0
  226. package/dist/services/git.d.ts +168 -0
  227. package/dist/services/git.d.ts.map +1 -0
  228. package/dist/services/git.js +957 -0
  229. package/dist/services/git.js.map +1 -0
  230. package/dist/services/repositories.d.ts +32 -0
  231. package/dist/services/repositories.d.ts.map +1 -0
  232. package/dist/services/repositories.js +70 -0
  233. package/dist/services/repositories.js.map +1 -0
  234. package/dist/services/session-state.d.ts +20 -0
  235. package/dist/services/session-state.d.ts.map +1 -0
  236. package/dist/services/session-state.js +89 -0
  237. package/dist/services/session-state.js.map +1 -0
  238. package/dist/services/sessions.d.ts +68 -0
  239. package/dist/services/sessions.d.ts.map +1 -0
  240. package/dist/services/sessions.js +136 -0
  241. package/dist/services/sessions.js.map +1 -0
  242. package/dist/services/thread-title.d.ts +23 -0
  243. package/dist/services/thread-title.d.ts.map +1 -0
  244. package/dist/services/thread-title.js +141 -0
  245. package/dist/services/thread-title.js.map +1 -0
  246. package/dist/services/threads.d.ts +64 -0
  247. package/dist/services/threads.d.ts.map +1 -0
  248. package/dist/services/threads.js +202 -0
  249. package/dist/services/threads.js.map +1 -0
  250. package/dist/services/users.d.ts +39 -0
  251. package/dist/services/users.d.ts.map +1 -0
  252. package/dist/services/users.js +203 -0
  253. package/dist/services/users.js.map +1 -0
  254. package/dist/sessions/contracts.d.ts +14 -0
  255. package/dist/sessions/contracts.d.ts.map +1 -0
  256. package/dist/sessions/contracts.js +2 -0
  257. package/dist/sessions/contracts.js.map +1 -0
  258. package/dist/surfaces/browser.d.ts +65 -0
  259. package/dist/surfaces/browser.d.ts.map +1 -0
  260. package/dist/surfaces/browser.js +615 -0
  261. package/dist/surfaces/browser.js.map +1 -0
  262. package/dist/surfaces/contracts.d.ts +34 -0
  263. package/dist/surfaces/contracts.d.ts.map +1 -0
  264. package/dist/surfaces/contracts.js +2 -0
  265. package/dist/surfaces/contracts.js.map +1 -0
  266. package/dist/surfaces/filesystem.d.ts +76 -0
  267. package/dist/surfaces/filesystem.d.ts.map +1 -0
  268. package/dist/surfaces/filesystem.js +245 -0
  269. package/dist/surfaces/filesystem.js.map +1 -0
  270. package/dist/surfaces/index.d.ts +6 -0
  271. package/dist/surfaces/index.d.ts.map +1 -0
  272. package/dist/surfaces/index.js +5 -0
  273. package/dist/surfaces/index.js.map +1 -0
  274. package/dist/surfaces/registry.d.ts +24 -0
  275. package/dist/surfaces/registry.d.ts.map +1 -0
  276. package/dist/surfaces/registry.js +59 -0
  277. package/dist/surfaces/registry.js.map +1 -0
  278. package/dist/surfaces/terminal.d.ts +76 -0
  279. package/dist/surfaces/terminal.d.ts.map +1 -0
  280. package/dist/surfaces/terminal.js +271 -0
  281. package/dist/surfaces/terminal.js.map +1 -0
  282. package/dist/tools/agent-loop.d.ts +302 -0
  283. package/dist/tools/agent-loop.d.ts.map +1 -0
  284. package/dist/tools/agent-loop.js +918 -0
  285. package/dist/tools/agent-loop.js.map +1 -0
  286. package/dist/tools/agent-tools.d.ts +39 -0
  287. package/dist/tools/agent-tools.d.ts.map +1 -0
  288. package/dist/tools/agent-tools.js +263 -0
  289. package/dist/tools/agent-tools.js.map +1 -0
  290. package/dist/tools/browser-tools.d.ts +38 -0
  291. package/dist/tools/browser-tools.d.ts.map +1 -0
  292. package/dist/tools/browser-tools.js +725 -0
  293. package/dist/tools/browser-tools.js.map +1 -0
  294. package/dist/tools/chat-modes.d.ts +75 -0
  295. package/dist/tools/chat-modes.d.ts.map +1 -0
  296. package/dist/tools/chat-modes.js +228 -0
  297. package/dist/tools/chat-modes.js.map +1 -0
  298. package/dist/tools/contracts.d.ts +69 -0
  299. package/dist/tools/contracts.d.ts.map +1 -0
  300. package/dist/tools/contracts.js +2 -0
  301. package/dist/tools/contracts.js.map +1 -0
  302. package/dist/tools/core/agent.d.ts +31 -0
  303. package/dist/tools/core/agent.d.ts.map +1 -0
  304. package/dist/tools/core/agent.js +65 -0
  305. package/dist/tools/core/agent.js.map +1 -0
  306. package/dist/tools/core/edit.d.ts +30 -0
  307. package/dist/tools/core/edit.d.ts.map +1 -0
  308. package/dist/tools/core/edit.js +109 -0
  309. package/dist/tools/core/edit.js.map +1 -0
  310. package/dist/tools/core/execute.d.ts +36 -0
  311. package/dist/tools/core/execute.d.ts.map +1 -0
  312. package/dist/tools/core/execute.js +81 -0
  313. package/dist/tools/core/execute.js.map +1 -0
  314. package/dist/tools/core/get-fs.d.ts +32 -0
  315. package/dist/tools/core/get-fs.d.ts.map +1 -0
  316. package/dist/tools/core/get-fs.js +143 -0
  317. package/dist/tools/core/get-fs.js.map +1 -0
  318. package/dist/tools/core/index.d.ts +26 -0
  319. package/dist/tools/core/index.d.ts.map +1 -0
  320. package/dist/tools/core/index.js +26 -0
  321. package/dist/tools/core/index.js.map +1 -0
  322. package/dist/tools/core/jait.d.ts +60 -0
  323. package/dist/tools/core/jait.d.ts.map +1 -0
  324. package/dist/tools/core/jait.js +256 -0
  325. package/dist/tools/core/jait.js.map +1 -0
  326. package/dist/tools/core/read.d.ts +26 -0
  327. package/dist/tools/core/read.d.ts.map +1 -0
  328. package/dist/tools/core/read.js +118 -0
  329. package/dist/tools/core/read.js.map +1 -0
  330. package/dist/tools/core/search.d.ts +34 -0
  331. package/dist/tools/core/search.d.ts.map +1 -0
  332. package/dist/tools/core/search.js +187 -0
  333. package/dist/tools/core/search.js.map +1 -0
  334. package/dist/tools/core/todo.d.ts +38 -0
  335. package/dist/tools/core/todo.d.ts.map +1 -0
  336. package/dist/tools/core/todo.js +116 -0
  337. package/dist/tools/core/todo.js.map +1 -0
  338. package/dist/tools/core/web.d.ts +34 -0
  339. package/dist/tools/core/web.d.ts.map +1 -0
  340. package/dist/tools/core/web.js +120 -0
  341. package/dist/tools/core/web.js.map +1 -0
  342. package/dist/tools/cron-tools.d.ts +7 -0
  343. package/dist/tools/cron-tools.d.ts.map +1 -0
  344. package/dist/tools/cron-tools.js +116 -0
  345. package/dist/tools/cron-tools.js.map +1 -0
  346. package/dist/tools/file-tools.d.ts +32 -0
  347. package/dist/tools/file-tools.d.ts.map +1 -0
  348. package/dist/tools/file-tools.js +178 -0
  349. package/dist/tools/file-tools.js.map +1 -0
  350. package/dist/tools/gateway-tools.d.ts +15 -0
  351. package/dist/tools/gateway-tools.d.ts.map +1 -0
  352. package/dist/tools/gateway-tools.js +39 -0
  353. package/dist/tools/gateway-tools.js.map +1 -0
  354. package/dist/tools/index.d.ts +57 -0
  355. package/dist/tools/index.d.ts.map +1 -0
  356. package/dist/tools/index.js +170 -0
  357. package/dist/tools/index.js.map +1 -0
  358. package/dist/tools/mcp-bridge.d.ts +111 -0
  359. package/dist/tools/mcp-bridge.d.ts.map +1 -0
  360. package/dist/tools/mcp-bridge.js +166 -0
  361. package/dist/tools/mcp-bridge.js.map +1 -0
  362. package/dist/tools/memory-tools.d.ts +19 -0
  363. package/dist/tools/memory-tools.d.ts.map +1 -0
  364. package/dist/tools/memory-tools.js +78 -0
  365. package/dist/tools/memory-tools.js.map +1 -0
  366. package/dist/tools/meta-tools.d.ts +25 -0
  367. package/dist/tools/meta-tools.d.ts.map +1 -0
  368. package/dist/tools/meta-tools.js +125 -0
  369. package/dist/tools/meta-tools.js.map +1 -0
  370. package/dist/tools/network-tools.d.ts +21 -0
  371. package/dist/tools/network-tools.d.ts.map +1 -0
  372. package/dist/tools/network-tools.js +189 -0
  373. package/dist/tools/network-tools.js.map +1 -0
  374. package/dist/tools/os-tools.d.ts +18 -0
  375. package/dist/tools/os-tools.d.ts.map +1 -0
  376. package/dist/tools/os-tools.js +210 -0
  377. package/dist/tools/os-tools.js.map +1 -0
  378. package/dist/tools/prompts/claude-prompt.d.ts +8 -0
  379. package/dist/tools/prompts/claude-prompt.d.ts.map +1 -0
  380. package/dist/tools/prompts/claude-prompt.js +228 -0
  381. package/dist/tools/prompts/claude-prompt.js.map +1 -0
  382. package/dist/tools/prompts/default-openai-prompt.d.ts +8 -0
  383. package/dist/tools/prompts/default-openai-prompt.d.ts.map +1 -0
  384. package/dist/tools/prompts/default-openai-prompt.js +67 -0
  385. package/dist/tools/prompts/default-openai-prompt.js.map +1 -0
  386. package/dist/tools/prompts/default-prompt.d.ts +7 -0
  387. package/dist/tools/prompts/default-prompt.d.ts.map +1 -0
  388. package/dist/tools/prompts/default-prompt.js +50 -0
  389. package/dist/tools/prompts/default-prompt.js.map +1 -0
  390. package/dist/tools/prompts/gemini-prompt.d.ts +8 -0
  391. package/dist/tools/prompts/gemini-prompt.d.ts.map +1 -0
  392. package/dist/tools/prompts/gemini-prompt.js +118 -0
  393. package/dist/tools/prompts/gemini-prompt.js.map +1 -0
  394. package/dist/tools/prompts/gpt5-codex-prompt.d.ts +8 -0
  395. package/dist/tools/prompts/gpt5-codex-prompt.d.ts.map +1 -0
  396. package/dist/tools/prompts/gpt5-codex-prompt.js +72 -0
  397. package/dist/tools/prompts/gpt5-codex-prompt.js.map +1 -0
  398. package/dist/tools/prompts/gpt5-prompt.d.ts +8 -0
  399. package/dist/tools/prompts/gpt5-prompt.d.ts.map +1 -0
  400. package/dist/tools/prompts/gpt5-prompt.js +177 -0
  401. package/dist/tools/prompts/gpt5-prompt.js.map +1 -0
  402. package/dist/tools/prompts/gpt51-prompt.d.ts +8 -0
  403. package/dist/tools/prompts/gpt51-prompt.d.ts.map +1 -0
  404. package/dist/tools/prompts/gpt51-prompt.js +178 -0
  405. package/dist/tools/prompts/gpt51-prompt.js.map +1 -0
  406. package/dist/tools/prompts/gpt52-prompt.d.ts +8 -0
  407. package/dist/tools/prompts/gpt52-prompt.d.ts.map +1 -0
  408. package/dist/tools/prompts/gpt52-prompt.js +198 -0
  409. package/dist/tools/prompts/gpt52-prompt.js.map +1 -0
  410. package/dist/tools/prompts/index.d.ts +22 -0
  411. package/dist/tools/prompts/index.d.ts.map +1 -0
  412. package/dist/tools/prompts/index.js +23 -0
  413. package/dist/tools/prompts/index.js.map +1 -0
  414. package/dist/tools/prompts/prompt-registry.d.ts +44 -0
  415. package/dist/tools/prompts/prompt-registry.d.ts.map +1 -0
  416. package/dist/tools/prompts/prompt-registry.js +60 -0
  417. package/dist/tools/prompts/prompt-registry.js.map +1 -0
  418. package/dist/tools/prompts/shared-sections.d.ts +28 -0
  419. package/dist/tools/prompts/shared-sections.d.ts.map +1 -0
  420. package/dist/tools/prompts/shared-sections.js +111 -0
  421. package/dist/tools/prompts/shared-sections.js.map +1 -0
  422. package/dist/tools/prompts/xai-prompt.d.ts +8 -0
  423. package/dist/tools/prompts/xai-prompt.d.ts.map +1 -0
  424. package/dist/tools/prompts/xai-prompt.js +68 -0
  425. package/dist/tools/prompts/xai-prompt.js.map +1 -0
  426. package/dist/tools/redeploy-tools.d.ts +30 -0
  427. package/dist/tools/redeploy-tools.d.ts.map +1 -0
  428. package/dist/tools/redeploy-tools.js +191 -0
  429. package/dist/tools/redeploy-tools.js.map +1 -0
  430. package/dist/tools/registry.d.ts +51 -0
  431. package/dist/tools/registry.d.ts.map +1 -0
  432. package/dist/tools/registry.js +148 -0
  433. package/dist/tools/registry.js.map +1 -0
  434. package/dist/tools/screen-share-tools.d.ts +31 -0
  435. package/dist/tools/screen-share-tools.d.ts.map +1 -0
  436. package/dist/tools/screen-share-tools.js +183 -0
  437. package/dist/tools/screen-share-tools.js.map +1 -0
  438. package/dist/tools/surface-tools.d.ts +23 -0
  439. package/dist/tools/surface-tools.d.ts.map +1 -0
  440. package/dist/tools/surface-tools.js +99 -0
  441. package/dist/tools/surface-tools.js.map +1 -0
  442. package/dist/tools/terminal-tools.d.ts +37 -0
  443. package/dist/tools/terminal-tools.d.ts.map +1 -0
  444. package/dist/tools/terminal-tools.js +448 -0
  445. package/dist/tools/terminal-tools.js.map +1 -0
  446. package/dist/tools/thread-tools.d.ts +61 -0
  447. package/dist/tools/thread-tools.d.ts.map +1 -0
  448. package/dist/tools/thread-tools.js +484 -0
  449. package/dist/tools/thread-tools.js.map +1 -0
  450. package/dist/tools/token-estimator.d.ts +55 -0
  451. package/dist/tools/token-estimator.d.ts.map +1 -0
  452. package/dist/tools/token-estimator.js +82 -0
  453. package/dist/tools/token-estimator.js.map +1 -0
  454. package/dist/tools/tool-names.d.ts +64 -0
  455. package/dist/tools/tool-names.d.ts.map +1 -0
  456. package/dist/tools/tool-names.js +76 -0
  457. package/dist/tools/tool-names.js.map +1 -0
  458. package/dist/tools/validate.d.ts +27 -0
  459. package/dist/tools/validate.d.ts.map +1 -0
  460. package/dist/tools/validate.js +99 -0
  461. package/dist/tools/validate.js.map +1 -0
  462. package/dist/tools/voice-tools.d.ts +8 -0
  463. package/dist/tools/voice-tools.d.ts.map +1 -0
  464. package/dist/tools/voice-tools.js +32 -0
  465. package/dist/tools/voice-tools.js.map +1 -0
  466. package/dist/voice/service.d.ts +42 -0
  467. package/dist/voice/service.d.ts.map +1 -0
  468. package/dist/voice/service.js +75 -0
  469. package/dist/voice/service.js.map +1 -0
  470. package/dist/ws.d.ts +90 -0
  471. package/dist/ws.d.ts.map +1 -0
  472. package/dist/ws.js +562 -0
  473. package/dist/ws.js.map +1 -0
  474. package/package.json +61 -0
@@ -0,0 +1,957 @@
1
+ /**
2
+ * Server-side git operations service.
3
+ *
4
+ * Executes git and `gh` CLI commands in the requested working directory.
5
+ * Adapted from the t3code GitService/GitManager pattern but running
6
+ * directly through child_process on the gateway.
7
+ */
8
+ import { exec as execCb } from "node:child_process";
9
+ import { readFile, writeFile, unlink, mkdir, rm } from "node:fs/promises";
10
+ import { existsSync } from "node:fs";
11
+ import { basename, join } from "node:path";
12
+ import { homedir, tmpdir } from "node:os";
13
+ import { promisify } from "node:util";
14
+ const exec = promisify(execCb);
15
+ const DEFAULT_TIMEOUT = 30_000;
16
+ // ── Helpers ────────────────────────────────────────────────────────
17
+ async function gitExec(cwd, args, timeout = DEFAULT_TIMEOUT) {
18
+ const { stdout } = await exec(`git ${args}`, { cwd, timeout });
19
+ return stdout.trim();
20
+ }
21
+ async function ghExec(cwd, args, timeout = DEFAULT_TIMEOUT) {
22
+ const { stdout } = await exec(`gh ${args}`, { cwd, timeout });
23
+ return stdout.trim();
24
+ }
25
+ async function ghAvailable(cwd) {
26
+ try {
27
+ await exec("gh --version", { cwd, timeout: 5_000 });
28
+ return true;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ function parseGithubRemote(raw) {
35
+ if (!raw)
36
+ return null;
37
+ // Normalise to https URL
38
+ let url = raw.replace(/\.git$/, "");
39
+ url = url.replace(/^git@([^:]+):(.+)$/, "https://$1/$2");
40
+ url = url.replace(/^ssh:\/\/git@([^/]+)\/(.+)$/, "https://$1/$2");
41
+ try {
42
+ const u = new URL(url);
43
+ const parts = u.pathname.replace(/^\/+|\/+$/g, "").split("/");
44
+ if (!u.hostname.includes("github") || parts.length < 2)
45
+ return null;
46
+ const repo = parts.pop();
47
+ const owner = parts.pop();
48
+ return { host: u.hostname, owner, repo };
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ function resolveGithubToken(explicit) {
55
+ return explicit ?? process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN ?? process.env.GITHUB_PAT ?? null;
56
+ }
57
+ /** Cache for git credential manager token (avoids shelling out every request). */
58
+ let _gitCredentialToken;
59
+ let _gitCredentialExpiry = 0;
60
+ /**
61
+ * Attempt to extract a GitHub token from git's credential manager.
62
+ * Returns null if git credential fill fails or isn't configured.
63
+ * Caches the result for 5 minutes to avoid repeated subprocess calls.
64
+ */
65
+ async function resolveGitCredentialToken() {
66
+ if (_gitCredentialToken !== undefined && Date.now() < _gitCredentialExpiry) {
67
+ return _gitCredentialToken;
68
+ }
69
+ try {
70
+ const { spawn } = await import("node:child_process");
71
+ const token = await new Promise((resolve) => {
72
+ const proc = spawn("git", ["credential", "fill"], { timeout: 5_000 });
73
+ let out = "";
74
+ proc.stdout.on("data", (d) => { out += d.toString(); });
75
+ proc.on("close", () => {
76
+ const match = out.match(/^password=(.+)$/m);
77
+ resolve(match?.[1]?.trim() ?? null);
78
+ });
79
+ proc.on("error", () => resolve(null));
80
+ proc.stdin.write("protocol=https\nhost=github.com\n\n");
81
+ proc.stdin.end();
82
+ });
83
+ _gitCredentialToken = token;
84
+ }
85
+ catch {
86
+ _gitCredentialToken = null;
87
+ }
88
+ _gitCredentialExpiry = Date.now() + 5 * 60 * 1000;
89
+ return _gitCredentialToken;
90
+ }
91
+ /**
92
+ * Resolve a usable GitHub token — explicit > env > git credential manager.
93
+ */
94
+ async function resolveGithubTokenWithFallback(explicit) {
95
+ const quick = resolveGithubToken(explicit);
96
+ if (quick)
97
+ return quick;
98
+ return resolveGitCredentialToken();
99
+ }
100
+ // ── Service ────────────────────────────────────────────────────────
101
+ export class GitService {
102
+ async isRepo(cwd) {
103
+ try {
104
+ await gitExec(cwd, "rev-parse --is-inside-work-tree");
105
+ return true;
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ }
111
+ async init(cwd) {
112
+ await gitExec(cwd, "init");
113
+ }
114
+ async status(cwd, _branch, githubToken) {
115
+ const effectiveToken = await resolveGithubTokenWithFallback(githubToken);
116
+ const isGit = await this.isRepo(cwd);
117
+ if (!isGit) {
118
+ return {
119
+ branch: null,
120
+ hasWorkingTreeChanges: false,
121
+ workingTree: { files: [], insertions: 0, deletions: 0 },
122
+ hasUpstream: false,
123
+ aheadCount: 0,
124
+ behindCount: 0,
125
+ pr: null,
126
+ ghAvailable: false,
127
+ };
128
+ }
129
+ // Branch
130
+ let branch = null;
131
+ try {
132
+ branch = await gitExec(cwd, "rev-parse --abbrev-ref HEAD");
133
+ if (branch === "HEAD")
134
+ branch = null;
135
+ }
136
+ catch { /* detached HEAD */ }
137
+ // Status summary
138
+ const porcelain = await gitExec(cwd, "status --porcelain").catch(() => "");
139
+ const hasChanges = porcelain.length > 0;
140
+ // Diff stats for changed files
141
+ const files = [];
142
+ let totalInsertions = 0;
143
+ let totalDeletions = 0;
144
+ if (hasChanges) {
145
+ try {
146
+ const diffStat = await gitExec(cwd, "diff --numstat HEAD").catch(() => gitExec(cwd, "diff --numstat"));
147
+ for (const line of diffStat.split("\n").filter(Boolean)) {
148
+ const [ins, del, filePath] = line.split("\t");
149
+ const insertions = ins === "-" ? 0 : parseInt(ins ?? "0", 10);
150
+ const deletions = del === "-" ? 0 : parseInt(del ?? "0", 10);
151
+ if (filePath) {
152
+ files.push({ path: filePath, insertions, deletions });
153
+ totalInsertions += insertions;
154
+ totalDeletions += deletions;
155
+ }
156
+ }
157
+ // Also count untracked files
158
+ const untracked = porcelain.split("\n").filter((l) => l.startsWith("??"));
159
+ for (const line of untracked) {
160
+ const filePath = line.slice(3).trim();
161
+ if (filePath && !files.some((f) => f.path === filePath)) {
162
+ files.push({ path: filePath, insertions: 0, deletions: 0 });
163
+ }
164
+ }
165
+ }
166
+ catch { /* ignore diff failures */ }
167
+ }
168
+ // Upstream tracking
169
+ let hasUpstream = false;
170
+ let aheadCount = 0;
171
+ let behindCount = 0;
172
+ if (branch) {
173
+ try {
174
+ const upstream = await gitExec(cwd, `rev-parse --abbrev-ref ${branch}@{upstream}`);
175
+ hasUpstream = !!upstream;
176
+ const counts = await gitExec(cwd, `rev-list --left-right --count ${branch}...${branch}@{upstream}`);
177
+ const [ahead, behind] = counts.split("\t").map(Number);
178
+ aheadCount = ahead ?? 0;
179
+ behindCount = behind ?? 0;
180
+ }
181
+ catch { /* no upstream */ }
182
+ }
183
+ // PR status (via gh cli)
184
+ let pr = null;
185
+ let ghIsAvailable = false;
186
+ if (branch) {
187
+ try {
188
+ const hasGh = await ghAvailable(cwd);
189
+ ghIsAvailable = hasGh;
190
+ if (hasGh) {
191
+ const json = await ghExec(cwd, `pr view --head "${branch}" --json number,title,url,state,baseRefName,headRefName`);
192
+ if (json) {
193
+ const parsed = JSON.parse(json);
194
+ if (parsed.number) {
195
+ const state = String(parsed.state ?? "OPEN").toUpperCase();
196
+ pr = {
197
+ number: Number(parsed.number),
198
+ title: String(parsed.title ?? ""),
199
+ url: String(parsed.url ?? ""),
200
+ baseBranch: String(parsed.baseRefName ?? ""),
201
+ headBranch: String(parsed.headRefName ?? ""),
202
+ state: state === "MERGED" ? "merged" : state === "CLOSED" ? "closed" : "open",
203
+ };
204
+ }
205
+ }
206
+ }
207
+ else {
208
+ if (effectiveToken) {
209
+ const remoteName = await this.getPreferredRemote(cwd, branch);
210
+ const remoteUrl = await this.getRemoteUrl(cwd, remoteName ?? "");
211
+ const githubRemote = parseGithubRemote(remoteUrl);
212
+ if (githubRemote) {
213
+ const apiPr = await this.fetchGithubPrByHead(githubRemote, effectiveToken, branch);
214
+ if (apiPr)
215
+ pr = apiPr;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ catch { /* gh not available or no PR */ }
221
+ }
222
+ return {
223
+ branch,
224
+ hasWorkingTreeChanges: hasChanges,
225
+ workingTree: { files, insertions: totalInsertions, deletions: totalDeletions },
226
+ hasUpstream,
227
+ aheadCount,
228
+ behindCount,
229
+ pr,
230
+ ghAvailable: ghIsAvailable,
231
+ };
232
+ }
233
+ async listBranches(cwd) {
234
+ const isGit = await this.isRepo(cwd);
235
+ if (!isGit)
236
+ return { branches: [], isRepo: false };
237
+ try {
238
+ const raw = await gitExec(cwd, "branch -a --format='%(HEAD) %(refname:short) %(upstream:short) %(worktreepath)'");
239
+ const branches = [];
240
+ const remotes = await this.listRemotes(cwd);
241
+ const remoteSet = new Set(remotes);
242
+ const preferredRemote = await this.getPreferredRemote(cwd);
243
+ const defaultBranch = await gitExec(cwd, `symbolic-ref refs/remotes/${preferredRemote ?? "origin"}/HEAD`)
244
+ .then((r) => r.replace(`refs/remotes/${preferredRemote ?? "origin"}/`, "").trim())
245
+ .catch(() => "main");
246
+ for (const line of raw.split("\n").filter(Boolean)) {
247
+ const clean = line.replace(/^'|'$/g, "").trim();
248
+ const current = clean.startsWith("*");
249
+ const parts = clean.replace(/^\*?\s*/, "").split(/\s+/);
250
+ const name = parts[0] ?? "";
251
+ const slashIndex = name.indexOf("/");
252
+ const remoteName = slashIndex > 0 ? name.slice(0, slashIndex) : "";
253
+ const isRemote = !!remoteName && remoteSet.has(remoteName);
254
+ const branchName = isRemote ? name.slice(slashIndex + 1) : name;
255
+ const worktreePath = parts.length > 2 ? parts.slice(2).join(" ") || null : null;
256
+ if (!name || (isRemote && branchName === "HEAD"))
257
+ continue;
258
+ branches.push({
259
+ name: branchName,
260
+ isRemote,
261
+ current,
262
+ isDefault: branchName === defaultBranch ||
263
+ (isRemote && remoteName === preferredRemote && name === `${preferredRemote}/${defaultBranch}`),
264
+ worktreePath,
265
+ });
266
+ }
267
+ return { branches, isRepo: true };
268
+ }
269
+ catch {
270
+ return { branches: [], isRepo: true };
271
+ }
272
+ }
273
+ async pull(cwd) {
274
+ const branch = await gitExec(cwd, "rev-parse --abbrev-ref HEAD");
275
+ let upstream = null;
276
+ try {
277
+ upstream = await gitExec(cwd, `rev-parse --abbrev-ref ${branch}@{upstream}`);
278
+ }
279
+ catch { /* no upstream */ }
280
+ const before = await gitExec(cwd, "rev-parse HEAD");
281
+ await gitExec(cwd, "pull --rebase");
282
+ const after = await gitExec(cwd, "rev-parse HEAD");
283
+ return {
284
+ status: before === after ? "skipped_up_to_date" : "pulled",
285
+ branch,
286
+ upstreamBranch: upstream,
287
+ };
288
+ }
289
+ async runStackedAction(cwd, action, commitMessage, featureBranch, baseBranch, githubToken) {
290
+ const effectiveToken = await resolveGithubTokenWithFallback(githubToken);
291
+ const result = {
292
+ commit: { status: "skipped_no_changes" },
293
+ push: { status: "skipped_not_requested" },
294
+ branch: { status: "skipped_not_requested" },
295
+ pr: { status: "skipped_not_requested" },
296
+ };
297
+ // Optionally create a feature branch
298
+ if (featureBranch) {
299
+ const timestamp = Date.now().toString(36);
300
+ const branchName = `feature/auto-${timestamp}`;
301
+ await gitExec(cwd, `checkout -b "${branchName}"`);
302
+ result.branch = { status: "created", name: branchName };
303
+ }
304
+ const currentBranch = await gitExec(cwd, "rev-parse --abbrev-ref HEAD").catch(() => null);
305
+ // Commit step
306
+ const porcelain = await gitExec(cwd, "status --porcelain").catch(() => "");
307
+ if (porcelain.length > 0) {
308
+ await gitExec(cwd, "add -A");
309
+ let msg = commitMessage?.trim();
310
+ if (!msg) {
311
+ // Auto-generate a commit message from the diff summary
312
+ try {
313
+ const diffSummary = await gitExec(cwd, "diff --cached --stat");
314
+ msg = `chore: auto-commit ${diffSummary.split("\n").length} file(s) changed`;
315
+ }
316
+ catch {
317
+ msg = "chore: auto-commit changes";
318
+ }
319
+ }
320
+ await gitExec(cwd, `commit -m "${msg.replace(/"/g, '\\"')}"`);
321
+ const sha = await gitExec(cwd, "rev-parse HEAD");
322
+ result.commit = { status: "created", commitSha: sha, subject: msg };
323
+ }
324
+ // Push step
325
+ if (action === "commit_push" || action === "commit_push_pr") {
326
+ if (currentBranch) {
327
+ let hasUpstream = false;
328
+ let upstreamBranch;
329
+ try {
330
+ upstreamBranch = await gitExec(cwd, `rev-parse --abbrev-ref ${currentBranch}@{upstream}`);
331
+ hasUpstream = true;
332
+ }
333
+ catch { /* no upstream */ }
334
+ if (hasUpstream) {
335
+ try {
336
+ await gitExec(cwd, "push");
337
+ result.push = { status: "pushed", branch: currentBranch, upstreamBranch };
338
+ }
339
+ catch {
340
+ // Already up-to-date or push failed — still proceed to PR
341
+ result.push = { status: "pushed", branch: currentBranch, upstreamBranch };
342
+ }
343
+ }
344
+ else {
345
+ const remoteName = await this.getPreferredRemote(cwd, currentBranch);
346
+ if (!remoteName) {
347
+ result.push = { status: "skipped_no_remote", branch: currentBranch };
348
+ // Don't return early — still try PR via gh CLI which may work
349
+ }
350
+ else {
351
+ await gitExec(cwd, `push --set-upstream "${remoteName}" "${currentBranch}"`);
352
+ result.push = {
353
+ status: "pushed",
354
+ branch: currentBranch,
355
+ upstreamBranch: `${remoteName}/${currentBranch}`,
356
+ setUpstream: true,
357
+ };
358
+ }
359
+ }
360
+ }
361
+ // Attach a "create PR" URL so the frontend can link to it
362
+ if (result.push.status === "pushed" && currentBranch) {
363
+ const upstreamRemote = result.push.upstreamBranch?.split("/")[0];
364
+ result.push.createPrUrl = await this.buildCreatePrUrl(cwd, currentBranch, upstreamRemote);
365
+ }
366
+ }
367
+ // PR creation step — try even if push was skipped (gh CLI can push internally)
368
+ if (action === "commit_push_pr" && currentBranch) {
369
+ try {
370
+ const hasGh = await ghAvailable(cwd);
371
+ const preferredRemote = await this.getPreferredRemote(cwd, currentBranch);
372
+ const remoteUrl = await this.getRemoteUrl(cwd, preferredRemote ?? "");
373
+ const githubRemote = parseGithubRemote(remoteUrl);
374
+ if (!hasGh && !effectiveToken) {
375
+ const manualUrl = result.push.createPrUrl ?? (preferredRemote ? await this.buildCreatePrUrl(cwd, currentBranch, preferredRemote) : undefined);
376
+ const hint = manualUrl ? ` Open ${manualUrl} to create the PR manually.` : "";
377
+ throw new Error(`Cannot create pull request automatically because GitHub CLI is not installed and no GITHUB_TOKEN is configured.${hint}`);
378
+ }
379
+ if (hasGh) {
380
+ // Check if PR already exists
381
+ try {
382
+ const existing = await ghExec(cwd, `pr view --head "${currentBranch}" --json number,url,title,state,baseRefName,headRefName`);
383
+ const parsed = JSON.parse(existing);
384
+ if (parsed.number) {
385
+ const state = String(parsed.state ?? "OPEN").toUpperCase();
386
+ if (state === "OPEN") {
387
+ result.pr = {
388
+ status: "opened_existing",
389
+ url: String(parsed.url ?? ""),
390
+ number: Number(parsed.number),
391
+ baseBranch: String(parsed.baseRefName ?? ""),
392
+ headBranch: String(parsed.headRefName ?? ""),
393
+ title: String(parsed.title ?? ""),
394
+ };
395
+ return result;
396
+ }
397
+ }
398
+ }
399
+ catch { /* no existing PR */ }
400
+ // Create new PR — use --push if branch wasn't pushed yet
401
+ const prTitle = result.commit.subject ?? commitMessage?.trim() ?? `Changes from ${currentBranch}`;
402
+ const baseFlag = baseBranch ? ` --base "${baseBranch}"` : '';
403
+ const pushFlag = result.push.status !== "pushed" ? " --push" : "";
404
+ // Generate PR body from diff context
405
+ const resolvedBase = baseBranch || await this.resolveDefaultBranch(cwd);
406
+ const prBody = await this.generatePrBody(cwd, resolvedBase, currentBranch, prTitle);
407
+ // Write body to temp file (avoids shell escaping issues with markdown)
408
+ const bodyFile = join(tmpdir(), `jait-pr-body-${Date.now()}.md`);
409
+ await writeFile(bodyFile, prBody, "utf-8");
410
+ try {
411
+ // gh pr create outputs the PR URL on stdout (--json is not supported)
412
+ const prUrl = await ghExec(cwd, `pr create --title "${prTitle.replace(/"/g, '\\"')}" --body-file "${bodyFile}"${baseFlag}${pushFlag}`, 60_000);
413
+ // Fetch full PR details via gh pr view
414
+ let prNumber = 0;
415
+ let prBaseBranch = baseBranch ?? "";
416
+ let prHeadBranch = currentBranch;
417
+ let prFinalTitle = prTitle;
418
+ try {
419
+ const details = await ghExec(cwd, `pr view "${prUrl.trim()}" --json number,title,baseRefName,headRefName`);
420
+ const parsed = JSON.parse(details);
421
+ prNumber = Number(parsed.number ?? 0);
422
+ prBaseBranch = String(parsed.baseRefName ?? prBaseBranch);
423
+ prHeadBranch = String(parsed.headRefName ?? prHeadBranch);
424
+ prFinalTitle = String(parsed.title ?? prTitle);
425
+ }
426
+ catch { /* details fetch failed — use what we have */ }
427
+ result.pr = {
428
+ status: "created",
429
+ url: prUrl.trim(),
430
+ number: prNumber,
431
+ baseBranch: prBaseBranch,
432
+ headBranch: prHeadBranch,
433
+ title: prFinalTitle,
434
+ };
435
+ // If gh pushed the branch for us, update push status
436
+ if (result.push.status !== "pushed") {
437
+ result.push = { status: "pushed", branch: currentBranch };
438
+ }
439
+ }
440
+ finally {
441
+ // Clean up temp file
442
+ await unlink(bodyFile).catch(() => { });
443
+ }
444
+ }
445
+ else if (githubRemote && effectiveToken) {
446
+ const resolvedBase = baseBranch || await this.resolveDefaultBranch(cwd);
447
+ const prTitle = result.commit.subject ?? commitMessage?.trim() ?? `Changes from ${currentBranch}`;
448
+ const prBody = await this.generatePrBody(cwd, resolvedBase, currentBranch, prTitle);
449
+ const apiResult = await this.createGithubPrViaApi(githubRemote, effectiveToken, {
450
+ title: prTitle,
451
+ baseBranch: resolvedBase,
452
+ headBranch: currentBranch,
453
+ body: prBody,
454
+ });
455
+ result.pr = {
456
+ status: apiResult.status,
457
+ url: apiResult.url,
458
+ number: apiResult.number,
459
+ baseBranch: apiResult.baseBranch,
460
+ headBranch: apiResult.headBranch,
461
+ title: apiResult.title,
462
+ };
463
+ }
464
+ else {
465
+ result.pr = { status: "skipped_not_requested" };
466
+ }
467
+ }
468
+ catch (err) {
469
+ // PR creation failed — report as error with details
470
+ const errMsg = err instanceof Error ? err.message : String(err);
471
+ result.pr = { status: "skipped_no_remote" };
472
+ const prefix = result.push.status === "skipped_no_remote"
473
+ ? "Push failed (no remote configured) and PR creation failed"
474
+ : "Pull request creation failed";
475
+ throw new Error(`${prefix}: ${errMsg}`);
476
+ }
477
+ }
478
+ return result;
479
+ }
480
+ async checkout(cwd, branch) {
481
+ await gitExec(cwd, `checkout "${branch}"`);
482
+ }
483
+ async createBranch(cwd, branch) {
484
+ await gitExec(cwd, `checkout -b "${branch}"`);
485
+ }
486
+ // ── Worktree operations ───────────────────────────────────────
487
+ /**
488
+ * Create a git worktree for a new branch.
489
+ * Worktrees live under ~/.jait/worktrees/{repoName}/{sanitizedBranch}.
490
+ * Uses `git worktree add -b <newBranch> <path> <baseBranch>`.
491
+ */
492
+ async createWorktree(cwd, baseBranch, newBranch, customPath) {
493
+ const sanitized = newBranch.replace(/\//g, "-");
494
+ const repoName = basename(cwd);
495
+ const worktreePath = customPath ??
496
+ join(homedir(), ".jait", "worktrees", repoName, sanitized);
497
+ // Ensure parent directory exists
498
+ await mkdir(join(worktreePath, ".."), { recursive: true });
499
+ await gitExec(cwd, `worktree add -b "${newBranch}" "${worktreePath}" "${baseBranch}"`, 60_000);
500
+ return { path: worktreePath, branch: newBranch };
501
+ }
502
+ /** Remove a git worktree. */
503
+ async removeWorktree(cwd, worktreePath, force = false) {
504
+ const forceFlag = force ? " --force" : "";
505
+ await gitExec(cwd, `worktree remove "${worktreePath}"${forceFlag}`, 30_000);
506
+ }
507
+ /**
508
+ * Clean up a worktree directory created for a thread.
509
+ * Resolves the main repo root, runs `git worktree remove --force`,
510
+ * and falls back to deleting the directory if that fails.
511
+ * No-ops silently when the path is not a worktree or doesn't exist.
512
+ */
513
+ async cleanupWorktree(worktreePath) {
514
+ if (!worktreePath || !existsSync(worktreePath))
515
+ return;
516
+ // Only act on paths that live inside the managed worktrees directory
517
+ const worktreeMarker = join(".jait", "worktrees");
518
+ if (!worktreePath.includes(worktreeMarker))
519
+ return;
520
+ try {
521
+ const mainRoot = await this.getMainRepoRoot(worktreePath);
522
+ await this.removeWorktree(mainRoot, worktreePath, true);
523
+ }
524
+ catch {
525
+ // git worktree remove may fail (dirty tree, missing refs, etc.).
526
+ // Fall back to a plain directory removal so we don't leak disk space.
527
+ try {
528
+ await rm(worktreePath, { recursive: true, force: true });
529
+ }
530
+ catch { /* best effort */ }
531
+ }
532
+ }
533
+ /** Get the top-level git directory (the main repo root, even from a worktree). */
534
+ async getMainRepoRoot(cwd) {
535
+ // In a worktree, --git-common-dir points to the main repo's .git
536
+ // and --show-toplevel gives the worktree root. We need the main root.
537
+ try {
538
+ const commonDir = await gitExec(cwd, "rev-parse --git-common-dir");
539
+ // commonDir is like /path/to/main-repo/.git
540
+ // We want /path/to/main-repo
541
+ if (commonDir.endsWith("/.git") || commonDir.endsWith("\\.git")) {
542
+ return commonDir.slice(0, -5);
543
+ }
544
+ // Fallback: it's a regular repo
545
+ return gitExec(cwd, "rev-parse --show-toplevel");
546
+ }
547
+ catch {
548
+ return gitExec(cwd, "rev-parse --show-toplevel");
549
+ }
550
+ }
551
+ /** Check whether a named remote (e.g. "origin") exists. */
552
+ async hasRemote(cwd, name) {
553
+ try {
554
+ await gitExec(cwd, `remote get-url ${name}`);
555
+ return true;
556
+ }
557
+ catch {
558
+ return false;
559
+ }
560
+ }
561
+ /** Get the remote URL for a named remote, or null if not set. */
562
+ async getRemoteUrl(cwd, name) {
563
+ try {
564
+ return (await gitExec(cwd, `remote get-url ${name}`)).trim() || null;
565
+ }
566
+ catch {
567
+ return null;
568
+ }
569
+ }
570
+ /** List configured remote names. */
571
+ async listRemotes(cwd) {
572
+ const raw = await gitExec(cwd, "remote").catch(() => "");
573
+ return raw
574
+ .split("\n")
575
+ .map((r) => r.trim())
576
+ .filter(Boolean);
577
+ }
578
+ /**
579
+ * Resolve the best remote for push/PR operations.
580
+ * Priority: branch-specific remote -> origin -> first configured remote.
581
+ * Falls back to main repo root remotes for worktrees.
582
+ */
583
+ async getPreferredRemote(cwd, branch) {
584
+ let remotes = await this.listRemotes(cwd);
585
+ // If no remotes found and we're in a worktree, try the main repo root
586
+ if (remotes.length === 0) {
587
+ try {
588
+ const mainRoot = await this.getMainRepoRoot(cwd);
589
+ if (mainRoot && mainRoot !== cwd) {
590
+ remotes = await this.listRemotes(mainRoot);
591
+ }
592
+ }
593
+ catch { /* ignore */ }
594
+ }
595
+ if (remotes.length === 0)
596
+ return null;
597
+ if (branch) {
598
+ const configuredRemote = await gitExec(cwd, `config --get branch.${branch}.remote`).catch(() => "");
599
+ if (configuredRemote && remotes.includes(configuredRemote)) {
600
+ return configuredRemote;
601
+ }
602
+ }
603
+ if (remotes.includes("origin"))
604
+ return "origin";
605
+ return remotes[0] ?? null;
606
+ }
607
+ /**
608
+ * Resolve the repository's default branch (e.g. "main" or "master").
609
+ * Tries gh CLI first, then falls back to common defaults.
610
+ */
611
+ async resolveDefaultBranch(cwd) {
612
+ try {
613
+ const json = await ghExec(cwd, "repo view --json defaultBranchRef", 15_000);
614
+ const parsed = JSON.parse(json);
615
+ const ref = parsed.defaultBranchRef;
616
+ if (ref?.name)
617
+ return String(ref.name);
618
+ }
619
+ catch { /* gh not available or not a github repo */ }
620
+ // Fallback: check if "main" or "master" branches exist
621
+ try {
622
+ await gitExec(cwd, "rev-parse --verify refs/heads/main");
623
+ return "main";
624
+ }
625
+ catch {
626
+ try {
627
+ await gitExec(cwd, "rev-parse --verify refs/heads/master");
628
+ return "master";
629
+ }
630
+ catch {
631
+ return "main";
632
+ }
633
+ }
634
+ }
635
+ async createGithubPrViaApi(remote, token, input) {
636
+ const apiBase = remote.host === "github.com"
637
+ ? "https://api.github.com"
638
+ : `https://${remote.host}/api/v3`;
639
+ const headers = {
640
+ Authorization: `Bearer ${token}`,
641
+ Accept: "application/vnd.github+json",
642
+ "User-Agent": "jait-gateway",
643
+ };
644
+ // If an open PR already exists for this head branch, reuse it
645
+ const headParam = `${remote.owner}:${input.headBranch}`;
646
+ try {
647
+ const existingRes = await fetch(`${apiBase}/repos/${remote.owner}/${remote.repo}/pulls?head=${encodeURIComponent(headParam)}&state=open`, { headers });
648
+ if (existingRes.ok) {
649
+ const existing = await existingRes.json();
650
+ const first = existing[0];
651
+ if (first?.html_url) {
652
+ return {
653
+ status: "opened_existing",
654
+ url: String(first.html_url),
655
+ number: Number(first.number ?? 0),
656
+ baseBranch: String(first.base?.ref ?? input.baseBranch),
657
+ headBranch: String(first.head?.ref ?? input.headBranch),
658
+ title: String(first.title ?? input.title),
659
+ };
660
+ }
661
+ }
662
+ }
663
+ catch { /* ignore fetch errors and proceed to create */ }
664
+ const res = await fetch(`${apiBase}/repos/${remote.owner}/${remote.repo}/pulls`, {
665
+ method: "POST",
666
+ headers: { ...headers, "Content-Type": "application/json" },
667
+ body: JSON.stringify({
668
+ title: input.title,
669
+ head: headParam,
670
+ base: input.baseBranch,
671
+ body: input.body,
672
+ }),
673
+ });
674
+ if (!res.ok) {
675
+ const text = await res.text().catch(() => "");
676
+ throw new Error(`GitHub API PR create failed (${res.status}): ${text.slice(0, 400)}`);
677
+ }
678
+ const json = await res.json();
679
+ return {
680
+ status: "created",
681
+ url: String(json.html_url ?? ""),
682
+ number: Number(json.number ?? 0),
683
+ baseBranch: String(json.base?.ref ?? input.baseBranch),
684
+ headBranch: String(json.head?.ref ?? input.headBranch),
685
+ title: String(json.title ?? input.title),
686
+ };
687
+ }
688
+ async fetchGithubPrByHead(remote, token, headBranch) {
689
+ const apiBase = remote.host === "github.com"
690
+ ? "https://api.github.com"
691
+ : `https://${remote.host}/api/v3`;
692
+ const headers = {
693
+ Authorization: `Bearer ${token}`,
694
+ Accept: "application/vnd.github+json",
695
+ "User-Agent": "jait-gateway",
696
+ };
697
+ const headParam = `${remote.owner}:${headBranch}`;
698
+ try {
699
+ const res = await fetch(`${apiBase}/repos/${remote.owner}/${remote.repo}/pulls?head=${encodeURIComponent(headParam)}&state=all`, { headers });
700
+ if (!res.ok)
701
+ return null;
702
+ const list = await res.json();
703
+ if (!Array.isArray(list) || list.length === 0)
704
+ return null;
705
+ // Prefer open PR, otherwise take the most recent
706
+ const prData = (list.find((p) => p?.state === "open") ?? list[0]);
707
+ if (!prData?.html_url)
708
+ return null;
709
+ const stateRaw = String(prData.state ?? "open").toLowerCase();
710
+ const mergedAt = prData.merged_at;
711
+ const state = mergedAt ? "merged"
712
+ : stateRaw === "closed" ? "closed"
713
+ : "open";
714
+ return {
715
+ number: Number(prData.number ?? 0),
716
+ title: String(prData.title ?? ""),
717
+ url: String(prData.html_url ?? ""),
718
+ baseBranch: String(prData.base?.ref ?? ""),
719
+ headBranch: String(prData.head?.ref ?? headBranch),
720
+ state,
721
+ };
722
+ }
723
+ catch {
724
+ return null;
725
+ }
726
+ }
727
+ /**
728
+ * Generate a pull request body from the diff between base and head.
729
+ * Collects commit log + diff stat and formats as markdown.
730
+ */
731
+ async generatePrBody(cwd, baseBranch, headBranch, prTitle) {
732
+ const MAX_COMMITS = 12_000;
733
+ const MAX_STAT = 12_000;
734
+ let commits = "";
735
+ try {
736
+ const raw = await gitExec(cwd, `log --oneline ${baseBranch}..${headBranch}`, 15_000);
737
+ commits = raw.length > MAX_COMMITS ? raw.slice(0, MAX_COMMITS) + "\n... (truncated)" : raw;
738
+ }
739
+ catch { /* no common ancestor or baseBranch doesn't exist locally */ }
740
+ let diffStat = "";
741
+ try {
742
+ const raw = await gitExec(cwd, `diff --stat ${baseBranch}..${headBranch}`, 15_000);
743
+ diffStat = raw.length > MAX_STAT ? raw.slice(0, MAX_STAT) + "\n... (truncated)" : raw;
744
+ }
745
+ catch { /* ignore */ }
746
+ // Build markdown body
747
+ const sections = [];
748
+ sections.push(`## Summary\n`);
749
+ sections.push(`${prTitle}\n`);
750
+ if (commits) {
751
+ sections.push(`## Commits\n`);
752
+ sections.push("```");
753
+ sections.push(commits);
754
+ sections.push("```\n");
755
+ }
756
+ if (diffStat) {
757
+ sections.push(`## Changes\n`);
758
+ sections.push("```");
759
+ sections.push(diffStat);
760
+ sections.push("```\n");
761
+ }
762
+ sections.push(`---\n*PR created by [Jait](https://github.com/JakobWl/Jait) automation.*`);
763
+ return sections.join("\n");
764
+ }
765
+ /**
766
+ * Build a URL to create a new pull request on the hosting provider.
767
+ * Supports GitHub, GitLab, Bitbucket, and Azure DevOps remote URLs.
768
+ */
769
+ async buildCreatePrUrl(cwd, branch, remoteName) {
770
+ const preferredRemote = remoteName ?? await this.getPreferredRemote(cwd, branch);
771
+ if (!preferredRemote)
772
+ return undefined;
773
+ const raw = await this.getRemoteUrl(cwd, preferredRemote);
774
+ if (!raw)
775
+ return undefined;
776
+ // Normalise SSH / HTTPS remote URL → "https://host/owner/repo"
777
+ let url = raw
778
+ .replace(/\.git$/, "")
779
+ .replace(/^git@([^:]+):(.+)$/, "https://$1/$2")
780
+ .replace(/^ssh:\/\/git@([^/]+)\/(.+)$/, "https://$1/$2");
781
+ // GitHub
782
+ if (url.includes("github.com")) {
783
+ return `${url}/compare/${encodeURIComponent(branch)}?expand=1`;
784
+ }
785
+ // GitLab
786
+ if (url.includes("gitlab")) {
787
+ return `${url}/-/merge_requests/new?merge_request[source_branch]=${encodeURIComponent(branch)}`;
788
+ }
789
+ // Bitbucket
790
+ if (url.includes("bitbucket")) {
791
+ return `${url}/pull-requests/new?source=${encodeURIComponent(branch)}`;
792
+ }
793
+ // Azure DevOps
794
+ if (url.includes("dev.azure.com") || url.includes("visualstudio.com")) {
795
+ return `${url}/pullrequestcreate?sourceRef=${encodeURIComponent(branch)}`;
796
+ }
797
+ return undefined;
798
+ }
799
+ /** Return the diff of uncommitted changes (staged + unstaged). */
800
+ async diff(cwd) {
801
+ const isGit = await this.isRepo(cwd);
802
+ if (!isGit)
803
+ return { diff: "", files: [], hasChanges: false };
804
+ // Combine staged and unstaged diff
805
+ let diffText = "";
806
+ try {
807
+ const staged = await gitExec(cwd, "diff --cached").catch(() => "");
808
+ const unstaged = await gitExec(cwd, "diff").catch(() => "");
809
+ diffText = [staged, unstaged].filter(Boolean).join("\n");
810
+ }
811
+ catch { /* ignore */ }
812
+ // Also include untracked files as a summary
813
+ const porcelain = await gitExec(cwd, "status --porcelain").catch(() => "");
814
+ const untrackedFiles = porcelain
815
+ .split("\n")
816
+ .filter((l) => l.startsWith("??"))
817
+ .map((l) => l.slice(3).trim())
818
+ .filter(Boolean);
819
+ if (untrackedFiles.length > 0) {
820
+ const untrackedSection = untrackedFiles.map((f) => `+++ new file: ${f}`).join("\n");
821
+ diffText = diffText ? `${diffText}\n\n# Untracked files:\n${untrackedSection}` : `# Untracked files:\n${untrackedSection}`;
822
+ }
823
+ const files = porcelain
824
+ .split("\n")
825
+ .filter(Boolean)
826
+ .map((l) => l.slice(3).trim())
827
+ .filter(Boolean);
828
+ return {
829
+ diff: diffText,
830
+ files,
831
+ hasChanges: files.length > 0,
832
+ };
833
+ }
834
+ /**
835
+ * Return per-file original and modified content so the frontend can
836
+ * render a Monaco diff editor.
837
+ *
838
+ * @param baseBranch — when given, diff working tree against that branch
839
+ * (shows all thread changes: committed + uncommitted). When omitted,
840
+ * only uncommitted working-tree changes are returned (original = HEAD).
841
+ */
842
+ async fileDiffs(cwd, baseBranch) {
843
+ const isGit = await this.isRepo(cwd);
844
+ if (!isGit)
845
+ return [];
846
+ if (baseBranch) {
847
+ return this.fileDiffsBranch(cwd, baseBranch);
848
+ }
849
+ const porcelain = await gitExec(cwd, "status --porcelain").catch(() => "");
850
+ const lines = porcelain.split("\n").filter(Boolean);
851
+ const entries = [];
852
+ for (const line of lines) {
853
+ const xy = line.slice(0, 2);
854
+ let filePath = line.slice(3).trim();
855
+ // Handle renames: "R old -> new"
856
+ if (filePath.includes(" -> ")) {
857
+ filePath = filePath.split(" -> ").pop().trim();
858
+ }
859
+ // Determine status code
860
+ let status = "M";
861
+ if (xy.includes("?"))
862
+ status = "?";
863
+ else if (xy.includes("A"))
864
+ status = "A";
865
+ else if (xy.includes("D"))
866
+ status = "D";
867
+ else if (xy.includes("R"))
868
+ status = "R";
869
+ // Get original from HEAD
870
+ let original = "";
871
+ if (status !== "A" && status !== "?") {
872
+ try {
873
+ original = await gitExec(cwd, `show HEAD:${JSON.stringify(filePath)}`);
874
+ }
875
+ catch {
876
+ original = "";
877
+ }
878
+ }
879
+ // Get current working tree content
880
+ let modified = "";
881
+ if (status !== "D") {
882
+ try {
883
+ modified = await readFile(join(cwd, filePath), "utf-8");
884
+ }
885
+ catch {
886
+ modified = "";
887
+ }
888
+ }
889
+ entries.push({ path: filePath, original, modified, status });
890
+ }
891
+ return entries;
892
+ }
893
+ /**
894
+ * Diff working tree against a base branch (shows all committed + uncommitted changes).
895
+ */
896
+ async fileDiffsBranch(cwd, baseBranch) {
897
+ // Get list of files that differ between baseBranch and working tree
898
+ const nameStatus = await gitExec(cwd, `diff --name-status ${baseBranch}`).catch(() => "");
899
+ const lines = nameStatus.split("\n").filter(Boolean);
900
+ const entries = [];
901
+ const seen = new Set();
902
+ for (const line of lines) {
903
+ const parts = line.split("\t");
904
+ const statusCode = parts[0]?.trim() ?? "M";
905
+ let filePath = parts[parts.length - 1]?.trim() ?? "";
906
+ let status = "M";
907
+ if (statusCode.startsWith("A"))
908
+ status = "A";
909
+ else if (statusCode.startsWith("D"))
910
+ status = "D";
911
+ else if (statusCode.startsWith("R")) {
912
+ status = "R";
913
+ filePath = parts[2]?.trim() ?? filePath;
914
+ }
915
+ if (!filePath || seen.has(filePath))
916
+ continue;
917
+ seen.add(filePath);
918
+ let original = "";
919
+ if (status !== "A") {
920
+ try {
921
+ original = await gitExec(cwd, `show ${baseBranch}:${JSON.stringify(filePath)}`);
922
+ }
923
+ catch {
924
+ original = "";
925
+ }
926
+ }
927
+ let modified = "";
928
+ if (status !== "D") {
929
+ try {
930
+ modified = await readFile(join(cwd, filePath), "utf-8");
931
+ }
932
+ catch {
933
+ modified = "";
934
+ }
935
+ }
936
+ entries.push({ path: filePath, original, modified, status });
937
+ }
938
+ // Also include untracked files that aren't already listed
939
+ const porcelain = await gitExec(cwd, "status --porcelain").catch(() => "");
940
+ for (const pl of porcelain.split("\n").filter(Boolean)) {
941
+ if (!pl.startsWith("??"))
942
+ continue;
943
+ const fp = pl.slice(3).trim();
944
+ if (!fp || seen.has(fp))
945
+ continue;
946
+ seen.add(fp);
947
+ let modified = "";
948
+ try {
949
+ modified = await readFile(join(cwd, fp), "utf-8");
950
+ }
951
+ catch { /* skip */ }
952
+ entries.push({ path: fp, original: "", modified, status: "?" });
953
+ }
954
+ return entries;
955
+ }
956
+ }
957
+ //# sourceMappingURL=git.js.map