@qotaq/lalphgram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/Events/package.json +6 -0
  2. package/LICENSE +21 -0
  3. package/Main/package.json +6 -0
  4. package/README.md +1 -0
  5. package/dist/cjs/Events.js +55 -0
  6. package/dist/cjs/Events.js.map +1 -0
  7. package/dist/cjs/LalphMain.js +59 -0
  8. package/dist/cjs/LalphMain.js.map +1 -0
  9. package/dist/cjs/Main.js +115 -0
  10. package/dist/cjs/Main.js.map +1 -0
  11. package/dist/cjs/index.js +281 -0
  12. package/dist/cjs/index.js.map +1 -0
  13. package/dist/cjs/lib/AnalysisPrompts.js +61 -0
  14. package/dist/cjs/lib/AnalysisPrompts.js.map +1 -0
  15. package/dist/cjs/lib/BranchParser.js +45 -0
  16. package/dist/cjs/lib/BranchParser.js.map +1 -0
  17. package/dist/cjs/lib/MermaidToPlantUml.js +96 -0
  18. package/dist/cjs/lib/MermaidToPlantUml.js.map +1 -0
  19. package/dist/cjs/lib/SpecHtmlGenerator.js +160 -0
  20. package/dist/cjs/lib/SpecHtmlGenerator.js.map +1 -0
  21. package/dist/cjs/lib/StreamJsonParser.js +88 -0
  22. package/dist/cjs/lib/StreamJsonParser.js.map +1 -0
  23. package/dist/cjs/lib/TelegramFormatter.js +122 -0
  24. package/dist/cjs/lib/TelegramFormatter.js.map +1 -0
  25. package/dist/cjs/lib/TelegraphHtml.js +65 -0
  26. package/dist/cjs/lib/TelegraphHtml.js.map +1 -0
  27. package/dist/cjs/lib/TelegraphMarkdown.js +346 -0
  28. package/dist/cjs/lib/TelegraphMarkdown.js.map +1 -0
  29. package/dist/cjs/schemas/CredentialSchemas.js +31 -0
  30. package/dist/cjs/schemas/CredentialSchemas.js.map +1 -0
  31. package/dist/cjs/schemas/GitHubSchemas.js +60 -0
  32. package/dist/cjs/schemas/GitHubSchemas.js.map +1 -0
  33. package/dist/cjs/schemas/LinearSchemas.js +63 -0
  34. package/dist/cjs/schemas/LinearSchemas.js.map +1 -0
  35. package/dist/cjs/schemas/ProjectSchemas.js +26 -0
  36. package/dist/cjs/schemas/ProjectSchemas.js.map +1 -0
  37. package/dist/cjs/schemas/TrackerSchemas.js +35 -0
  38. package/dist/cjs/schemas/TrackerSchemas.js.map +1 -0
  39. package/dist/cjs/services/AppContext.js +57 -0
  40. package/dist/cjs/services/AppContext.js.map +1 -0
  41. package/dist/cjs/services/AppRuntimeConfig.js +35 -0
  42. package/dist/cjs/services/AppRuntimeConfig.js.map +1 -0
  43. package/dist/cjs/services/AutoMerge.js +140 -0
  44. package/dist/cjs/services/AutoMerge.js.map +1 -0
  45. package/dist/cjs/services/ChatMachine.js +873 -0
  46. package/dist/cjs/services/ChatMachine.js.map +1 -0
  47. package/dist/cjs/services/CommentTimer.js +81 -0
  48. package/dist/cjs/services/CommentTimer.js.map +1 -0
  49. package/dist/cjs/services/CredentialStore.js +68 -0
  50. package/dist/cjs/services/CredentialStore.js.map +1 -0
  51. package/dist/cjs/services/CredentialWatcher.js +76 -0
  52. package/dist/cjs/services/CredentialWatcher.js.map +1 -0
  53. package/dist/cjs/services/Credentials.js +130 -0
  54. package/dist/cjs/services/Credentials.js.map +1 -0
  55. package/dist/cjs/services/EventLoop.js +203 -0
  56. package/dist/cjs/services/EventLoop.js.map +1 -0
  57. package/dist/cjs/services/GitHubClient/GitHubClient.js +236 -0
  58. package/dist/cjs/services/GitHubClient/GitHubClient.js.map +1 -0
  59. package/dist/cjs/services/GitHubClient/OctokitClient.js +329 -0
  60. package/dist/cjs/services/GitHubClient/OctokitClient.js.map +1 -0
  61. package/dist/cjs/services/GitHubClient.js +236 -0
  62. package/dist/cjs/services/GitHubClient.js.map +1 -0
  63. package/dist/cjs/services/GitHubEventSource.js +145 -0
  64. package/dist/cjs/services/GitHubEventSource.js.map +1 -0
  65. package/dist/cjs/services/GitHubIssueTracker.js +118 -0
  66. package/dist/cjs/services/GitHubIssueTracker.js.map +1 -0
  67. package/dist/cjs/services/LalphConfig.js +139 -0
  68. package/dist/cjs/services/LalphConfig.js.map +1 -0
  69. package/dist/cjs/services/LalphConfigReader.js +82 -0
  70. package/dist/cjs/services/LalphConfigReader.js.map +1 -0
  71. package/dist/cjs/services/LalphDirectory.js +45 -0
  72. package/dist/cjs/services/LalphDirectory.js.map +1 -0
  73. package/dist/cjs/services/LinearSdkClient.js +149 -0
  74. package/dist/cjs/services/LinearSdkClient.js.map +1 -0
  75. package/dist/cjs/services/LinearTracker.js +89 -0
  76. package/dist/cjs/services/LinearTracker.js.map +1 -0
  77. package/dist/cjs/services/MessengerAdapter/MessengerAdapter.js +30 -0
  78. package/dist/cjs/services/MessengerAdapter/MessengerAdapter.js.map +1 -0
  79. package/dist/cjs/services/MessengerAdapter/TelegramAdapter.js +131 -0
  80. package/dist/cjs/services/MessengerAdapter/TelegramAdapter.js.map +1 -0
  81. package/dist/cjs/services/MessengerAdapter/TelegramConfig.js +81 -0
  82. package/dist/cjs/services/MessengerAdapter/TelegramConfig.js.map +1 -0
  83. package/dist/cjs/services/MessengerAdapter.js +30 -0
  84. package/dist/cjs/services/MessengerAdapter.js.map +1 -0
  85. package/dist/cjs/services/OctokitClient.js +350 -0
  86. package/dist/cjs/services/OctokitClient.js.map +1 -0
  87. package/dist/cjs/services/PlanOverviewUploader.js +119 -0
  88. package/dist/cjs/services/PlanOverviewUploader.js.map +1 -0
  89. package/dist/cjs/services/PlanOverviewUploaderMap.js +25 -0
  90. package/dist/cjs/services/PlanOverviewUploaderMap.js.map +1 -0
  91. package/dist/cjs/services/PlanSession.js +489 -0
  92. package/dist/cjs/services/PlanSession.js.map +1 -0
  93. package/dist/cjs/services/ProjectStore.js +107 -0
  94. package/dist/cjs/services/ProjectStore.js.map +1 -0
  95. package/dist/cjs/services/PullRequestTracker.js +186 -0
  96. package/dist/cjs/services/PullRequestTracker.js.map +1 -0
  97. package/dist/cjs/services/SpecUploader.js +111 -0
  98. package/dist/cjs/services/SpecUploader.js.map +1 -0
  99. package/dist/cjs/services/SpecUploaderMap.js +25 -0
  100. package/dist/cjs/services/SpecUploaderMap.js.map +1 -0
  101. package/dist/cjs/services/TaskEventSource.js +58 -0
  102. package/dist/cjs/services/TaskEventSource.js.map +1 -0
  103. package/dist/cjs/services/TaskTracker/GitHubIssueTracker.js +155 -0
  104. package/dist/cjs/services/TaskTracker/GitHubIssueTracker.js.map +1 -0
  105. package/dist/cjs/services/TaskTracker/LinearSdkClient.js +149 -0
  106. package/dist/cjs/services/TaskTracker/LinearSdkClient.js.map +1 -0
  107. package/dist/cjs/services/TaskTracker/LinearTracker.js +126 -0
  108. package/dist/cjs/services/TaskTracker/LinearTracker.js.map +1 -0
  109. package/dist/cjs/services/TaskTracker/TaskTracker.js +20 -0
  110. package/dist/cjs/services/TaskTracker/TaskTracker.js.map +1 -0
  111. package/dist/cjs/services/TaskTracker/TrackerLayerMap.js +27 -0
  112. package/dist/cjs/services/TaskTracker/TrackerLayerMap.js.map +1 -0
  113. package/dist/cjs/services/TaskTracker/buildEventStream.js +55 -0
  114. package/dist/cjs/services/TaskTracker/buildEventStream.js.map +1 -0
  115. package/dist/cjs/services/TaskTracker.js +20 -0
  116. package/dist/cjs/services/TaskTracker.js.map +1 -0
  117. package/dist/cjs/services/TelegramAdapter.js +125 -0
  118. package/dist/cjs/services/TelegramAdapter.js.map +1 -0
  119. package/dist/cjs/services/TelegramConfig.js +81 -0
  120. package/dist/cjs/services/TelegramConfig.js.map +1 -0
  121. package/dist/cjs/services/TelegramConfigStore.js +81 -0
  122. package/dist/cjs/services/TelegramConfigStore.js.map +1 -0
  123. package/dist/cjs/services/TelegramNotifier.js +50 -0
  124. package/dist/cjs/services/TelegramNotifier.js.map +1 -0
  125. package/dist/cjs/services/TrackerLayerMap.js +27 -0
  126. package/dist/cjs/services/TrackerLayerMap.js.map +1 -0
  127. package/dist/cjs/services/TrackerResolver.js +45 -0
  128. package/dist/cjs/services/TrackerResolver.js.map +1 -0
  129. package/dist/cjs/services/messenger/MessengerAdapter.js +30 -0
  130. package/dist/cjs/services/messenger/MessengerAdapter.js.map +1 -0
  131. package/dist/cjs/services/messenger/TelegramAdapter.js +125 -0
  132. package/dist/cjs/services/messenger/TelegramAdapter.js.map +1 -0
  133. package/dist/cjs/services/task-tracker/GitHubIssueTracker.js +157 -0
  134. package/dist/cjs/services/task-tracker/GitHubIssueTracker.js.map +1 -0
  135. package/dist/cjs/services/task-tracker/LinearTracker.js +127 -0
  136. package/dist/cjs/services/task-tracker/LinearTracker.js.map +1 -0
  137. package/dist/cjs/services/task-tracker/TaskTracker.js +20 -0
  138. package/dist/cjs/services/task-tracker/TaskTracker.js.map +1 -0
  139. package/dist/cjs/services/task-tracker/TrackerLayerMap.js +27 -0
  140. package/dist/cjs/services/task-tracker/TrackerLayerMap.js.map +1 -0
  141. package/dist/cjs/shim/bin.js +31 -0
  142. package/dist/cjs/shim/bin.js.map +1 -0
  143. package/dist/cjs/shim/main.js +217 -0
  144. package/dist/cjs/shim/main.js.map +1 -0
  145. package/dist/cjs/shim/parseArgs.js +45 -0
  146. package/dist/cjs/shim/parseArgs.js.map +1 -0
  147. package/dist/cjs/shim/schemas.js +35 -0
  148. package/dist/cjs/shim/schemas.js.map +1 -0
  149. package/dist/dts/Events.d.ts +108 -0
  150. package/dist/dts/Events.d.ts.map +1 -0
  151. package/dist/dts/LalphMain.d.ts +2 -0
  152. package/dist/dts/LalphMain.d.ts.map +1 -0
  153. package/dist/dts/Main.d.ts +2 -0
  154. package/dist/dts/Main.d.ts.map +1 -0
  155. package/dist/dts/index.d.ts +101 -0
  156. package/dist/dts/index.d.ts.map +1 -0
  157. package/dist/dts/lib/AnalysisPrompts.d.ts +12 -0
  158. package/dist/dts/lib/AnalysisPrompts.d.ts.map +1 -0
  159. package/dist/dts/lib/BranchParser.d.ts +40 -0
  160. package/dist/dts/lib/BranchParser.d.ts.map +1 -0
  161. package/dist/dts/lib/MermaidToPlantUml.d.ts +19 -0
  162. package/dist/dts/lib/MermaidToPlantUml.d.ts.map +1 -0
  163. package/dist/dts/lib/SpecHtmlGenerator.d.ts +18 -0
  164. package/dist/dts/lib/SpecHtmlGenerator.d.ts.map +1 -0
  165. package/dist/dts/lib/StreamJsonParser.d.ts +147 -0
  166. package/dist/dts/lib/StreamJsonParser.d.ts.map +1 -0
  167. package/dist/dts/lib/TelegramFormatter.d.ts +35 -0
  168. package/dist/dts/lib/TelegramFormatter.d.ts.map +1 -0
  169. package/dist/dts/lib/TelegraphHtml.d.ts +11 -0
  170. package/dist/dts/lib/TelegraphHtml.d.ts.map +1 -0
  171. package/dist/dts/lib/TelegraphMarkdown.d.ts +21 -0
  172. package/dist/dts/lib/TelegraphMarkdown.d.ts.map +1 -0
  173. package/dist/dts/schemas/CredentialSchemas.d.ts +41 -0
  174. package/dist/dts/schemas/CredentialSchemas.d.ts.map +1 -0
  175. package/dist/dts/schemas/GitHubSchemas.d.ts +130 -0
  176. package/dist/dts/schemas/GitHubSchemas.d.ts.map +1 -0
  177. package/dist/dts/schemas/LinearSchemas.d.ts +121 -0
  178. package/dist/dts/schemas/LinearSchemas.d.ts.map +1 -0
  179. package/dist/dts/schemas/ProjectSchemas.d.ts +40 -0
  180. package/dist/dts/schemas/ProjectSchemas.d.ts.map +1 -0
  181. package/dist/dts/schemas/TrackerSchemas.d.ts +57 -0
  182. package/dist/dts/schemas/TrackerSchemas.d.ts.map +1 -0
  183. package/dist/dts/services/AppContext.d.ts +40 -0
  184. package/dist/dts/services/AppContext.d.ts.map +1 -0
  185. package/dist/dts/services/AppRuntimeConfig.d.ts +51 -0
  186. package/dist/dts/services/AppRuntimeConfig.d.ts.map +1 -0
  187. package/dist/dts/services/AutoMerge.d.ts +42 -0
  188. package/dist/dts/services/AutoMerge.d.ts.map +1 -0
  189. package/dist/dts/services/ChatMachine.d.ts +478 -0
  190. package/dist/dts/services/ChatMachine.d.ts.map +1 -0
  191. package/dist/dts/services/CommentTimer.d.ts +44 -0
  192. package/dist/dts/services/CommentTimer.d.ts.map +1 -0
  193. package/dist/dts/services/CredentialStore.d.ts +43 -0
  194. package/dist/dts/services/CredentialStore.d.ts.map +1 -0
  195. package/dist/dts/services/CredentialWatcher.d.ts +41 -0
  196. package/dist/dts/services/CredentialWatcher.d.ts.map +1 -0
  197. package/dist/dts/services/Credentials.d.ts +44 -0
  198. package/dist/dts/services/Credentials.d.ts.map +1 -0
  199. package/dist/dts/services/EventLoop.d.ts +26 -0
  200. package/dist/dts/services/EventLoop.d.ts.map +1 -0
  201. package/dist/dts/services/GitHubClient/GitHubClient.d.ts +69 -0
  202. package/dist/dts/services/GitHubClient/GitHubClient.d.ts.map +1 -0
  203. package/dist/dts/services/GitHubClient/OctokitClient.d.ts +214 -0
  204. package/dist/dts/services/GitHubClient/OctokitClient.d.ts.map +1 -0
  205. package/dist/dts/services/GitHubClient.d.ts +69 -0
  206. package/dist/dts/services/GitHubClient.d.ts.map +1 -0
  207. package/dist/dts/services/GitHubEventSource.d.ts +43 -0
  208. package/dist/dts/services/GitHubEventSource.d.ts.map +1 -0
  209. package/dist/dts/services/GitHubIssueTracker.d.ts +9 -0
  210. package/dist/dts/services/GitHubIssueTracker.d.ts.map +1 -0
  211. package/dist/dts/services/LalphConfig.d.ts +44 -0
  212. package/dist/dts/services/LalphConfig.d.ts.map +1 -0
  213. package/dist/dts/services/LalphConfigReader.d.ts +66 -0
  214. package/dist/dts/services/LalphConfigReader.d.ts.map +1 -0
  215. package/dist/dts/services/LalphDirectory.d.ts +39 -0
  216. package/dist/dts/services/LalphDirectory.d.ts.map +1 -0
  217. package/dist/dts/services/LinearSdkClient.d.ts +71 -0
  218. package/dist/dts/services/LinearSdkClient.d.ts.map +1 -0
  219. package/dist/dts/services/LinearTracker.d.ts +9 -0
  220. package/dist/dts/services/LinearTracker.d.ts.map +1 -0
  221. package/dist/dts/services/MessengerAdapter/MessengerAdapter.d.ts +69 -0
  222. package/dist/dts/services/MessengerAdapter/MessengerAdapter.d.ts.map +1 -0
  223. package/dist/dts/services/MessengerAdapter/TelegramAdapter.d.ts +13 -0
  224. package/dist/dts/services/MessengerAdapter/TelegramAdapter.d.ts.map +1 -0
  225. package/dist/dts/services/MessengerAdapter/TelegramConfig.d.ts +58 -0
  226. package/dist/dts/services/MessengerAdapter/TelegramConfig.d.ts.map +1 -0
  227. package/dist/dts/services/MessengerAdapter.d.ts +69 -0
  228. package/dist/dts/services/MessengerAdapter.d.ts.map +1 -0
  229. package/dist/dts/services/OctokitClient.d.ts +232 -0
  230. package/dist/dts/services/OctokitClient.d.ts.map +1 -0
  231. package/dist/dts/services/PlanOverviewUploader.d.ts +55 -0
  232. package/dist/dts/services/PlanOverviewUploader.d.ts.map +1 -0
  233. package/dist/dts/services/PlanOverviewUploaderMap.d.ts +14 -0
  234. package/dist/dts/services/PlanOverviewUploaderMap.d.ts.map +1 -0
  235. package/dist/dts/services/PlanSession.d.ts +169 -0
  236. package/dist/dts/services/PlanSession.d.ts.map +1 -0
  237. package/dist/dts/services/ProjectStore.d.ts +50 -0
  238. package/dist/dts/services/ProjectStore.d.ts.map +1 -0
  239. package/dist/dts/services/PullRequestTracker.d.ts +42 -0
  240. package/dist/dts/services/PullRequestTracker.d.ts.map +1 -0
  241. package/dist/dts/services/SpecUploader.d.ts +51 -0
  242. package/dist/dts/services/SpecUploader.d.ts.map +1 -0
  243. package/dist/dts/services/SpecUploaderMap.d.ts +14 -0
  244. package/dist/dts/services/SpecUploaderMap.d.ts.map +1 -0
  245. package/dist/dts/services/TaskEventSource.d.ts +21 -0
  246. package/dist/dts/services/TaskEventSource.d.ts.map +1 -0
  247. package/dist/dts/services/TaskTracker/GitHubIssueTracker.d.ts +10 -0
  248. package/dist/dts/services/TaskTracker/GitHubIssueTracker.d.ts.map +1 -0
  249. package/dist/dts/services/TaskTracker/LinearSdkClient.d.ts +71 -0
  250. package/dist/dts/services/TaskTracker/LinearSdkClient.d.ts.map +1 -0
  251. package/dist/dts/services/TaskTracker/LinearTracker.d.ts +10 -0
  252. package/dist/dts/services/TaskTracker/LinearTracker.d.ts.map +1 -0
  253. package/dist/dts/services/TaskTracker/TaskTracker.d.ts +39 -0
  254. package/dist/dts/services/TaskTracker/TaskTracker.d.ts.map +1 -0
  255. package/dist/dts/services/TaskTracker/TrackerLayerMap.d.ts +14 -0
  256. package/dist/dts/services/TaskTracker/TrackerLayerMap.d.ts.map +1 -0
  257. package/dist/dts/services/TaskTracker/buildEventStream.d.ts +16 -0
  258. package/dist/dts/services/TaskTracker/buildEventStream.d.ts.map +1 -0
  259. package/dist/dts/services/TaskTracker.d.ts +38 -0
  260. package/dist/dts/services/TaskTracker.d.ts.map +1 -0
  261. package/dist/dts/services/TelegramAdapter.d.ts +13 -0
  262. package/dist/dts/services/TelegramAdapter.d.ts.map +1 -0
  263. package/dist/dts/services/TelegramConfig.d.ts +58 -0
  264. package/dist/dts/services/TelegramConfig.d.ts.map +1 -0
  265. package/dist/dts/services/TelegramConfigStore.d.ts +57 -0
  266. package/dist/dts/services/TelegramConfigStore.d.ts.map +1 -0
  267. package/dist/dts/services/TelegramNotifier.d.ts +40 -0
  268. package/dist/dts/services/TelegramNotifier.d.ts.map +1 -0
  269. package/dist/dts/services/TrackerLayerMap.d.ts +14 -0
  270. package/dist/dts/services/TrackerLayerMap.d.ts.map +1 -0
  271. package/dist/dts/services/TrackerResolver.d.ts +43 -0
  272. package/dist/dts/services/TrackerResolver.d.ts.map +1 -0
  273. package/dist/dts/services/messenger/MessengerAdapter.d.ts +69 -0
  274. package/dist/dts/services/messenger/MessengerAdapter.d.ts.map +1 -0
  275. package/dist/dts/services/messenger/TelegramAdapter.d.ts +13 -0
  276. package/dist/dts/services/messenger/TelegramAdapter.d.ts.map +1 -0
  277. package/dist/dts/services/task-tracker/GitHubIssueTracker.d.ts +10 -0
  278. package/dist/dts/services/task-tracker/GitHubIssueTracker.d.ts.map +1 -0
  279. package/dist/dts/services/task-tracker/LinearTracker.d.ts +10 -0
  280. package/dist/dts/services/task-tracker/LinearTracker.d.ts.map +1 -0
  281. package/dist/dts/services/task-tracker/TaskTracker.d.ts +38 -0
  282. package/dist/dts/services/task-tracker/TaskTracker.d.ts.map +1 -0
  283. package/dist/dts/services/task-tracker/TrackerLayerMap.d.ts +14 -0
  284. package/dist/dts/services/task-tracker/TrackerLayerMap.d.ts.map +1 -0
  285. package/dist/dts/shim/bin.d.ts +3 -0
  286. package/dist/dts/shim/bin.d.ts.map +1 -0
  287. package/dist/dts/shim/main.d.ts +36 -0
  288. package/dist/dts/shim/main.d.ts.map +1 -0
  289. package/dist/dts/shim/parseArgs.d.ts +11 -0
  290. package/dist/dts/shim/parseArgs.d.ts.map +1 -0
  291. package/dist/dts/shim/schemas.d.ts +55 -0
  292. package/dist/dts/shim/schemas.d.ts.map +1 -0
  293. package/dist/esm/Events.js +41 -0
  294. package/dist/esm/Events.js.map +1 -0
  295. package/dist/esm/LalphMain.js +56 -0
  296. package/dist/esm/LalphMain.js.map +1 -0
  297. package/dist/esm/Main.js +112 -0
  298. package/dist/esm/Main.js.map +1 -0
  299. package/dist/esm/index.js +101 -0
  300. package/dist/esm/index.js.map +1 -0
  301. package/dist/esm/lib/AnalysisPrompts.js +54 -0
  302. package/dist/esm/lib/AnalysisPrompts.js.map +1 -0
  303. package/dist/esm/lib/BranchParser.js +36 -0
  304. package/dist/esm/lib/BranchParser.js.map +1 -0
  305. package/dist/esm/lib/MermaidToPlantUml.js +89 -0
  306. package/dist/esm/lib/MermaidToPlantUml.js.map +1 -0
  307. package/dist/esm/lib/SpecHtmlGenerator.js +153 -0
  308. package/dist/esm/lib/SpecHtmlGenerator.js.map +1 -0
  309. package/dist/esm/lib/StreamJsonParser.js +79 -0
  310. package/dist/esm/lib/StreamJsonParser.js.map +1 -0
  311. package/dist/esm/lib/TelegramFormatter.js +114 -0
  312. package/dist/esm/lib/TelegramFormatter.js.map +1 -0
  313. package/dist/esm/lib/TelegraphHtml.js +57 -0
  314. package/dist/esm/lib/TelegraphHtml.js.map +1 -0
  315. package/dist/esm/lib/TelegraphMarkdown.js +338 -0
  316. package/dist/esm/lib/TelegraphMarkdown.js.map +1 -0
  317. package/dist/esm/package.json +4 -0
  318. package/dist/esm/schemas/CredentialSchemas.js +22 -0
  319. package/dist/esm/schemas/CredentialSchemas.js.map +1 -0
  320. package/dist/esm/schemas/GitHubSchemas.js +50 -0
  321. package/dist/esm/schemas/GitHubSchemas.js.map +1 -0
  322. package/dist/esm/schemas/LinearSchemas.js +52 -0
  323. package/dist/esm/schemas/LinearSchemas.js.map +1 -0
  324. package/dist/esm/schemas/ProjectSchemas.js +18 -0
  325. package/dist/esm/schemas/ProjectSchemas.js.map +1 -0
  326. package/dist/esm/schemas/TrackerSchemas.js +26 -0
  327. package/dist/esm/schemas/TrackerSchemas.js.map +1 -0
  328. package/dist/esm/services/AppContext.js +48 -0
  329. package/dist/esm/services/AppContext.js.map +1 -0
  330. package/dist/esm/services/AppRuntimeConfig.js +26 -0
  331. package/dist/esm/services/AppRuntimeConfig.js.map +1 -0
  332. package/dist/esm/services/AutoMerge.js +131 -0
  333. package/dist/esm/services/AutoMerge.js.map +1 -0
  334. package/dist/esm/services/ChatMachine.js +855 -0
  335. package/dist/esm/services/ChatMachine.js.map +1 -0
  336. package/dist/esm/services/CommentTimer.js +72 -0
  337. package/dist/esm/services/CommentTimer.js.map +1 -0
  338. package/dist/esm/services/CredentialStore.js +59 -0
  339. package/dist/esm/services/CredentialStore.js.map +1 -0
  340. package/dist/esm/services/CredentialWatcher.js +67 -0
  341. package/dist/esm/services/CredentialWatcher.js.map +1 -0
  342. package/dist/esm/services/Credentials.js +121 -0
  343. package/dist/esm/services/Credentials.js.map +1 -0
  344. package/dist/esm/services/EventLoop.js +128 -0
  345. package/dist/esm/services/EventLoop.js.map +1 -0
  346. package/dist/esm/services/GitHubClient/GitHubClient.js +227 -0
  347. package/dist/esm/services/GitHubClient/GitHubClient.js.map +1 -0
  348. package/dist/esm/services/GitHubClient/OctokitClient.js +320 -0
  349. package/dist/esm/services/GitHubClient/OctokitClient.js.map +1 -0
  350. package/dist/esm/services/GitHubClient.js +227 -0
  351. package/dist/esm/services/GitHubClient.js.map +1 -0
  352. package/dist/esm/services/GitHubEventSource.js +136 -0
  353. package/dist/esm/services/GitHubEventSource.js.map +1 -0
  354. package/dist/esm/services/GitHubIssueTracker.js +111 -0
  355. package/dist/esm/services/GitHubIssueTracker.js.map +1 -0
  356. package/dist/esm/services/LalphConfig.js +130 -0
  357. package/dist/esm/services/LalphConfig.js.map +1 -0
  358. package/dist/esm/services/LalphConfigReader.js +71 -0
  359. package/dist/esm/services/LalphConfigReader.js.map +1 -0
  360. package/dist/esm/services/LalphDirectory.js +36 -0
  361. package/dist/esm/services/LalphDirectory.js.map +1 -0
  362. package/dist/esm/services/LinearSdkClient.js +140 -0
  363. package/dist/esm/services/LinearSdkClient.js.map +1 -0
  364. package/dist/esm/services/LinearTracker.js +82 -0
  365. package/dist/esm/services/LinearTracker.js.map +1 -0
  366. package/dist/esm/services/MessengerAdapter/MessengerAdapter.js +21 -0
  367. package/dist/esm/services/MessengerAdapter/MessengerAdapter.js.map +1 -0
  368. package/dist/esm/services/MessengerAdapter/TelegramAdapter.js +124 -0
  369. package/dist/esm/services/MessengerAdapter/TelegramAdapter.js.map +1 -0
  370. package/dist/esm/services/MessengerAdapter/TelegramConfig.js +71 -0
  371. package/dist/esm/services/MessengerAdapter/TelegramConfig.js.map +1 -0
  372. package/dist/esm/services/MessengerAdapter.js +21 -0
  373. package/dist/esm/services/MessengerAdapter.js.map +1 -0
  374. package/dist/esm/services/OctokitClient.js +341 -0
  375. package/dist/esm/services/OctokitClient.js.map +1 -0
  376. package/dist/esm/services/PlanOverviewUploader.js +109 -0
  377. package/dist/esm/services/PlanOverviewUploader.js.map +1 -0
  378. package/dist/esm/services/PlanOverviewUploaderMap.js +17 -0
  379. package/dist/esm/services/PlanOverviewUploaderMap.js.map +1 -0
  380. package/dist/esm/services/PlanSession.js +471 -0
  381. package/dist/esm/services/PlanSession.js.map +1 -0
  382. package/dist/esm/services/ProjectStore.js +98 -0
  383. package/dist/esm/services/ProjectStore.js.map +1 -0
  384. package/dist/esm/services/PullRequestTracker.js +177 -0
  385. package/dist/esm/services/PullRequestTracker.js.map +1 -0
  386. package/dist/esm/services/SpecUploader.js +101 -0
  387. package/dist/esm/services/SpecUploader.js.map +1 -0
  388. package/dist/esm/services/SpecUploaderMap.js +17 -0
  389. package/dist/esm/services/SpecUploaderMap.js.map +1 -0
  390. package/dist/esm/services/TaskEventSource.js +50 -0
  391. package/dist/esm/services/TaskEventSource.js.map +1 -0
  392. package/dist/esm/services/TaskTracker/GitHubIssueTracker.js +148 -0
  393. package/dist/esm/services/TaskTracker/GitHubIssueTracker.js.map +1 -0
  394. package/dist/esm/services/TaskTracker/LinearSdkClient.js +140 -0
  395. package/dist/esm/services/TaskTracker/LinearSdkClient.js.map +1 -0
  396. package/dist/esm/services/TaskTracker/LinearTracker.js +119 -0
  397. package/dist/esm/services/TaskTracker/LinearTracker.js.map +1 -0
  398. package/dist/esm/services/TaskTracker/TaskTracker.js +12 -0
  399. package/dist/esm/services/TaskTracker/TaskTracker.js.map +1 -0
  400. package/dist/esm/services/TaskTracker/TrackerLayerMap.js +19 -0
  401. package/dist/esm/services/TaskTracker/TrackerLayerMap.js.map +1 -0
  402. package/dist/esm/services/TaskTracker/buildEventStream.js +47 -0
  403. package/dist/esm/services/TaskTracker/buildEventStream.js.map +1 -0
  404. package/dist/esm/services/TaskTracker.js +12 -0
  405. package/dist/esm/services/TaskTracker.js.map +1 -0
  406. package/dist/esm/services/TelegramAdapter.js +118 -0
  407. package/dist/esm/services/TelegramAdapter.js.map +1 -0
  408. package/dist/esm/services/TelegramConfig.js +71 -0
  409. package/dist/esm/services/TelegramConfig.js.map +1 -0
  410. package/dist/esm/services/TelegramConfigStore.js +71 -0
  411. package/dist/esm/services/TelegramConfigStore.js.map +1 -0
  412. package/dist/esm/services/TelegramNotifier.js +41 -0
  413. package/dist/esm/services/TelegramNotifier.js.map +1 -0
  414. package/dist/esm/services/TrackerLayerMap.js +19 -0
  415. package/dist/esm/services/TrackerLayerMap.js.map +1 -0
  416. package/dist/esm/services/TrackerResolver.js +36 -0
  417. package/dist/esm/services/TrackerResolver.js.map +1 -0
  418. package/dist/esm/services/messenger/MessengerAdapter.js +21 -0
  419. package/dist/esm/services/messenger/MessengerAdapter.js.map +1 -0
  420. package/dist/esm/services/messenger/TelegramAdapter.js +118 -0
  421. package/dist/esm/services/messenger/TelegramAdapter.js.map +1 -0
  422. package/dist/esm/services/task-tracker/GitHubIssueTracker.js +150 -0
  423. package/dist/esm/services/task-tracker/GitHubIssueTracker.js.map +1 -0
  424. package/dist/esm/services/task-tracker/LinearTracker.js +120 -0
  425. package/dist/esm/services/task-tracker/LinearTracker.js.map +1 -0
  426. package/dist/esm/services/task-tracker/TaskTracker.js +12 -0
  427. package/dist/esm/services/task-tracker/TaskTracker.js.map +1 -0
  428. package/dist/esm/services/task-tracker/TrackerLayerMap.js +19 -0
  429. package/dist/esm/services/task-tracker/TrackerLayerMap.js.map +1 -0
  430. package/dist/esm/shim/bin.js +28 -0
  431. package/dist/esm/shim/bin.js.map +1 -0
  432. package/dist/esm/shim/main.js +196 -0
  433. package/dist/esm/shim/main.js.map +1 -0
  434. package/dist/esm/shim/parseArgs.js +39 -0
  435. package/dist/esm/shim/parseArgs.js.map +1 -0
  436. package/dist/esm/shim/schemas.js +28 -0
  437. package/dist/esm/shim/schemas.js.map +1 -0
  438. package/lib/AnalysisPrompts/package.json +6 -0
  439. package/lib/BranchParser/package.json +6 -0
  440. package/lib/MermaidToPlantUml/package.json +6 -0
  441. package/lib/SpecHtmlGenerator/package.json +6 -0
  442. package/lib/StreamJsonParser/package.json +6 -0
  443. package/lib/TelegramFormatter/package.json +6 -0
  444. package/lib/TelegraphMarkdown/package.json +6 -0
  445. package/package.json +360 -0
  446. package/schemas/CredentialSchemas/package.json +6 -0
  447. package/schemas/GitHubSchemas/package.json +6 -0
  448. package/schemas/LinearSchemas/package.json +6 -0
  449. package/schemas/ProjectSchemas/package.json +6 -0
  450. package/schemas/TrackerSchemas/package.json +6 -0
  451. package/services/AppContext/package.json +6 -0
  452. package/services/AppRuntimeConfig/package.json +6 -0
  453. package/services/AutoMerge/package.json +6 -0
  454. package/services/ChatMachine/package.json +6 -0
  455. package/services/CommentTimer/package.json +6 -0
  456. package/services/EventLoop/package.json +6 -0
  457. package/services/GitHubClient/package.json +6 -0
  458. package/services/LalphConfig/package.json +6 -0
  459. package/services/LinearSdkClient/package.json +6 -0
  460. package/services/MessengerAdapter/MessengerAdapter/package.json +6 -0
  461. package/services/MessengerAdapter/TelegramAdapter/package.json +6 -0
  462. package/services/OctokitClient/package.json +6 -0
  463. package/services/PlanOverviewUploader/package.json +6 -0
  464. package/services/PlanOverviewUploaderMap/package.json +6 -0
  465. package/services/PlanSession/package.json +6 -0
  466. package/services/ProjectStore/package.json +6 -0
  467. package/services/PullRequestTracker/package.json +6 -0
  468. package/services/TaskTracker/GitHubIssueTracker/package.json +6 -0
  469. package/services/TaskTracker/LinearTracker/package.json +6 -0
  470. package/services/TaskTracker/TaskTracker/package.json +6 -0
  471. package/services/TelegramConfig/package.json +6 -0
  472. package/services/TrackerLayerMap/package.json +6 -0
  473. package/shim/bin/package.json +6 -0
  474. package/shim/main/package.json +6 -0
  475. package/shim/parseArgs/package.json +6 -0
  476. package/shim/schemas/package.json +6 -0
  477. package/src/Events.ts +98 -0
  478. package/src/Main.ts +177 -0
  479. package/src/index.ts +124 -0
  480. package/src/lib/AnalysisPrompts.ts +54 -0
  481. package/src/lib/BranchParser.ts +58 -0
  482. package/src/lib/MermaidToPlantUml.ts +103 -0
  483. package/src/lib/SpecHtmlGenerator.ts +199 -0
  484. package/src/lib/StreamJsonParser.ts +99 -0
  485. package/src/lib/TelegramFormatter.ts +151 -0
  486. package/src/lib/TelegraphMarkdown.ts +305 -0
  487. package/src/schemas/CredentialSchemas.ts +23 -0
  488. package/src/schemas/GitHubSchemas.ts +50 -0
  489. package/src/schemas/LinearSchemas.ts +57 -0
  490. package/src/schemas/ProjectSchemas.ts +18 -0
  491. package/src/schemas/TrackerSchemas.ts +27 -0
  492. package/src/services/AppContext.ts +72 -0
  493. package/src/services/AppRuntimeConfig.ts +23 -0
  494. package/src/services/AutoMerge.ts +198 -0
  495. package/src/services/ChatMachine.ts +911 -0
  496. package/src/services/CommentTimer.ts +133 -0
  497. package/src/services/EventLoop.ts +321 -0
  498. package/src/services/GitHubClient.ts +282 -0
  499. package/src/services/LalphConfig.ts +218 -0
  500. package/src/services/LinearSdkClient.ts +181 -0
  501. package/src/services/MessengerAdapter/MessengerAdapter.ts +53 -0
  502. package/src/services/MessengerAdapter/TelegramAdapter.ts +145 -0
  503. package/src/services/OctokitClient.ts +628 -0
  504. package/src/services/PlanOverviewUploader.ts +160 -0
  505. package/src/services/PlanOverviewUploaderMap.ts +17 -0
  506. package/src/services/PlanSession.ts +589 -0
  507. package/src/services/ProjectStore.ts +140 -0
  508. package/src/services/PullRequestTracker.ts +253 -0
  509. package/src/services/TaskTracker/GitHubIssueTracker.ts +162 -0
  510. package/src/services/TaskTracker/LinearTracker.ts +141 -0
  511. package/src/services/TaskTracker/TaskTracker.ts +34 -0
  512. package/src/services/TelegramConfig.ts +120 -0
  513. package/src/services/TrackerLayerMap.ts +19 -0
  514. package/src/shim/bin.ts +36 -0
  515. package/src/shim/main.ts +255 -0
  516. package/src/shim/parseArgs.ts +43 -0
  517. package/src/shim/schemas.ts +38 -0
