@ebowwa/coder 0.2.1 → 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 (401) hide show
  1. package/README.md +31 -32
  2. package/dist/core/__tests__/permissions.test.d.ts +12 -0
  3. package/dist/core/__tests__/permissions.test.d.ts.map +1 -0
  4. package/dist/core/__tests__/permissions.test.js +851 -0
  5. package/dist/core/agent-loop/__tests__/compaction.test.d.ts +5 -0
  6. package/dist/core/agent-loop/__tests__/compaction.test.d.ts.map +1 -0
  7. package/dist/core/agent-loop/__tests__/compaction.test.js +209 -0
  8. package/dist/core/agent-loop/__tests__/formatters.test.d.ts +5 -0
  9. package/dist/core/agent-loop/__tests__/formatters.test.d.ts.map +1 -0
  10. package/dist/core/agent-loop/__tests__/formatters.test.js +195 -0
  11. package/dist/core/agent-loop/__tests__/index.test.d.ts +5 -0
  12. package/dist/core/agent-loop/__tests__/index.test.d.ts.map +1 -0
  13. package/dist/core/agent-loop/__tests__/index.test.js +121 -0
  14. package/dist/core/agent-loop/__tests__/loop-state.test.d.ts +5 -0
  15. package/dist/core/agent-loop/__tests__/loop-state.test.d.ts.map +1 -0
  16. package/dist/core/agent-loop/__tests__/loop-state.test.js +340 -0
  17. package/dist/core/agent-loop/__tests__/message-builder.test.d.ts +5 -0
  18. package/dist/core/agent-loop/__tests__/message-builder.test.d.ts.map +1 -0
  19. package/dist/core/agent-loop/__tests__/message-builder.test.js +178 -0
  20. package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts +5 -0
  21. package/dist/core/agent-loop/__tests__/tool-executor.test.d.ts.map +1 -0
  22. package/dist/core/agent-loop/__tests__/tool-executor.test.js +331 -0
  23. package/dist/core/agent-loop/compaction.d.ts +39 -0
  24. package/dist/core/agent-loop/compaction.d.ts.map +1 -0
  25. package/dist/core/agent-loop/compaction.js +51 -0
  26. package/dist/core/agent-loop/formatters.d.ts +21 -0
  27. package/dist/core/agent-loop/formatters.d.ts.map +1 -0
  28. package/dist/core/agent-loop/formatters.js +42 -0
  29. package/dist/core/agent-loop/index.d.ts +25 -0
  30. package/dist/core/agent-loop/index.d.ts.map +1 -0
  31. package/dist/core/agent-loop/index.js +83 -0
  32. package/dist/core/agent-loop/loop-state.d.ts +74 -0
  33. package/dist/core/agent-loop/loop-state.d.ts.map +1 -0
  34. package/dist/core/agent-loop/loop-state.js +147 -0
  35. package/dist/core/agent-loop/message-builder.d.ts +13 -0
  36. package/dist/core/agent-loop/message-builder.d.ts.map +1 -0
  37. package/dist/core/agent-loop/message-builder.js +49 -0
  38. package/dist/core/agent-loop/tool-executor.d.ts +23 -0
  39. package/dist/core/agent-loop/tool-executor.d.ts.map +1 -0
  40. package/dist/core/agent-loop/tool-executor.js +152 -0
  41. package/dist/core/agent-loop/turn-executor.d.ts +57 -0
  42. package/dist/core/agent-loop/turn-executor.d.ts.map +1 -0
  43. package/dist/core/agent-loop/turn-executor.js +124 -0
  44. package/dist/core/agent-loop/types.d.ts +141 -0
  45. package/dist/core/agent-loop/types.d.ts.map +1 -0
  46. package/dist/core/agent-loop/types.js +4 -0
  47. package/dist/core/agent-loop.d.ts +17 -0
  48. package/dist/core/agent-loop.d.ts.map +1 -0
  49. package/dist/core/agent-loop.js +16 -0
  50. package/dist/core/api-client-impl.d.ts +62 -0
  51. package/dist/core/api-client-impl.d.ts.map +1 -0
  52. package/dist/core/api-client-impl.js +479 -0
  53. package/dist/core/api-client.d.ts +6 -0
  54. package/dist/core/api-client.d.ts.map +1 -0
  55. package/dist/core/api-client.js +5 -0
  56. package/dist/core/checkpoints.d.ts +128 -0
  57. package/dist/core/checkpoints.d.ts.map +1 -0
  58. package/dist/core/checkpoints.js +438 -0
  59. package/dist/core/claude-md.d.ts +71 -0
  60. package/dist/core/claude-md.d.ts.map +1 -0
  61. package/dist/core/claude-md.js +198 -0
  62. package/dist/core/cognitive-security/hooks.d.ts +138 -0
  63. package/dist/core/cognitive-security/hooks.d.ts.map +1 -0
  64. package/dist/core/cognitive-security/hooks.js +389 -0
  65. package/dist/core/cognitive-security/index.d.ts +751 -0
  66. package/dist/core/cognitive-security/index.d.ts.map +1 -0
  67. package/dist/core/cognitive-security/index.js +1123 -0
  68. package/dist/core/cognitive-security/middleware.d.ts +136 -0
  69. package/dist/core/cognitive-security/middleware.d.ts.map +1 -0
  70. package/dist/core/cognitive-security/middleware.js +376 -0
  71. package/dist/core/config-loader.d.ts +127 -0
  72. package/dist/core/config-loader.d.ts.map +1 -0
  73. package/dist/core/config-loader.js +219 -0
  74. package/dist/core/context-compaction.d.ts +87 -0
  75. package/dist/core/context-compaction.d.ts.map +1 -0
  76. package/dist/core/context-compaction.js +428 -0
  77. package/dist/core/git-status.d.ts +25 -0
  78. package/dist/core/git-status.d.ts.map +1 -0
  79. package/dist/core/git-status.js +204 -0
  80. package/dist/core/image.d.ts +69 -0
  81. package/dist/core/image.d.ts.map +1 -0
  82. package/dist/core/image.js +290 -0
  83. package/dist/core/image.test.d.ts +2 -0
  84. package/dist/core/image.test.d.ts.map +1 -0
  85. package/dist/core/image.test.js +149 -0
  86. package/dist/core/models.d.ts +123 -0
  87. package/dist/core/models.d.ts.map +1 -0
  88. package/dist/core/models.js +325 -0
  89. package/dist/core/permissions.d.ts +81 -0
  90. package/dist/core/permissions.d.ts.map +1 -0
  91. package/dist/core/permissions.js +327 -0
  92. package/dist/core/retry.d.ts +25 -0
  93. package/dist/core/retry.d.ts.map +1 -0
  94. package/dist/core/retry.js +121 -0
  95. package/dist/core/session-store.d.ts +9 -0
  96. package/dist/core/session-store.d.ts.map +1 -0
  97. package/dist/core/session-store.js +10 -0
  98. package/dist/core/sessions/export.d.ts +47 -0
  99. package/dist/core/sessions/export.d.ts.map +1 -0
  100. package/dist/core/sessions/export.js +256 -0
  101. package/dist/core/sessions/index.d.ts +132 -0
  102. package/dist/core/sessions/index.d.ts.map +1 -0
  103. package/dist/core/sessions/index.js +442 -0
  104. package/dist/core/sessions/metadata.d.ts +77 -0
  105. package/dist/core/sessions/metadata.d.ts.map +1 -0
  106. package/dist/core/sessions/metadata.js +233 -0
  107. package/dist/core/sessions/persistence.d.ts +72 -0
  108. package/dist/core/sessions/persistence.d.ts.map +1 -0
  109. package/dist/core/sessions/persistence.js +201 -0
  110. package/dist/core/sessions/types.d.ts +110 -0
  111. package/dist/core/sessions/types.d.ts.map +1 -0
  112. package/dist/core/sessions/types.js +4 -0
  113. package/dist/core/stream-highlighter.d.ts +18 -0
  114. package/dist/core/stream-highlighter.d.ts.map +1 -0
  115. package/dist/core/stream-highlighter.js +916 -0
  116. package/dist/core/system-reminders.d.ts +89 -0
  117. package/dist/core/system-reminders.d.ts.map +1 -0
  118. package/dist/core/system-reminders.js +285 -0
  119. package/dist/ecosystem/hooks/__tests__/index.test.d.ts +5 -0
  120. package/dist/ecosystem/hooks/__tests__/index.test.d.ts.map +1 -0
  121. package/dist/ecosystem/hooks/__tests__/index.test.js +458 -0
  122. package/dist/ecosystem/hooks/index.d.ts +59 -0
  123. package/dist/ecosystem/hooks/index.d.ts.map +1 -0
  124. package/dist/ecosystem/hooks/index.js +294 -0
  125. package/dist/ecosystem/hooks/prompt-evaluator.d.ts +32 -0
  126. package/dist/ecosystem/hooks/prompt-evaluator.d.ts.map +1 -0
  127. package/dist/ecosystem/hooks/prompt-evaluator.js +229 -0
  128. package/dist/ecosystem/skills/index.d.ts +55 -0
  129. package/dist/ecosystem/skills/index.d.ts.map +1 -0
  130. package/dist/ecosystem/skills/index.js +258 -0
  131. package/dist/ecosystem/tools/__tests__/index.test.d.ts +7 -0
  132. package/dist/ecosystem/tools/__tests__/index.test.d.ts.map +1 -0
  133. package/dist/ecosystem/tools/__tests__/index.test.js +856 -0
  134. package/dist/ecosystem/tools/index.d.ts +24 -0
  135. package/dist/ecosystem/tools/index.d.ts.map +1 -0
  136. package/dist/ecosystem/tools/index.js +1709 -0
  137. package/dist/index.d.ts +24 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +32 -2
  140. package/dist/interfaces/mcp/client.d.ts +40 -0
  141. package/dist/interfaces/mcp/client.d.ts.map +1 -0
  142. package/dist/interfaces/mcp/client.js +309 -0
  143. package/dist/interfaces/ui/index.d.ts +36 -0
  144. package/dist/interfaces/ui/index.d.ts.map +1 -0
  145. package/dist/interfaces/ui/index.js +61 -0
  146. package/dist/interfaces/ui/spinner.d.ts +140 -0
  147. package/dist/interfaces/ui/spinner.d.ts.map +1 -0
  148. package/dist/interfaces/ui/spinner.js +342 -0
  149. package/dist/interfaces/ui/terminal/cli/index.d.ts +12 -0
  150. package/dist/interfaces/ui/terminal/cli/index.d.ts.map +1 -0
  151. package/dist/interfaces/ui/terminal/cli/index.js +167 -0
  152. package/dist/interfaces/ui/terminal/shared/args.d.ts +39 -0
  153. package/dist/interfaces/ui/terminal/shared/args.d.ts.map +1 -0
  154. package/dist/interfaces/ui/terminal/shared/args.js +176 -0
  155. package/dist/interfaces/ui/terminal/shared/index.d.ts +11 -0
  156. package/dist/interfaces/ui/terminal/shared/index.d.ts.map +1 -0
  157. package/dist/interfaces/ui/terminal/shared/index.js +16 -0
  158. package/dist/interfaces/ui/terminal/shared/loading-state.d.ts +124 -0
  159. package/dist/interfaces/ui/terminal/shared/loading-state.d.ts.map +1 -0
  160. package/dist/interfaces/ui/terminal/shared/loading-state.js +246 -0
  161. package/dist/interfaces/ui/terminal/shared/query.d.ts +22 -0
  162. package/dist/interfaces/ui/terminal/shared/query.d.ts.map +1 -0
  163. package/dist/interfaces/ui/terminal/shared/query.js +100 -0
  164. package/dist/interfaces/ui/terminal/shared/setup.d.ts +33 -0
  165. package/dist/interfaces/ui/terminal/shared/setup.d.ts.map +1 -0
  166. package/dist/interfaces/ui/terminal/shared/setup.js +226 -0
  167. package/dist/interfaces/ui/terminal/shared/status-line.d.ts +117 -0
  168. package/dist/interfaces/ui/terminal/shared/status-line.d.ts.map +1 -0
  169. package/dist/interfaces/ui/terminal/shared/status-line.js +267 -0
  170. package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts +38 -0
  171. package/dist/interfaces/ui/terminal/shared/system-prompt.d.ts.map +1 -0
  172. package/dist/interfaces/ui/terminal/shared/system-prompt.js +102 -0
  173. package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts +39 -0
  174. package/dist/interfaces/ui/terminal/tui/HelpPanel.d.ts.map +1 -0
  175. package/dist/interfaces/ui/terminal/tui/HelpPanel.js +215 -0
  176. package/dist/interfaces/ui/terminal/tui/InputContext.d.ts +91 -0
  177. package/dist/interfaces/ui/terminal/tui/InputContext.d.ts.map +1 -0
  178. package/dist/interfaces/ui/terminal/tui/InputContext.js +154 -0
  179. package/dist/interfaces/ui/terminal/tui/InputField.d.ts +18 -0
  180. package/dist/interfaces/ui/terminal/tui/InputField.d.ts.map +1 -0
  181. package/dist/interfaces/ui/terminal/tui/InputField.js +41 -0
  182. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts +16 -0
  183. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.d.ts.map +1 -0
  184. package/dist/interfaces/ui/terminal/tui/InteractiveTUI.js +451 -0
  185. package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts +10 -0
  186. package/dist/interfaces/ui/terminal/tui/MessageArea.d.ts.map +1 -0
  187. package/dist/interfaces/ui/terminal/tui/MessageArea.js +91 -0
  188. package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts +48 -0
  189. package/dist/interfaces/ui/terminal/tui/MessageStore.d.ts.map +1 -0
  190. package/dist/interfaces/ui/terminal/tui/MessageStore.js +151 -0
  191. package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts +9 -0
  192. package/dist/interfaces/ui/terminal/tui/StatusBar.d.ts.map +1 -0
  193. package/dist/interfaces/ui/terminal/tui/StatusBar.js +36 -0
  194. package/dist/interfaces/ui/terminal/tui/commands.d.ts +21 -0
  195. package/dist/interfaces/ui/terminal/tui/commands.d.ts.map +1 -0
  196. package/dist/interfaces/ui/terminal/tui/commands.js +359 -0
  197. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts +115 -0
  198. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.d.ts.map +1 -0
  199. package/dist/interfaces/ui/terminal/tui/components/InteractiveElements.js +306 -0
  200. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts +92 -0
  201. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.d.ts.map +1 -0
  202. package/dist/interfaces/ui/terminal/tui/components/MultilineInput.js +399 -0
  203. package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts +59 -0
  204. package/dist/interfaces/ui/terminal/tui/components/PaneManager.d.ts.map +1 -0
  205. package/dist/interfaces/ui/terminal/tui/components/PaneManager.js +139 -0
  206. package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts +68 -0
  207. package/dist/interfaces/ui/terminal/tui/components/Sidebar.d.ts.map +1 -0
  208. package/dist/interfaces/ui/terminal/tui/components/Sidebar.js +340 -0
  209. package/dist/interfaces/ui/terminal/tui/components/index.d.ts +23 -0
  210. package/dist/interfaces/ui/terminal/tui/components/index.d.ts.map +1 -0
  211. package/dist/interfaces/ui/terminal/tui/components/index.js +51 -0
  212. package/dist/interfaces/ui/terminal/tui/console.d.ts +20 -0
  213. package/dist/interfaces/ui/terminal/tui/console.d.ts.map +1 -0
  214. package/dist/interfaces/ui/terminal/tui/console.js +46 -0
  215. package/dist/interfaces/ui/terminal/tui/index.d.ts +20 -0
  216. package/dist/interfaces/ui/terminal/tui/index.d.ts.map +1 -0
  217. package/dist/interfaces/ui/terminal/tui/index.js +28 -0
  218. package/dist/interfaces/ui/terminal/tui/run.d.ts +13 -0
  219. package/dist/interfaces/ui/terminal/tui/run.d.ts.map +1 -0
  220. package/dist/interfaces/ui/terminal/tui/run.js +31 -0
  221. package/dist/interfaces/ui/terminal/tui/spinner.d.ts +44 -0
  222. package/dist/interfaces/ui/terminal/tui/spinner.d.ts.map +1 -0
  223. package/dist/interfaces/ui/terminal/tui/spinner.js +59 -0
  224. package/dist/interfaces/ui/terminal/tui/tui-app.d.ts +39 -0
  225. package/dist/interfaces/ui/terminal/tui/tui-app.d.ts.map +1 -0
  226. package/dist/interfaces/ui/terminal/tui/tui-app.js +198 -0
  227. package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts +167 -0
  228. package/dist/interfaces/ui/terminal/tui/tui-footer.d.ts.map +1 -0
  229. package/dist/interfaces/ui/terminal/tui/tui-footer.js +330 -0
  230. package/dist/interfaces/ui/terminal/tui/types.d.ts +165 -0
  231. package/dist/interfaces/ui/terminal/tui/types.d.ts.map +1 -0
  232. package/dist/interfaces/ui/terminal/tui/types.js +5 -0
  233. package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts +23 -0
  234. package/dist/interfaces/ui/terminal/tui/useInputHandler.d.ts.map +1 -0
  235. package/dist/interfaces/ui/terminal/tui/useInputHandler.js +72 -0
  236. package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts +90 -0
  237. package/dist/interfaces/ui/terminal/tui/useNativeInput.d.ts.map +1 -0
  238. package/dist/interfaces/ui/terminal/tui/useNativeInput.js +188 -0
  239. package/dist/native/index.d.ts +480 -0
  240. package/dist/native/index.d.ts.map +1 -0
  241. package/dist/native/index.js +1625 -0
  242. package/dist/teammates/index.d.ts +161 -0
  243. package/dist/teammates/index.d.ts.map +1 -0
  244. package/dist/teammates/index.js +827 -0
  245. package/dist/types/index.d.ts +482 -0
  246. package/dist/types/index.d.ts.map +1 -0
  247. package/dist/types/index.js +52 -0
  248. package/native/README.md +5 -5
  249. package/native/index.darwin-arm64.node +0 -0
  250. package/native/index.node +0 -0
  251. package/native/package.json +4 -4
  252. package/package.json +33 -16
  253. package/packages/src/core/__tests__/permissions.test.ts +1091 -0
  254. package/packages/src/core/agent-loop/__tests__/compaction.test.ts +280 -0
  255. package/packages/src/core/agent-loop/__tests__/formatters.test.ts +234 -0
  256. package/packages/src/core/agent-loop/__tests__/index.test.ts +162 -0
  257. package/packages/src/core/agent-loop/__tests__/loop-state.test.ts +413 -0
  258. package/packages/src/core/agent-loop/__tests__/message-builder.test.ts +229 -0
  259. package/packages/src/core/agent-loop/__tests__/tool-executor.test.ts +457 -0
  260. package/packages/src/core/agent-loop/compaction.ts +88 -0
  261. package/packages/src/core/agent-loop/formatters.ts +50 -0
  262. package/packages/src/core/agent-loop/index.ts +135 -0
  263. package/packages/src/core/agent-loop/loop-state.ts +187 -0
  264. package/packages/src/core/agent-loop/message-builder.ts +62 -0
  265. package/packages/src/core/agent-loop/tool-executor.ts +211 -0
  266. package/packages/src/core/agent-loop/turn-executor.ts +222 -0
  267. package/packages/src/core/agent-loop/types.ts +148 -0
  268. package/packages/src/core/agent-loop.ts +18 -0
  269. package/packages/src/core/api-client-impl.ts +619 -0
  270. package/packages/src/core/api-client.ts +6 -0
  271. package/packages/src/core/checkpoints.ts +606 -0
  272. package/packages/src/core/claude-md.ts +272 -0
  273. package/packages/src/core/cognitive-security/hooks.ts +590 -0
  274. package/packages/src/core/cognitive-security/index.ts +2041 -0
  275. package/packages/src/core/cognitive-security/middleware.ts +536 -0
  276. package/packages/src/core/config-loader.ts +324 -0
  277. package/packages/src/core/context-compaction.ts +578 -0
  278. package/packages/src/core/git-status.ts +262 -0
  279. package/packages/src/core/image.test.ts +180 -0
  280. package/packages/src/core/image.ts +350 -0
  281. package/packages/src/core/lmdb.db +0 -0
  282. package/packages/src/core/lmdb.db-lock +0 -0
  283. package/packages/src/core/models.ts +430 -0
  284. package/packages/src/core/normalizers/todo +4 -0
  285. package/packages/src/core/permissions.ts +431 -0
  286. package/packages/src/core/retry.ts +170 -0
  287. package/packages/src/core/session-store.ts +36 -0
  288. package/packages/src/core/sessions/export.ts +329 -0
  289. package/packages/src/core/sessions/index.ts +587 -0
  290. package/packages/src/core/sessions/metadata.ts +309 -0
  291. package/packages/src/core/sessions/persistence.ts +244 -0
  292. package/packages/src/core/sessions/types.ts +169 -0
  293. package/packages/src/core/stream-highlighter.ts +1123 -0
  294. package/packages/src/core/system-reminders.ts +402 -0
  295. package/packages/src/core/todo +8 -0
  296. package/packages/src/ecosystem/hooks/__tests__/index.test.ts +561 -0
  297. package/packages/src/ecosystem/hooks/index.ts +341 -0
  298. package/packages/src/ecosystem/hooks/prompt-evaluator.ts +300 -0
  299. package/packages/src/ecosystem/skills/index.ts +295 -0
  300. package/packages/src/ecosystem/tools/__tests__/index.test.ts +1335 -0
  301. package/packages/src/ecosystem/tools/index.ts +1877 -0
  302. package/packages/src/index.ts +120 -0
  303. package/packages/src/interfaces/mcp/client.ts +389 -0
  304. package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
  305. package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
  306. package/packages/src/interfaces/ui/index.ts +161 -0
  307. package/packages/src/interfaces/ui/lmdb.db +0 -0
  308. package/packages/src/interfaces/ui/lmdb.db-lock +0 -0
  309. package/packages/src/interfaces/ui/spinner.ts +451 -0
  310. package/packages/src/interfaces/ui/terminal/cli/index.ts +228 -0
  311. package/packages/src/interfaces/ui/terminal/lmdb.db +0 -0
  312. package/packages/src/interfaces/ui/terminal/lmdb.db-lock +0 -0
  313. package/packages/src/interfaces/ui/terminal/shared/args.ts +222 -0
  314. package/packages/src/interfaces/ui/terminal/shared/index.ts +71 -0
  315. package/packages/src/interfaces/ui/terminal/shared/loading-state.ts +322 -0
  316. package/packages/src/interfaces/ui/terminal/shared/query.ts +146 -0
  317. package/packages/src/interfaces/ui/terminal/shared/setup.ts +295 -0
  318. package/packages/src/interfaces/ui/terminal/shared/status-line.ts +358 -0
  319. package/packages/src/interfaces/ui/terminal/shared/system-prompt.ts +146 -0
  320. package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +262 -0
  321. package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +232 -0
  322. package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +62 -0
  323. package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +537 -0
  324. package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +107 -0
  325. package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +240 -0
  326. package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +54 -0
  327. package/packages/src/interfaces/ui/terminal/tui/commands.ts +438 -0
  328. package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +584 -0
  329. package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +614 -0
  330. package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +333 -0
  331. package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +604 -0
  332. package/packages/src/interfaces/ui/terminal/tui/components/index.ts +118 -0
  333. package/packages/src/interfaces/ui/terminal/tui/console.ts +49 -0
  334. package/packages/src/interfaces/ui/terminal/tui/index.ts +90 -0
  335. package/packages/src/interfaces/ui/terminal/tui/run.tsx +42 -0
  336. package/packages/src/interfaces/ui/terminal/tui/spinner.ts +69 -0
  337. package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +390 -0
  338. package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +422 -0
  339. package/packages/src/interfaces/ui/terminal/tui/types.ts +186 -0
  340. package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +104 -0
  341. package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +239 -0
  342. package/packages/src/lmdb.db +0 -0
  343. package/packages/src/lmdb.db-lock +0 -0
  344. package/packages/src/native/index.ts +2345 -0
  345. package/packages/src/teammates/index.ts +982 -0
  346. package/packages/src/types/index.ts +722 -0
  347. package/dist/cli.js +0 -148
  348. package/dist/index-0pkak453.js +0 -136
  349. package/dist/index-0qd0x8b4.js +0 -110
  350. package/dist/index-0x3kprq6.js +0 -240
  351. package/dist/index-1eawy937.js +0 -308
  352. package/dist/index-24m2aygy.js +0 -240
  353. package/dist/index-29xcjnne.js +0 -280
  354. package/dist/index-2avyytn5.js +0 -349
  355. package/dist/index-4ms367ey.js +0 -136
  356. package/dist/index-4w2t3b0m.js +0 -240
  357. package/dist/index-4xfgd8nz.js +0 -261
  358. package/dist/index-5acjp9gc.js +0 -157
  359. package/dist/index-5s15hr56.js +0 -136
  360. package/dist/index-6e4wf341.js +0 -349
  361. package/dist/index-6fvnkedw.js +0 -240
  362. package/dist/index-6rqpmd4g.js +0 -128
  363. package/dist/index-77ckwnbm.js +0 -280
  364. package/dist/index-9knxy49k.js +0 -128
  365. package/dist/index-9zrnw4zx.js +0 -128
  366. package/dist/index-bk21w99v.js +0 -280
  367. package/dist/index-c41n76fv.js +0 -240
  368. package/dist/index-cb4ppjdt.js +0 -255
  369. package/dist/index-cfb2edt6.js +0 -240
  370. package/dist/index-cmfa38hh.js +0 -308
  371. package/dist/index-datjz8q1.js +0 -257
  372. package/dist/index-eadf4wvn.js +0 -240
  373. package/dist/index-em5k0m3z.js +0 -345
  374. package/dist/index-gh8r333a.js +0 -110
  375. package/dist/index-gkx6k2tr.js +0 -261
  376. package/dist/index-h5cabfks.js +0 -155
  377. package/dist/index-hcrpwyy3.js +0 -261
  378. package/dist/index-hk7fwwa8.js +0 -257
  379. package/dist/index-jb8cw7f8.js +0 -136
  380. package/dist/index-kbyw4th1.js +0 -347
  381. package/dist/index-kgj5gqnm.js +0 -345
  382. package/dist/index-mdf6xp1z.js +0 -255
  383. package/dist/index-mrhv8kvc.js +0 -280
  384. package/dist/index-mt4743dd.js +0 -161
  385. package/dist/index-qnwsg97q.js +0 -240
  386. package/dist/index-qwdy6x44.js +0 -261
  387. package/dist/index-rmj77261.js +0 -157
  388. package/dist/index-sbbw1a61.js +0 -349
  389. package/dist/index-svy5bcpn.js +0 -345
  390. package/dist/index-tvmy7tm9.js +0 -261
  391. package/dist/index-tzz4vzkj.js +0 -312
  392. package/dist/index-vz80zmhe.js +0 -110
  393. package/dist/index-wed2fk67.js +0 -240
  394. package/dist/index-wksgzz8e.js +0 -280
  395. package/dist/index-wn2m4wma.js +0 -240
  396. package/dist/index-xha05vjc.js +0 -257
  397. package/dist/index-yc6eh8p8.js +0 -136
  398. package/dist/index-ycjxx9ft.js +0 -240
  399. package/dist/index-z0gzd0fc.js +0 -110
  400. package/dist/index-z8cwtf8j.js +0 -240
  401. package/dist/index-zy5mtt00.js +0 -128
