@interactive-inc/claude-funnel 0.7.1 → 0.8.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 (349) hide show
  1. package/README.md +155 -133
  2. package/dist/bin.js +1417 -0
  3. package/dist/gateway/daemon.js +513 -0
  4. package/dist/highlights-eq9cgrbb.scm +604 -0
  5. package/dist/highlights-ghv9g403.scm +205 -0
  6. package/dist/highlights-hk7bwhj4.scm +284 -0
  7. package/dist/highlights-r812a2qc.scm +150 -0
  8. package/dist/highlights-x6tmsnaa.scm +115 -0
  9. package/dist/injections-73j83es3.scm +27 -0
  10. package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  11. package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
  12. package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  13. package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  14. package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
  15. package/lib/bin.ts +78 -0
  16. package/lib/{modules → cli}/router/to-request.ts +13 -20
  17. package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +27 -0
  18. package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +40 -0
  19. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +41 -0
  20. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +22 -0
  21. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +23 -0
  22. package/lib/cli/routes/channels.$channel.connectors.$connector.ts +26 -0
  23. package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +92 -0
  24. package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +22 -0
  25. package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +63 -0
  26. package/lib/cli/routes/channels.$channel.connectors.ts +26 -0
  27. package/lib/cli/routes/channels.$channel.rename.$newName.ts +22 -0
  28. package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +34 -0
  29. package/lib/cli/routes/channels.$channel.ts +34 -0
  30. package/lib/cli/routes/channels.add.$channel.ts +33 -0
  31. package/lib/cli/routes/channels.remove.$channel.ts +20 -0
  32. package/lib/cli/routes/channels.ts +39 -0
  33. package/lib/cli/routes/claude.ts +69 -0
  34. package/lib/cli/routes/gateway.listeners.ts +41 -0
  35. package/lib/cli/routes/gateway.logs.ts +123 -0
  36. package/lib/{routes/gateway/restart.ts → cli/routes/gateway.restart.ts} +20 -5
  37. package/lib/cli/routes/gateway.run.ts +41 -0
  38. package/lib/cli/routes/gateway.start.ts +50 -0
  39. package/lib/cli/routes/gateway.status.ts +19 -0
  40. package/lib/cli/routes/gateway.stop.ts +32 -0
  41. package/lib/cli/routes/gateway.ts +55 -0
  42. package/lib/cli/routes/index.ts +202 -0
  43. package/lib/cli/routes/profiles.$profile.as-default.ts +22 -0
  44. package/lib/cli/routes/profiles.$profile.rename.$newName.ts +22 -0
  45. package/lib/cli/routes/profiles.$profile.run.ts +36 -0
  46. package/lib/cli/routes/profiles.add.$profile.ts +46 -0
  47. package/lib/cli/routes/profiles.remove.$profile.ts +20 -0
  48. package/lib/cli/routes/profiles.set.$profile.ts +46 -0
  49. package/lib/cli/routes/profiles.ts +40 -0
  50. package/lib/cli/routes/status.ts +93 -0
  51. package/lib/cli/routes/update.ts +27 -0
  52. package/lib/connectors/connector-config-schema.ts +16 -0
  53. package/lib/connectors/connector-factory.ts +94 -0
  54. package/lib/connectors/connector-listener.ts +20 -0
  55. package/lib/{modules/connectors/funnel-discord-adapter.ts → connectors/discord-adapter.ts} +6 -11
  56. package/lib/{modules/connectors → connectors}/discord-connector-schema.ts +4 -1
  57. package/lib/connectors/discord-listener.ts +111 -0
  58. package/lib/{modules/connectors/funnel-gh-adapter.ts → connectors/gh-adapter.ts} +3 -6
  59. package/lib/{modules/connectors → connectors}/gh-connector-schema.ts +4 -1
  60. package/lib/{modules/connectors/funnel-gh-listener.ts → connectors/gh-listener.ts} +45 -19
  61. package/lib/{modules/connectors → connectors}/match-cron.ts +10 -4
  62. package/lib/connectors/schedule-connector-schema.ts +33 -0
  63. package/lib/connectors/schedule-listener.ts +207 -0
  64. package/lib/connectors/schedule-state-store.ts +54 -0
  65. package/lib/connectors/slack-adapter.ts +36 -0
  66. package/lib/{modules/connectors → connectors}/slack-connector-schema.ts +4 -1
  67. package/lib/{modules/connectors/funnel-slack-event-processor.ts → connectors/slack-event-processor.ts} +15 -9
  68. package/lib/{modules/connectors/funnel-slack-listener.ts → connectors/slack-listener.ts} +33 -14
  69. package/lib/engine/channels/channels.ts +520 -0
  70. package/lib/{modules/claude/funnel-claude.ts → engine/claude/claude.ts} +28 -55
  71. package/lib/engine/claude/gateway-controller.ts +4 -0
  72. package/lib/{modules/fs/funnel-file-system.ts → engine/fs/file-system.ts} +4 -0
  73. package/lib/{modules/fs/memory-funnel-file-system.ts → engine/fs/memory-file-system.ts} +20 -3
  74. package/lib/{modules/fs/node-funnel-file-system.ts → engine/fs/node-file-system.ts} +14 -2
  75. package/lib/{modules/http/memory-funnel-http-client.ts → engine/http/memory-http-client.ts} +1 -5
  76. package/lib/{modules/http/node-funnel-http-client.ts → engine/http/node-http-client.ts} +1 -5
  77. package/lib/{modules/id/memory-funnel-id-generator.ts → engine/id/memory-id-generator.ts} +1 -1
  78. package/lib/{modules/id/node-funnel-id-generator.ts → engine/id/node-id-generator.ts} +1 -1
  79. package/lib/{modules/logger/memory-funnel-logger.ts → engine/logger/memory-logger.ts} +1 -1
  80. package/lib/{modules/logger/node-funnel-logger.ts → engine/logger/node-logger.ts} +1 -1
  81. package/lib/{modules/logger/noop-funnel-logger.ts → engine/logger/noop-logger.ts} +1 -1
  82. package/lib/engine/mcp/channel-server.ts +204 -0
  83. package/lib/{modules/mcp/funnel-mcp.ts → engine/mcp/mcp.ts} +24 -10
  84. package/lib/{modules/process/memory-funnel-process-runner.ts → engine/process/memory-process-runner.ts} +1 -1
  85. package/lib/{modules/process/node-funnel-process-runner.ts → engine/process/node-process-runner.ts} +12 -21
  86. package/lib/engine/profiles/profile-channel-checker.ts +7 -0
  87. package/lib/{modules/profiles/funnel-profiles.ts → engine/profiles/profiles.ts} +41 -43
  88. package/lib/{modules/settings/mock-funnel-settings-reader.ts → engine/settings/mock-settings-reader.ts} +4 -3
  89. package/lib/{modules/settings/funnel-settings-reader.ts → engine/settings/settings-reader.ts} +1 -1
  90. package/lib/engine/settings/settings-schema.ts +46 -0
  91. package/lib/engine/settings/settings-store.ts +110 -0
  92. package/lib/{modules/time/memory-funnel-clock.ts → engine/time/memory-clock.ts} +1 -1
  93. package/lib/{modules/time/node-funnel-clock.ts → engine/time/node-clock.ts} +1 -1
  94. package/lib/funnel.ts +83 -78
  95. package/lib/gateway/auth-middleware.ts +44 -0
  96. package/lib/gateway/broadcaster.ts +319 -0
  97. package/lib/gateway/daemon.ts +47 -0
  98. package/lib/gateway/factory.ts +10 -0
  99. package/lib/gateway/funnel-event-store.ts +155 -0
  100. package/lib/gateway/gateway-server.ts +414 -0
  101. package/lib/gateway/gateway-token.ts +79 -0
  102. package/lib/{modules/gateway/funnel-gateway.ts → gateway/gateway.ts} +27 -13
  103. package/lib/{modules/gateway → gateway}/kill-competing-slack-gateways.ts +4 -4
  104. package/lib/gateway/listener-supervisor.ts +339 -0
  105. package/lib/gateway/listeners-client.ts +128 -0
  106. package/lib/gateway/resolve-daemon-script.ts +26 -0
  107. package/lib/gateway/routes/channels.connectors.call.ts +39 -0
  108. package/lib/gateway/routes/health.ts +13 -0
  109. package/lib/gateway/routes/index.ts +24 -0
  110. package/lib/gateway/routes/listeners.list.ts +6 -0
  111. package/lib/gateway/routes/listeners.restart.ts +15 -0
  112. package/lib/gateway/routes/listeners.start.ts +15 -0
  113. package/lib/gateway/routes/listeners.stop.ts +15 -0
  114. package/lib/gateway/routes/route-deps.ts +11 -0
  115. package/lib/gateway/routes/status.ts +15 -0
  116. package/lib/gateway/routes/validator.ts +17 -0
  117. package/lib/index.ts +50 -92
  118. package/lib/logger/leuco-human-file-writer.ts +65 -0
  119. package/lib/logger/leuco-human-logger.ts +98 -0
  120. package/lib/logger/leuco-human-record.ts +16 -0
  121. package/lib/logger/leuco-human-stdout-writer.ts +26 -0
  122. package/lib/logger/leuco-human-writer.ts +14 -0
  123. package/lib/logger/leuco-logger-memory-sink.ts +67 -0
  124. package/lib/logger/leuco-logger-record.ts +13 -0
  125. package/lib/logger/leuco-logger-sink.ts +33 -0
  126. package/lib/logger/leuco-logger-sqlite-sink.ts +355 -0
  127. package/lib/logger/leuco-logger.ts +135 -0
  128. package/lib/tui/app.tsx +357 -0
  129. package/lib/tui/components/add-row.tsx +18 -0
  130. package/lib/tui/components/brand.tsx +27 -0
  131. package/lib/tui/components/card.tsx +44 -0
  132. package/lib/tui/components/detail-bar.tsx +46 -0
  133. package/lib/tui/components/editable-field.tsx +33 -0
  134. package/lib/tui/components/empty-state.tsx +11 -0
  135. package/lib/tui/components/gateway-status.tsx +66 -0
  136. package/lib/tui/components/keymap.tsx +29 -0
  137. package/lib/tui/components/menu-item.tsx +73 -0
  138. package/lib/tui/components/menu.tsx +26 -0
  139. package/lib/tui/components/panel-header.tsx +22 -0
  140. package/lib/tui/components/readonly-field.tsx +18 -0
  141. package/lib/tui/components/section-header.tsx +25 -0
  142. package/lib/tui/components/selection-accent.tsx +32 -0
  143. package/lib/tui/components/session-item.tsx +33 -0
  144. package/lib/tui/components/session-list.tsx +33 -0
  145. package/lib/tui/components/ui/hascii/accordion-item.tsx +88 -0
  146. package/lib/tui/components/ui/hascii/accordion.tsx +96 -0
  147. package/lib/tui/components/ui/hascii/alert-dialog.tsx +43 -0
  148. package/lib/tui/components/ui/hascii/badge.tsx +51 -0
  149. package/lib/tui/components/ui/hascii/breadcrumb.tsx +58 -0
  150. package/lib/tui/components/ui/hascii/button.tsx +194 -0
  151. package/lib/tui/components/ui/hascii/card-content.tsx +14 -0
  152. package/lib/tui/components/ui/hascii/card-description.tsx +13 -0
  153. package/lib/tui/components/ui/hascii/card-footer.tsx +14 -0
  154. package/lib/tui/components/ui/hascii/card-header.tsx +14 -0
  155. package/lib/tui/components/ui/hascii/card-title.tsx +13 -0
  156. package/lib/tui/components/ui/hascii/card.tsx +27 -0
  157. package/lib/tui/components/ui/hascii/checkbox.tsx +65 -0
  158. package/lib/tui/components/ui/hascii/command.tsx +159 -0
  159. package/lib/tui/components/ui/hascii/dialog-content.tsx +14 -0
  160. package/lib/tui/components/ui/hascii/dialog-description.tsx +13 -0
  161. package/lib/tui/components/ui/hascii/dialog-footer.tsx +14 -0
  162. package/lib/tui/components/ui/hascii/dialog-header.tsx +14 -0
  163. package/lib/tui/components/ui/hascii/dialog-title.tsx +13 -0
  164. package/lib/tui/components/ui/hascii/dialog.tsx +27 -0
  165. package/lib/tui/components/ui/hascii/file-tree.tsx +142 -0
  166. package/lib/tui/components/ui/hascii/focus-group.tsx +62 -0
  167. package/lib/tui/components/ui/hascii/form-item.tsx +43 -0
  168. package/lib/tui/components/ui/hascii/input-otp.tsx +86 -0
  169. package/lib/tui/components/ui/hascii/input.tsx +130 -0
  170. package/lib/tui/components/ui/hascii/pagination.tsx +105 -0
  171. package/lib/tui/components/ui/hascii/progress.tsx +28 -0
  172. package/lib/tui/components/ui/hascii/select.tsx +131 -0
  173. package/lib/tui/components/ui/hascii/separator.tsx +35 -0
  174. package/lib/tui/components/ui/hascii/sidebar-content.tsx +23 -0
  175. package/lib/tui/components/ui/hascii/sidebar-header.tsx +14 -0
  176. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +67 -0
  177. package/lib/tui/components/ui/hascii/sidebar.tsx +24 -0
  178. package/lib/tui/components/ui/hascii/skeleton.tsx +60 -0
  179. package/lib/tui/components/ui/hascii/slider.tsx +91 -0
  180. package/lib/tui/components/ui/hascii/snackbar.tsx +75 -0
  181. package/lib/tui/components/ui/hascii/sparkline.tsx +53 -0
  182. package/lib/tui/components/ui/hascii/spinner.tsx +47 -0
  183. package/lib/tui/components/ui/hascii/stepper.tsx +54 -0
  184. package/lib/tui/components/ui/hascii/switch.tsx +66 -0
  185. package/lib/tui/components/ui/hascii/table.tsx +95 -0
  186. package/lib/tui/components/ui/hascii/tabs.tsx +59 -0
  187. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +45 -0
  188. package/lib/tui/components/ui/hascii/toggle-group.tsx +99 -0
  189. package/lib/tui/components/ui/hascii/tree.tsx +104 -0
  190. package/lib/tui/components/view-shell.tsx +44 -0
  191. package/lib/tui/filter-input.tsx +33 -0
  192. package/lib/tui/hooks/hascii/use-pressable.ts +54 -0
  193. package/lib/tui/parse-comma-list.ts +14 -0
  194. package/lib/tui/profile-launcher.tsx +61 -0
  195. package/lib/tui/scrollbar-options.ts +19 -0
  196. package/lib/tui/sidebar.tsx +50 -0
  197. package/lib/tui/theme.ts +40 -0
  198. package/lib/tui/tui.tsx +20 -0
  199. package/lib/tui/types.ts +38 -0
  200. package/lib/tui/unique-name.ts +18 -0
  201. package/lib/tui/use-event-stream.ts +133 -0
  202. package/lib/tui/use-snapshot.ts +99 -0
  203. package/lib/tui/utils/hascii/form-item-context.tsx +23 -0
  204. package/lib/tui/utils/hascii/input-focus-context.tsx +31 -0
  205. package/lib/tui/utils/hascii/theme-context.tsx +26 -0
  206. package/lib/tui/utils/hascii/theme.ts +176 -0
  207. package/lib/tui/views/channels-view.tsx +108 -0
  208. package/lib/tui/views/connectors-view.tsx +164 -0
  209. package/lib/tui/views/events-view.tsx +160 -0
  210. package/lib/tui/views/listeners-view.tsx +80 -0
  211. package/lib/tui/views/profiles-view.tsx +152 -0
  212. package/package.json +50 -44
  213. package/lib/api.ts +0 -54
  214. package/lib/modules/channels/channel-connector-ref-updater.ts +0 -4
  215. package/lib/modules/channels/funnel-channels.ts +0 -160
  216. package/lib/modules/connectors/connector-config-schema.ts +0 -16
  217. package/lib/modules/connectors/connector-existence-checker.ts +0 -3
  218. package/lib/modules/connectors/funnel-callable-connector-store.ts +0 -9
  219. package/lib/modules/connectors/funnel-connector-listener.ts +0 -5
  220. package/lib/modules/connectors/funnel-connector-stores.ts +0 -52
  221. package/lib/modules/connectors/funnel-connector-type-store.ts +0 -24
  222. package/lib/modules/connectors/funnel-connectors.ts +0 -151
  223. package/lib/modules/connectors/funnel-discord-listener.ts +0 -71
  224. package/lib/modules/connectors/funnel-discord-store.ts +0 -88
  225. package/lib/modules/connectors/funnel-gh-store.ts +0 -101
  226. package/lib/modules/connectors/funnel-json-connector-store.ts +0 -100
  227. package/lib/modules/connectors/funnel-schedule-listener.ts +0 -130
  228. package/lib/modules/connectors/funnel-schedule-store.ts +0 -195
  229. package/lib/modules/connectors/funnel-slack-adapter.ts +0 -31
  230. package/lib/modules/connectors/funnel-slack-store.ts +0 -90
  231. package/lib/modules/connectors/migrate-legacy-connectors.ts +0 -81
  232. package/lib/modules/connectors/schedule-connector-schema.ts +0 -18
  233. package/lib/modules/connectors/schedule-last-fired-store.ts +0 -48
  234. package/lib/modules/gateway/daemon.ts +0 -74
  235. package/lib/modules/gateway/funnel-broadcaster.ts +0 -37
  236. package/lib/modules/gateway/funnel-event-logger.ts +0 -59
  237. package/lib/modules/gateway/funnel-gateway-server.ts +0 -241
  238. package/lib/modules/mcp/channel-server.ts +0 -76
  239. package/lib/modules/profiles/profile-channel-checker.ts +0 -3
  240. package/lib/modules/profiles/profile-channel-ref-updater.ts +0 -3
  241. package/lib/modules/repos/funnel-repositories.ts +0 -112
  242. package/lib/modules/schedule/funnel-schedule.ts +0 -39
  243. package/lib/modules/settings/funnel-settings-store.ts +0 -56
  244. package/lib/modules/settings/settings-schema.ts +0 -33
  245. package/lib/modules/tui/app.tsx +0 -44
  246. package/lib/modules/tui/tui.tsx +0 -13
  247. package/lib/routes/channels/add.help.ts +0 -3
  248. package/lib/routes/channels/add.ts +0 -21
  249. package/lib/routes/channels/connectors-attach.help.ts +0 -3
  250. package/lib/routes/channels/connectors-attach.ts +0 -17
  251. package/lib/routes/channels/connectors-detach.help.ts +0 -3
  252. package/lib/routes/channels/connectors-detach.ts +0 -17
  253. package/lib/routes/channels/group.help.ts +0 -16
  254. package/lib/routes/channels/group.ts +0 -22
  255. package/lib/routes/channels/remove.help.ts +0 -3
  256. package/lib/routes/channels/remove.ts +0 -17
  257. package/lib/routes/channels/rename.help.ts +0 -5
  258. package/lib/routes/channels/rename.ts +0 -17
  259. package/lib/routes/channels/routes.ts +0 -19
  260. package/lib/routes/channels/show.help.ts +0 -1
  261. package/lib/routes/channels/show.ts +0 -26
  262. package/lib/routes/claude/claude.help.ts +0 -16
  263. package/lib/routes/claude/claude.ts +0 -76
  264. package/lib/routes/claude/routes.ts +0 -4
  265. package/lib/routes/connectors/add.help.ts +0 -28
  266. package/lib/routes/connectors/add.ts +0 -64
  267. package/lib/routes/connectors/group.help.ts +0 -14
  268. package/lib/routes/connectors/group.ts +0 -18
  269. package/lib/routes/connectors/remove.help.ts +0 -3
  270. package/lib/routes/connectors/remove.ts +0 -17
  271. package/lib/routes/connectors/rename.help.ts +0 -5
  272. package/lib/routes/connectors/rename.ts +0 -17
  273. package/lib/routes/connectors/routes.ts +0 -23
  274. package/lib/routes/connectors/schedules-add.help.ts +0 -11
  275. package/lib/routes/connectors/schedules-add.ts +0 -33
  276. package/lib/routes/connectors/schedules-group.help.ts +0 -1
  277. package/lib/routes/connectors/schedules-group.ts +0 -38
  278. package/lib/routes/connectors/schedules-remove.help.ts +0 -3
  279. package/lib/routes/connectors/schedules-remove.ts +0 -17
  280. package/lib/routes/connectors/set.help.ts +0 -8
  281. package/lib/routes/connectors/set.ts +0 -72
  282. package/lib/routes/connectors/show.help.ts +0 -1
  283. package/lib/routes/connectors/show.ts +0 -41
  284. package/lib/routes/gateway/group.help.ts +0 -15
  285. package/lib/routes/gateway/group.ts +0 -28
  286. package/lib/routes/gateway/logs.help.ts +0 -13
  287. package/lib/routes/gateway/logs.ts +0 -102
  288. package/lib/routes/gateway/restart.help.ts +0 -10
  289. package/lib/routes/gateway/routes.ts +0 -18
  290. package/lib/routes/gateway/run.help.ts +0 -12
  291. package/lib/routes/gateway/run.ts +0 -35
  292. package/lib/routes/gateway/start.help.ts +0 -15
  293. package/lib/routes/gateway/start.ts +0 -32
  294. package/lib/routes/gateway/status.help.ts +0 -9
  295. package/lib/routes/gateway/status.ts +0 -28
  296. package/lib/routes/gateway/stop.help.ts +0 -8
  297. package/lib/routes/gateway/stop.ts +0 -21
  298. package/lib/routes/profiles/add.help.ts +0 -3
  299. package/lib/routes/profiles/add.ts +0 -33
  300. package/lib/routes/profiles/group.help.ts +0 -16
  301. package/lib/routes/profiles/group.ts +0 -25
  302. package/lib/routes/profiles/launch.help.ts +0 -4
  303. package/lib/routes/profiles/launch.ts +0 -36
  304. package/lib/routes/profiles/remove.help.ts +0 -3
  305. package/lib/routes/profiles/remove.ts +0 -17
  306. package/lib/routes/profiles/rename.help.ts +0 -5
  307. package/lib/routes/profiles/rename.ts +0 -17
  308. package/lib/routes/profiles/routes.ts +0 -18
  309. package/lib/routes/profiles/set.help.ts +0 -5
  310. package/lib/routes/profiles/set.ts +0 -32
  311. package/lib/routes/repos/add.help.ts +0 -6
  312. package/lib/routes/repos/add.ts +0 -20
  313. package/lib/routes/repos/group.help.ts +0 -11
  314. package/lib/routes/repos/group.ts +0 -18
  315. package/lib/routes/repos/remove.help.ts +0 -3
  316. package/lib/routes/repos/remove.ts +0 -17
  317. package/lib/routes/repos/rename.help.ts +0 -5
  318. package/lib/routes/repos/rename.ts +0 -17
  319. package/lib/routes/repos/routes.ts +0 -17
  320. package/lib/routes/repos/set.help.ts +0 -5
  321. package/lib/routes/repos/set.ts +0 -21
  322. package/lib/routes/repos/show.help.ts +0 -1
  323. package/lib/routes/repos/show.ts +0 -19
  324. package/lib/routes/request/discord-help.ts +0 -9
  325. package/lib/routes/request/discord.help.ts +0 -19
  326. package/lib/routes/request/discord.ts +0 -65
  327. package/lib/routes/request/group.help.ts +0 -15
  328. package/lib/routes/request/group.ts +0 -9
  329. package/lib/routes/request/routes.ts +0 -14
  330. package/lib/routes/request/slack-help.ts +0 -9
  331. package/lib/routes/request/slack.help.ts +0 -19
  332. package/lib/routes/request/slack.ts +0 -61
  333. package/lib/routes/status/routes.ts +0 -4
  334. package/lib/routes/status/status.help.ts +0 -6
  335. package/lib/routes/status/status.ts +0 -77
  336. package/lib/routes/update/routes.ts +0 -4
  337. package/lib/routes/update/update.help.ts +0 -5
  338. package/lib/routes/update/update.ts +0 -21
  339. package/lib/routes.ts +0 -40
  340. /package/lib/{factory.ts → cli/factory.ts} +0 -0
  341. /package/lib/{modules → cli}/router/query-to-cli-args.ts +0 -0
  342. /package/lib/{modules → cli}/router/validator.ts +0 -0
  343. /package/lib/{modules/connectors/funnel-connector-adapter.ts → connectors/connector-adapter.ts} +0 -0
  344. /package/lib/{modules/connectors/funnel-discord-event-processor.ts → connectors/discord-event-processor.ts} +0 -0
  345. /package/lib/{modules/http/funnel-http-client.ts → engine/http/http-client.ts} +0 -0
  346. /package/lib/{modules/id/funnel-id-generator.ts → engine/id/id-generator.ts} +0 -0
  347. /package/lib/{modules/logger/funnel-logger.ts → engine/logger/logger.ts} +0 -0
  348. /package/lib/{modules/process/funnel-process-runner.ts → engine/process/process-runner.ts} +0 -0
  349. /package/lib/{modules/time/funnel-clock.ts → engine/time/clock.ts} +0 -0