@@ -0,0 +1,911 @@
1
+ /**
2
+ * Chat state machine using @effect/experimental/Machine
3
+ * @since 1.0.0
4
+ */
5
+ import * as Machine from "@effect/experimental/Machine"
6
+ import { Data, Effect, Option, Schema } from "effect"
7
+ import { getAnalysisPrompt } from "../lib/AnalysisPrompts.js"
8
+ import type { SpecFile } from "../lib/SpecHtmlGenerator.js"
9
+ import { markdownToTelegramHtml, splitMessage } from "../lib/TelegramFormatter.js"
10
+ import { MessengerAdapter, type OutgoingMessage } from "./MessengerAdapter/MessengerAdapter.js"
11
+ import { PlanOverviewUploader } from "./PlanOverviewUploader.js"
12
+ import { PlanSession } from "./PlanSession.js"
13
+ import { ProjectStore } from "./ProjectStore.js"
14
+
15
+ // ── Button labels ────────────────────────────────────────────────
16
+
17
+ export const PLAN_BUTTON_LABEL = "Plan"
18
+ export const DONE_BUTTON_LABEL = "Done"
19
+ export const FEATURE_BUTTON_LABEL = "Feature"
20
+ export const BUG_BUTTON_LABEL = "Bug"
21
+ export const REFACTOR_BUTTON_LABEL = "Refactor"
22
+ export const OTHER_BUTTON_LABEL = "Other"
23
+ export const APPROVE_BUTTON_LABEL = "Approve"
24
+ export const BUFFER_BUTTON_LABEL = "Buffer"
25
+ export const INTERRUPT_BUTTON_LABEL = "Interrupt"
26
+ export const DISCARD_BUTTON_LABEL = "Discard"
27
+ export const ABORT_BUTTON_LABEL = "Abort"
28
+ export const NEW_PROJECT_BUTTON_LABEL = "New project"
29
+
30
+ const MY_ANSWER_BUTTON_LABEL = "Custom answer"
31
+ const BACK_BUTTON_LABEL = "Back"
32
+
33
+ const PLAN_TYPE_LABELS = [
34
+ FEATURE_BUTTON_LABEL,
35
+ BUG_BUTTON_LABEL,
36
+ REFACTOR_BUTTON_LABEL,
37
+ OTHER_BUTTON_LABEL
38
+ ]
39
+
40
+ // ── Keyboards ────────────────────────────────────────────────────
41
+
42
+ export const IDLE_KEYBOARD = [{ label: PLAN_BUTTON_LABEL }, { label: NEW_PROJECT_BUTTON_LABEL }]
43
+ const COLLECTING_KEYBOARD = [{ label: DONE_BUTTON_LABEL }, { label: ABORT_BUTTON_LABEL }]
44
+ const SESSION_KEYBOARD = [{ label: ABORT_BUTTON_LABEL }]
45
+ const SPEC_READY_KEYBOARD = [{ label: APPROVE_BUTTON_LABEL }, { label: ABORT_BUTTON_LABEL }]
46
+
47
+ // ── State types ──────────────────────────────────────────────────
48
+
49
+ type CreatingProjectStep = "Name" | "Concurrency" | "TargetBranch" | "GitFlow" | "ReviewAgent"
50
+
51
+ interface CreatingProjectData {
52
+ readonly name?: string
53
+ readonly concurrency?: number
54
+ readonly targetBranch?: string | null
55
+ readonly gitFlow?: "pr" | "commit"
56
+ readonly reviewAgent?: boolean
57
+ }
58
+
59
+ export class ReadyFlags extends Data.Class<{
60
+ readonly spec: boolean
61
+ readonly analysis: boolean
62
+ readonly idle: boolean
63
+ }> {}
64
+
65
+ /* eslint-disable @typescript-eslint/no-empty-object-type */
66
+ export type ChatState = Data.TaggedEnum<{
67
+ Idle: {}
68
+ SelectingProject: {}
69
+ /* eslint-enable @typescript-eslint/no-empty-object-type */
70
+ SelectingPlanType: { readonly projectId: string }
71
+ CollectingPlan: {
72
+ readonly projectId: string
73
+ readonly planType: string
74
+ readonly buffer: ReadonlyArray<string>
75
+ }
76
+ SessionRunning: {
77
+ readonly projectId: string
78
+ readonly planType: string
79
+ readonly pendingAnswerCount: number
80
+ readonly pendingOptionLabels: ReadonlySet<string>
81
+ readonly answersBuffer: ReadonlyArray<string>
82
+ readonly awaitingFreeTextAnswer: boolean
83
+ readonly lastQuestionMessage: Option.Option<OutgoingMessage>
84
+ readonly readyFlags: ReadyFlags
85
+ readonly analysisFollowUpSent: boolean
86
+ }
87
+ AwaitingFollowUpDecision: {
88
+ readonly projectId: string
89
+ readonly planType: string
90
+ readonly message: string
91
+ readonly readyFlags: ReadyFlags
92
+ readonly analysisFollowUpSent: boolean
93
+ }
94
+ SpecReady: {
95
+ readonly projectId: string
96
+ readonly planType: string
97
+ readonly readyFlags: ReadyFlags
98
+ readonly analysisFollowUpSent: boolean
99
+ }
100
+ CreatingProject: {
101
+ readonly step: CreatingProjectStep
102
+ readonly data: CreatingProjectData
103
+ readonly continueWithPlan: boolean
104
+ }
105
+ }>
106
+
107
+ export const ChatState = Data.taggedEnum<ChatState>()
108
+
109
+ const initialSessionRunning = (projectId: string, planType: string): ChatState =>
110
+ ChatState.SessionRunning({
111
+ projectId,
112
+ planType,
113
+ pendingAnswerCount: 0,
114
+ pendingOptionLabels: new Set(),
115
+ answersBuffer: [],
116
+ awaitingFreeTextAnswer: false,
117
+ lastQuestionMessage: Option.none(),
118
+ readyFlags: new ReadyFlags({ spec: false, analysis: false, idle: false }),
119
+ analysisFollowUpSent: false
120
+ })
121
+
122
+ // ── Request types ────────────────────────────────────────────────
123
+
124
+ export class UserMessage extends Schema.TaggedRequest<UserMessage>()("UserMessage", {
125
+ failure: Schema.Never,
126
+ success: Schema.Void,
127
+ payload: { text: Schema.String }
128
+ }) {}
129
+
130
+ export class PlanTextOutput extends Schema.TaggedRequest<PlanTextOutput>()("PlanTextOutput", {
131
+ failure: Schema.Never,
132
+ success: Schema.Void,
133
+ payload: { text: Schema.String }
134
+ }) {}
135
+
136
+ export class PlanQuestionReceived extends Schema.TaggedRequest<PlanQuestionReceived>()(
137
+ "PlanQuestionReceived",
138
+ {
139
+ failure: Schema.Never,
140
+ success: Schema.Void,
141
+ payload: {
142
+ questions: Schema.Array(Schema.Struct({
143
+ question: Schema.String,
144
+ header: Schema.optional(Schema.String),
145
+ options: Schema.optional(Schema.Array(Schema.Struct({ label: Schema.String })))
146
+ }))
147
+ }
148
+ }
149
+ ) {}
150
+
151
+ export class PlanSpecCreatedReq extends Schema.TaggedRequest<PlanSpecCreatedReq>()(
152
+ "PlanSpecCreatedReq",
153
+ { failure: Schema.Never, success: Schema.Void, payload: {} }
154
+ ) {}
155
+
156
+ export class PlanSpecUpdatedReq extends Schema.TaggedRequest<PlanSpecUpdatedReq>()(
157
+ "PlanSpecUpdatedReq",
158
+ { failure: Schema.Never, success: Schema.Void, payload: {} }
159
+ ) {}
160
+
161
+ export class PlanAnalysisReadyReq extends Schema.TaggedRequest<PlanAnalysisReadyReq>()(
162
+ "PlanAnalysisReadyReq",
163
+ { failure: Schema.Never, success: Schema.Void, payload: {} }
164
+ ) {}
165
+
166
+ export class PlanAwaitingInputReq extends Schema.TaggedRequest<PlanAwaitingInputReq>()(
167
+ "PlanAwaitingInputReq",
168
+ { failure: Schema.Never, success: Schema.Void, payload: {} }
169
+ ) {}
170
+
171
+ export class PlanCompletedReq extends Schema.TaggedRequest<PlanCompletedReq>()(
172
+ "PlanCompletedReq",
173
+ { failure: Schema.Never, success: Schema.Void, payload: {} }
174
+ ) {}
175
+
176
+ export class PlanFailedReq extends Schema.TaggedRequest<PlanFailedReq>()(
177
+ "PlanFailedReq",
178
+ { failure: Schema.Never, success: Schema.Void, payload: { message: Schema.String } }
179
+ ) {}
180
+
181
+ // ── Machine definition ───────────────────────────────────────────
182
+
183
+ type HandlerResult = readonly [void, ChatState]
184
+ const reply = (state: ChatState): HandlerResult => [undefined, state]
185
+
186
+ export const chatMachine = Machine.make(
187
+ Effect.gen(function*() {
188
+ // Capture services at init (closures — handlers have R = never)
189
+ const notifier = yield* MessengerAdapter
190
+ const planSession = yield* PlanSession
191
+ const projectStore = yield* ProjectStore
192
+ const planOverviewUploader = yield* PlanOverviewUploader
193
+
194
+ // ── Helpers ────────────────────────────────────────────────
195
+
196
+ const readSpecFiles = (planType: string) =>
197
+ planType === "Feature"
198
+ ? planSession.readFeatureAnalysis.pipe(
199
+ Effect.map((files) => [
200
+ { name: "analysis.md", content: files.analysis, mermaid: false },
201
+ { name: "services.mmd", content: files.services, mermaid: true },
202
+ { name: "test.md", content: files.test, mermaid: false }
203
+ ])
204
+ )
205
+ : planType === "Bug"
206
+ ? planSession.readBugAnalysis.pipe(
207
+ Effect.map((files) => [{ name: "analysis.md", content: files.analysis, mermaid: false }])
208
+ )
209
+ : planType === "Refactor"
210
+ ? planSession.readRefactorAnalysis.pipe(
211
+ Effect.map((files) => [{ name: "analysis.md", content: files.analysis, mermaid: false }])
212
+ )
213
+ : planSession.readDefaultAnalysis.pipe(
214
+ Effect.map((files) => [{ name: "analysis.md", content: files.analysis, mermaid: false }])
215
+ )
216
+
217
+ const sendSpecFilesRaw = (files: ReadonlyArray<SpecFile>) =>
218
+ Effect.forEach(files, (file) => {
219
+ const formatted = file.mermaid
220
+ ? markdownToTelegramHtml(`\`\`\`mermaid\n${file.content}\n\`\`\``)
221
+ : markdownToTelegramHtml(file.content)
222
+ const text = `<b>${file.name}</b>\n${formatted}`
223
+ return Effect.forEach(splitMessage(text), (chunk) => notifier.sendMessage(chunk))
224
+ })
225
+
226
+ const sendSpecFiles = (planType: string) =>
227
+ Effect.gen(function*() {
228
+ const files = yield* readSpecFiles(planType)
229
+ yield* planOverviewUploader.upload({ files, description: `Spec: ${planType}` }).pipe(
230
+ Effect.tap((result) => notifier.sendMessage(`<a href="${result.url}">View spec</a>`)),
231
+ Effect.catchTag("PlanOverviewUploaderError", (err) =>
232
+ Effect.gen(function*() {
233
+ yield* Effect.logError(`Spec upload failed, sending raw: ${err.message}`)
234
+ yield* sendSpecFilesRaw(files)
235
+ }))
236
+ )
237
+ })
238
+
239
+ const rejectSession = planSession.reject.pipe(
240
+ Effect.tapError((err) => Effect.logError(`Plan abort error: ${err.message}`)),
241
+ Effect.orElseSucceed(() => undefined)
242
+ )
243
+
244
+ const abortToIdle = (hasSession: boolean) =>
245
+ Effect.gen(function*() {
246
+ if (hasSession) {
247
+ yield* rejectSession
248
+ }
249
+ yield* Effect.log("Plan aborted")
250
+ yield* notifier.sendMessage({ text: "Plan aborted.", replyKeyboard: IDLE_KEYBOARD })
251
+ })
252
+
253
+ const submitAnswers = (answers: ReadonlyArray<string>) =>
254
+ Effect.gen(function*() {
255
+ const combined = answers.join("\n")
256
+ yield* Effect.log("Flushing batched answers to plan session")
257
+ yield* planSession.answer(combined).pipe(
258
+ Effect.tapError((err) => Effect.logError(`Plan answer error: ${err.message}`)),
259
+ Effect.orElseSucceed(() => undefined)
260
+ )
261
+ })
262
+
263
+ const showFollowUpChoice = (
264
+ text: string,
265
+ projectId: string,
266
+ planType: string,
267
+ readyFlags: ReadyFlags,
268
+ analysisFollowUpSent: boolean
269
+ ) =>
270
+ Effect.gen(function*() {
271
+ yield* Effect.log("Holding follow-up message, showing buffer/interrupt buttons")
272
+ yield* notifier.sendMessage({
273
+ text: "Send as follow-up or interrupt Claude?",
274
+ options: [
275
+ { label: BUFFER_BUTTON_LABEL },
276
+ { label: INTERRUPT_BUTTON_LABEL },
277
+ { label: DISCARD_BUTTON_LABEL }
278
+ ]
279
+ })
280
+ return ChatState.AwaitingFollowUpDecision({
281
+ projectId,
282
+ planType,
283
+ message: text,
284
+ readyFlags,
285
+ analysisFollowUpSent
286
+ })
287
+ })
288
+
289
+ const checkAllReady = (
290
+ projectId: string,
291
+ planType: string,
292
+ flags: ReadyFlags,
293
+ analysisFollowUpSent: boolean
294
+ ): Effect.Effect<ChatState, never, never> =>
295
+ Effect.gen(function*() {
296
+ if (!flags.spec || !flags.analysis || !flags.idle) {
297
+ return ChatState.SessionRunning({
298
+ projectId,
299
+ planType,
300
+ pendingAnswerCount: 0,
301
+ pendingOptionLabels: new Set(),
302
+ answersBuffer: [],
303
+ awaitingFreeTextAnswer: false,
304
+ lastQuestionMessage: Option.none(),
305
+ readyFlags: flags,
306
+ analysisFollowUpSent
307
+ })
308
+ }
309
+ yield* sendSpecFiles(planType).pipe(
310
+ Effect.catchAll((err) => Effect.logError(`Failed to send spec files: ${String(err)}`))
311
+ )
312
+ yield* notifier.sendMessage({
313
+ text: "Spec ready. Reply with questions or approve to proceed.",
314
+ replyKeyboard: SPEC_READY_KEYBOARD
315
+ }).pipe(Effect.orElseSucceed(() => undefined))
316
+ return ChatState.SpecReady({ projectId, planType, readyFlags: flags, analysisFollowUpSent })
317
+ })
318
+
319
+ const maybeAnalysisFollowUp = (
320
+ planType: string,
321
+ alreadySent: boolean
322
+ ) =>
323
+ alreadySent
324
+ ? Effect.succeed(true)
325
+ : planSession.sendFollowUp(getAnalysisPrompt(planType)).pipe(
326
+ Effect.tapError((err) => Effect.logError(`Analysis follow-up error: ${err.message}`)),
327
+ Effect.orElseSucceed(() => undefined),
328
+ Effect.as(true)
329
+ )
330
+
331
+ /**
332
+ * Extract projectId, planType, readyFlags, and analysisFollowUpSent from any
333
+ * "active session" state (SessionRunning, AwaitingFollowUpDecision, SpecReady).
334
+ */
335
+ const extractActiveState = (state: ChatState) => {
336
+ switch (state._tag) {
337
+ case "SessionRunning":
338
+ return Option.some({
339
+ projectId: state.projectId,
340
+ planType: state.planType,
341
+ readyFlags: state.readyFlags,
342
+ analysisFollowUpSent: state.analysisFollowUpSent
343
+ })
344
+ case "AwaitingFollowUpDecision":
345
+ return Option.some({
346
+ projectId: state.projectId,
347
+ planType: state.planType,
348
+ readyFlags: state.readyFlags,
349
+ analysisFollowUpSent: state.analysisFollowUpSent
350
+ })
351
+ case "SpecReady":
352
+ return Option.some({
353
+ projectId: state.projectId,
354
+ planType: state.planType,
355
+ readyFlags: state.readyFlags,
356
+ analysisFollowUpSent: state.analysisFollowUpSent
357
+ })
358
+ default:
359
+ return Option.none()
360
+ }
361
+ }
362
+
363
+ // ── Procedure handlers ─────────────────────────────────────
364
+
365
+ return Machine.procedures.make<ChatState>(ChatState.Idle()).pipe(
366
+ // ── UserMessage ────────────────────────────────────────
367
+ Machine.procedures.add<UserMessage>()("UserMessage", (ctx) =>
368
+ Effect.gen(function*() {
369
+ const { state } = ctx
370
+ const text = ctx.request.text
371
+ yield* Effect.log(`Incoming message: ${text}`)
372
+
373
+ switch (state._tag) {
374
+ case "Idle": {
375
+ if (text === PLAN_BUTTON_LABEL) {
376
+ const projects = yield* projectStore.listProjects.pipe(
377
+ Effect.orElseSucceed((): ReadonlyArray<{ id: string }> => [])
378
+ )
379
+ if (projects.length === 0) {
380
+ yield* notifier.sendMessage("No projects. Create one first.")
381
+ return reply(state)
382
+ }
383
+ if (projects.length === 1) {
384
+ yield* Effect.log("Single project auto-selected").pipe(
385
+ Effect.annotateLogs("projectId", projects[0]!.id)
386
+ )
387
+ yield* notifier.sendMessage({
388
+ text: "What type of change?",
389
+ options: [...PLAN_TYPE_LABELS.map((label) => ({ label })), { label: ABORT_BUTTON_LABEL }]
390
+ })
391
+ return reply(ChatState.SelectingPlanType({ projectId: projects[0]!.id }))
392
+ }
393
+ yield* notifier.sendMessage({
394
+ text: "Select a project:",
395
+ options: [
396
+ ...projects.map((p) => ({ label: p.id })),
397
+ { label: NEW_PROJECT_BUTTON_LABEL },
398
+ { label: ABORT_BUTTON_LABEL }
399
+ ]
400
+ })
401
+ return reply(ChatState.SelectingProject())
402
+ }
403
+ if (text === NEW_PROJECT_BUTTON_LABEL) {
404
+ yield* notifier.sendMessage("Enter project name:")
405
+ return reply(ChatState.CreatingProject({
406
+ step: "Name",
407
+ data: {},
408
+ continueWithPlan: false
409
+ }))
410
+ }
411
+ return reply(state)
412
+ }
413
+
414
+ case "SelectingProject": {
415
+ if (text === ABORT_BUTTON_LABEL) {
416
+ yield* notifier.sendMessage({ text: "Plan aborted.", replyKeyboard: IDLE_KEYBOARD })
417
+ return reply(ChatState.Idle())
418
+ }
419
+ if (text === NEW_PROJECT_BUTTON_LABEL) {
420
+ yield* notifier.sendMessage("Enter project name:")
421
+ return reply(ChatState.CreatingProject({
422
+ step: "Name",
423
+ data: {},
424
+ continueWithPlan: true
425
+ }))
426
+ }
427
+ yield* Effect.log("Project selected").pipe(
428
+ Effect.annotateLogs("projectId", text)
429
+ )
430
+ yield* notifier.sendMessage({
431
+ text: "What type of change?",
432
+ options: [...PLAN_TYPE_LABELS.map((label) => ({ label })), { label: ABORT_BUTTON_LABEL }]
433
+ })
434
+ return reply(ChatState.SelectingPlanType({ projectId: text }))
435
+ }
436
+
437
+ case "SelectingPlanType": {
438
+ if (text === ABORT_BUTTON_LABEL) {
439
+ yield* notifier.sendMessage({ text: "Plan aborted.", replyKeyboard: IDLE_KEYBOARD })
440
+ return reply(ChatState.Idle())
441
+ }
442
+ if (PLAN_TYPE_LABELS.includes(text)) {
443
+ yield* Effect.log("Plan type selected, collection started").pipe(
444
+ Effect.annotateLogs("planType", text)
445
+ )
446
+ yield* notifier.sendMessage({
447
+ text: "Describe what you'd like to plan. Tap <b>Done</b> when ready.",
448
+ replyKeyboard: COLLECTING_KEYBOARD
449
+ })
450
+ return reply(ChatState.CollectingPlan({
451
+ projectId: state.projectId,
452
+ planType: text,
453
+ buffer: []
454
+ }))
455
+ }
456
+ return reply(state)
457
+ }
458
+
459
+ case "CollectingPlan": {
460
+ if (text === ABORT_BUTTON_LABEL) {
461
+ yield* abortToIdle(false)
462
+ return reply(ChatState.Idle())
463
+ }
464
+ if (text === DONE_BUTTON_LABEL) {
465
+ const joinedText = state.buffer.join("\n")
466
+ if (joinedText.trim().length === 0) {
467
+ yield* Effect.log("Plan collection done with empty buffer")
468
+ yield* notifier.sendMessage("No plan description provided.")
469
+ return reply(state)
470
+ }
471
+ yield* Effect.log("Plan collection done, starting session").pipe(
472
+ Effect.annotateLogs("planText", joinedText)
473
+ )
474
+ const totalProjects = yield* projectStore.listProjects.pipe(
475
+ Effect.map((ps) => ps.length),
476
+ Effect.orElseSucceed(() => 1)
477
+ )
478
+ yield* planSession.start(joinedText, totalProjects > 1 ? state.projectId : undefined).pipe(
479
+ Effect.tapError((err) => notifier.sendMessage(`Plan error: ${err.message}`)),
480
+ Effect.orElseSucceed(() => undefined)
481
+ )
482
+ yield* notifier.sendMessage({
483
+ text: "Planning started...",
484
+ replyKeyboard: SESSION_KEYBOARD
485
+ })
486
+ return reply(initialSessionRunning(state.projectId, state.planType))
487
+ }
488
+ yield* Effect.log("Buffering plan message").pipe(
489
+ Effect.annotateLogs("bufferedText", text)
490
+ )
491
+ yield* notifier.sendMessage("✓ Added. Tap <b>Done</b> when ready.")
492
+ return reply(ChatState.CollectingPlan({
493
+ projectId: state.projectId,
494
+ planType: state.planType,
495
+ buffer: [...state.buffer, text]
496
+ }))
497
+ }
498
+
499
+ case "SessionRunning": {
500
+ if (text === ABORT_BUTTON_LABEL) {
501
+ yield* abortToIdle(true)
502
+ return reply(ChatState.Idle())
503
+ }
504
+ if (state.pendingAnswerCount > 0 && state.pendingOptionLabels.has(text)) {
505
+ if (text === MY_ANSWER_BUTTON_LABEL) {
506
+ yield* notifier.sendMessage({
507
+ text: "Type your answer:",
508
+ options: [{ label: BACK_BUTTON_LABEL }]
509
+ })
510
+ return reply(ChatState.SessionRunning({ ...state, awaitingFreeTextAnswer: true }))
511
+ }
512
+ if (text === BACK_BUTTON_LABEL) {
513
+ if (Option.isSome(state.lastQuestionMessage)) {
514
+ yield* notifier.sendMessage(state.lastQuestionMessage.value)
515
+ }
516
+ return reply(ChatState.SessionRunning({ ...state, awaitingFreeTextAnswer: false }))
517
+ }
518
+ const newBuffer = [...state.answersBuffer, text]
519
+ const newPending = state.pendingAnswerCount - 1
520
+ if (newPending <= 0) {
521
+ yield* Effect.log("Buffering answer")
522
+ yield* submitAnswers(newBuffer)
523
+ return reply(ChatState.SessionRunning({
524
+ ...state,
525
+ awaitingFreeTextAnswer: false,
526
+ answersBuffer: [],
527
+ pendingAnswerCount: 0,
528
+ pendingOptionLabels: new Set(),
529
+ readyFlags: new ReadyFlags({ ...state.readyFlags, idle: false })
530
+ }))
531
+ }
532
+ yield* Effect.log("Buffering answer")
533
+ return reply(ChatState.SessionRunning({
534
+ ...state,
535
+ awaitingFreeTextAnswer: false,
536
+ answersBuffer: newBuffer,
537
+ pendingAnswerCount: newPending
538
+ }))
539
+ }
540
+ if (state.awaitingFreeTextAnswer) {
541
+ const newBuffer = [...state.answersBuffer, text]
542
+ const newPending = state.pendingAnswerCount - 1
543
+ if (newPending <= 0) {
544
+ yield* Effect.log("Buffering free-text answer")
545
+ yield* submitAnswers(newBuffer)
546
+ return reply(ChatState.SessionRunning({
547
+ ...state,
548
+ awaitingFreeTextAnswer: false,
549
+ answersBuffer: [],
550
+ pendingAnswerCount: 0,
551
+ pendingOptionLabels: new Set(),
552
+ readyFlags: new ReadyFlags({ ...state.readyFlags, idle: false })
553
+ }))
554
+ }
555
+ yield* Effect.log("Buffering free-text answer")
556
+ return reply(ChatState.SessionRunning({
557
+ ...state,
558
+ awaitingFreeTextAnswer: false,
559
+ answersBuffer: newBuffer,
560
+ pendingAnswerCount: newPending
561
+ }))
562
+ }
563
+ const idle = yield* planSession.isIdle
564
+ if (idle) {
565
+ yield* planSession.sendFollowUp(text).pipe(
566
+ Effect.tap(() => notifier.sendMessage("Follow-up sent.")),
567
+ Effect.tapError((err) => Effect.logError(`Plan follow-up error: ${err.message}`)),
568
+ Effect.orElseSucceed(() => undefined)
569
+ )
570
+ return reply(ChatState.SessionRunning({
571
+ ...state,
572
+ readyFlags: new ReadyFlags({ ...state.readyFlags, idle: false })
573
+ }))
574
+ }
575
+ const newState = yield* showFollowUpChoice(
576
+ text,
577
+ state.projectId,
578
+ state.planType,
579
+ state.readyFlags,
580
+ state.analysisFollowUpSent
581
+ )
582
+ return reply(newState)
583
+ }
584
+
585
+ case "AwaitingFollowUpDecision": {
586
+ if (text === BUFFER_BUTTON_LABEL) {
587
+ yield* Effect.log("Buffering follow-up message")
588
+ yield* planSession.sendFollowUp(state.message).pipe(
589
+ Effect.tap(() => notifier.sendMessage("Message buffered — Claude will process it shortly.")),
590
+ Effect.tapError((err) => Effect.logError(`Plan follow-up error: ${err.message}`)),
591
+ Effect.orElseSucceed(() => undefined)
592
+ )
593
+ return reply(ChatState.SessionRunning({
594
+ projectId: state.projectId,
595
+ planType: state.planType,
596
+ pendingAnswerCount: 0,
597
+ pendingOptionLabels: new Set(),
598
+ answersBuffer: [],
599
+ awaitingFreeTextAnswer: false,
600
+ lastQuestionMessage: Option.none(),
601
+ readyFlags: new ReadyFlags({ ...state.readyFlags, idle: false }),
602
+ analysisFollowUpSent: state.analysisFollowUpSent
603
+ }))
604
+ }
605
+ if (text === INTERRUPT_BUTTON_LABEL) {
606
+ yield* Effect.log("Interrupting Claude with follow-up message")
607
+ yield* planSession.interrupt(state.message).pipe(
608
+ Effect.tap(() => notifier.sendMessage("Claude interrupted — processing your message now.")),
609
+ Effect.tapError((err) => Effect.logError(`Plan interrupt error: ${err.message}`)),
610
+ Effect.orElseSucceed(() => undefined)
611
+ )
612
+ return reply(ChatState.SessionRunning({
613
+ projectId: state.projectId,
614
+ planType: state.planType,
615
+ pendingAnswerCount: 0,
616
+ pendingOptionLabels: new Set(),
617
+ answersBuffer: [],
618
+ awaitingFreeTextAnswer: false,
619
+ lastQuestionMessage: Option.none(),
620
+ readyFlags: new ReadyFlags({ ...state.readyFlags, idle: false }),
621
+ analysisFollowUpSent: state.analysisFollowUpSent
622
+ }))
623
+ }
624
+ if (text === DISCARD_BUTTON_LABEL) {
625
+ yield* Effect.log("Follow-up message discarded")
626
+ yield* notifier.sendMessage("Message discarded.")
627
+ return reply(ChatState.SessionRunning({
628
+ projectId: state.projectId,
629
+ planType: state.planType,
630
+ pendingAnswerCount: 0,
631
+ pendingOptionLabels: new Set(),
632
+ answersBuffer: [],
633
+ awaitingFreeTextAnswer: false,
634
+ lastQuestionMessage: Option.none(),
635
+ readyFlags: state.readyFlags,
636
+ analysisFollowUpSent: state.analysisFollowUpSent
637
+ }))
638
+ }
639
+ if (text === ABORT_BUTTON_LABEL) {
640
+ yield* abortToIdle(true)
641
+ return reply(ChatState.Idle())
642
+ }
643
+ return reply(state)
644
+ }
645
+
646
+ case "SpecReady": {
647
+ if (text === APPROVE_BUTTON_LABEL) {
648
+ yield* Effect.log("User approved task creation")
649
+ yield* planSession.approve.pipe(
650
+ Effect.tapError((err) => Effect.logError(`Plan approve error: ${err.message}`)),
651
+ Effect.orElseSucceed(() => undefined)
652
+ )
653
+ yield* notifier.sendMessage({ text: "Spec approved.", replyKeyboard: IDLE_KEYBOARD })
654
+ return reply(ChatState.Idle())
655
+ }
656
+ if (text === ABORT_BUTTON_LABEL) {
657
+ yield* abortToIdle(true)
658
+ return reply(ChatState.Idle())
659
+ }
660
+ const idleSpec = yield* planSession.isIdle
661
+ if (idleSpec) {
662
+ yield* planSession.sendFollowUp(text).pipe(
663
+ Effect.tap(() => notifier.sendMessage("Follow-up sent.")),
664
+ Effect.tapError((err) => Effect.logError(`Plan follow-up error: ${err.message}`)),
665
+ Effect.orElseSucceed(() => undefined)
666
+ )
667
+ return reply(ChatState.SessionRunning({
668
+ projectId: state.projectId,
669
+ planType: state.planType,
670
+ pendingAnswerCount: 0,
671
+ pendingOptionLabels: new Set(),
672
+ answersBuffer: [],
673
+ awaitingFreeTextAnswer: false,
674
+ lastQuestionMessage: Option.none(),
675
+ readyFlags: new ReadyFlags({ ...state.readyFlags, idle: false }),
676
+ analysisFollowUpSent: state.analysisFollowUpSent
677
+ }))
678
+ }
679
+ const followUpState = yield* showFollowUpChoice(
680
+ text,
681
+ state.projectId,
682
+ state.planType,
683
+ state.readyFlags,
684
+ state.analysisFollowUpSent
685
+ )
686
+ return reply(followUpState)
687
+ }
688
+
689
+ case "CreatingProject": {
690
+ if (text === ABORT_BUTTON_LABEL) {
691
+ yield* notifier.sendMessage({ text: "Project creation cancelled.", replyKeyboard: IDLE_KEYBOARD })
692
+ return reply(ChatState.Idle())
693
+ }
694
+ switch (state.step) {
695
+ case "Name": {
696
+ yield* notifier.sendMessage({
697
+ text: "Concurrency (tasks in parallel):",
698
+ options: [
699
+ { label: "1" },
700
+ { label: "2" },
701
+ { label: "3" },
702
+ { label: "4" },
703
+ { label: ABORT_BUTTON_LABEL }
704
+ ]
705
+ })
706
+ return reply(ChatState.CreatingProject({
707
+ ...state,
708
+ step: "Concurrency",
709
+ data: { ...state.data, name: text }
710
+ }))
711
+ }
712
+ case "Concurrency": {
713
+ const n = Number(text)
714
+ if (isNaN(n) || n < 1) return reply(state)
715
+ yield* notifier.sendMessage({
716
+ text: "Target branch (type branch name or skip):",
717
+ options: [{ label: "Skip" }, { label: ABORT_BUTTON_LABEL }]
718
+ })
719
+ return reply(ChatState.CreatingProject({
720
+ ...state,
721
+ step: "TargetBranch",
722
+ data: { ...state.data, concurrency: n }
723
+ }))
724
+ }
725
+ case "TargetBranch": {
726
+ const targetBranch = text === "Skip" ? null : text
727
+ yield* notifier.sendMessage({
728
+ text: "Git flow:",
729
+ options: [
730
+ { label: "PR" },
731
+ { label: "Commit" },
732
+ { label: ABORT_BUTTON_LABEL }
733
+ ]
734
+ })
735
+ return reply(ChatState.CreatingProject({
736
+ ...state,
737
+ step: "GitFlow",
738
+ data: { ...state.data, targetBranch }
739
+ }))
740
+ }
741
+ case "GitFlow": {
742
+ const gitFlow = text === "Commit" ? "commit" as const : "pr" as const
743
+ yield* notifier.sendMessage({
744
+ text: "Enable review agent?",
745
+ options: [
746
+ { label: "Yes" },
747
+ { label: "No" },
748
+ { label: ABORT_BUTTON_LABEL }
749
+ ]
750
+ })
751
+ return reply(ChatState.CreatingProject({
752
+ ...state,
753
+ step: "ReviewAgent",
754
+ data: { ...state.data, gitFlow }
755
+ }))
756
+ }
757
+ case "ReviewAgent": {
758
+ const reviewAgent = text === "Yes"
759
+ const data = state.data
760
+ yield* projectStore.createProject({
761
+ id: data.name!,
762
+ targetBranch: data.targetBranch != null ? Option.some(data.targetBranch) : Option.none(),
763
+ concurrency: data.concurrency!,
764
+ gitFlow: data.gitFlow!,
765
+ reviewAgent
766
+ }).pipe(
767
+ Effect.tapError((err) => notifier.sendMessage(`Failed to create project: ${err.message}`)),
768
+ Effect.orElseSucceed(() => undefined)
769
+ )
770
+ yield* notifier.sendMessage(`Project <b>${data.name!}</b> created.`)
771
+ if (state.continueWithPlan) {
772
+ yield* notifier.sendMessage({
773
+ text: "What type of change?",
774
+ options: [...PLAN_TYPE_LABELS.map((label) => ({ label })), { label: ABORT_BUTTON_LABEL }]
775
+ })
776
+ return reply(ChatState.SelectingPlanType({ projectId: data.name! }))
777
+ }
778
+ yield* notifier.sendMessage({ text: "Ready.", replyKeyboard: IDLE_KEYBOARD })
779
+ return reply(ChatState.Idle())
780
+ }
781
+ }
782
+ }
783
+ }
784
+ }).pipe(
785
+ Effect.annotateLogs("service", "PlanInput"),
786
+ Effect.tapError((err) => Effect.logError(`Incoming message error: ${String(err)}`)),
787
+ Effect.orElseSucceed(() => reply(ctx.state))
788
+ )),
789
+ // ── PlanTextOutput ─────────────────────────────────────
790
+ Machine.procedures.add<PlanTextOutput>()("PlanTextOutput", (ctx) =>
791
+ Effect.gen(function*() {
792
+ yield* Effect.forEach(splitMessage(markdownToTelegramHtml(ctx.request.text)), (chunk) =>
793
+ notifier.sendMessage(chunk))
794
+ return reply(ctx.state)
795
+ }).pipe(
796
+ Effect.tapError((err) =>
797
+ Effect.logError(`Plan event relay error: ${String(err)}`)
798
+ ),
799
+ Effect.orElseSucceed(() => reply(ctx.state))
800
+ )),
801
+ // ── PlanQuestionReceived ───────────────────────────────
802
+ Machine.procedures.add<PlanQuestionReceived>()("PlanQuestionReceived", (ctx) =>
803
+ Effect.gen(function*() {
804
+ const { request, state } = ctx
805
+ if (state._tag !== "SessionRunning") {
806
+ return reply(state)
807
+ }
808
+ const labels = request.questions.flatMap((q) => q.options?.map((o) => o.label) ?? [])
809
+ const newPendingLabels = new Set([...labels, MY_ANSWER_BUTTON_LABEL, BACK_BUTTON_LABEL])
810
+ let lastQ: Option.Option<OutgoingMessage> = Option.none()
811
+ yield* Effect.forEach(request.questions, (q) => {
812
+ const formatted = markdownToTelegramHtml(q.question)
813
+ const header = q.header != null ? `<b>${markdownToTelegramHtml(q.header)}</b>\n` : ""
814
+ const baseOptions = q.options?.map((o) => ({ label: o.label })) ?? []
815
+ const msg: OutgoingMessage = {
816
+ text: `${header}${formatted}`,
817
+ options: [...baseOptions, { label: MY_ANSWER_BUTTON_LABEL }]
818
+ }
819
+ lastQ = Option.some(msg)
820
+ return notifier.sendMessage(msg)
821
+ })
822
+ return reply(ChatState.SessionRunning({
823
+ ...state,
824
+ pendingAnswerCount: request.questions.length,
825
+ pendingOptionLabels: newPendingLabels,
826
+ answersBuffer: [],
827
+ awaitingFreeTextAnswer: false,
828
+ lastQuestionMessage: lastQ
829
+ }))
830
+ }).pipe(
831
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
832
+ Effect.orElseSucceed(() => reply(ctx.state))
833
+ )),
834
+ // ── PlanSpecCreatedReq ─────────────────────────────────
835
+ Machine.procedures.add<PlanSpecCreatedReq>()("PlanSpecCreatedReq", (ctx) =>
836
+ Effect.gen(function*() {
837
+ const active = extractActiveState(ctx.state)
838
+ if (Option.isNone(active)) return reply(ctx.state)
839
+ const { analysisFollowUpSent, planType, projectId, readyFlags } = active.value
840
+ const sent = yield* maybeAnalysisFollowUp(planType, analysisFollowUpSent)
841
+ const newFlags = new ReadyFlags({ ...readyFlags, spec: true })
842
+ const newState = yield* checkAllReady(projectId, planType, newFlags, sent)
843
+ return reply(newState)
844
+ }).pipe(
845
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
846
+ Effect.orElseSucceed(() => reply(ctx.state))
847
+ )),
848
+ // ── PlanSpecUpdatedReq ─────────────────────────────────
849
+ Machine.procedures.add<PlanSpecUpdatedReq>()("PlanSpecUpdatedReq", (ctx) =>
850
+ Effect.gen(function*() {
851
+ const active = extractActiveState(ctx.state)
852
+ if (Option.isNone(active)) return reply(ctx.state)
853
+ const { analysisFollowUpSent, planType, projectId, readyFlags } = active.value
854
+ const sent = yield* maybeAnalysisFollowUp(planType, analysisFollowUpSent)
855
+ const newFlags = new ReadyFlags({ ...readyFlags, spec: true })
856
+ const newState = yield* checkAllReady(projectId, planType, newFlags, sent)
857
+ return reply(newState)
858
+ }).pipe(
859
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
860
+ Effect.orElseSucceed(() => reply(ctx.state))
861
+ )),
862
+ // ── PlanAnalysisReadyReq ───────────────────────────────
863
+ Machine.procedures.add<PlanAnalysisReadyReq>()("PlanAnalysisReadyReq", (ctx) =>
864
+ Effect.gen(function*() {
865
+ const active = extractActiveState(ctx.state)
866
+ if (Option.isNone(active)) return reply(ctx.state)
867
+ const { analysisFollowUpSent, planType, projectId, readyFlags } = active.value
868
+ const newFlags = new ReadyFlags({ ...readyFlags, analysis: true })
869
+ const newState = yield* checkAllReady(projectId, planType, newFlags, analysisFollowUpSent)
870
+ return reply(newState)
871
+ }).pipe(
872
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
873
+ Effect.orElseSucceed(() => reply(ctx.state))
874
+ )),
875
+ // ── PlanAwaitingInputReq ───────────────────────────────
876
+ Machine.procedures.add<PlanAwaitingInputReq>()("PlanAwaitingInputReq", (ctx) =>
877
+ Effect.gen(function*() {
878
+ const active = extractActiveState(ctx.state)
879
+ if (Option.isNone(active)) return reply(ctx.state)
880
+ const { analysisFollowUpSent, planType, projectId, readyFlags } = active.value
881
+ const newFlags = new ReadyFlags({ ...readyFlags, idle: true })
882
+ const newState = yield* checkAllReady(projectId, planType, newFlags, analysisFollowUpSent)
883
+ return reply(newState)
884
+ }).pipe(
885
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
886
+ Effect.orElseSucceed(() => reply(ctx.state))
887
+ )),
888
+ // ── PlanCompletedReq ───────────────────────────────────
889
+ Machine.procedures.add<PlanCompletedReq>()("PlanCompletedReq", (ctx) =>
890
+ Effect.gen(function*() {
891
+ yield* notifier.sendMessage({ text: "Plan completed.", replyKeyboard: IDLE_KEYBOARD })
892
+ return reply(ChatState.Idle())
893
+ }).pipe(
894
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
895
+ Effect.orElseSucceed(() => reply(ctx.state))
896
+ )),
897
+ // ── PlanFailedReq ──────────────────────────────────────
898
+ Machine.procedures.add<PlanFailedReq>()("PlanFailedReq", (ctx) =>
899
+ Effect.gen(function*() {
900
+ yield* notifier.sendMessage({
901
+ text: `Plan failed: ${ctx.request.message}`,
902
+ replyKeyboard: IDLE_KEYBOARD
903
+ })
904
+ return reply(ChatState.Idle())
905
+ }).pipe(
906
+ Effect.tapError((err) => Effect.logError(`Plan event relay error: ${String(err)}`)),
907
+ Effect.orElseSucceed(() => reply(ctx.state))
908
+ ))
909
+ )
910
+ })
911
+ )