@@ -0,0 +1,982 @@
1
+ /**
2
+ * Teammate System - Multi-agent coordination
3
+ *
4
+ * Messaging Architecture:
5
+ * - File-based inbox system for cross-process communication
6
+ * - Messages stored as JSON files in ~/.claude/teams/{team}/inboxes/{teammateId}/
7
+ * - pending/ for unread messages, processed/ for read messages
8
+ */
9
+
10
+ import type { Teammate, Team, TeammateMessage, TeammateStatus } from "../types/index.js";
11
+ import { spawn } from "child_process";
12
+ import { mkdirSync, rmSync, existsSync, readFileSync, readdirSync, renameSync, writeFileSync, statSync } from "fs";
13
+ import { join, basename } from "path";
14
+
15
+ // ============================================
16
+ // FILE-BASED INBOX TYPES
17
+ // ============================================
18
+
19
+ interface StoredMessage extends TeammateMessage {
20
+ id: string;
21
+ teamName: string;
22
+ createdAt: number;
23
+ readAt?: number;
24
+ }
25
+
26
+ // ============================================
27
+ // TEAMMATE MANAGER
28
+ // ============================================
29
+
30
+ export class TeammateManager {
31
+ private teams = new Map<string, Team>();
32
+ private teammates = new Map<string, Teammate>();
33
+ private storagePath: string;
34
+
35
+ constructor(storagePath = "~/.claude/teams") {
36
+ this.storagePath = storagePath.replace("~", process.env.HOME || "");
37
+ // Ensure storage directory exists
38
+ if (!existsSync(this.storagePath)) {
39
+ mkdirSync(this.storagePath, { recursive: true });
40
+ }
41
+ // Load existing teams from disk
42
+ this.loadTeams();
43
+ // Ensure inbox directories exist for all teammates
44
+ this.ensureInboxDirectories();
45
+ }
46
+
47
+ // ============================================
48
+ // INBOX PATH HELPERS
49
+ // ============================================
50
+
51
+ private getInboxPath(teamName: string, teammateId: string): string {
52
+ return join(this.storagePath, teamName, "inboxes", teammateId);
53
+ }
54
+
55
+ private getPendingPath(teamName: string, teammateId: string): string {
56
+ return join(this.getInboxPath(teamName, teammateId), "pending");
57
+ }
58
+
59
+ private getProcessedPath(teamName: string, teammateId: string): string {
60
+ return join(this.getInboxPath(teamName, teammateId), "processed");
61
+ }
62
+
63
+ private ensureInboxDirectories(): void {
64
+ for (const team of this.teams.values()) {
65
+ for (const teammate of team.teammates) {
66
+ const pendingPath = this.getPendingPath(team.name, teammate.teammateId);
67
+ const processedPath = this.getProcessedPath(team.name, teammate.teammateId);
68
+
69
+ if (!existsSync(pendingPath)) {
70
+ mkdirSync(pendingPath, { recursive: true });
71
+ }
72
+ if (!existsSync(processedPath)) {
73
+ mkdirSync(processedPath, { recursive: true });
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ private generateMessageId(): string {
80
+ return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
81
+ }
82
+
83
+ // ============================================
84
+ // TEAM MANAGEMENT
85
+ // ============================================
86
+
87
+ createTeam(config: Omit<Team, "status">): Team {
88
+ const team: Team = {
89
+ ...config,
90
+ status: "active",
91
+ };
92
+
93
+ this.teams.set(config.name, team);
94
+
95
+ // Store teammates
96
+ for (const teammate of config.teammates) {
97
+ this.teammates.set(teammate.teammateId, teammate);
98
+ }
99
+
100
+ // Create inbox directories for all teammates
101
+ for (const teammate of config.teammates) {
102
+ const pendingPath = this.getPendingPath(config.name, teammate.teammateId);
103
+ const processedPath = this.getProcessedPath(config.name, teammate.teammateId);
104
+
105
+ if (!existsSync(pendingPath)) {
106
+ mkdirSync(pendingPath, { recursive: true });
107
+ }
108
+ if (!existsSync(processedPath)) {
109
+ mkdirSync(processedPath, { recursive: true });
110
+ }
111
+ }
112
+
113
+ // Persist to disk (fire and forget)
114
+ this.persistTeam(team).catch((err) => {
115
+ console.error(`Failed to persist team ${config.name}:`, err);
116
+ });
117
+
118
+ return team;
119
+ }
120
+
121
+ getTeam(name: string): Team | undefined {
122
+ return this.teams.get(name);
123
+ }
124
+
125
+ listTeams(): Team[] {
126
+ return Array.from(this.teams.values());
127
+ }
128
+
129
+ deleteTeam(name: string): void {
130
+ const team = this.teams.get(name);
131
+ if (team) {
132
+ // Remove teammates and their message queues
133
+ for (const teammate of team.teammates) {
134
+ this.teammates.delete(teammate.teammateId);
135
+ }
136
+ this.teams.delete(name);
137
+
138
+ // Delete team directory from disk
139
+ const teamDir = join(this.storagePath, name);
140
+ try {
141
+ rmSync(teamDir, { recursive: true, force: true });
142
+ } catch (err) {
143
+ console.error(`Failed to delete team directory ${teamDir}:`, err);
144
+ }
145
+ }
146
+ }
147
+
148
+ // ============================================
149
+ // TEAMMATE MANAGEMENT
150
+ // ============================================
151
+
152
+ getTeammate(id: string): Teammate | undefined {
153
+ return this.teammates.get(id);
154
+ }
155
+
156
+ updateTeammateStatus(id: string, status: TeammateStatus): void {
157
+ const teammate = this.teammates.get(id);
158
+ if (teammate) {
159
+ teammate.status = status;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Add a new teammate to an existing team
165
+ */
166
+ addTeammate(teamName: string, teammate: Teammate): boolean {
167
+ const team = this.teams.get(teamName);
168
+ if (!team) return false;
169
+
170
+ // Add to team
171
+ team.teammates.push(teammate);
172
+ this.teammates.set(teammate.teammateId, teammate);
173
+
174
+ // Create inbox directories
175
+ const pendingPath = this.getPendingPath(teamName, teammate.teammateId);
176
+ const processedPath = this.getProcessedPath(teamName, teammate.teammateId);
177
+
178
+ if (!existsSync(pendingPath)) {
179
+ mkdirSync(pendingPath, { recursive: true });
180
+ }
181
+ if (!existsSync(processedPath)) {
182
+ mkdirSync(processedPath, { recursive: true });
183
+ }
184
+
185
+ // Persist updated team
186
+ this.persistTeam(team).catch((err) => {
187
+ console.error(`Failed to persist team ${teamName}:`, err);
188
+ });
189
+
190
+ return true;
191
+ }
192
+
193
+ /**
194
+ * Remove a teammate from a team
195
+ */
196
+ removeTeammate(teamName: string, teammateId: string): boolean {
197
+ const team = this.teams.get(teamName);
198
+ if (!team) return false;
199
+
200
+ // Remove from team
201
+ const index = team.teammates.findIndex(t => t.teammateId === teammateId);
202
+ if (index === -1) return false;
203
+
204
+ team.teammates.splice(index, 1);
205
+ this.teammates.delete(teammateId);
206
+
207
+ // Keep inbox directory for history (don't delete)
208
+
209
+ // Persist updated team
210
+ this.persistTeam(team).catch((err) => {
211
+ console.error(`Failed to persist team ${teamName}:`, err);
212
+ });
213
+
214
+ return true;
215
+ }
216
+
217
+ // ============================================
218
+ // SPAWNING
219
+ // ============================================
220
+
221
+ async spawnTeammate(
222
+ teammate: Teammate,
223
+ options: {
224
+ session?: string;
225
+ workingDir?: string;
226
+ } = {}
227
+ ): Promise<void> {
228
+ const { session, workingDir = process.cwd() } = options;
229
+
230
+ // Check if inside tmux
231
+ const insideTmux = !!process.env.TMUX;
232
+
233
+ if (!insideTmux) {
234
+ // Spawn in new terminal
235
+ await this.spawnInTerminal(teammate, { session, workingDir });
236
+ } else {
237
+ // Spawn in tmux pane
238
+ await this.spawnInTmux(teammate, { session, workingDir });
239
+ }
240
+
241
+ this.updateTeammateStatus(teammate.teammateId, "in_progress");
242
+ }
243
+
244
+ private async spawnInTerminal(
245
+ teammate: Teammate,
246
+ options: { session?: string; workingDir: string }
247
+ ): Promise<void> {
248
+ // Build claude command
249
+ const args = [
250
+ "bun",
251
+ "run",
252
+ "src/interfaces/ui/terminal/cli/index.ts",
253
+ "--teammate-mode",
254
+ "--agent-id",
255
+ teammate.teammateId,
256
+ "--agent-name",
257
+ teammate.name,
258
+ "--team-name",
259
+ teammate.teamName,
260
+ "--agent-color",
261
+ teammate.color,
262
+ ];
263
+
264
+ if (teammate.planModeRequired) {
265
+ args.push("--permission-mode", "plan");
266
+ }
267
+
268
+ // Use AppleScript on macOS to open new Terminal
269
+ if (process.platform === "darwin") {
270
+ const script = `
271
+ tell application "Terminal"
272
+ do script "cd ${options.workingDir} && ${args.join(" ")}"
273
+ activate
274
+ end tell
275
+ `;
276
+
277
+ spawn("osascript", ["-e", script]);
278
+ } else {
279
+ // Linux: use xterm or similar
280
+ spawn("xterm", ["-e", args.join(" ")]);
281
+ }
282
+ }
283
+
284
+ private async spawnInTmux(
285
+ teammate: Teammate,
286
+ options: { session?: string; workingDir: string }
287
+ ): Promise<void> {
288
+ const sessionName = options.session || process.env.TMUX?.split(",")[0]?.split(":")[0] || "claude";
289
+
290
+ // Create new pane
291
+ await this.tmuxCommand(["split-window", "-t", sessionName, "-c", options.workingDir]);
292
+
293
+ // Get pane ID
294
+ const paneId = await this.tmuxCommand(["display-message", "-p", "#{pane_id}"]);
295
+
296
+ if (paneId) {
297
+ teammate.paneId = paneId.trim();
298
+ }
299
+
300
+ // Send claude command
301
+ const args = [
302
+ "bun",
303
+ "run",
304
+ "src/interfaces/ui/terminal/cli/index.ts",
305
+ "--teammate-mode",
306
+ "--agent-id",
307
+ teammate.teammateId,
308
+ "--agent-name",
309
+ teammate.name,
310
+ "--team-name",
311
+ teammate.teamName,
312
+ ];
313
+
314
+ await this.tmuxCommand(["send-keys", "-t", teammate.paneId || "", args.join(" "), "Enter"]);
315
+ }
316
+
317
+ private async tmuxCommand(args: string[]): Promise<string> {
318
+ return new Promise((resolve) => {
319
+ const proc = spawn("tmux", args);
320
+ let output = "";
321
+
322
+ proc.stdout?.on("data", (data: Buffer) => {
323
+ output += data.toString();
324
+ });
325
+
326
+ proc.on("close", () => {
327
+ resolve(output);
328
+ });
329
+ });
330
+ }
331
+
332
+ // ============================================
333
+ // MESSAGING (FILE-BASED)
334
+ // ============================================
335
+
336
+ /**
337
+ * Get the team name for a teammate
338
+ */
339
+ private getTeamNameForTeammate(teammateId: string): string | undefined {
340
+ const teammate = this.teammates.get(teammateId);
341
+ return teammate?.teamName;
342
+ }
343
+
344
+ /**
345
+ * Write a message to a teammate's file-based inbox
346
+ */
347
+ private writeMessageToInbox(
348
+ teamName: string,
349
+ toId: string,
350
+ msg: StoredMessage
351
+ ): void {
352
+ const pendingPath = this.getPendingPath(teamName, toId);
353
+
354
+ // Ensure inbox exists
355
+ if (!existsSync(pendingPath)) {
356
+ mkdirSync(pendingPath, { recursive: true });
357
+ }
358
+
359
+ // Write message as JSON file
360
+ const msgPath = join(pendingPath, `${msg.id}.json`);
361
+ writeFileSync(msgPath, JSON.stringify(msg, null, 2));
362
+ }
363
+
364
+ /**
365
+ * Read all pending messages from a teammate's inbox
366
+ */
367
+ private readPendingMessages(teamName: string, teammateId: string): StoredMessage[] {
368
+ const pendingPath = this.getPendingPath(teamName, teammateId);
369
+ const messages: StoredMessage[] = [];
370
+
371
+ if (!existsSync(pendingPath)) {
372
+ return messages;
373
+ }
374
+
375
+ try {
376
+ const files = readdirSync(pendingPath)
377
+ .filter(f => f.endsWith('.json'))
378
+ .sort(); // Oldest first (by filename timestamp)
379
+
380
+ for (const file of files) {
381
+ try {
382
+ const msgPath = join(pendingPath, file);
383
+ const content = readFileSync(msgPath, 'utf-8');
384
+ const msg = JSON.parse(content) as StoredMessage;
385
+ messages.push(msg);
386
+ } catch {
387
+ // Skip malformed messages
388
+ }
389
+ }
390
+ } catch {
391
+ // Directory read error
392
+ }
393
+
394
+ return messages;
395
+ }
396
+
397
+ /**
398
+ * Move a message from pending to processed
399
+ */
400
+ private markMessageProcessed(teamName: string, teammateId: string, msgId: string): void {
401
+ const pendingPath = this.getPendingPath(teamName, teammateId);
402
+ const processedPath = this.getProcessedPath(teamName, teammateId);
403
+
404
+ const pendingFile = join(pendingPath, `${msgId}.json`);
405
+ const processedFile = join(processedPath, `${msgId}.json`);
406
+
407
+ if (existsSync(pendingFile)) {
408
+ // Ensure processed directory exists
409
+ if (!existsSync(processedPath)) {
410
+ mkdirSync(processedPath, { recursive: true });
411
+ }
412
+
413
+ // Update message with readAt timestamp
414
+ try {
415
+ const content = readFileSync(pendingFile, 'utf-8');
416
+ const msg = JSON.parse(content) as StoredMessage;
417
+ msg.readAt = Date.now();
418
+ writeFileSync(processedFile, JSON.stringify(msg, null, 2));
419
+ rmSync(pendingFile);
420
+ } catch {
421
+ // If update fails, just move the file
422
+ try {
423
+ renameSync(pendingFile, processedFile);
424
+ } catch {
425
+ // Ignore move errors
426
+ }
427
+ }
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Broadcast a message to all teammates in a team
433
+ */
434
+ broadcast(teamName: string, message: string, fromId?: string): void {
435
+ const team = this.teams.get(teamName);
436
+ if (!team) return;
437
+
438
+ const baseMsg: Omit<StoredMessage, 'id' | 'teamName' | 'createdAt'> = {
439
+ type: "broadcast",
440
+ from: fromId || "system",
441
+ content: message,
442
+ timestamp: Date.now(),
443
+ };
444
+
445
+ // Write message to each teammate's inbox
446
+ for (const teammate of team.teammates) {
447
+ // Don't send to sender
448
+ if (fromId && teammate.teammateId === fromId) continue;
449
+
450
+ const msg: StoredMessage = {
451
+ ...baseMsg,
452
+ id: this.generateMessageId(),
453
+ teamName,
454
+ createdAt: Date.now(),
455
+ to: teammate.teammateId,
456
+ };
457
+
458
+ this.writeMessageToInbox(teamName, teammate.teammateId, msg);
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Send a direct message to a specific teammate
464
+ */
465
+ sendDirect(toId: string, fromId: string, message: string): void {
466
+ const teamName = this.getTeamNameForTeammate(toId);
467
+ if (!teamName) return;
468
+
469
+ const msg: StoredMessage = {
470
+ id: this.generateMessageId(),
471
+ type: "direct",
472
+ from: fromId,
473
+ to: toId,
474
+ content: message,
475
+ timestamp: Date.now(),
476
+ teamName,
477
+ createdAt: Date.now(),
478
+ };
479
+
480
+ this.writeMessageToInbox(teamName, toId, msg);
481
+ }
482
+
483
+ /**
484
+ * Inject a message as if it came from the user (for teammate integration)
485
+ * This integrates messages into conversation flow
486
+ */
487
+ injectUserMessageToTeammate(toId: string, message: string): void {
488
+ const teamName = this.getTeamNameForTeammate(toId);
489
+ if (!teamName) return;
490
+
491
+ const msg: StoredMessage = {
492
+ id: this.generateMessageId(),
493
+ type: "notification", // Use notification type for injected messages
494
+ from: "user",
495
+ to: toId,
496
+ content: message,
497
+ timestamp: Date.now(),
498
+ teamName,
499
+ createdAt: Date.now(),
500
+ };
501
+
502
+ this.writeMessageToInbox(teamName, toId, msg);
503
+ }
504
+
505
+ /**
506
+ * Retrieve and mark all messages as processed for a teammate
507
+ * Returns messages in chronological order (oldest first)
508
+ */
509
+ getMessages(teammateId: string): TeammateMessage[] {
510
+ const teamName = this.getTeamNameForTeammate(teammateId);
511
+ if (!teamName) return [];
512
+
513
+ const storedMsgs = this.readPendingMessages(teamName, teammateId);
514
+
515
+ // Mark all as processed
516
+ for (const msg of storedMsgs) {
517
+ this.markMessageProcessed(teamName, teammateId, msg.id);
518
+ }
519
+
520
+ // Convert to TeammateMessage format
521
+ return storedMsgs.map(msg => ({
522
+ type: msg.type,
523
+ from: msg.from,
524
+ to: msg.to,
525
+ content: msg.content,
526
+ timestamp: msg.timestamp,
527
+ }));
528
+ }
529
+
530
+ /**
531
+ * Check if a teammate has pending messages
532
+ */
533
+ hasMessages(teammateId: string): boolean {
534
+ const teamName = this.getTeamNameForTeammate(teammateId);
535
+ if (!teamName) return false;
536
+
537
+ const pendingPath = this.getPendingPath(teamName, teammateId);
538
+
539
+ if (!existsSync(pendingPath)) return false;
540
+
541
+ try {
542
+ const files = readdirSync(pendingPath);
543
+ return files.some(f => f.endsWith('.json'));
544
+ } catch {
545
+ return false;
546
+ }
547
+ }
548
+
549
+ /**
550
+ * Peek at messages without marking them as processed
551
+ */
552
+ peekMessages(teammateId: string): TeammateMessage[] {
553
+ const teamName = this.getTeamNameForTeammate(teammateId);
554
+ if (!teamName) return [];
555
+
556
+ const storedMsgs = this.readPendingMessages(teamName, teammateId);
557
+
558
+ return storedMsgs.map(msg => ({
559
+ type: msg.type,
560
+ from: msg.from,
561
+ to: msg.to,
562
+ content: msg.content,
563
+ timestamp: msg.timestamp,
564
+ }));
565
+ }
566
+
567
+ /**
568
+ * Clear all pending messages for a teammate (move to processed)
569
+ */
570
+ clearMessages(teammateId: string): void {
571
+ const teamName = this.getTeamNameForTeammate(teammateId);
572
+ if (!teamName) return;
573
+
574
+ const pendingPath = this.getPendingPath(teamName, teammateId);
575
+
576
+ if (!existsSync(pendingPath)) return;
577
+
578
+ const storedMsgs = this.readPendingMessages(teamName, teammateId);
579
+ for (const msg of storedMsgs) {
580
+ this.markMessageProcessed(teamName, teammateId, msg.id);
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Get count of pending messages for a teammate
586
+ */
587
+ getMessageCount(teammateId: string): number {
588
+ const teamName = this.getTeamNameForTeammate(teammateId);
589
+ if (!teamName) return 0;
590
+
591
+ const pendingPath = this.getPendingPath(teamName, teammateId);
592
+
593
+ if (!existsSync(pendingPath)) return 0;
594
+
595
+ try {
596
+ const files = readdirSync(pendingPath);
597
+ return files.filter(f => f.endsWith('.json')).length;
598
+ } catch {
599
+ return 0;
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Get processed messages (history) for a teammate
605
+ */
606
+ getProcessedMessages(teammateId: string, limit = 100): StoredMessage[] {
607
+ const teamName = this.getTeamNameForTeammate(teammateId);
608
+ if (!teamName) return [];
609
+
610
+ const processedPath = this.getProcessedPath(teamName, teammateId);
611
+ const messages: StoredMessage[] = [];
612
+
613
+ if (!existsSync(processedPath)) {
614
+ return messages;
615
+ }
616
+
617
+ try {
618
+ const files = readdirSync(processedPath)
619
+ .filter(f => f.endsWith('.json'))
620
+ .sort()
621
+ .reverse() // Newest first
622
+ .slice(0, limit);
623
+
624
+ for (const file of files) {
625
+ try {
626
+ const msgPath = join(processedPath, file);
627
+ const content = readFileSync(msgPath, 'utf-8');
628
+ messages.push(JSON.parse(content) as StoredMessage);
629
+ } catch {
630
+ // Skip malformed
631
+ }
632
+ }
633
+ } catch {
634
+ // Directory read error
635
+ }
636
+
637
+ return messages;
638
+ }
639
+
640
+ /**
641
+ * Clean up old processed messages (older than maxAgeMs)
642
+ */
643
+ cleanupProcessedMessages(teammateId: string, maxAgeMs = 7 * 24 * 60 * 60 * 1000): number {
644
+ const teamName = this.getTeamNameForTeammate(teammateId);
645
+ if (!teamName) return 0;
646
+
647
+ const processedPath = this.getProcessedPath(teamName, teammateId);
648
+ const cutoff = Date.now() - maxAgeMs;
649
+ let deleted = 0;
650
+
651
+ if (!existsSync(processedPath)) return 0;
652
+
653
+ try {
654
+ const files = readdirSync(processedPath).filter(f => f.endsWith('.json'));
655
+
656
+ for (const file of files) {
657
+ try {
658
+ const msgPath = join(processedPath, file);
659
+ const content = readFileSync(msgPath, 'utf-8');
660
+ const msg = JSON.parse(content) as StoredMessage;
661
+
662
+ if (msg.readAt && msg.readAt < cutoff) {
663
+ rmSync(msgPath);
664
+ deleted++;
665
+ }
666
+ } catch {
667
+ // Skip errors
668
+ }
669
+ }
670
+ } catch {
671
+ // Directory read error
672
+ }
673
+
674
+ return deleted;
675
+ }
676
+
677
+ /**
678
+ * Wait for all teammates in a team to become idle
679
+ * Returns when all teammates have status 'idle', 'completed', or 'failed'
680
+ */
681
+ async waitForTeammatesToBecomeIdle(
682
+ teamName: string,
683
+ options: { timeout?: number; pollInterval?: number } = {}
684
+ ): Promise<{ success: boolean; timedOut: boolean; statuses: Record<string, TeammateStatus> }> {
685
+ const { timeout = 60000, pollInterval = 1000 } = options;
686
+ const startTime = Date.now();
687
+ const idleStatuses: TeammateStatus[] = ['idle', 'completed', 'failed'];
688
+
689
+ while (true) {
690
+ const team = this.teams.get(teamName);
691
+ if (!team) {
692
+ return { success: false, timedOut: false, statuses: {} };
693
+ }
694
+
695
+ const statuses: Record<string, TeammateStatus> = {};
696
+ let allIdle = true;
697
+
698
+ for (const teammate of team.teammates) {
699
+ statuses[teammate.teammateId] = teammate.status;
700
+ if (!idleStatuses.includes(teammate.status)) {
701
+ allIdle = false;
702
+ }
703
+ }
704
+
705
+ if (allIdle) {
706
+ return { success: true, timedOut: false, statuses };
707
+ }
708
+
709
+ // Check timeout
710
+ if (Date.now() - startTime > timeout) {
711
+ return { success: false, timedOut: true, statuses };
712
+ }
713
+
714
+ // Wait before polling again
715
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
716
+ }
717
+ }
718
+
719
+ /**
720
+ * Get inbox statistics for a teammate
721
+ */
722
+ getInboxStats(teammateId: string): {
723
+ pending: number;
724
+ processed: number;
725
+ oldestPending?: number;
726
+ newestPending?: number;
727
+ } {
728
+ const teamName = this.getTeamNameForTeammate(teammateId);
729
+ if (!teamName) return { pending: 0, processed: 0 };
730
+
731
+ const pendingPath = this.getPendingPath(teamName, teammateId);
732
+ const processedPath = this.getProcessedPath(teamName, teammateId);
733
+
734
+ let pending = 0;
735
+ let processed = 0;
736
+ let oldestPending: number | undefined;
737
+ let newestPending: number | undefined;
738
+
739
+ // Count pending
740
+ if (existsSync(pendingPath)) {
741
+ try {
742
+ const files = readdirSync(pendingPath).filter(f => f.endsWith('.json'));
743
+ pending = files.length;
744
+
745
+ for (const file of files) {
746
+ try {
747
+ const msgPath = join(pendingPath, file);
748
+ const content = readFileSync(msgPath, 'utf-8');
749
+ const msg = JSON.parse(content) as StoredMessage;
750
+
751
+ if (!oldestPending || msg.createdAt < oldestPending) {
752
+ oldestPending = msg.createdAt;
753
+ }
754
+ if (!newestPending || msg.createdAt > newestPending) {
755
+ newestPending = msg.createdAt;
756
+ }
757
+ } catch {
758
+ // Skip
759
+ }
760
+ }
761
+ } catch {
762
+ // Skip
763
+ }
764
+ }
765
+
766
+ // Count processed
767
+ if (existsSync(processedPath)) {
768
+ try {
769
+ const files = readdirSync(processedPath).filter(f => f.endsWith('.json'));
770
+ processed = files.length;
771
+ } catch {
772
+ // Skip
773
+ }
774
+ }
775
+
776
+ return { pending, processed, oldestPending, newestPending };
777
+ }
778
+
779
+ // ============================================
780
+ // PERSISTENCE
781
+ // ============================================
782
+
783
+ /**
784
+ * Persist a team configuration to disk
785
+ */
786
+ private async persistTeam(team: Team): Promise<void> {
787
+ const teamDir = join(this.storagePath, team.name);
788
+ const configPath = join(teamDir, "config.json");
789
+
790
+ // Ensure directory exists
791
+ if (!existsSync(teamDir)) {
792
+ mkdirSync(teamDir, { recursive: true });
793
+ }
794
+
795
+ // Write .gitkeep to ensure directory is tracked
796
+ await Bun.write(join(teamDir, ".gitkeep"), "");
797
+
798
+ // Build config object
799
+ const config = {
800
+ name: team.name,
801
+ description: team.description,
802
+ teammates: team.teammates,
803
+ taskListId: team.taskListId,
804
+ status: team.status,
805
+ coordination: team.coordination,
806
+ updatedAt: Date.now(),
807
+ };
808
+
809
+ // Write config as formatted JSON
810
+ await Bun.write(configPath, JSON.stringify(config, null, 2));
811
+ }
812
+
813
+ /**
814
+ * Load all teams from disk at startup
815
+ * Uses synchronous operations for constructor compatibility
816
+ */
817
+ loadTeams(): void {
818
+ // Use Bun's glob to find team configs
819
+ const glob = new Bun.Glob("**/config.json");
820
+
821
+ try {
822
+ const files = Array.from(glob.scanSync(this.storagePath));
823
+ for (const file of files) {
824
+ try {
825
+ const filePath = join(this.storagePath, file);
826
+ const content = Bun.file(filePath);
827
+
828
+ // Check if file exists and is readable (sync check via size)
829
+ const size = content.size;
830
+ if (size === 0) {
831
+ continue;
832
+ }
833
+
834
+ // Read file synchronously using readFileSync
835
+ // Bun.file().text() is async, so we use fs.readFileSync for sync operation
836
+ const text = readFileSync(filePath, "utf-8");
837
+ const config = JSON.parse(text);
838
+
839
+ // Validate required fields - skip if missing teammates or name
840
+ if (!config.name || !config.teammates || !Array.isArray(config.teammates)) {
841
+ // Skip configs that don't match our expected structure
842
+ continue;
843
+ }
844
+
845
+ const team: Team = {
846
+ name: config.name,
847
+ description: config.description || "",
848
+ teammates: config.teammates,
849
+ taskListId: config.taskListId || "",
850
+ status: config.status || "active",
851
+ coordination: config.coordination || {
852
+ dependencyOrder: [],
853
+ communicationProtocol: "broadcast",
854
+ taskAssignmentStrategy: "manual",
855
+ },
856
+ };
857
+
858
+ this.teams.set(team.name, team);
859
+
860
+ // Index teammates
861
+ for (const teammate of team.teammates) {
862
+ this.teammates.set(teammate.teammateId, teammate);
863
+ }
864
+ } catch (error) {
865
+ // Silently skip malformed configs
866
+ }
867
+ }
868
+ } catch (error) {
869
+ // Storage path may not exist yet - that's okay
870
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
871
+ // Ignore permission errors too
872
+ }
873
+ }
874
+ }
875
+
876
+ /**
877
+ * Persist all teams to disk (useful for shutdown)
878
+ */
879
+ async persistAllTeams(): Promise<void> {
880
+ const promises = Array.from(this.teams.values()).map((team) => this.persistTeam(team));
881
+ await Promise.all(promises);
882
+ }
883
+ }
884
+
885
+ // ============================================
886
+ // TEAMMATE TEMPLATES
887
+ // ============================================
888
+
889
+ export const teammateTemplates = {
890
+ /**
891
+ * Architect - Plans and designs
892
+ */
893
+ architect: (teamName: string): Omit<Teammate, "teammateId"> => ({
894
+ name: "architect",
895
+ teamName,
896
+ color: "blue",
897
+ prompt: `You are an architect on the ${teamName} team.
898
+ Your role is to design and plan the technical architecture.
899
+ Focus on:
900
+ - System design and component relationships
901
+ - API contracts and interfaces
902
+ - Data models and schemas
903
+ - Trade-offs and design decisions`,
904
+ planModeRequired: true,
905
+ status: "pending",
906
+ }),
907
+
908
+ /**
909
+ * Implementer - Writes code
910
+ */
911
+ implementer: (teamName: string): Omit<Teammate, "teammateId"> => ({
912
+ name: "implementer",
913
+ teamName,
914
+ color: "green",
915
+ prompt: `You are an implementer on the ${teamName} team.
916
+ Your role is to write clean, working code based on the architecture.
917
+ Focus on:
918
+ - Implementing the designed architecture
919
+ - Writing tests
920
+ - Following coding standards
921
+ - Handling edge cases`,
922
+ planModeRequired: false,
923
+ status: "pending",
924
+ }),
925
+
926
+ /**
927
+ * Reviewer - Reviews code
928
+ */
929
+ reviewer: (teamName: string): Omit<Teammate, "teammateId"> => ({
930
+ name: "reviewer",
931
+ teamName,
932
+ color: "yellow",
933
+ prompt: `You are a code reviewer on the ${teamName} team.
934
+ Your role is to review code changes and provide feedback.
935
+ Focus on:
936
+ - Code quality and readability
937
+ - Potential bugs and issues
938
+ - Performance considerations
939
+ - Test coverage`,
940
+ planModeRequired: false,
941
+ status: "pending",
942
+ }),
943
+
944
+ /**
945
+ * Tester - Tests features
946
+ */
947
+ tester: (teamName: string): Omit<Teammate, "teammateId"> => ({
948
+ name: "tester",
949
+ teamName,
950
+ color: "orange",
951
+ prompt: `You are a tester on the ${teamName} team.
952
+ Your role is to ensure features work correctly.
953
+ Focus on:
954
+ - Writing comprehensive tests
955
+ - Finding edge cases
956
+ - Verifying requirements
957
+ - Reporting bugs`,
958
+ planModeRequired: false,
959
+ status: "pending",
960
+ }),
961
+ };
962
+
963
+ // ============================================
964
+ // HELPER FUNCTIONS
965
+ // ============================================
966
+
967
+ export function generateTeammateId(): string {
968
+ return `teammate_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
969
+ }
970
+
971
+ export function createTeammate(
972
+ config: Omit<Teammate, "teammateId" | "status">
973
+ ): Teammate {
974
+ return {
975
+ ...config,
976
+ teammateId: generateTeammateId(),
977
+ status: "pending",
978
+ };
979
+ }
980
+
981
+ // Export types
982
+ export type { StoredMessage };