@ebowwa/coder 0.7.63 → 0.7.64

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 (341) hide show
  1. package/dist/core/__tests__/permissions.test.d.ts +12 -0
  2. package/dist/core/__tests__/permissions.test.d.ts.map +1 -0
  3. package/dist/core/__tests__/permissions.test.js +851 -0
  4. package/dist/core/agent-loop/__tests__/compaction.test.d.ts +5 -0
  5. package/dist/core/agent-loop/__tests__/compaction.test.d.ts.map +1 -0
  6. package/dist/core/agent-loop/__tests__/compaction.test.js +209 -0
  7. package/dist/core/agent-loop/__tests__/formatters.test.d.ts +5 -0
  8. package/dist/core/agent-loop/__tests__/formatters.test.d.ts.map +1 -0
  9. package/dist/core/agent-loop/__tests__/formatters.test.js +195 -0
  10. package/dist/core/agent-loop/__tests__/index.test.d.ts +5 -0
  11. package/dist/core/agent-loop/__tests__/index.test.d.ts.map +1 -0
  12. package/dist/core/agent-loop/__tests__/index.test.js +121 -0
  13. package/dist/core/agent-loop/__tests__/loop-state.test.d.ts +5 -0
  14. package/dist/core/agent-loop/__tests__/loop-state.test.d.ts.map +1 -0
  15. package/dist/core/agent-loop/__tests__/loop-state.test.js +340 -0
  16. package/dist/core/agent-loop/__tests__/message-builder.test.d.ts +5 -0
  17. package/dist/core/agent-loop/__tests__/message-builder.test.d.ts.map +1 -0
  18. package/dist/core/agent-loop/__tests__/message-builder.test.js +178 -0
  19. package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts +5 -0
  20. package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts.map +1 -0
  21. package/dist/core/agent-loop/__tests__/tool-executor.test.js +331 -0
  22. package/dist/core/agent-loop/compaction.d.ts +39 -0
  23. package/dist/core/agent-loop/compaction.d.ts.map +1 -0
  24. package/dist/core/agent-loop/compaction.js +51 -0
  25. package/dist/core/agent-loop/formatters.d.ts +21 -0
  26. package/dist/core/agent-loop/formatters.d.ts.map +1 -0
  27. package/dist/core/agent-loop/formatters.js +42 -0
  28. package/dist/core/agent-loop/index.d.ts +25 -0
  29. package/dist/core/agent-loop/index.d.ts.map +1 -0
  30. package/dist/core/agent-loop/index.js +83 -0
  31. package/dist/core/agent-loop/loop-state.d.ts +74 -0
  32. package/dist/core/agent-loop/loop-state.d.ts.map +1 -0
  33. package/dist/core/agent-loop/loop-state.js +147 -0
  34. package/dist/core/agent-loop/message-builder.d.ts +13 -0
  35. package/dist/core/agent-loop/message-builder.d.ts.map +1 -0
  36. package/dist/core/agent-loop/message-builder.js +49 -0
  37. package/dist/core/agent-loop/tool-executor.d.ts +23 -0
  38. package/dist/core/agent-loop/tool-executor.d.ts.map +1 -0
  39. package/dist/core/agent-loop/tool-executor.js +152 -0
  40. package/dist/core/agent-loop/turn-executor.d.ts +57 -0
  41. package/dist/core/agent-loop/turn-executor.d.ts.map +1 -0
  42. package/dist/core/agent-loop/turn-executor.js +124 -0
  43. package/dist/core/agent-loop/types.d.ts +141 -0
  44. package/dist/core/agent-loop/types.d.ts.map +1 -0
  45. package/dist/core/agent-loop/types.js +4 -0
  46. package/dist/core/agent-loop.d.ts +17 -0
  47. package/dist/core/agent-loop.d.ts.map +1 -0
  48. package/dist/core/agent-loop.js +16 -0
  49. package/dist/core/api-client-impl.d.ts +62 -0
  50. package/dist/core/api-client-impl.d.ts.map +1 -0
  51. package/dist/core/api-client-impl.js +479 -0
  52. package/dist/core/api-client.d.ts +6 -0
  53. package/dist/core/api-client.d.ts.map +1 -0
  54. package/dist/core/api-client.js +5 -0
  55. package/dist/core/checkpoints.d.ts +128 -0
  56. package/dist/core/checkpoints.d.ts.map +1 -0
  57. package/dist/core/checkpoints.js +438 -0
  58. package/dist/core/claude-md.d.ts +71 -0
  59. package/dist/core/claude-md.d.ts.map +1 -0
  60. package/dist/core/claude-md.js +198 -0
  61. package/dist/core/cognitive-security/hooks.d.ts +138 -0
  62. package/dist/core/cognitive-security/hooks.d.ts.map +1 -0
  63. package/dist/core/cognitive-security/hooks.js +389 -0
  64. package/dist/core/cognitive-security/index.d.ts +751 -0
  65. package/dist/core/cognitive-security/index.d.ts.map +1 -0
  66. package/dist/core/cognitive-security/index.js +1123 -0
  67. package/dist/core/cognitive-security/middleware.d.ts +136 -0
  68. package/dist/core/cognitive-security/middleware.d.ts.map +1 -0
  69. package/dist/core/cognitive-security/middleware.js +376 -0
  70. package/dist/core/config-loader.d.ts +127 -0
  71. package/dist/core/config-loader.d.ts.map +1 -0
  72. package/dist/core/config-loader.js +219 -0
  73. package/dist/core/context-compaction.d.ts +87 -0
  74. package/dist/core/context-compaction.d.ts.map +1 -0
  75. package/dist/core/context-compaction.js +428 -0
  76. package/dist/core/git-status.d.ts +25 -0
  77. package/dist/core/git-status.d.ts.map +1 -0
  78. package/dist/core/git-status.js +204 -0
  79. package/dist/core/image.d.ts +69 -0
  80. package/dist/core/image.d.ts.map +1 -0
  81. package/dist/core/image.js +290 -0
  82. package/dist/core/image.test.d.ts +2 -0
  83. package/dist/core/image.test.d.ts.map +1 -0
  84. package/dist/core/image.test.js +149 -0
  85. package/dist/core/models.d.ts +123 -0
  86. package/dist/core/models.d.ts.map +1 -0
  87. package/dist/core/models.js +325 -0
  88. package/dist/core/permissions.d.ts +81 -0
  89. package/dist/core/permissions.d.ts.map +1 -0
  90. package/dist/core/permissions.js +327 -0
  91. package/dist/core/retry.d.ts +25 -0
  92. package/dist/core/retry.d.ts.map +1 -0
  93. package/dist/core/retry.js +121 -0
  94. package/dist/core/session-store.d.ts +9 -0
  95. package/dist/core/session-store.d.ts.map +1 -0
  96. package/dist/core/session-store.js +10 -0
  97. package/dist/core/sessions/export.d.ts +47 -0
  98. package/dist/core/sessions/export.d.ts.map +1 -0
  99. package/dist/core/sessions/export.js +256 -0
  100. package/dist/core/sessions/index.d.ts +132 -0
  101. package/dist/core/sessions/index.d.ts.map +1 -0
  102. package/dist/core/sessions/index.js +442 -0
  103. package/dist/core/sessions/metadata.d.ts +77 -0
  104. package/dist/core/sessions/metadata.d.ts.map +1 -0
  105. package/dist/core/sessions/metadata.js +233 -0
  106. package/dist/core/sessions/persistence.d.ts +72 -0
  107. package/dist/core/sessions/persistence.d.ts.map +1 -0
  108. package/dist/core/sessions/persistence.js +201 -0
  109. package/dist/core/sessions/types.d.ts +110 -0
  110. package/dist/core/sessions/types.d.ts.map +1 -0
  111. package/dist/core/sessions/types.js +4 -0
  112. package/dist/core/stream-highlighter.d.ts +18 -0
  113. package/dist/core/stream-highlighter.d.ts.map +1 -0
  114. package/dist/core/stream-highlighter.js +916 -0
  115. package/dist/core/system-reminders.d.ts +89 -0
  116. package/dist/core/system-reminders.d.ts.map +1 -0
  117. package/dist/core/system-reminders.js +285 -0
  118. package/dist/ecosystem/hooks/__tests__/index.test.d.ts +5 -0
  119. package/dist/ecosystem/hooks/__tests__/index.test.d.ts.map +1 -0
  120. package/dist/ecosystem/hooks/__tests__/index.test.js +458 -0
  121. package/dist/ecosystem/hooks/index.d.ts +59 -0
  122. package/dist/ecosystem/hooks/index.d.ts.map +1 -0
  123. package/dist/ecosystem/hooks/index.js +294 -0
  124. package/dist/ecosystem/hooks/prompt-evaluator.d.ts +32 -0
  125. package/dist/ecosystem/hooks/prompt-evaluator.d.ts.map +1 -0
  126. package/dist/ecosystem/hooks/prompt-evaluator.js +229 -0
  127. package/dist/ecosystem/skills/index.d.ts +55 -0
  128. package/dist/ecosystem/skills/index.d.ts.map +1 -0
  129. package/dist/ecosystem/skills/index.js +258 -0
  130. package/dist/ecosystem/tools/__tests__/index.test.d.ts +7 -0
  131. package/dist/ecosystem/tools/__tests__/index.test.d.ts.map +1 -0
  132. package/dist/ecosystem/tools/__tests__/index.test.js +856 -0
  133. package/dist/ecosystem/tools/index.d.ts +24 -0
  134. package/dist/ecosystem/tools/index.d.ts.map +1 -0
  135. package/dist/ecosystem/tools/index.js +1709 -0
  136. package/dist/index.d.ts +24 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +32 -52192
  139. package/dist/interfaces/mcp/client.d.ts +40 -0
  140. package/dist/interfaces/mcp/client.d.ts.map +1 -0
  141. package/dist/interfaces/mcp/client.js +309 -0
  142. package/dist/interfaces/ui/index.d.ts +36 -0
  143. package/dist/interfaces/ui/index.d.ts.map +1 -0
  144. package/dist/interfaces/ui/index.js +61 -0
  145. package/dist/interfaces/ui/spinner.d.ts +140 -0
  146. package/dist/interfaces/ui/spinner.d.ts.map +1 -0
  147. package/dist/interfaces/ui/spinner.js +342 -0
  148. package/dist/interfaces/ui/terminal/cli/index.d.ts +12 -0
  149. package/dist/interfaces/ui/terminal/cli/index.d.ts.map +1 -0
  150. package/dist/interfaces/ui/terminal/cli/index.js +159 -52768
  151. package/dist/interfaces/ui/terminal/shared/args.d.ts +39 -0
  152. package/dist/interfaces/ui/terminal/shared/args.d.ts.map +1 -0
  153. package/dist/interfaces/ui/terminal/shared/args.js +176 -0
  154. package/dist/interfaces/ui/terminal/shared/index.d.ts +11 -0
  155. package/dist/interfaces/ui/terminal/shared/index.d.ts.map +1 -0
  156. package/dist/interfaces/ui/terminal/shared/index.js +16 -0
  157. package/dist/interfaces/ui/terminal/shared/loading-state.d.ts +124 -0
  158. package/dist/interfaces/ui/terminal/shared/loading-state.d.ts.map +1 -0
  159. package/dist/interfaces/ui/terminal/shared/loading-state.js +246 -0
  160. package/dist/interfaces/ui/terminal/shared/query.d.ts +22 -0
  161. package/dist/interfaces/ui/terminal/shared/query.d.ts.map +1 -0
  162. package/dist/interfaces/ui/terminal/shared/query.js +100 -0
  163. package/dist/interfaces/ui/terminal/shared/setup.d.ts +33 -0
  164. package/dist/interfaces/ui/terminal/shared/setup.d.ts.map +1 -0
  165. package/dist/interfaces/ui/terminal/shared/setup.js +226 -0
  166. package/dist/interfaces/ui/terminal/shared/status-line.d.ts +117 -0
  167. package/dist/interfaces/ui/terminal/shared/status-line.d.ts.map +1 -0
  168. package/dist/interfaces/ui/terminal/shared/status-line.js +267 -0
  169. package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts +38 -0
  170. package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts.map +1 -0
  171. package/dist/interfaces/ui/terminal/shared/system-prompt.js +102 -0
  172. package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts +39 -0
  173. package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts.map +1 -0
  174. package/dist/interfaces/ui/terminal/tui/HelpPanel.js +215 -0
  175. package/dist/interfaces/ui/terminal/tui/InputContext.d.ts +91 -0
  176. package/dist/interfaces/ui/terminal/tui/InputContext.d.ts.map +1 -0
  177. package/dist/interfaces/ui/terminal/tui/InputContext.js +154 -0
  178. package/dist/interfaces/ui/terminal/tui/InputField.d.ts +18 -0
  179. package/dist/interfaces/ui/terminal/tui/InputField.d.ts.map +1 -0
  180. package/dist/interfaces/ui/terminal/tui/InputField.js +41 -0
  181. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts +16 -0
  182. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts.map +1 -0
  183. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.js +451 -0
  184. package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts +10 -0
  185. package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts.map +1 -0
  186. package/dist/interfaces/ui/terminal/tui/MessageArea.js +91 -0
  187. package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts +48 -0
  188. package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts.map +1 -0
  189. package/dist/interfaces/ui/terminal/tui/MessageStore.js +151 -0
  190. package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts +9 -0
  191. package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts.map +1 -0
  192. package/dist/interfaces/ui/terminal/tui/StatusBar.js +36 -0
  193. package/dist/interfaces/ui/terminal/tui/commands.d.ts +21 -0
  194. package/dist/interfaces/ui/terminal/tui/commands.d.ts.map +1 -0
  195. package/dist/interfaces/ui/terminal/tui/commands.js +359 -0
  196. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts +115 -0
  197. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts.map +1 -0
  198. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.js +306 -0
  199. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts +92 -0
  200. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts.map +1 -0
  201. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.js +399 -0
  202. package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts +59 -0
  203. package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts.map +1 -0
  204. package/dist/interfaces/ui/terminal/tui/components/PaneManager.js +139 -0
  205. package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts +68 -0
  206. package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts.map +1 -0
  207. package/dist/interfaces/ui/terminal/tui/components/Sidebar.js +340 -0
  208. package/dist/interfaces/ui/terminal/tui/components/index.d.ts +23 -0
  209. package/dist/interfaces/ui/terminal/tui/components/index.d.ts.map +1 -0
  210. package/dist/interfaces/ui/terminal/tui/components/index.js +51 -0
  211. package/dist/interfaces/ui/terminal/tui/console.d.ts +20 -0
  212. package/dist/interfaces/ui/terminal/tui/console.d.ts.map +1 -0
  213. package/dist/interfaces/ui/terminal/tui/console.js +46 -0
  214. package/dist/interfaces/ui/terminal/tui/index.d.ts +20 -0
  215. package/dist/interfaces/ui/terminal/tui/index.d.ts.map +1 -0
  216. package/dist/interfaces/ui/terminal/tui/index.js +28 -0
  217. package/dist/interfaces/ui/terminal/tui/run.d.ts +13 -0
  218. package/dist/interfaces/ui/terminal/tui/run.d.ts.map +1 -0
  219. package/dist/interfaces/ui/terminal/tui/run.js +31 -0
  220. package/dist/interfaces/ui/terminal/tui/spinner.d.ts +44 -0
  221. package/dist/interfaces/ui/terminal/tui/spinner.d.ts.map +1 -0
  222. package/dist/interfaces/ui/terminal/tui/spinner.js +59 -0
  223. package/dist/interfaces/ui/terminal/tui/tui-app.d.ts +39 -0
  224. package/dist/interfaces/ui/terminal/tui/tui-app.d.ts.map +1 -0
  225. package/dist/interfaces/ui/terminal/tui/tui-app.js +198 -0
  226. package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts +167 -0
  227. package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts.map +1 -0
  228. package/dist/interfaces/ui/terminal/tui/tui-footer.js +330 -0
  229. package/dist/interfaces/ui/terminal/tui/types.d.ts +165 -0
  230. package/dist/interfaces/ui/terminal/tui/types.d.ts.map +1 -0
  231. package/dist/interfaces/ui/terminal/tui/types.js +5 -0
  232. package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts +23 -0
  233. package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts.map +1 -0
  234. package/dist/interfaces/ui/terminal/tui/useInputHandler.js +72 -0
  235. package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts +90 -0
  236. package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts.map +1 -0
  237. package/dist/interfaces/ui/terminal/tui/useNativeInput.js +188 -0
  238. package/dist/native/index.d.ts +480 -0
  239. package/dist/native/index.d.ts.map +1 -0
  240. package/dist/native/index.js +1625 -0
  241. package/dist/teammates/index.d.ts +161 -0
  242. package/dist/teammates/index.d.ts.map +1 -0
  243. package/dist/teammates/index.js +827 -0
  244. package/dist/types/index.d.ts +482 -0
  245. package/dist/types/index.d.ts.map +1 -0
  246. package/dist/types/index.js +52 -0
  247. package/package.json +4 -2
  248. package/packages/src/core/__tests__/permissions.test.ts +1091 -0
  249. package/packages/src/core/agent-loop/__tests__/compaction.test.ts +280 -0
  250. package/packages/src/core/agent-loop/__tests__/formatters.test.ts +234 -0
  251. package/packages/src/core/agent-loop/__tests__/index.test.ts +162 -0
  252. package/packages/src/core/agent-loop/__tests__/loop-state.test.ts +413 -0
  253. package/packages/src/core/agent-loop/__tests__/message-builder.test.ts +229 -0
  254. package/packages/src/core/agent-loop/__tests__/tool-executor.test.ts +457 -0
  255. package/packages/src/core/agent-loop/compaction.ts +88 -0
  256. package/packages/src/core/agent-loop/formatters.ts +50 -0
  257. package/packages/src/core/agent-loop/index.ts +135 -0
  258. package/packages/src/core/agent-loop/loop-state.ts +187 -0
  259. package/packages/src/core/agent-loop/message-builder.ts +62 -0
  260. package/packages/src/core/agent-loop/tool-executor.ts +211 -0
  261. package/packages/src/core/agent-loop/turn-executor.ts +222 -0
  262. package/packages/src/core/agent-loop/types.ts +148 -0
  263. package/packages/src/core/agent-loop.ts +18 -0
  264. package/packages/src/core/api-client-impl.ts +619 -0
  265. package/packages/src/core/api-client.ts +6 -0
  266. package/packages/src/core/checkpoints.ts +606 -0
  267. package/packages/src/core/claude-md.ts +272 -0
  268. package/packages/src/core/cognitive-security/hooks.ts +590 -0
  269. package/packages/src/core/cognitive-security/index.ts +2041 -0
  270. package/packages/src/core/cognitive-security/middleware.ts +536 -0
  271. package/packages/src/core/config-loader.ts +324 -0
  272. package/packages/src/core/context-compaction.ts +578 -0
  273. package/packages/src/core/git-status.ts +262 -0
  274. package/packages/src/core/image.test.ts +180 -0
  275. package/packages/src/core/image.ts +350 -0
  276. package/packages/src/core/lmdb.db +0 -0
  277. package/packages/src/core/lmdb.db-lock +0 -0
  278. package/packages/src/core/models.ts +430 -0
  279. package/packages/src/core/normalizers/todo +4 -0
  280. package/packages/src/core/permissions.ts +431 -0
  281. package/packages/src/core/retry.ts +170 -0
  282. package/packages/src/core/session-store.ts +36 -0
  283. package/packages/src/core/sessions/export.ts +329 -0
  284. package/packages/src/core/sessions/index.ts +587 -0
  285. package/packages/src/core/sessions/metadata.ts +309 -0
  286. package/packages/src/core/sessions/persistence.ts +244 -0
  287. package/packages/src/core/sessions/types.ts +169 -0
  288. package/packages/src/core/stream-highlighter.ts +1123 -0
  289. package/packages/src/core/system-reminders.ts +402 -0
  290. package/packages/src/core/todo +8 -0
  291. package/packages/src/ecosystem/hooks/__tests__/index.test.ts +561 -0
  292. package/packages/src/ecosystem/hooks/index.ts +341 -0
  293. package/packages/src/ecosystem/hooks/prompt-evaluator.ts +300 -0
  294. package/packages/src/ecosystem/skills/index.ts +295 -0
  295. package/packages/src/ecosystem/tools/__tests__/index.test.ts +1335 -0
  296. package/packages/src/ecosystem/tools/index.ts +1877 -0
  297. package/packages/src/index.ts +120 -0
  298. package/packages/src/interfaces/mcp/client.ts +389 -0
  299. package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
  300. package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
  301. package/packages/src/interfaces/ui/index.ts +161 -0
  302. package/packages/src/interfaces/ui/lmdb.db +0 -0
  303. package/packages/src/interfaces/ui/lmdb.db-lock +0 -0
  304. package/packages/src/interfaces/ui/spinner.ts +451 -0
  305. package/packages/src/interfaces/ui/terminal/cli/index.ts +228 -0
  306. package/packages/src/interfaces/ui/terminal/lmdb.db +0 -0
  307. package/packages/src/interfaces/ui/terminal/lmdb.db-lock +0 -0
  308. package/packages/src/interfaces/ui/terminal/shared/args.ts +222 -0
  309. package/packages/src/interfaces/ui/terminal/shared/index.ts +71 -0
  310. package/packages/src/interfaces/ui/terminal/shared/loading-state.ts +322 -0
  311. package/packages/src/interfaces/ui/terminal/shared/query.ts +146 -0
  312. package/packages/src/interfaces/ui/terminal/shared/setup.ts +295 -0
  313. package/packages/src/interfaces/ui/terminal/shared/status-line.ts +358 -0
  314. package/packages/src/interfaces/ui/terminal/shared/system-prompt.ts +146 -0
  315. package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +262 -0
  316. package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +232 -0
  317. package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +62 -0
  318. package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +537 -0
  319. package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +107 -0
  320. package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +240 -0
  321. package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +54 -0
  322. package/packages/src/interfaces/ui/terminal/tui/commands.ts +438 -0
  323. package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +584 -0
  324. package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +614 -0
  325. package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +333 -0
  326. package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +604 -0
  327. package/packages/src/interfaces/ui/terminal/tui/components/index.ts +118 -0
  328. package/packages/src/interfaces/ui/terminal/tui/console.ts +49 -0
  329. package/packages/src/interfaces/ui/terminal/tui/index.ts +90 -0
  330. package/packages/src/interfaces/ui/terminal/tui/run.tsx +42 -0
  331. package/packages/src/interfaces/ui/terminal/tui/spinner.ts +69 -0
  332. package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +390 -0
  333. package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +422 -0
  334. package/packages/src/interfaces/ui/terminal/tui/types.ts +186 -0
  335. package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +104 -0
  336. package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +239 -0
  337. package/packages/src/lmdb.db +0 -0
  338. package/packages/src/lmdb.db-lock +0 -0
  339. package/packages/src/native/index.ts +2345 -0
  340. package/packages/src/teammates/index.ts +982 -0
  341. package/packages/src/types/index.ts +722 -0
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Context Compaction - Reduces context size while preserving important information
3
+ *
4
+ * When the context window fills up, we need to compact messages to continue.
5
+ * This module provides token estimation, summarization, and compaction utilities.
6
+ */
7
+ import { SUMMARIZATION_MODEL } from "./models.js";
8
+ // ============================================
9
+ // CONSTANTS
10
+ // ============================================
11
+ /** Approximate characters per token (rough estimate for Claude models) */
12
+ const CHARS_PER_TOKEN = 4;
13
+ /** Default number of recent messages to keep during compaction */
14
+ const DEFAULT_KEEP_LAST = 5;
15
+ /** Default number of initial messages to keep (usually just the first user query) */
16
+ const DEFAULT_KEEP_FIRST = 1;
17
+ /** Minimum messages required before compaction is possible */
18
+ const MIN_MESSAGES_FOR_COMPACTION = 8;
19
+ /** Default threshold for proactive compaction (90% of max tokens) */
20
+ const DEFAULT_COMPACTION_THRESHOLD = 0.9;
21
+ /** Maximum length for summary text before truncation */
22
+ const MAX_SUMMARY_LENGTH = 8000;
23
+ /** Maximum tokens for summary output */
24
+ const SUMMARY_MAX_TOKENS = 2000;
25
+ /** System prompt for summarization */
26
+ const SUMMARIZATION_SYSTEM_PROMPT = `You are a context summarizer. Your job is to create concise, information-dense summaries of conversation history.
27
+
28
+ Guidelines:
29
+ - Preserve all important decisions, file changes, and key information
30
+ - Keep track of what tools were used and their outcomes
31
+ - Note any errors encountered and how they were resolved
32
+ - Maintain chronological flow
33
+ - Be extremely concise - use bullet points and short sentences
34
+ - Focus on information that would be needed to continue the conversation
35
+ - Do not include pleasantries or filler text
36
+
37
+ Format your summary as:
38
+ ## Summary
39
+ [Brief overview of what was discussed]
40
+
41
+ ## Key Actions
42
+ - [Action 1]
43
+ - [Action 2]
44
+
45
+ ## Files Modified
46
+ - [file]: [what changed]
47
+
48
+ ## Important Context
49
+ [Any critical information needed going forward]`;
50
+ /** User prompt template for summarization */
51
+ const SUMMARIZATION_PROMPT = `Summarize the following conversation messages for context compaction. Preserve all important information in a concise format.
52
+
53
+ Messages to summarize:
54
+ {{MESSAGES}}
55
+
56
+ Provide a dense, information-rich summary that captures everything needed to continue this conversation.`;
57
+ // ============================================
58
+ // TOKEN ESTIMATION
59
+ // ============================================
60
+ /**
61
+ * Estimate the number of tokens in a text string.
62
+ * Uses a simple heuristic of ~4 characters per token.
63
+ * This is a rough estimate; actual tokenization varies by model.
64
+ */
65
+ export function estimateTokens(text) {
66
+ if (!text || text.length === 0)
67
+ return 0;
68
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
69
+ }
70
+ /**
71
+ * Estimate tokens for a single content block
72
+ */
73
+ function estimateBlockTokens(block) {
74
+ switch (block.type) {
75
+ case "text":
76
+ return estimateTokens(block.text);
77
+ case "image":
78
+ // Images are roughly 85-110 tokens for standard sizes
79
+ // Use 100 as an average estimate
80
+ return 100;
81
+ case "tool_use":
82
+ // Tool use: name + JSON input
83
+ const toolInput = JSON.stringify(block.input);
84
+ return estimateTokens(block.name) + estimateTokens(toolInput) + 10; // overhead
85
+ case "tool_result":
86
+ // Tool result: content + metadata
87
+ if (typeof block.content === "string") {
88
+ return estimateTokens(block.content) + 10;
89
+ }
90
+ return block.content.reduce((sum, b) => sum + estimateBlockTokens(b), 0) + 10;
91
+ case "thinking":
92
+ return estimateTokens(block.thinking);
93
+ default:
94
+ return 0;
95
+ }
96
+ }
97
+ /**
98
+ * Estimate the total number of tokens in a message
99
+ */
100
+ function estimateMessageTokens(message) {
101
+ // Role overhead (~4 tokens)
102
+ const roleOverhead = 4;
103
+ // Sum all content blocks
104
+ const contentTokens = message.content.reduce((sum, block) => sum + estimateBlockTokens(block), 0);
105
+ return roleOverhead + contentTokens;
106
+ }
107
+ /**
108
+ * Get total estimated tokens across all messages
109
+ */
110
+ export function estimateMessagesTokens(messages) {
111
+ if (!messages || messages.length === 0)
112
+ return 0;
113
+ return messages.reduce((sum, msg) => sum + estimateMessageTokens(msg), 0);
114
+ }
115
+ // ============================================
116
+ // CONTENT EXTRACTION
117
+ // ============================================
118
+ /**
119
+ * Extract text content from a message for summarization
120
+ */
121
+ function extractTextFromMessage(message) {
122
+ const parts = [];
123
+ for (const block of message.content) {
124
+ switch (block.type) {
125
+ case "text":
126
+ parts.push(block.text);
127
+ break;
128
+ case "tool_use":
129
+ parts.push(`[Tool: ${block.name}(${JSON.stringify(block.input)})]`);
130
+ break;
131
+ case "tool_result":
132
+ const content = typeof block.content === "string"
133
+ ? block.content
134
+ : block.content.map(b => b.type === "text" ? b.text : "[content]").join("");
135
+ parts.push(`[Result: ${content.slice(0, 500)}${content.length > 500 ? "..." : ""}]`);
136
+ break;
137
+ case "thinking":
138
+ parts.push(`[Thinking: ${block.thinking.slice(0, 200)}...]`);
139
+ break;
140
+ }
141
+ }
142
+ return parts.join("\n");
143
+ }
144
+ /**
145
+ * Extract tool use/result pairs from messages for preservation
146
+ */
147
+ function extractToolPairs(messages) {
148
+ const toolPairs = new Map();
149
+ // First pass: collect all tool uses
150
+ for (const message of messages) {
151
+ for (const block of message.content) {
152
+ if (block.type === "tool_use") {
153
+ toolPairs.set(block.id, { use: block });
154
+ }
155
+ }
156
+ }
157
+ // Second pass: match results to uses
158
+ for (const message of messages) {
159
+ for (const block of message.content) {
160
+ if (block.type === "tool_result") {
161
+ const pair = toolPairs.get(block.tool_use_id);
162
+ if (pair) {
163
+ pair.result = block;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ return toolPairs;
169
+ }
170
+ // ============================================
171
+ // SUMMARIZATION
172
+ // ============================================
173
+ /**
174
+ * Summarize a range of messages into a single text.
175
+ * This is a simple implementation that concatenates and truncates.
176
+ * Can be enhanced later with LLM-based summarization.
177
+ */
178
+ export async function summarizeMessages(messages) {
179
+ if (!messages || messages.length === 0) {
180
+ return "";
181
+ }
182
+ const summaryParts = [];
183
+ summaryParts.push(`[Context Summary: ${messages.length} messages compacted]\n`);
184
+ // Track tool operations for a cleaner summary
185
+ const toolOperations = [];
186
+ for (let i = 0; i < messages.length; i++) {
187
+ const message = messages[i];
188
+ if (!message)
189
+ continue;
190
+ const role = message.role.toUpperCase();
191
+ const text = extractTextFromMessage(message);
192
+ // Track tool operations
193
+ for (const block of message.content) {
194
+ if (block.type === "tool_use") {
195
+ toolOperations.push(`${block.name}`);
196
+ }
197
+ }
198
+ // Add truncated message content
199
+ const truncated = text.length > 300 ? `${text.slice(0, 300)}...` : text;
200
+ summaryParts.push(`${role}: ${truncated}\n`);
201
+ }
202
+ // Add tool summary
203
+ if (toolOperations.length > 0) {
204
+ const toolCounts = toolOperations.reduce((acc, tool) => {
205
+ acc[tool] = (acc[tool] || 0) + 1;
206
+ return acc;
207
+ }, {});
208
+ const toolSummary = Object.entries(toolCounts)
209
+ .map(([name, count]) => `${name}(${count})`)
210
+ .join(", ");
211
+ summaryParts.push(`\nTools used: ${toolSummary}\n`);
212
+ }
213
+ let summary = summaryParts.join("");
214
+ // Truncate if too long
215
+ if (summary.length > MAX_SUMMARY_LENGTH) {
216
+ summary = summary.slice(0, MAX_SUMMARY_LENGTH) + "\n...[truncated]";
217
+ }
218
+ return summary;
219
+ }
220
+ /**
221
+ * Summarize messages using an LLM for better context preservation.
222
+ * Falls back to simple truncation if LLM fails or no API key provided.
223
+ */
224
+ export async function summarizeWithLLM(messages, options = {}) {
225
+ const { apiKey = process.env.ANTHROPIC_AUTH_TOKEN || process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_API_KEY, model = SUMMARIZATION_MODEL, baseUrl = process.env.ANTHROPIC_BASE_URL || "https://api.anthropic.com", timeout = 30000, } = options;
226
+ // No API key - fall back to simple summarization
227
+ if (!apiKey) {
228
+ return summarizeMessages(messages);
229
+ }
230
+ try {
231
+ // Build the conversation text for summarization
232
+ const conversationText = messages.map((msg) => {
233
+ const role = msg.role.toUpperCase();
234
+ const text = extractTextFromMessage(msg);
235
+ // Extract tool info
236
+ const tools = [];
237
+ for (const block of msg.content) {
238
+ if (block.type === "tool_use") {
239
+ tools.push(`[TOOL_USE: ${block.name}]`);
240
+ }
241
+ else if (block.type === "tool_result") {
242
+ const resultBlock = block;
243
+ const preview = typeof resultBlock.content === "string"
244
+ ? resultBlock.content.slice(0, 200)
245
+ : "[complex result]";
246
+ tools.push(`[TOOL_RESULT: ${resultBlock.is_error ? "ERROR" : "OK"}] ${preview}`);
247
+ }
248
+ }
249
+ const toolsStr = tools.length > 0 ? `\n${tools.join("\n")}` : "";
250
+ return `${role}:\n${text.slice(0, 2000)}${toolsStr}`;
251
+ }).join("\n\n---\n\n");
252
+ // Build request
253
+ const requestBody = {
254
+ model,
255
+ max_tokens: SUMMARY_MAX_TOKENS,
256
+ system: SUMMARIZATION_SYSTEM_PROMPT,
257
+ messages: [{
258
+ role: "user",
259
+ content: SUMMARIZATION_PROMPT.replace("{{MESSAGES}}", conversationText),
260
+ }],
261
+ };
262
+ // Make API call with timeout
263
+ const controller = new AbortController();
264
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
265
+ try {
266
+ const response = await fetch(`${baseUrl}/v1/messages`, {
267
+ method: "POST",
268
+ headers: {
269
+ "Content-Type": "application/json",
270
+ "x-api-key": apiKey,
271
+ "anthropic-version": "2023-06-01",
272
+ },
273
+ body: JSON.stringify(requestBody),
274
+ signal: controller.signal,
275
+ });
276
+ clearTimeout(timeoutId);
277
+ if (!response.ok) {
278
+ const errorText = await response.text();
279
+ console.error(`\x1b[33m[Compaction] LLM summarization failed: ${response.status} - ${errorText}\x1b[0m`);
280
+ return summarizeMessages(messages);
281
+ }
282
+ const data = await response.json();
283
+ // Extract text from response
284
+ const summaryText = data.content
285
+ ?.filter((block) => block.type === "text")
286
+ .map((block) => block.text || "")
287
+ .join("\n") || "";
288
+ if (!summaryText) {
289
+ console.error("\x1b[33m[Compaction] LLM returned empty summary\x1b[0m");
290
+ return summarizeMessages(messages);
291
+ }
292
+ // Log usage for debugging
293
+ if (data.usage) {
294
+ console.log(`\x1b[90m[Compaction] LLM summary: ${data.usage.input_tokens} in, ${data.usage.output_tokens} out\x1b[0m`);
295
+ }
296
+ return `[LLM Summary of ${messages.length} messages]\n\n${summaryText}`;
297
+ }
298
+ finally {
299
+ clearTimeout(timeoutId);
300
+ }
301
+ }
302
+ catch (error) {
303
+ const errorMsg = error instanceof Error ? error.message : String(error);
304
+ console.error(`\x1b[33m[Compaction] LLM summarization error: ${errorMsg}\x1b[0m`);
305
+ // Fall back to simple summarization
306
+ return summarizeMessages(messages);
307
+ }
308
+ }
309
+ /**
310
+ * Compact messages to fit within a token limit.
311
+ *
312
+ * Strategy:
313
+ * 1. Always keep the first N messages (original query)
314
+ * 2. Always keep the last M messages (recent context)
315
+ * 3. Summarize middle messages into a single "context summary" user message
316
+ * 4. Preserve tool_use/tool_result pairs when possible
317
+ */
318
+ export async function compactMessages(messages, maxTokens, options = {}) {
319
+ const { keepFirst = DEFAULT_KEEP_FIRST, keepLast = DEFAULT_KEEP_LAST, preserveToolPairs = true, useLLMSummarization = true, // Default to LLM summarization
320
+ apiKey, baseUrl, } = options;
321
+ const tokensBefore = estimateMessagesTokens(messages);
322
+ // If already under limit, no compaction needed
323
+ if (tokensBefore <= maxTokens) {
324
+ return {
325
+ messages,
326
+ messagesRemoved: 0,
327
+ tokensBefore,
328
+ tokensAfter: tokensBefore,
329
+ didCompact: false,
330
+ };
331
+ }
332
+ // Not enough messages to compact - silent return
333
+ if (messages.length <= keepFirst + keepLast) {
334
+ return {
335
+ messages,
336
+ messagesRemoved: 0,
337
+ tokensBefore,
338
+ tokensAfter: tokensBefore,
339
+ didCompact: false,
340
+ };
341
+ }
342
+ // Extract segments
343
+ const firstMessages = messages.slice(0, keepFirst);
344
+ const middleMessages = messages.slice(keepFirst, -keepLast);
345
+ const lastMessages = messages.slice(-keepLast);
346
+ // Create summary of middle messages (use LLM if available, fallback to simple)
347
+ const summary = useLLMSummarization
348
+ ? await summarizeWithLLM(middleMessages, { apiKey, baseUrl })
349
+ : await summarizeMessages(middleMessages);
350
+ // Build summary message
351
+ const summaryMessage = {
352
+ role: "user",
353
+ content: [{
354
+ type: "text",
355
+ text: `[Previous context has been compacted for continuity]\n\n${summary}`,
356
+ }],
357
+ };
358
+ // Optionally preserve important tool pairs
359
+ let preservedBlocks = [];
360
+ if (preserveToolPairs && middleMessages.length > 0) {
361
+ const toolPairs = extractToolPairs(middleMessages);
362
+ // Keep the most recent tool use/result pairs (up to 3)
363
+ const recentPairs = Array.from(toolPairs.values())
364
+ .slice(-3)
365
+ .filter(pair => pair.result && !pair.result.is_error);
366
+ for (const pair of recentPairs) {
367
+ preservedBlocks.push(pair.use);
368
+ if (pair.result) {
369
+ preservedBlocks.push(pair.result);
370
+ }
371
+ }
372
+ }
373
+ // Build compacted message list
374
+ const compacted = [
375
+ ...firstMessages,
376
+ summaryMessage,
377
+ ];
378
+ // Add preserved tool results if any
379
+ if (preservedBlocks.length > 0) {
380
+ compacted.push({
381
+ role: "assistant",
382
+ content: preservedBlocks.filter(b => b.type === "tool_use"),
383
+ });
384
+ compacted.push({
385
+ role: "user",
386
+ content: preservedBlocks.filter(b => b.type === "tool_result"),
387
+ });
388
+ }
389
+ // Add recent messages
390
+ compacted.push(...lastMessages);
391
+ const tokensAfter = estimateMessagesTokens(compacted);
392
+ const messagesRemoved = messages.length - compacted.length;
393
+ console.log(`Context compaction: ${messages.length} -> ${compacted.length} messages, ${tokensBefore} -> ${tokensAfter} tokens`);
394
+ return {
395
+ messages: compacted,
396
+ messagesRemoved,
397
+ tokensBefore,
398
+ tokensAfter,
399
+ didCompact: true,
400
+ };
401
+ }
402
+ /**
403
+ * Check if compaction is needed proactively.
404
+ * Returns true if current token usage exceeds the threshold AND there are enough messages to compact.
405
+ */
406
+ export function needsCompaction(messages, maxTokens, threshold = DEFAULT_COMPACTION_THRESHOLD) {
407
+ // Not enough messages to meaningfully compact
408
+ if (messages.length < MIN_MESSAGES_FOR_COMPACTION) {
409
+ return false;
410
+ }
411
+ const currentTokens = estimateMessagesTokens(messages);
412
+ const thresholdTokens = Math.floor(maxTokens * threshold);
413
+ return currentTokens >= thresholdTokens;
414
+ }
415
+ /**
416
+ * Get compaction statistics for logging/metrics
417
+ */
418
+ export function getCompactionStats(result) {
419
+ if (!result.didCompact) {
420
+ return { reductionPercent: 0, tokensSaved: 0 };
421
+ }
422
+ const tokensSaved = result.tokensBefore - result.tokensAfter;
423
+ const reductionPercent = (tokensSaved / result.tokensBefore) * 100;
424
+ return {
425
+ reductionPercent: Math.round(reductionPercent * 100) / 100,
426
+ tokensSaved,
427
+ };
428
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Git Status - Retrieve repository status information
3
+ * Uses Bun subprocess to run git commands
4
+ */
5
+ import type { GitStatus } from "../types/index.js";
6
+ /**
7
+ * Get comprehensive git status for a working directory
8
+ *
9
+ * @param workingDirectory - The directory to check git status for
10
+ * @returns GitStatus object if in a git repository, null otherwise
11
+ */
12
+ export declare function getGitStatus(workingDirectory: string): Promise<GitStatus | null>;
13
+ /**
14
+ * Check if there are any uncommitted changes
15
+ */
16
+ export declare function hasUncommittedChanges(status: GitStatus): boolean;
17
+ /**
18
+ * Check if the repository is clean (no changes and synced with upstream)
19
+ */
20
+ export declare function isRepositoryClean(status: GitStatus): boolean;
21
+ /**
22
+ * Get a human-readable summary of the git status
23
+ */
24
+ export declare function getGitStatusSummary(status: GitStatus): string;
25
+ //# sourceMappingURL=git-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-status.d.ts","sourceRoot":"","sources":["../../packages/src/core/git-status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AA4KnD;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CA6B3B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAOhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAE5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAuB7D"}
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Git Status - Retrieve repository status information
3
+ * Uses Bun subprocess to run git commands
4
+ */
5
+ /**
6
+ * Run a git command and return its output
7
+ */
8
+ async function runGitCommand(args, cwd) {
9
+ const proc = Bun.spawn(["git", ...args], {
10
+ cwd,
11
+ stdout: "pipe",
12
+ stderr: "pipe",
13
+ });
14
+ const stdout = await new Response(proc.stdout).text();
15
+ const stderr = await new Response(proc.stderr).text();
16
+ const exitCode = await proc.exited;
17
+ return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode };
18
+ }
19
+ /**
20
+ * Check if the directory is a git repository
21
+ */
22
+ async function isGitRepository(workingDirectory) {
23
+ const { exitCode } = await runGitCommand(["rev-parse", "--git-dir"], workingDirectory);
24
+ return exitCode === 0;
25
+ }
26
+ /**
27
+ * Get the current branch name
28
+ */
29
+ async function getBranch(workingDirectory) {
30
+ const { stdout, exitCode } = await runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], workingDirectory);
31
+ if (exitCode !== 0) {
32
+ return "HEAD";
33
+ }
34
+ return stdout || "HEAD";
35
+ }
36
+ /**
37
+ * Get ahead/behind counts compared to upstream
38
+ */
39
+ async function getAheadBehind(workingDirectory) {
40
+ const { stdout, exitCode } = await runGitCommand(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"], workingDirectory);
41
+ if (exitCode !== 0 || !stdout) {
42
+ return { ahead: 0, behind: 0 };
43
+ }
44
+ const parts = stdout.split(/\s+/);
45
+ if (parts.length >= 2 && parts[0] !== undefined && parts[1] !== undefined) {
46
+ return {
47
+ ahead: parseInt(parts[0], 10) || 0,
48
+ behind: parseInt(parts[1], 10) || 0,
49
+ };
50
+ }
51
+ return { ahead: 0, behind: 0 };
52
+ }
53
+ /**
54
+ * Parse git status porcelain output
55
+ * Format: XY PATH where X is staged status, Y is unstaged status
56
+ *
57
+ * Status codes:
58
+ * - ' ' (space): unmodified
59
+ * - 'M': modified
60
+ * - 'A': added (staged)
61
+ * - 'D': deleted
62
+ * - 'R': renamed
63
+ * - 'C': copied
64
+ * - 'U': unmerged (conflict)
65
+ * - '?': untracked
66
+ * - '!': ignored
67
+ */
68
+ async function getFileStatus(workingDirectory) {
69
+ const { stdout, exitCode } = await runGitCommand(["status", "--porcelain"], workingDirectory);
70
+ if (exitCode !== 0 || !stdout) {
71
+ return {
72
+ staged: [],
73
+ unstaged: [],
74
+ untracked: [],
75
+ conflicted: [],
76
+ };
77
+ }
78
+ const staged = [];
79
+ const unstaged = [];
80
+ const untracked = [];
81
+ const conflicted = [];
82
+ const lines = stdout.split("\n");
83
+ for (const line of lines) {
84
+ if (!line)
85
+ continue;
86
+ // Porcelain format: XY PATH or XY ORIG_PATH -> PATH
87
+ const x = line[0]; // Staged status
88
+ const y = line[1]; // Unstaged status
89
+ let path = line.substring(3);
90
+ // Handle renamed files (format: "R old -> new")
91
+ if (path.includes(" -> ")) {
92
+ const splitPath = path.split(" -> ");
93
+ path = splitPath[1] ?? splitPath[0] ?? path;
94
+ }
95
+ // Check for conflicts (U or both A/A, D/D, etc.)
96
+ const isConflicted = x === "U" ||
97
+ y === "U" ||
98
+ (x === "A" && y === "A") ||
99
+ (x === "D" && y === "D") ||
100
+ x === "A" && y === "U" ||
101
+ x === "U" && y === "A" ||
102
+ x === "D" && y === "U" ||
103
+ x === "U" && y === "D";
104
+ if (isConflicted) {
105
+ conflicted.push(path);
106
+ continue;
107
+ }
108
+ // Untracked files
109
+ if (x === "?" && y === "?") {
110
+ untracked.push(path);
111
+ continue;
112
+ }
113
+ // Staged changes (X is not space or ?)
114
+ if (x !== " " && x !== "?" && x !== "!") {
115
+ staged.push(path);
116
+ }
117
+ // Unstaged changes (Y is not space or ?)
118
+ if (y !== " " && y !== "?" && y !== "!") {
119
+ // Don't double-add if already in staged
120
+ if (!staged.includes(path)) {
121
+ unstaged.push(path);
122
+ }
123
+ }
124
+ }
125
+ return { staged, unstaged, untracked, conflicted };
126
+ }
127
+ /**
128
+ * Get comprehensive git status for a working directory
129
+ *
130
+ * @param workingDirectory - The directory to check git status for
131
+ * @returns GitStatus object if in a git repository, null otherwise
132
+ */
133
+ export async function getGitStatus(workingDirectory) {
134
+ try {
135
+ // First check if this is a git repository
136
+ const isRepo = await isGitRepository(workingDirectory);
137
+ if (!isRepo) {
138
+ return null;
139
+ }
140
+ // Run all status queries in parallel for better performance
141
+ const [branch, aheadBehind, fileStatus] = await Promise.all([
142
+ getBranch(workingDirectory),
143
+ getAheadBehind(workingDirectory),
144
+ getFileStatus(workingDirectory),
145
+ ]);
146
+ return {
147
+ branch,
148
+ ahead: aheadBehind.ahead,
149
+ behind: aheadBehind.behind,
150
+ staged: fileStatus.staged,
151
+ unstaged: fileStatus.unstaged,
152
+ untracked: fileStatus.untracked,
153
+ conflicted: fileStatus.conflicted,
154
+ };
155
+ }
156
+ catch (error) {
157
+ // Log error for debugging but return null gracefully
158
+ console.error("Error getting git status:", error);
159
+ return null;
160
+ }
161
+ }
162
+ /**
163
+ * Check if there are any uncommitted changes
164
+ */
165
+ export function hasUncommittedChanges(status) {
166
+ return (status.staged.length > 0 ||
167
+ status.unstaged.length > 0 ||
168
+ status.untracked.length > 0 ||
169
+ status.conflicted.length > 0);
170
+ }
171
+ /**
172
+ * Check if the repository is clean (no changes and synced with upstream)
173
+ */
174
+ export function isRepositoryClean(status) {
175
+ return !hasUncommittedChanges(status) && status.ahead === 0 && status.behind === 0;
176
+ }
177
+ /**
178
+ * Get a human-readable summary of the git status
179
+ */
180
+ export function getGitStatusSummary(status) {
181
+ const parts = [];
182
+ parts.push(`branch: ${status.branch}`);
183
+ if (status.ahead > 0 || status.behind > 0) {
184
+ const sync = [];
185
+ if (status.ahead > 0)
186
+ sync.push(`ahead ${status.ahead}`);
187
+ if (status.behind > 0)
188
+ sync.push(`behind ${status.behind}`);
189
+ parts.push(`(${sync.join(", ")})`);
190
+ }
191
+ const changes = [];
192
+ if (status.staged.length > 0)
193
+ changes.push(`${status.staged.length} staged`);
194
+ if (status.unstaged.length > 0)
195
+ changes.push(`${status.unstaged.length} unstaged`);
196
+ if (status.untracked.length > 0)
197
+ changes.push(`${status.untracked.length} untracked`);
198
+ if (status.conflicted.length > 0)
199
+ changes.push(`${status.conflicted.length} conflicted`);
200
+ if (changes.length > 0) {
201
+ parts.push(`[${changes.join(", ")}]`);
202
+ }
203
+ return parts.join(" ");
204
+ }