@@ -0,0 +1,142 @@
1
+ import { useState } from "react"
2
+ import type { HasciiTheme } from "@/tui/utils/hascii/theme"
3
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
4
+ import { usePressable } from "@/tui/hooks/hascii/use-pressable"
5
+
6
+ export type FileTreeNode = {
7
+ id: string
8
+ label: string
9
+ children?: FileTreeNode[]
10
+ }
11
+
12
+ export type Props = {
13
+ nodes: FileTreeNode[]
14
+ indent?: number
15
+ defaultExpanded?: string[]
16
+ expanded?: string[]
17
+ onToggle?: (id: string, isOpen: boolean) => void
18
+ selectedId?: string | null
19
+ onSelect?: (id: string) => void
20
+ }
21
+
22
+ type Row = {
23
+ node: FileTreeNode
24
+ depth: number
25
+ hasChildren: boolean
26
+ isOpen: boolean
27
+ }
28
+
29
+ type RowProps = {
30
+ row: Row
31
+ indent: number
32
+ isSelected: boolean
33
+ onPress: () => void
34
+ }
35
+
36
+ const pickRowBg = (
37
+ isActive: boolean,
38
+ isHovered: boolean,
39
+ isPressed: boolean,
40
+ theme: HasciiTheme,
41
+ ): string | undefined => {
42
+ if (isPressed) return theme.color.secondaryActive
43
+ if (isHovered && isActive) return theme.color.hoverActive
44
+ if (isHovered) return theme.color.secondaryHover
45
+ if (isActive) return theme.color.secondaryActive
46
+ return undefined
47
+ }
48
+
49
+ /** Internal row used by HasciiFileTree. Tracks the standard hover/active palette and renders ▾/▸ for folders. */
50
+ function HasciiFileTreeRow(props: RowProps) {
51
+ const theme = useHasciiTheme()
52
+ const press = usePressable({ onPress: props.onPress })
53
+
54
+ const bg = pickRowBg(props.isSelected, press.isHovered, press.isPressed, theme)
55
+
56
+ const indentText = " ".repeat(props.row.depth * props.indent)
57
+ const marker = !props.row.hasChildren ? " " : props.row.isOpen ? "▾" : "▸"
58
+
59
+ return (
60
+ <box
61
+ flexDirection="row"
62
+ alignItems="center"
63
+ paddingLeft={1}
64
+ paddingRight={1}
65
+ height={1}
66
+ backgroundColor={bg}
67
+ {...press.bind}
68
+ >
69
+ <text fg={theme.color.mutedForeground}>{indentText}</text>
70
+ <text fg={theme.color.mutedForeground}>{marker} </text>
71
+ <text fg={theme.color.foreground}>{props.row.node.label}</text>
72
+ </box>
73
+ )
74
+ }
75
+
76
+ const flatten = (nodes: FileTreeNode[], depth: number, openSet: Set<string>): Row[] => {
77
+ const rows: Row[] = []
78
+
79
+ for (const node of nodes) {
80
+ const hasChildren = (node.children?.length ?? 0) > 0
81
+ const isOpen = openSet.has(node.id)
82
+
83
+ rows.push({ node, depth, hasChildren, isOpen })
84
+
85
+ if (hasChildren && isOpen) {
86
+ const childRows = flatten(node.children ?? [], depth + 1, openSet)
87
+ for (const childRow of childRows) rows.push(childRow)
88
+ }
89
+ }
90
+
91
+ return rows
92
+ }
93
+
94
+ /** Indented IDE-style file tree. Folder rows show ▾/▸ and toggle on click; leaf rows just select. */
95
+ export function HasciiFileTree(props: Props) {
96
+ const indent = props.indent ?? 2
97
+
98
+ const internalState = useState<string[]>(props.defaultExpanded ?? [])
99
+ const internalOpen = internalState[0]
100
+ const setInternalOpen = internalState[1]
101
+
102
+ const expanded = props.expanded ?? internalOpen
103
+ const openSet = new Set(expanded)
104
+
105
+ const selectedState = useState<string | null>(props.selectedId ?? null)
106
+ const internalSelected = selectedState[0]
107
+ const setInternalSelected = selectedState[1]
108
+
109
+ const selected = props.selectedId !== undefined ? props.selectedId : internalSelected
110
+
111
+ const toggle = (id: string) => {
112
+ const isOpen = openSet.has(id)
113
+ const next = isOpen ? expanded.filter((entry) => entry !== id) : [...expanded, id]
114
+
115
+ if (props.expanded === undefined) setInternalOpen(next)
116
+ props.onToggle?.(id, !isOpen)
117
+ }
118
+
119
+ const select = (id: string) => {
120
+ if (props.selectedId === undefined) setInternalSelected(id)
121
+ props.onSelect?.(id)
122
+ }
123
+
124
+ const rows = flatten(props.nodes, 0, openSet)
125
+
126
+ return (
127
+ <box flexDirection="column">
128
+ {rows.map((row) => (
129
+ <HasciiFileTreeRow
130
+ key={row.node.id}
131
+ row={row}
132
+ indent={indent}
133
+ isSelected={row.node.id === selected}
134
+ onPress={() => {
135
+ select(row.node.id)
136
+ if (row.hasChildren) toggle(row.node.id)
137
+ }}
138
+ />
139
+ ))}
140
+ </box>
141
+ )
142
+ }
@@ -0,0 +1,62 @@
1
+ import { useKeyboard } from "@opentui/react"
2
+ import { createContext, useContext, useState } from "react"
3
+ import type { ReactNode } from "react"
4
+
5
+ type ContextValue = {
6
+ currentId: string | null
7
+ setCurrentId: (id: string) => void
8
+ }
9
+
10
+ const HasciiFocusContext = createContext<ContextValue | null>(null)
11
+
12
+ /** Returns whether the provided focusId matches the surrounding HasciiFocusGroup's current focus. */
13
+ export function useHasciiFocus(focusId: string | undefined): boolean {
14
+ const ctx = useContext(HasciiFocusContext)
15
+
16
+ if (!ctx || focusId === undefined) return false
17
+ return ctx.currentId === focusId
18
+ }
19
+
20
+ /** Returns the imperative API of the surrounding HasciiFocusGroup. Null when used outside a group. */
21
+ export function useHasciiFocusController(): ContextValue | null {
22
+ return useContext(HasciiFocusContext)
23
+ }
24
+
25
+ /** Computes the next focus index. Wraps at both ends. */
26
+ export function nextFocusIndex(currentIndex: number, length: number, isShift: boolean): number {
27
+ if (length === 0) return -1
28
+
29
+ return isShift ? (currentIndex - 1 + length) % length : (currentIndex + 1) % length
30
+ }
31
+
32
+ export type Props = {
33
+ ids: readonly string[]
34
+ defaultId?: string
35
+ children: ReactNode
36
+ }
37
+
38
+ /** Manages keyboard focus across an ordered list of children. Tab cycles forward; Shift+Tab cycles back. */
39
+ export function HasciiFocusGroup(props: Props) {
40
+ const initialId = props.defaultId ?? props.ids[0] ?? null
41
+
42
+ const currentState = useState<string | null>(initialId)
43
+ const currentId = currentState[0]
44
+ const setCurrentId = currentState[1]
45
+
46
+ useKeyboard((key) => {
47
+ if (key.name !== "tab" || props.ids.length === 0) return
48
+
49
+ const currentIndex = currentId ? props.ids.indexOf(currentId) : -1
50
+ const next = nextFocusIndex(currentIndex, props.ids.length, key.shift ?? false)
51
+
52
+ const target = props.ids[next]
53
+ if (target !== undefined) setCurrentId(target)
54
+ })
55
+
56
+ const value: ContextValue = {
57
+ currentId,
58
+ setCurrentId: (id: string) => setCurrentId(id),
59
+ }
60
+
61
+ return <HasciiFocusContext.Provider value={value}>{props.children}</HasciiFocusContext.Provider>
62
+ }
@@ -0,0 +1,43 @@
1
+ import { useId } from "react"
2
+ import type { ReactNode } from "react"
3
+ import { HasciiFormItemProvider } from "@/tui/utils/hascii/form-item-context"
4
+ import { useHasciiInputFocus } from "@/tui/utils/hascii/input-focus-context"
5
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
6
+
7
+ export type Props = {
8
+ label: string
9
+ labelWidth?: number
10
+ children: ReactNode
11
+ }
12
+
13
+ /** Horizontal form row: a fixed-width label on the left, the field on the right. The label background brightens while the wrapped HasciiInput is focused (requires HasciiInputFocusProvider in the tree). */
14
+ export function HasciiFormItem(props: Props) {
15
+ const labelWidth = props.labelWidth ?? 12
16
+ const theme = useHasciiTheme()
17
+
18
+ const id = useId()
19
+ const inputFocus = useHasciiInputFocus()
20
+ const isInputFocused = inputFocus?.focusedId === id
21
+
22
+ const labelBg = isInputFocused ? theme.color.secondaryActive : theme.color.popover
23
+ const labelFg = isInputFocused ? theme.color.foreground : theme.color.mutedForeground
24
+
25
+ return (
26
+ <HasciiFormItemProvider value={{ focusId: id }}>
27
+ <box flexDirection="row" alignItems="center">
28
+ <box
29
+ width={labelWidth}
30
+ height={3}
31
+ paddingLeft={2}
32
+ paddingRight={2}
33
+ alignItems="flex-start"
34
+ justifyContent="center"
35
+ backgroundColor={labelBg}
36
+ >
37
+ <text fg={labelFg}>{props.label}</text>
38
+ </box>
39
+ {props.children}
40
+ </box>
41
+ </HasciiFormItemProvider>
42
+ )
43
+ }
@@ -0,0 +1,86 @@
1
+ import { useKeyboard } from "@opentui/react"
2
+ import { useState } from "react"
3
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
4
+
5
+ export type Props = {
6
+ length?: number
7
+ value?: string
8
+ defaultValue?: string
9
+ isFocused?: boolean
10
+ onChange?: (value: string) => void
11
+ onComplete?: (value: string) => void
12
+ }
13
+
14
+ const isDigit = (key: string): boolean => /^[0-9]$/.test(key)
15
+
16
+ /** OTP slot row. Uncontrolled by default — type digits to fill, backspace to erase. */
17
+ export function HasciiInputOtp(props: Props) {
18
+ const length = props.length ?? 6
19
+ const isFocused = props.isFocused ?? true
20
+ const theme = useHasciiTheme()
21
+
22
+ const internalState = useState(props.defaultValue ?? "")
23
+ const internal = internalState[0]
24
+ const setInternal = internalState[1]
25
+
26
+ const value = props.value ?? internal
27
+
28
+ const setValue = (next: string) => {
29
+ if (props.value === undefined) setInternal(next)
30
+ props.onChange?.(next)
31
+
32
+ if (next.length === length) props.onComplete?.(next)
33
+ }
34
+
35
+ useKeyboard((key) => {
36
+ if (!isFocused) return
37
+
38
+ if (key.name === "backspace") {
39
+ if (value.length > 0) setValue(value.slice(0, -1))
40
+ return
41
+ }
42
+
43
+ if (isDigit(key.name) && value.length < length) {
44
+ setValue(value + key.name)
45
+ }
46
+ })
47
+
48
+ const slots: number[] = []
49
+ for (let index = 0; index < length; index++) slots.push(index)
50
+
51
+ const focusedIndex = Math.min(value.length, length - 1)
52
+
53
+ return (
54
+ <box flexDirection="row" gap={1}>
55
+ {slots.map((index) => {
56
+ const char = value[index]
57
+ const isSlotFocused = isFocused && index === focusedIndex
58
+ const isFilled = char !== undefined
59
+
60
+ const borderColor = isSlotFocused
61
+ ? theme.color.ring
62
+ : isFilled
63
+ ? theme.color.border
64
+ : theme.color.input
65
+
66
+ return (
67
+ <box
68
+ key={index}
69
+ border
70
+ borderStyle="rounded"
71
+ borderColor={borderColor}
72
+ backgroundColor={theme.color.background}
73
+ width={5}
74
+ height={3}
75
+ alignItems="center"
76
+ justifyContent="center"
77
+ >
78
+ <text fg={isFilled ? theme.color.foreground : theme.color.mutedForeground}>
79
+ {char ?? "·"}
80
+ </text>
81
+ </box>
82
+ )
83
+ })}
84
+ </box>
85
+ )
86
+ }
@@ -0,0 +1,130 @@
1
+ import { useKeyboard } from "@opentui/react"
2
+ import { useId, useState } from "react"
3
+ import { useHasciiFormItem } from "@/tui/utils/hascii/form-item-context"
4
+ import { useHasciiInputFocus } from "@/tui/utils/hascii/input-focus-context"
5
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
6
+ import { usePressable } from "@/tui/hooks/hascii/use-pressable"
7
+
8
+ type Variant = "default" | "outline"
9
+
10
+ export type Props = {
11
+ variant?: Variant
12
+ placeholder?: string
13
+ value?: string
14
+ width?: number
15
+ defaultFocused?: boolean
16
+ onInput?: (value: string) => void
17
+ onChange?: (value: string) => void
18
+ }
19
+
20
+ /** Single-line text input. Click to focus, Esc / outside click to blur (requires HasciiInputFocusProvider for outside click). */
21
+ export function HasciiInput(props: Props) {
22
+ const variant = props.variant ?? "default"
23
+ const width = props.width ?? 32
24
+ const placeholder = props.placeholder ?? ""
25
+
26
+ const fallbackId = useId()
27
+ const formItem = useHasciiFormItem()
28
+ const id = formItem?.focusId ?? fallbackId
29
+ const focusCtx = useHasciiInputFocus()
30
+ const fallbackState = useState(props.defaultFocused ?? false)
31
+ const isFocused = focusCtx ? focusCtx.focusedId === id : fallbackState[0]
32
+
33
+ const focus = (): void => {
34
+ if (focusCtx) focusCtx.setFocusedId(id)
35
+ else fallbackState[1](true)
36
+ }
37
+
38
+ const blur = (): void => {
39
+ if (focusCtx) focusCtx.setFocusedId(null)
40
+ else fallbackState[1](false)
41
+ }
42
+
43
+ const theme = useHasciiTheme()
44
+ const press = usePressable()
45
+
46
+ useKeyboard((key) => {
47
+ if (!isFocused) return
48
+ if (key.name === "escape") blur()
49
+ })
50
+
51
+ if (variant === "outline") {
52
+ const borderColor = press.isPressed
53
+ ? theme.color.foreground
54
+ : isFocused
55
+ ? theme.color.ring
56
+ : press.isHovered
57
+ ? theme.color.mutedForeground
58
+ : theme.color.input
59
+
60
+ return (
61
+ <box
62
+ border
63
+ borderStyle="rounded"
64
+ borderColor={borderColor}
65
+ height={3}
66
+ width={width}
67
+ paddingLeft={1}
68
+ paddingRight={1}
69
+ backgroundColor={theme.color.background}
70
+ justifyContent="center"
71
+ {...press.bind}
72
+ onMouseDown={(event) => {
73
+ event.stopPropagation()
74
+ press.bind.onMouseDown()
75
+ focus()
76
+ }}
77
+ >
78
+ <input
79
+ focused={isFocused}
80
+ placeholder={placeholder}
81
+ value={props.value}
82
+ textColor={theme.color.foreground}
83
+ placeholderColor={theme.color.mutedForeground}
84
+ cursorColor={theme.color.foreground}
85
+ onInput={props.onInput}
86
+ onChange={props.onChange}
87
+ />
88
+ </box>
89
+ )
90
+ }
91
+
92
+ const bg = press.isPressed
93
+ ? theme.color.secondaryActive
94
+ : isFocused || press.isHovered
95
+ ? theme.color.secondaryHover
96
+ : theme.color.muted
97
+
98
+ return (
99
+ <box
100
+ height={3}
101
+ width={width}
102
+ paddingLeft={2}
103
+ paddingRight={2}
104
+ backgroundColor={bg}
105
+ justifyContent="center"
106
+ {...press.bind}
107
+ onMouseDown={(event) => {
108
+ event.stopPropagation()
109
+ press.bind.onMouseDown()
110
+ focus()
111
+ }}
112
+ >
113
+ <input
114
+ focused={isFocused}
115
+ placeholder={placeholder}
116
+ value={props.value}
117
+ textColor={theme.color.foreground}
118
+ placeholderColor={theme.color.mutedForeground}
119
+ cursorColor={theme.color.foreground}
120
+ onInput={props.onInput}
121
+ onChange={props.onChange}
122
+ />
123
+ {isFocused ? (
124
+ <box position="absolute" bottom={0} left={0} right={0}>
125
+ <text fg={theme.color.primary}>{"▁".repeat(width)}</text>
126
+ </box>
127
+ ) : null}
128
+ </box>
129
+ )
130
+ }
@@ -0,0 +1,105 @@
1
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
2
+ import { usePressable } from "@/tui/hooks/hascii/use-pressable"
3
+
4
+ export type Props = {
5
+ page: number
6
+ pageCount: number
7
+ onChange?: (page: number) => void
8
+ }
9
+
10
+ type ButtonProps = {
11
+ label: string
12
+ isActive?: boolean
13
+ isDisabled?: boolean
14
+ onPress: () => void
15
+ }
16
+
17
+ function PageButton(props: ButtonProps) {
18
+ const theme = useHasciiTheme()
19
+ const press = usePressable({
20
+ isDisabled: props.isDisabled,
21
+ onPress: props.onPress,
22
+ })
23
+
24
+ const fg = props.isDisabled
25
+ ? theme.color.mutedForeground
26
+ : props.isActive
27
+ ? theme.color.primaryForeground
28
+ : theme.color.foreground
29
+
30
+ const bg = props.isDisabled
31
+ ? undefined
32
+ : props.isActive
33
+ ? theme.color.primary
34
+ : press.isPressed
35
+ ? theme.color.accentActive
36
+ : press.isHovered
37
+ ? theme.color.accentHover
38
+ : undefined
39
+
40
+ return (
41
+ <box paddingLeft={1} paddingRight={1} height={1} backgroundColor={bg} {...press.bind}>
42
+ <text fg={fg}>{props.label}</text>
43
+ </box>
44
+ )
45
+ }
46
+
47
+ export const buildPageList = (page: number, pageCount: number): (number | null)[] => {
48
+ if (pageCount <= 7) {
49
+ const list: number[] = []
50
+ for (let index = 1; index <= pageCount; index++) list.push(index)
51
+ return list
52
+ }
53
+
54
+ const result: (number | null)[] = [1]
55
+ const left = Math.max(2, page - 1)
56
+ const right = Math.min(pageCount - 1, page + 1)
57
+
58
+ if (left > 2) result.push(null)
59
+ for (let index = left; index <= right; index++) result.push(index)
60
+ if (right < pageCount - 1) result.push(null)
61
+
62
+ result.push(pageCount)
63
+ return result
64
+ }
65
+
66
+ /** Page navigator with previous, next, and numeric jumps. Renders ellipses when collapsed. */
67
+ export function HasciiPagination(props: Props) {
68
+ const theme = useHasciiTheme()
69
+
70
+ const change = (next: number) => {
71
+ if (next < 1 || next > props.pageCount || next === props.page) return
72
+ props.onChange?.(next)
73
+ }
74
+
75
+ const pages = buildPageList(props.page, props.pageCount)
76
+
77
+ return (
78
+ <box flexDirection="row" gap={1} alignItems="center">
79
+ <PageButton label="<" isDisabled={props.page <= 1} onPress={() => change(props.page - 1)} />
80
+ {pages.map((entry, index) => {
81
+ if (entry === null) {
82
+ return (
83
+ <box key={`gap-${index}`} paddingLeft={1} paddingRight={1}>
84
+ <text fg={theme.color.mutedForeground}>…</text>
85
+ </box>
86
+ )
87
+ }
88
+
89
+ return (
90
+ <PageButton
91
+ key={entry}
92
+ label={String(entry)}
93
+ isActive={entry === props.page}
94
+ onPress={() => change(entry)}
95
+ />
96
+ )
97
+ })}
98
+ <PageButton
99
+ label=">"
100
+ isDisabled={props.page >= props.pageCount}
101
+ onPress={() => change(props.page + 1)}
102
+ />
103
+ </box>
104
+ )
105
+ }
@@ -0,0 +1,28 @@
1
+ import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
2
+
3
+ export type Props = {
4
+ value?: number
5
+ width?: number
6
+ fillColor?: string
7
+ trackColor?: string
8
+ }
9
+
10
+ /** Horizontal progress bar. Value is 0–1; clamped on render. */
11
+ export function HasciiProgress(props: Props) {
12
+ const value = Math.max(0, Math.min(1, props.value ?? 0))
13
+ const width = props.width ?? 32
14
+
15
+ const theme = useHasciiTheme()
16
+ const fillColor = props.fillColor ?? theme.color.primary
17
+ const trackColor = props.trackColor ?? theme.color.muted
18
+
19
+ const filled = Math.round(value * width)
20
+ const empty = width - filled
21
+
22
+ return (
23
+ <box flexDirection="row" width={width} height={1}>
24
+ {filled > 0 ? <box width={filled} height={1} backgroundColor={fillColor} /> : null}
25
+ {empty > 0 ? <box width={empty} height={1} backgroundColor={trackColor} /> : null}
26
+ </box>
27
+ )
28
+ }