lastlight 0.1.15 → 0.2.1

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 (478) hide show
  1. package/README.md +265 -118
  2. package/agent-context/rules.md +62 -9
  3. package/agent-context/security.md +69 -0
  4. package/config/default.yaml +89 -0
  5. package/deploy/.env.production.example +20 -2
  6. package/deploy/entrypoint.sh +16 -17
  7. package/deploy/native/README.md +160 -0
  8. package/deploy/native/install.sh +110 -0
  9. package/deploy/native/lastlight.env.example +95 -0
  10. package/deploy/native/lastlight.service +50 -0
  11. package/deploy/sandbox-entrypoint.sh +59 -42
  12. package/dist/admin/auth.d.ts +1 -1
  13. package/dist/admin/auth.js +3 -1
  14. package/dist/admin/auth.js.map +1 -1
  15. package/dist/admin/chat-session-reader.d.ts +11 -14
  16. package/dist/admin/chat-session-reader.js +27 -51
  17. package/dist/admin/chat-session-reader.js.map +1 -1
  18. package/dist/admin/docker.d.ts +37 -0
  19. package/dist/admin/docker.js +86 -1
  20. package/dist/admin/docker.js.map +1 -1
  21. package/dist/admin/index.js +38 -3
  22. package/dist/admin/index.js.map +1 -1
  23. package/dist/admin/log-search.test.d.ts +1 -0
  24. package/dist/admin/log-search.test.js +78 -0
  25. package/dist/admin/log-search.test.js.map +1 -0
  26. package/dist/admin/routes.d.ts +52 -3
  27. package/dist/admin/routes.js +780 -63
  28. package/dist/admin/routes.js.map +1 -1
  29. package/dist/admin/routes.test.js +680 -14
  30. package/dist/admin/routes.test.js.map +1 -1
  31. package/dist/admin/server-logs.test.d.ts +1 -0
  32. package/dist/admin/server-logs.test.js +58 -0
  33. package/dist/admin/server-logs.test.js.map +1 -0
  34. package/dist/admin/sessions.d.ts +26 -39
  35. package/dist/admin/sessions.js +86 -310
  36. package/dist/admin/sessions.js.map +1 -1
  37. package/dist/admin/sessions.test.d.ts +1 -0
  38. package/dist/admin/sessions.test.js +78 -0
  39. package/dist/admin/sessions.test.js.map +1 -0
  40. package/dist/admin/version.d.ts +15 -0
  41. package/dist/admin/version.js +73 -0
  42. package/dist/admin/version.js.map +1 -0
  43. package/dist/admin/version.test.d.ts +1 -0
  44. package/dist/admin/version.test.js +71 -0
  45. package/dist/admin/version.test.js.map +1 -0
  46. package/dist/cli-config.d.ts +55 -0
  47. package/dist/cli-config.js +119 -0
  48. package/dist/cli-config.js.map +1 -0
  49. package/dist/cli-config.test.d.ts +1 -0
  50. package/dist/cli-config.test.js +92 -0
  51. package/dist/cli-config.test.js.map +1 -0
  52. package/dist/cli-format.d.ts +19 -0
  53. package/dist/cli-format.js +107 -0
  54. package/dist/cli-format.js.map +1 -0
  55. package/dist/cli-server.d.ts +73 -0
  56. package/dist/cli-server.js +347 -0
  57. package/dist/cli-server.js.map +1 -0
  58. package/dist/cli-server.test.d.ts +1 -0
  59. package/dist/cli-server.test.js +38 -0
  60. package/dist/cli-server.test.js.map +1 -0
  61. package/dist/cli-timeline.d.ts +35 -0
  62. package/dist/cli-timeline.js +373 -0
  63. package/dist/cli-timeline.js.map +1 -0
  64. package/dist/cli-timeline.test.d.ts +1 -0
  65. package/dist/cli-timeline.test.js +104 -0
  66. package/dist/cli-timeline.test.js.map +1 -0
  67. package/dist/cli.d.ts +0 -19
  68. package/dist/cli.js +910 -194
  69. package/dist/cli.js.map +1 -1
  70. package/dist/config-overlay.test.d.ts +1 -0
  71. package/dist/config-overlay.test.js +112 -0
  72. package/dist/config-overlay.test.js.map +1 -0
  73. package/dist/config-resolve.d.ts +28 -0
  74. package/dist/config-resolve.js +47 -0
  75. package/dist/config-resolve.js.map +1 -0
  76. package/dist/config-resolve.test.d.ts +1 -0
  77. package/dist/config-resolve.test.js +68 -0
  78. package/dist/config-resolve.test.js.map +1 -0
  79. package/dist/config.d.ts +62 -49
  80. package/dist/config.js +452 -76
  81. package/dist/config.js.map +1 -1
  82. package/dist/config.test.js +201 -32
  83. package/dist/config.test.js.map +1 -1
  84. package/dist/connectors/github-webhook.js +83 -5
  85. package/dist/connectors/github-webhook.js.map +1 -1
  86. package/dist/connectors/github-webhook.test.d.ts +1 -0
  87. package/dist/connectors/github-webhook.test.js +156 -0
  88. package/dist/connectors/github-webhook.test.js.map +1 -0
  89. package/dist/connectors/messaging/session-manager.d.ts +18 -0
  90. package/dist/connectors/messaging/session-manager.js +83 -2
  91. package/dist/connectors/messaging/session-manager.js.map +1 -1
  92. package/dist/connectors/messaging/session-manager.test.d.ts +1 -0
  93. package/dist/connectors/messaging/session-manager.test.js +144 -0
  94. package/dist/connectors/messaging/session-manager.test.js.map +1 -0
  95. package/dist/connectors/slack/connector.d.ts +9 -0
  96. package/dist/connectors/slack/connector.js +15 -0
  97. package/dist/connectors/slack/connector.js.map +1 -1
  98. package/dist/connectors/slack/mrkdwn.js +81 -0
  99. package/dist/connectors/slack/mrkdwn.js.map +1 -1
  100. package/dist/connectors/slack/mrkdwn.test.js +49 -0
  101. package/dist/connectors/slack/mrkdwn.test.js.map +1 -1
  102. package/dist/connectors/types.d.ts +1 -1
  103. package/dist/cron/fanout.d.ts +33 -0
  104. package/dist/cron/fanout.js +55 -0
  105. package/dist/cron/fanout.js.map +1 -0
  106. package/dist/cron/fanout.test.d.ts +1 -0
  107. package/dist/cron/fanout.test.js +73 -0
  108. package/dist/cron/fanout.test.js.map +1 -0
  109. package/dist/cron/jobs.d.ts +5 -0
  110. package/dist/cron/jobs.js +10 -3
  111. package/dist/cron/jobs.js.map +1 -1
  112. package/dist/cron/scheduler.d.ts +12 -0
  113. package/dist/cron/scheduler.js +27 -1
  114. package/dist/cron/scheduler.js.map +1 -1
  115. package/dist/engine/agent-executor.d.ts +158 -0
  116. package/dist/engine/agent-executor.js +1140 -0
  117. package/dist/engine/agent-executor.js.map +1 -0
  118. package/dist/engine/agent-executor.test.d.ts +1 -0
  119. package/dist/engine/agent-executor.test.js +395 -0
  120. package/dist/engine/agent-executor.test.js.map +1 -0
  121. package/dist/engine/chat-runner.d.ts +123 -0
  122. package/dist/engine/chat-runner.js +379 -0
  123. package/dist/engine/chat-runner.js.map +1 -0
  124. package/dist/engine/chat-runner.test.d.ts +1 -0
  125. package/dist/engine/chat-runner.test.js +104 -0
  126. package/dist/engine/chat-runner.test.js.map +1 -0
  127. package/dist/engine/chat-skills.d.ts +45 -0
  128. package/dist/engine/chat-skills.js +184 -0
  129. package/dist/engine/chat-skills.js.map +1 -0
  130. package/dist/engine/chat-skills.test.d.ts +1 -0
  131. package/dist/engine/chat-skills.test.js +20 -0
  132. package/dist/engine/chat-skills.test.js.map +1 -0
  133. package/dist/engine/chat.d.ts +20 -23
  134. package/dist/engine/chat.js +251 -155
  135. package/dist/engine/chat.js.map +1 -1
  136. package/dist/engine/chat.test.d.ts +1 -0
  137. package/dist/engine/chat.test.js +42 -0
  138. package/dist/engine/chat.test.js.map +1 -0
  139. package/dist/engine/classifier.d.ts +35 -2
  140. package/dist/engine/classifier.js +170 -31
  141. package/dist/engine/classifier.js.map +1 -1
  142. package/dist/engine/classifier.test.d.ts +1 -0
  143. package/dist/engine/classifier.test.js +115 -0
  144. package/dist/engine/classifier.test.js.map +1 -0
  145. package/dist/engine/dispatcher.d.ts +60 -0
  146. package/dist/engine/dispatcher.js +601 -0
  147. package/dist/engine/dispatcher.js.map +1 -0
  148. package/dist/engine/dispatcher.test.d.ts +1 -0
  149. package/dist/engine/dispatcher.test.js +511 -0
  150. package/dist/engine/dispatcher.test.js.map +1 -0
  151. package/dist/engine/event-shim.d.ts +117 -0
  152. package/dist/engine/event-shim.js +435 -0
  153. package/dist/engine/event-shim.js.map +1 -0
  154. package/dist/engine/event-shim.test.d.ts +1 -0
  155. package/dist/engine/event-shim.test.js +194 -0
  156. package/dist/engine/event-shim.test.js.map +1 -0
  157. package/dist/engine/git-auth.d.ts +13 -17
  158. package/dist/engine/git-auth.js +99 -42
  159. package/dist/engine/git-auth.js.map +1 -1
  160. package/dist/engine/git-auth.test.d.ts +1 -0
  161. package/dist/engine/git-auth.test.js +117 -0
  162. package/dist/engine/git-auth.test.js.map +1 -0
  163. package/dist/engine/github-app-client.d.ts +7 -0
  164. package/dist/engine/github-app-client.js +16 -0
  165. package/dist/engine/github-app-client.js.map +1 -0
  166. package/dist/engine/github-app-client.test.d.ts +1 -0
  167. package/dist/engine/github-app-client.test.js +44 -0
  168. package/dist/engine/github-app-client.test.js.map +1 -0
  169. package/dist/engine/github-tools.d.ts +12 -0
  170. package/dist/engine/github-tools.js +261 -0
  171. package/dist/engine/github-tools.js.map +1 -0
  172. package/dist/engine/github.d.ts +1027 -43
  173. package/dist/engine/github.js +153 -14
  174. package/dist/engine/github.js.map +1 -1
  175. package/dist/engine/llm.d.ts +47 -0
  176. package/dist/engine/llm.js +221 -0
  177. package/dist/engine/llm.js.map +1 -0
  178. package/dist/engine/llm.test.d.ts +1 -0
  179. package/dist/engine/llm.test.js +189 -0
  180. package/dist/engine/llm.test.js.map +1 -0
  181. package/dist/engine/profiles.d.ts +249 -0
  182. package/dist/engine/profiles.js +42 -0
  183. package/dist/engine/profiles.js.map +1 -0
  184. package/dist/engine/router.d.ts +12 -6
  185. package/dist/engine/router.js +339 -81
  186. package/dist/engine/router.js.map +1 -1
  187. package/dist/engine/router.test.js +428 -78
  188. package/dist/engine/router.test.js.map +1 -1
  189. package/dist/engine/screen.d.ts +41 -0
  190. package/dist/engine/screen.js +93 -0
  191. package/dist/engine/screen.js.map +1 -0
  192. package/dist/engine/screen.test.d.ts +1 -0
  193. package/dist/engine/screen.test.js +82 -0
  194. package/dist/engine/screen.test.js.map +1 -0
  195. package/dist/index.js +377 -432
  196. package/dist/index.js.map +1 -1
  197. package/dist/managed-repos.d.ts +7 -9
  198. package/dist/managed-repos.js +12 -15
  199. package/dist/managed-repos.js.map +1 -1
  200. package/dist/managed-repos.test.js +24 -19
  201. package/dist/managed-repos.test.js.map +1 -1
  202. package/dist/notify/index.d.ts +15 -0
  203. package/dist/notify/index.js +6 -0
  204. package/dist/notify/index.js.map +1 -0
  205. package/dist/notify/model.d.ts +57 -0
  206. package/dist/notify/model.js +89 -0
  207. package/dist/notify/model.js.map +1 -0
  208. package/dist/notify/model.test.d.ts +1 -0
  209. package/dist/notify/model.test.js +109 -0
  210. package/dist/notify/model.test.js.map +1 -0
  211. package/dist/notify/notifier.d.ts +17 -0
  212. package/dist/notify/notifier.js +87 -0
  213. package/dist/notify/notifier.js.map +1 -0
  214. package/dist/notify/notifier.test.d.ts +1 -0
  215. package/dist/notify/notifier.test.js +100 -0
  216. package/dist/notify/notifier.test.js.map +1 -0
  217. package/dist/notify/render.d.ts +21 -0
  218. package/dist/notify/render.js +59 -0
  219. package/dist/notify/render.js.map +1 -0
  220. package/dist/notify/render.test.d.ts +1 -0
  221. package/dist/notify/render.test.js +65 -0
  222. package/dist/notify/render.test.js.map +1 -0
  223. package/dist/notify/transports/github.d.ts +26 -0
  224. package/dist/notify/transports/github.js +26 -0
  225. package/dist/notify/transports/github.js.map +1 -0
  226. package/dist/notify/transports/slack.d.ts +26 -0
  227. package/dist/notify/transports/slack.js +28 -0
  228. package/dist/notify/transports/slack.js.map +1 -0
  229. package/dist/notify/transports.test.d.ts +1 -0
  230. package/dist/notify/transports.test.js +71 -0
  231. package/dist/notify/transports.test.js.map +1 -0
  232. package/dist/notify/types.d.ts +95 -0
  233. package/dist/notify/types.js +16 -0
  234. package/dist/notify/types.js.map +1 -0
  235. package/dist/sandbox/docker-compose.test.d.ts +1 -0
  236. package/dist/sandbox/docker-compose.test.js +128 -0
  237. package/dist/sandbox/docker-compose.test.js.map +1 -0
  238. package/dist/sandbox/docker.d.ts +97 -9
  239. package/dist/sandbox/docker.js +192 -41
  240. package/dist/sandbox/docker.js.map +1 -1
  241. package/dist/sandbox/docker.test.d.ts +1 -0
  242. package/dist/sandbox/docker.test.js +137 -0
  243. package/dist/sandbox/docker.test.js.map +1 -0
  244. package/dist/sandbox/egress-allowlist.d.ts +69 -0
  245. package/dist/sandbox/egress-allowlist.js +164 -0
  246. package/dist/sandbox/egress-allowlist.js.map +1 -0
  247. package/dist/sandbox/egress-allowlist.test.d.ts +1 -0
  248. package/dist/sandbox/egress-allowlist.test.js +65 -0
  249. package/dist/sandbox/egress-allowlist.test.js.map +1 -0
  250. package/dist/sandbox/egress-firewall-config.d.ts +127 -0
  251. package/dist/sandbox/egress-firewall-config.js +434 -0
  252. package/dist/sandbox/egress-firewall-config.js.map +1 -0
  253. package/dist/sandbox/egress-firewall-config.test.d.ts +1 -0
  254. package/dist/sandbox/egress-firewall-config.test.js +244 -0
  255. package/dist/sandbox/egress-firewall-config.test.js.map +1 -0
  256. package/dist/sandbox/images.d.ts +23 -0
  257. package/dist/sandbox/images.js +55 -0
  258. package/dist/sandbox/images.js.map +1 -0
  259. package/dist/sandbox/index.d.ts +82 -3
  260. package/dist/sandbox/index.js +223 -93
  261. package/dist/sandbox/index.js.map +1 -1
  262. package/dist/sandbox/index.test.d.ts +1 -0
  263. package/dist/sandbox/index.test.js +157 -0
  264. package/dist/sandbox/index.test.js.map +1 -0
  265. package/dist/session-log.d.ts +63 -0
  266. package/dist/session-log.js +290 -0
  267. package/dist/session-log.js.map +1 -0
  268. package/dist/session-log.test.d.ts +1 -0
  269. package/dist/session-log.test.js +85 -0
  270. package/dist/session-log.test.js.map +1 -0
  271. package/dist/setup.d.ts +30 -1
  272. package/dist/setup.js +241 -25
  273. package/dist/setup.js.map +1 -1
  274. package/dist/setup.test.js +138 -6
  275. package/dist/setup.test.js.map +1 -1
  276. package/dist/state/approval-store.d.ts +86 -0
  277. package/dist/state/approval-store.js +140 -0
  278. package/dist/state/approval-store.js.map +1 -0
  279. package/dist/state/build-assets.d.ts +50 -0
  280. package/dist/state/build-assets.js +154 -0
  281. package/dist/state/build-assets.js.map +1 -0
  282. package/dist/state/build-assets.test.d.ts +1 -0
  283. package/dist/state/build-assets.test.js +106 -0
  284. package/dist/state/build-assets.test.js.map +1 -0
  285. package/dist/state/db.d.ts +65 -333
  286. package/dist/state/db.js +112 -835
  287. package/dist/state/db.js.map +1 -1
  288. package/dist/state/db.test.js +127 -91
  289. package/dist/state/db.test.js.map +1 -1
  290. package/dist/state/execution-store.d.ts +257 -0
  291. package/dist/state/execution-store.js +527 -0
  292. package/dist/state/execution-store.js.map +1 -0
  293. package/dist/state/migrate.d.ts +15 -0
  294. package/dist/state/migrate.js +175 -0
  295. package/dist/state/migrate.js.map +1 -0
  296. package/dist/state/workflow-run-store.d.ts +195 -0
  297. package/dist/state/workflow-run-store.js +363 -0
  298. package/dist/state/workflow-run-store.js.map +1 -0
  299. package/dist/state/workflow-run-store.test.d.ts +1 -0
  300. package/dist/state/workflow-run-store.test.js +264 -0
  301. package/dist/state/workflow-run-store.test.js.map +1 -0
  302. package/dist/telemetry/index.d.ts +38 -0
  303. package/dist/telemetry/index.js +253 -0
  304. package/dist/telemetry/index.js.map +1 -0
  305. package/dist/telemetry/index.test.d.ts +1 -0
  306. package/dist/telemetry/index.test.js +73 -0
  307. package/dist/telemetry/index.test.js.map +1 -0
  308. package/dist/telemetry/pi-events.d.ts +12 -0
  309. package/dist/telemetry/pi-events.js +134 -0
  310. package/dist/telemetry/pi-events.js.map +1 -0
  311. package/dist/telemetry/pi-events.test.d.ts +1 -0
  312. package/dist/telemetry/pi-events.test.js +69 -0
  313. package/dist/telemetry/pi-events.test.js.map +1 -0
  314. package/dist/workflows/dag.d.ts +13 -1
  315. package/dist/workflows/dag.js +7 -3
  316. package/dist/workflows/dag.js.map +1 -1
  317. package/dist/workflows/dag.test.js +22 -0
  318. package/dist/workflows/dag.test.js.map +1 -1
  319. package/dist/workflows/golden-build.test.d.ts +1 -0
  320. package/dist/workflows/golden-build.test.js +45 -0
  321. package/dist/workflows/golden-build.test.js.map +1 -0
  322. package/dist/workflows/loader-overlay.test.d.ts +1 -0
  323. package/dist/workflows/loader-overlay.test.js +101 -0
  324. package/dist/workflows/loader-overlay.test.js.map +1 -0
  325. package/dist/workflows/loader.d.ts +36 -11
  326. package/dist/workflows/loader.js +311 -87
  327. package/dist/workflows/loader.js.map +1 -1
  328. package/dist/workflows/loader.test.js +114 -10
  329. package/dist/workflows/loader.test.js.map +1 -1
  330. package/dist/workflows/loop-eval.js +2 -0
  331. package/dist/workflows/loop-eval.js.map +1 -1
  332. package/dist/workflows/loop-eval.test.js +14 -0
  333. package/dist/workflows/loop-eval.test.js.map +1 -1
  334. package/dist/workflows/phase-executor.d.ts +145 -0
  335. package/dist/workflows/phase-executor.js +691 -0
  336. package/dist/workflows/phase-executor.js.map +1 -0
  337. package/dist/workflows/phase-executor.test.d.ts +1 -0
  338. package/dist/workflows/phase-executor.test.js +434 -0
  339. package/dist/workflows/phase-executor.test.js.map +1 -0
  340. package/dist/workflows/phase-ref.d.ts +44 -0
  341. package/dist/workflows/phase-ref.js +78 -0
  342. package/dist/workflows/phase-ref.js.map +1 -0
  343. package/dist/workflows/phase-ref.test.d.ts +1 -0
  344. package/dist/workflows/phase-ref.test.js +24 -0
  345. package/dist/workflows/phase-ref.test.js.map +1 -0
  346. package/dist/workflows/resume.d.ts +5 -11
  347. package/dist/workflows/resume.js +88 -7
  348. package/dist/workflows/resume.js.map +1 -1
  349. package/dist/workflows/runner.d.ts +34 -27
  350. package/dist/workflows/runner.js +252 -930
  351. package/dist/workflows/runner.js.map +1 -1
  352. package/dist/workflows/runner.test.js +346 -35
  353. package/dist/workflows/runner.test.js.map +1 -1
  354. package/dist/workflows/schema.d.ts +52 -10
  355. package/dist/workflows/schema.js +147 -7
  356. package/dist/workflows/schema.js.map +1 -1
  357. package/dist/workflows/simple.d.ts +67 -3
  358. package/dist/workflows/simple.js +248 -87
  359. package/dist/workflows/simple.js.map +1 -1
  360. package/dist/workflows/simple.test.d.ts +1 -0
  361. package/dist/workflows/simple.test.js +107 -0
  362. package/dist/workflows/simple.test.js.map +1 -0
  363. package/dist/workflows/templates.d.ts +29 -0
  364. package/dist/workflows/templates.js +42 -4
  365. package/dist/workflows/templates.js.map +1 -1
  366. package/dist/workflows/templates.test.js +103 -0
  367. package/dist/workflows/templates.test.js.map +1 -1
  368. package/dist/workflows/triggers.d.ts +21 -0
  369. package/dist/workflows/triggers.js +44 -0
  370. package/dist/workflows/triggers.js.map +1 -0
  371. package/dist/workflows/verdict.d.ts +19 -0
  372. package/dist/workflows/verdict.js +30 -0
  373. package/dist/workflows/verdict.js.map +1 -0
  374. package/dist/workflows/verdict.test.d.ts +1 -0
  375. package/dist/workflows/verdict.test.js +44 -0
  376. package/dist/workflows/verdict.test.js.map +1 -0
  377. package/dist/worktree/manager.d.ts +1 -1
  378. package/dist/worktree/manager.js +14 -14
  379. package/dist/worktree/manager.js.map +1 -1
  380. package/dist/worktree/manager.test.d.ts +1 -0
  381. package/dist/worktree/manager.test.js +87 -0
  382. package/dist/worktree/manager.test.js.map +1 -0
  383. package/docker-compose.yml +206 -5
  384. package/package.json +19 -9
  385. package/sandbox.Dockerfile +97 -18
  386. package/skills/README.md +40 -0
  387. package/skills/browser-qa/SKILL.md +193 -0
  388. package/skills/browser-qa/scripts/agent-browser.mjs +380 -0
  389. package/skills/building/SKILL.md +95 -0
  390. package/skills/chat/SKILL.md +30 -11
  391. package/skills/code-review/SKILL.md +59 -0
  392. package/skills/debug-production/SKILL.md +108 -0
  393. package/skills/demo/SKILL.md +194 -0
  394. package/skills/demo/scripts/compose-demo.sh +205 -0
  395. package/skills/issue-answer/SKILL.md +90 -0
  396. package/skills/issue-comment/SKILL.md +38 -24
  397. package/skills/issue-triage/SKILL.md +103 -37
  398. package/skills/issue-triage/references/AGENT-BRIEF.md +61 -0
  399. package/skills/pr-comment/SKILL.md +64 -0
  400. package/skills/pr-review/SKILL.md +65 -49
  401. package/skills/qa-test/SKILL.md +97 -0
  402. package/skills/repo-health/SKILL.md +48 -45
  403. package/skills/security-feedback/SKILL.md +173 -0
  404. package/skills/security-feedback/references/templates.md +77 -0
  405. package/skills/security-review/SKILL.md +213 -0
  406. package/skills/security-review/references/issue-format.md +302 -0
  407. package/skills/verify/SKILL.md +118 -0
  408. package/workflows/answer.yaml +37 -0
  409. package/workflows/build.yaml +32 -22
  410. package/workflows/cron-security.yaml +6 -0
  411. package/workflows/demo.yaml +55 -0
  412. package/workflows/explore.yaml +9 -0
  413. package/workflows/pr-comment.yaml +16 -0
  414. package/workflows/pr-fix.yaml +3 -0
  415. package/workflows/pr-review.yaml +4 -1
  416. package/workflows/prompts/answer.md +67 -0
  417. package/workflows/prompts/architect.md +24 -7
  418. package/workflows/prompts/demo.md +100 -0
  419. package/workflows/prompts/executor.md +29 -20
  420. package/workflows/prompts/explore-ask.md +51 -22
  421. package/workflows/prompts/explore-publish.md +2 -2
  422. package/workflows/prompts/explore-read.md +24 -10
  423. package/workflows/prompts/explore-synthesize.md +15 -8
  424. package/workflows/prompts/fix.md +9 -10
  425. package/workflows/prompts/guardrails.md +26 -8
  426. package/workflows/prompts/pr-fix.md +6 -7
  427. package/workflows/prompts/pr.md +11 -10
  428. package/workflows/prompts/qa-browser.md +95 -0
  429. package/workflows/prompts/qa-synth.md +44 -0
  430. package/workflows/prompts/qa-test.md +60 -0
  431. package/workflows/prompts/re-reviewer.md +8 -9
  432. package/workflows/prompts/reviewer.md +10 -9
  433. package/workflows/prompts/verify-browser.md +93 -0
  434. package/workflows/prompts/verify-synth.md +44 -0
  435. package/workflows/prompts/verify.md +63 -0
  436. package/workflows/qa-test.yaml +87 -0
  437. package/workflows/security-feedback.yaml +25 -0
  438. package/workflows/security-review.yaml +29 -0
  439. package/workflows/verify.yaml +85 -0
  440. package/deploy/mcp-config.tmpl.json +0 -14
  441. package/dist/cron/rate-limits.d.ts +0 -26
  442. package/dist/cron/rate-limits.js +0 -193
  443. package/dist/cron/rate-limits.js.map +0 -1
  444. package/dist/engine/executor.d.ts +0 -75
  445. package/dist/engine/executor.js +0 -292
  446. package/dist/engine/executor.js.map +0 -1
  447. package/mcp-github-app/package.json +0 -18
  448. package/mcp-github-app/src/auth.js +0 -66
  449. package/mcp-github-app/src/github.js +0 -371
  450. package/mcp-github-app/src/index.js +0 -534
  451. package/skills/devops/webhook-subscriptions/SKILL.md +0 -180
  452. package/skills/github/DESCRIPTION.md +0 -3
  453. package/skills/github/codebase-inspection/SKILL.md +0 -115
  454. package/skills/github/github-auth/SKILL.md +0 -54
  455. package/skills/github/github-auth/scripts/gh-env.sh +0 -66
  456. package/skills/github/github-code-review/SKILL.md +0 -480
  457. package/skills/github/github-code-review/references/review-output-template.md +0 -74
  458. package/skills/github/github-issues/SKILL.md +0 -369
  459. package/skills/github/github-issues/templates/bug-report.md +0 -35
  460. package/skills/github/github-issues/templates/feature-request.md +0 -31
  461. package/skills/github/github-pr-workflow/SKILL.md +0 -395
  462. package/skills/github/github-pr-workflow/references/ci-troubleshooting.md +0 -183
  463. package/skills/github/github-pr-workflow/references/conventional-commits.md +0 -71
  464. package/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +0 -35
  465. package/skills/github/github-pr-workflow/templates/pr-body-feature.md +0 -33
  466. package/skills/github/github-repo-management/SKILL.md +0 -515
  467. package/skills/github/github-repo-management/references/github-api-cheatsheet.md +0 -161
  468. package/skills/github-orchestrator/SKILL.md +0 -103
  469. package/skills/mcp/DESCRIPTION.md +0 -3
  470. package/skills/mcp/mcporter/SKILL.md +0 -122
  471. package/skills/mcp/native-mcp/SKILL.md +0 -356
  472. package/skills/software-development/architect/SKILL.md +0 -154
  473. package/skills/software-development/assure-guardrails/SKILL.md +0 -239
  474. package/skills/software-development/plan/SKILL.md +0 -64
  475. package/skills/software-development/requesting-code-review/SKILL.md +0 -291
  476. package/skills/software-development/subagent-driven-development/SKILL.md +0 -433
  477. package/skills/software-development/systematic-debugging/SKILL.md +0 -366
  478. package/skills/software-development/test-driven-development/SKILL.md +0 -342
@@ -5,66 +5,195 @@
5
5
  * a GitHub comment is requesting a code change (build/fix), an idea
6
6
  * exploration, or a lightweight action (close, label, question, etc.).
7
7
  */
8
+ import { chat as realChat, defaultFastModel as realDefaultFastModel } from "./llm.js";
8
9
  const CLASSIFIER_PROMPT = `You are a router for messages directed at a GitHub/Slack bot.
9
10
  Classify the user's message into exactly one category, and extract any repository or issue references.
10
11
 
11
12
  Categories:
12
- BUILD — The user wants code changes NOW: implement a feature, fix a bug, write code, create/send a PR, resolve an issue with code. They already know what to build.
13
- EXPLORE The user has a half-formed idea and wants help thinking it through BEFORE code: "help me think through X", "brainstorm Y", "spec this out", "explore an idea for Z".
14
- TRIAGE — The user wants to scan/triage issues on a repo: "triage cliftonc/repo", "scan for new issues".
15
- REVIEW — The user wants to review PRs on a repo: "review cliftonc/repo", "check PRs".
13
+ BUILD — The user is ASKING YOU (the bot) to make code changes NOW in a GitHub repo: implement a feature, fix a bug, create/send a PR, resolve an issue with code. BUILD requires a GitHub target — either an explicit repo reference (owner/name or github.com URL) in the message, OR an ISSUE TITLE context line indicating the comment is a reply on an existing issue/PR. If neither is present, classify as CHAT — local filesystem operations ("delete files in ~/foo", "clean up my downloads"), shell-style commands, or vague "build something" with no target are NOT BUILD.
14
+ BUILD is a REQUEST for NEW work directed at you. A comment that merely REPORTS work the human has ALREADY done is NOT BUILD — it is CHAT. Tells: past-tense ("fixed", "done", "implemented", "added a test", "pushed a fix", "handled it"), a reference to a commit the human made ("addressed in <sha>", "see commit abc123"), thanking you, or explaining/justifying a change they made in response to your review. These are status reports, not requests — classify them CHAT even when the comment is on a PR and even when it @-mentions you. Only classify BUILD when the human asks you to do NEW work (imperative request: "fix X", "now also handle Y", "can you update Z").
15
+ EXPLORE — The user wants help shaping an idea BEFORE writing code: "help me think through X", "brainstorm Y", "spec this out", "explore an idea for Z". A bare "explore" / "explore this" / "let's explore" is also EXPLORE — especially as a reply on an existing issue (ISSUE TITLE present), where the implicit object is the issue's idea. EXPLORE = shape the idea / write a spec; BUILD = write the code now.
16
+ QUESTION — The user is asking a substantive INFORMATIONAL question that warrants research to answer well: "how does X work?", "what's the difference between X and Y?", "how does <repo> compare to <other tool>?", "is it possible to do Z?", "why does X happen?". The deliverable is an ANSWER, not code and not a spec. QUESTION is for real questions that benefit from reading docs/code or searching the web — NOT casual chat, thanks, or one-word replies (those stay CHAT). EXPLORE shapes a NEW idea into a spec; QUESTION answers an EXISTING question about how something works or compares.
17
+ TRIAGE — The user wants to scan/triage issues on a repo: "triage cliftonc/repo", "scan for new issues", "can you triage <repo>?".
18
+ REVIEW — The user wants to review PRs on a repo: "review cliftonc/repo", "check PRs", "can you review PRs on <repo>?".
19
+ SECURITY — The user wants a security scan/review of a repo: "security review cliftonc/repo", "scan for vulnerabilities", "check security", "can you do a security review of <repo>?".
20
+ VERIFY — The user wants you to TEST whether a specific claim or behaviour is actually true and report the evidence: "verify that the rate limiter blocks", "does this PR really fix the crash?", "confirm X actually works", "check that Y no longer happens", "prove the --fork flag creates a new session". The deliverable is a CONFIRMED/REFUTED verdict backed by RUNNING the code — not a code change and not a code-quality review. Prefer VERIFY over REVIEW when the user wants proof a behaviour works/is fixed, rather than an assessment of the diff.
21
+ QATEST — The user wants you to drive an app or CLI through a flow and report step-level pass/fail: "qa test the signup flow", "run through login and tell me what breaks", "smoke-test the CLI commands", "exercise the checkout flow". The deliverable is a step-by-step QA report. Prefer QATEST over VERIFY when it's a multi-step flow to exercise rather than a single claim to confirm.
22
+ DEMO — The user wants you to record a DEMO VIDEO of a feature/PR: "demo this", "record a demo of the new dashboard", "make a video showing the dark-mode toggle", "show me a before/after of the fix". The deliverable is a short screen-recorded mp4 of the running web UI, not a pass/fail report and not a verdict. Prefer DEMO over QATEST/VERIFY when the user explicitly asks for a recording, a video, or "show"/"demo" rather than to test or confirm a behaviour.
16
23
  APPROVE — The user is approving a pending gate: "approve", "go ahead", "looks good, continue", "yes proceed".
17
24
  REJECT — The user is rejecting a pending gate: "reject", "abort", "cancel this", "no don't proceed". Extract any reason given.
18
25
  STATUS — The user wants to know what's running: "status", "what's running", "any tasks active?".
19
26
  RESET — The user wants to start a fresh session: "new", "reset", "start over", "fresh session".
20
- CHAT — Anything else: questions, conversation, thanks, general discussion.
27
+ CHAT — Anything else: questions, conversation, thanks, general discussion, and status reports of work the human already did ("thanks, fixed in <sha>", "addressed your comments", "done — added tests").
21
28
 
22
- When ambiguous between EXPLORE and CHAT, prefer CHAT. Only pick EXPLORE when the user is explicitly asking for brainstorming / spec-shaping / design exploration.
29
+ Polite or question phrasings are still clear intent. "Can you do a security review of X?",
30
+ "could you triage <repo>?", "please review PRs on <repo>" are SECURITY, TRIAGE, REVIEW
31
+ respectively — not CHAT. The "prefer CHAT when ambiguous" rule only applies when the
32
+ message has no clear action verb. Presence of "security review", "triage", "review PRs",
33
+ "scan for vulnerabilities", etc. makes the intent unambiguous regardless of politeness.
34
+
35
+ When ambiguous between EXPLORE and CHAT, prefer CHAT. Only pick EXPLORE when the user is explicitly asking for brainstorming / spec-shaping / design exploration — OR gives a bare "explore"/"explore this" command (see the issue-reply rule below), which is unambiguous, not chat.
36
+ When ambiguous between QUESTION and CHAT, prefer CHAT — only pick QUESTION for a substantive informational question that genuinely benefits from research (reading docs/code or a web search). Casual conversation, greetings, thanks, and trivial one-liners stay CHAT.
23
37
  When ambiguous between BUILD and CHAT, prefer CHAT.
38
+ When ambiguous between VERIFY/QATEST and REVIEW/CHAT, prefer the existing category — only pick VERIFY or QATEST when the user explicitly asks you to test, run, confirm, or exercise a behaviour/flow.
39
+ When ambiguous between DEMO and VERIFY/QATEST, prefer the existing category — only pick DEMO when the user explicitly asks for a video, a recording, or to "demo"/"show" the feature.
24
40
  When ambiguous between APPROVE/REJECT and CHAT, prefer CHAT — only classify as APPROVE/REJECT when the intent is clearly about a pending workflow gate.
25
41
 
42
+ Repo extraction: always emit REPO as "owner/name" (never a URL). If the message contains
43
+ a github.com URL, convert it: https://github.com/cliftonc/lastlight → cliftonc/lastlight.
44
+ URL paths like /issues/42 or /pull/5 should populate ISSUE as well.
45
+
46
+ When the message is a reply on an existing issue/PR, the issue title is provided
47
+ as ISSUE TITLE. Short imperative replies classify by their verb, with the issue
48
+ as the implicit object — the "prefer CHAT when ambiguous" rule does NOT apply to
49
+ a clear command directed at the issue's subject:
50
+ - "lets build this", "build it", "go ahead", "ship it", "do it", "implement
51
+ this", "make it so" → BUILD (write the code now).
52
+ - "explore", "explore this", "let's explore", "think this through", "spec this
53
+ out", "brainstorm this" → EXPLORE (shape the idea / write a spec first).
54
+ A bare command word ("explore", "build") on an existing issue is a clear
55
+ command, NOT ambiguous chat.
56
+ This verb rule is for IMPERATIVE requests only. A PR/issue reply that REPORTS
57
+ already-completed work or responds to your review — "thanks, fixed in <sha>",
58
+ "addressed in commit abc123", "done, added a regression test", "this is
59
+ intentional, confirmed with the maintainer" — is a status report, NOT a
60
+ command. Classify it CHAT regardless of the ISSUE TITLE or an @mention. Do not
61
+ let words like "fix"/"build"/"add" inside a past-tense report flip it to BUILD.
62
+
26
63
  Respond in exactly this format (each on its own line, no extra text):
27
- INTENT: BUILD|EXPLORE|TRIAGE|REVIEW|APPROVE|REJECT|STATUS|RESET|CHAT
64
+ INTENT: BUILD|EXPLORE|QUESTION|TRIAGE|REVIEW|SECURITY|VERIFY|QATEST|DEMO|APPROVE|REJECT|STATUS|RESET|CHAT
28
65
  REPO: owner/name or NONE
29
66
  ISSUE: number or NONE
30
67
  REASON: text or NONE
31
68
 
32
69
  Examples:
33
70
  "explore adding webhooks to cliftonc/drizby" → INTENT: EXPLORE, REPO: cliftonc/drizby, ISSUE: NONE, REASON: NONE
71
+ "how does cliftonc/lastlight compare to Vercel's Eve framework?" → INTENT: QUESTION, REPO: cliftonc/lastlight, ISSUE: NONE, REASON: NONE
72
+ "what's the difference between a sandbox phase and a chat session in cliftonc/lastlight?" → INTENT: QUESTION, REPO: cliftonc/lastlight, ISSUE: NONE, REASON: NONE
73
+ "thanks, that makes sense!" → INTENT: CHAT, REPO: NONE, ISSUE: NONE, REASON: NONE
34
74
  "build cliftonc/drizzle-cube#42" → INTENT: BUILD, REPO: cliftonc/drizzle-cube, ISSUE: 42, REASON: NONE
75
+ "lets build this!" with ISSUE TITLE "Security Review" → INTENT: BUILD, REPO: NONE, ISSUE: NONE, REASON: NONE
76
+ "go ahead" with ISSUE TITLE "Add CSV export" → INTENT: BUILD, REPO: NONE, ISSUE: NONE, REASON: NONE
77
+ "Thanks @last-light — addressed in 49ccadf. Fixed the nested body in the core and added a regression test; point 3 is intentional, confirmed with the maintainer." with ISSUE TITLE "Port fastify/hono/nextjs adapters" → INTENT: CHAT, REPO: NONE, ISSUE: NONE, REASON: NONE
78
+ "done, pushed a fix for the type error in 1a2b3c4" with ISSUE TITLE "Fix build" → INTENT: CHAT, REPO: NONE, ISSUE: NONE, REASON: NONE
79
+ "now also handle the GET /sql case please" with ISSUE TITLE "Port adapters" → INTENT: BUILD, REPO: NONE, ISSUE: NONE, REASON: NONE
80
+ "verify that the --fork flag creates a new session in cliftonc/foo#12" → INTENT: VERIFY, REPO: cliftonc/foo, ISSUE: 12, REASON: NONE
81
+ "does this actually fix the crash?" with ISSUE TITLE "Fix null deref on resize" → INTENT: VERIFY, REPO: NONE, ISSUE: NONE, REASON: NONE
82
+ "can you confirm the rate limiter actually blocks at 100 req/s?" with ISSUE TITLE "Add rate limiter" → INTENT: VERIFY, REPO: NONE, ISSUE: NONE, REASON: NONE
83
+ "qa test the login flow on this PR" with ISSUE TITLE "Add login" → INTENT: QATEST, REPO: NONE, ISSUE: NONE, REASON: NONE
84
+ "run through the signup flow and tell me what breaks" with ISSUE TITLE "Signup v2" → INTENT: QATEST, REPO: NONE, ISSUE: NONE, REASON: NONE
85
+ "record a demo of this" with ISSUE TITLE "Add dark-mode toggle" → INTENT: DEMO, REPO: NONE, ISSUE: NONE, REASON: NONE
86
+ "make a short before/after video of the fix on cliftonc/foo#12" → INTENT: DEMO, REPO: cliftonc/foo, ISSUE: 12, REASON: NONE
87
+ "explore" with ISSUE TITLE "Feature: Allow configuration of otel endpoints" → INTENT: EXPLORE, REPO: NONE, ISSUE: NONE, REASON: NONE
88
+ "explore this" with ISSUE TITLE "Add webhook support" → INTENT: EXPLORE, REPO: NONE, ISSUE: NONE, REASON: NONE
35
89
  "approve" → INTENT: APPROVE, REPO: NONE, ISSUE: NONE, REASON: NONE
36
90
  "reject, the plan is too complex" → INTENT: REJECT, REPO: NONE, ISSUE: NONE, REASON: the plan is too complex
37
- "what's running?" → INTENT: STATUS, REPO: NONE, ISSUE: NONE, REASON: NONE`;
91
+ "what's running?" → INTENT: STATUS, REPO: NONE, ISSUE: NONE, REASON: NONE
92
+ "run a security review on cliftonc/lastlight" → INTENT: SECURITY, REPO: cliftonc/lastlight, ISSUE: NONE, REASON: NONE
93
+ "can you do a security review of https://github.com/cliftonc/lastlight" → INTENT: SECURITY, REPO: cliftonc/lastlight, ISSUE: NONE, REASON: NONE
94
+ "could you triage https://github.com/foo/bar?" → INTENT: TRIAGE, REPO: foo/bar, ISSUE: NONE, REASON: NONE
95
+ "please review https://github.com/foo/bar/pull/42" → INTENT: REVIEW, REPO: foo/bar, ISSUE: 42, REASON: NONE
96
+ "scan https://github.com/cliftonc/lastlight for vulnerabilities" → INTENT: SECURITY, REPO: cliftonc/lastlight, ISSUE: NONE, REASON: NONE
97
+ "delete any files in ~/work/lastlight/docs" → INTENT: CHAT, REPO: NONE, ISSUE: NONE, REASON: NONE
98
+ "can you remove the old docs folder for me" (no ISSUE TITLE, no repo) → INTENT: CHAT, REPO: NONE, ISSUE: NONE, REASON: NONE
99
+ "build something cool" (no repo, no ISSUE TITLE) → INTENT: CHAT, REPO: NONE, ISSUE: NONE, REASON: NONE`;
100
+ /**
101
+ * Extract owner/repo and optional issue/PR number from any github.com URL
102
+ * in the text. Belt-and-suspenders for the LLM — if the classifier forgets
103
+ * to normalize a URL to owner/name, this fallback still recovers the repo.
104
+ * Returns undefined when no github.com URL is present.
105
+ */
106
+ export function extractGithubRefFromText(text) {
107
+ // URL with /issues/N or /pull/N — capture the number too
108
+ const withNumber = text.match(/github\.com\/([\w-]+)\/([\w.-]+?)\/(?:issues|pull)\/(\d+)\b/i);
109
+ if (withNumber) {
110
+ return {
111
+ repo: `${withNumber[1]}/${cleanRepoName(withNumber[2])}`,
112
+ issueNumber: parseInt(withNumber[3], 10),
113
+ };
114
+ }
115
+ // Bare repo URL — stop at next slash / whitespace / query / fragment, then
116
+ // strip trailing sentence punctuation and any .git suffix.
117
+ const bare = text.match(/github\.com\/([\w-]+)\/([\w.-]+?)(?=[\s/?#,]|$)/i);
118
+ if (bare) {
119
+ return { repo: `${bare[1]}/${cleanRepoName(bare[2])}` };
120
+ }
121
+ return undefined;
122
+ }
123
+ function cleanRepoName(name) {
124
+ // Repo names don't end in punctuation; a trailing `.` or `,` is sentence
125
+ // punctuation that leaked into the match.
126
+ return name.replace(/[.,]+$/, "").replace(/\.git$/i, "");
127
+ }
128
+ const ISSUE_QUESTION_PROMPT = `You are a router for newly-opened GitHub issues.
129
+ Decide whether the issue is a QUESTION or a WORK item.
130
+
131
+ QUESTION — the issue asks for information, explanation, comparison, or guidance.
132
+ The reporter wants an ANSWER, not code: "How does X work?", "What's the difference
133
+ between X and Y?", "Is it possible to...?", "Which approach should I use?", "Why
134
+ does X happen?". The deliverable is a written reply.
135
+
136
+ WORK — the issue requests a code change: a bug report (something is broken and
137
+ should be fixed) or a feature/enhancement request (build or change something).
138
+ The deliverable is a commit or PR.
139
+
140
+ Decide by the DOMINANT intent. If the issue asks a question AND also requests a
141
+ change ("How do I do X — and could you add it?"), it is WORK: the change is the
142
+ deliverable and triage should handle it. Only pure information requests are QUESTION.
143
+ When genuinely unsure, answer WORK — triage is the safe default.
144
+
145
+ Respond with exactly one word on its own line: QUESTION or WORK`;
146
+ /**
147
+ * Classify whether a newly-opened issue is a pure question (wants an answer)
148
+ * versus a work item (wants a code change). Used by the router to send
149
+ * question issues down the dedicated answer path instead of triage.
150
+ *
151
+ * Falls back to `false` (WORK → triage) on any error — triage is the safe
152
+ * default, and a question that slips through still hits the issue-triage
153
+ * skill's question safety net.
154
+ */
155
+ export async function classifyIssueIsQuestion(title, body, options = {}) {
156
+ try {
157
+ const chat = options.chat ?? realChat;
158
+ const defaultFastModel = options.defaultFastModel ?? realDefaultFastModel;
159
+ const output = await chat(options.model ?? defaultFastModel("classifier"), [
160
+ { role: "system", content: ISSUE_QUESTION_PROMPT },
161
+ { role: "user", content: `TITLE: ${title}\n\nBODY: ${body}` },
162
+ ], { maxTokens: 16 });
163
+ return /\bQUESTION\b/i.test(output);
164
+ }
165
+ catch (err) {
166
+ console.error(`[classifier] Error classifying issue: ${err.message}`);
167
+ return false;
168
+ }
169
+ }
38
170
  /**
39
171
  * Classify a GitHub/Slack comment's intent and extract a repo reference.
40
172
  * Falls back to intent=action on any error (safe default).
41
173
  */
42
- export async function classifyComment(commentBody, model) {
174
+ export async function classifyComment(commentBody, context, options = {}) {
43
175
  try {
44
- const { query } = await import("@anthropic-ai/claude-agent-sdk");
45
- let output = "";
46
- for await (const msg of query({
47
- prompt: `Classify this comment:\n\n${commentBody}`,
48
- options: {
49
- model: model || "claude-haiku-4-5-20251001",
50
- systemPrompt: CLASSIFIER_PROMPT,
51
- permissionMode: "bypassPermissions",
52
- maxTurns: 1,
53
- allowedTools: [],
54
- settingSources: [],
55
- },
56
- })) {
57
- const m = msg;
58
- if (m.type === "result") {
59
- output = m.result || "";
60
- }
61
- }
176
+ const userPrompt = context?.issueTitle
177
+ ? `Classify this comment (replying on an existing ${context.isPullRequest ? "PR" : "issue"}):\n\nISSUE TITLE: ${context.issueTitle}\n\nCOMMENT: ${commentBody}`
178
+ : `Classify this comment:\n\n${commentBody}`;
179
+ const resolvedOptions = typeof options === "string" ? { model: options } : options;
180
+ const chat = resolvedOptions.chat ?? realChat;
181
+ const defaultFastModel = resolvedOptions.defaultFastModel ?? realDefaultFastModel;
182
+ const output = await chat(resolvedOptions.model ?? defaultFastModel("classifier"), [
183
+ { role: "system", content: CLASSIFIER_PROMPT },
184
+ { role: "user", content: userPrompt },
185
+ ], { maxTokens: 128 });
62
186
  const upper = output.trim().toUpperCase();
63
187
  const intentMap = {
64
188
  BUILD: "build",
65
189
  EXPLORE: "explore",
190
+ QUESTION: "question",
66
191
  TRIAGE: "triage",
67
192
  REVIEW: "review",
193
+ SECURITY: "security",
194
+ VERIFY: "verify",
195
+ QATEST: "qa-test",
196
+ DEMO: "demo",
68
197
  APPROVE: "approve",
69
198
  REJECT: "reject",
70
199
  STATUS: "status",
@@ -76,12 +205,22 @@ export async function classifyComment(commentBody, model) {
76
205
  const intent = intentMatch
77
206
  ? (intentMap[intentMatch[1]] ?? "chat")
78
207
  : "chat";
79
- // Extract repo from "REPO: owner/name" line
208
+ // Extract repo from "REPO: owner/name" line. If the classifier didn't
209
+ // emit one (e.g. the user pasted a full github.com URL and the model
210
+ // left REPO as NONE), recover it from the raw message.
80
211
  const repoMatch = output.match(/REPO:\s*([\w-]+\/[\w.-]+)/i);
81
- const repo = repoMatch?.[1];
82
- // Extract issue number
83
212
  const issueMatch = output.match(/ISSUE:\s*(\d+)/i);
84
- const issueNumber = issueMatch ? parseInt(issueMatch[1], 10) : undefined;
213
+ let repo = repoMatch?.[1];
214
+ let issueNumber = issueMatch ? parseInt(issueMatch[1], 10) : undefined;
215
+ if (!repo) {
216
+ const fallback = extractGithubRefFromText(commentBody);
217
+ if (fallback) {
218
+ repo = fallback.repo;
219
+ if (issueNumber === undefined && fallback.issueNumber !== undefined) {
220
+ issueNumber = fallback.issueNumber;
221
+ }
222
+ }
223
+ }
85
224
  // Extract reject reason
86
225
  const reasonMatch = output.match(/REASON:\s*(.+)/i);
87
226
  const reason = reasonMatch && reasonMatch[1].trim().toUpperCase() !== "NONE"
@@ -1 +1 @@
1
- {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/engine/classifier.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAuBH,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0EA6BgD,CAAC;AAE3E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,WAAmB,EACnB,KAAc;IAEd,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;QAEjE,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,CAAC;YAC5B,MAAM,EAAE,6BAA6B,WAAW,EAAE;YAClD,OAAO,EAAE;gBACP,KAAK,EAAE,KAAK,IAAI,2BAA2B;gBAC3C,YAAY,EAAE,iBAAiB;gBAC/B,cAAc,EAAE,mBAA4B;gBAC5C,QAAQ,EAAE,CAAC;gBACX,YAAY,EAAE,EAAE;gBAChB,cAAc,EAAE,EAAE;aACnB;SACF,CAAC,EAAE,CAAC;YACH,MAAM,CAAC,GAAG,GAAU,CAAC;YACrB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACxB,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE1C,MAAM,SAAS,GAAkC;YAC/C,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,MAAM;SACb,CAAC;QAEF,oBAAoB;QACpB,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnD,MAAM,MAAM,GAAkB,WAAW;YACvC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;YACvC,CAAC,CAAC,MAAM,CAAC;QAEX,4CAA4C;QAC5C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;QAE5B,uBAAuB;QACvB,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAEzE,wBAAwB;QACxB,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM;YAC1E,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;YACvB,CAAC,CAAC,SAAS,CAAC;QAEd,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,2CAA2C,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/engine/classifier.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,gBAAgB,IAAI,oBAAoB,EAAqB,MAAM,UAAU,CAAC;AA0CzG,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uGA0F6E,CAAC;AAExG;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAAY;IAEZ,yDAAyD;IACzD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAC3B,8DAA8D,CAC/D,CAAC;IACF,IAAI,UAAU,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE;YACxD,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SACzC,CAAC;IACJ,CAAC;IACD,2EAA2E;IAC3E,2DAA2D;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC5E,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,yEAAyE;IACzE,0CAA0C;IAC1C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;gEAiBkC,CAAC;AAEjE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAa,EACb,IAAY,EACZ,UAA6B,EAAE;IAE/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC;QACtC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,oBAAoB,CAAC;QAC1E,MAAM,MAAM,GAAG,MAAM,IAAI,CACvB,OAAO,CAAC,KAAK,IAAI,gBAAgB,CAAC,YAAY,CAAC,EAC/C;YACE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,qBAAqB,EAAE;YAClD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,aAAa,IAAI,EAAE,EAAE;SAC9D,EACD,EAAE,SAAS,EAAE,EAAE,EAAE,CAClB,CAAC;QACF,OAAO,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,yCAAyC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,WAAmB,EACnB,OAA2B,EAC3B,UAAsC,EAAE;IAExC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU;YACpC,CAAC,CAAC,kDAAkD,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,sBAAsB,OAAO,CAAC,UAAU,gBAAgB,WAAW,EAAE;YAC/J,CAAC,CAAC,6BAA6B,WAAW,EAAE,CAAC;QAE/C,MAAM,eAAe,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;QACnF,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,IAAI,QAAQ,CAAC;QAC9C,MAAM,gBAAgB,GAAG,eAAe,CAAC,gBAAgB,IAAI,oBAAoB,CAAC;QAClF,MAAM,MAAM,GAAG,MAAM,IAAI,CACvB,eAAe,CAAC,KAAK,IAAI,gBAAgB,CAAC,YAAY,CAAC,EACvD;YACE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;YAC9C,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE;SACtC,EACD,EAAE,SAAS,EAAE,GAAG,EAAE,CACnB,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE1C,MAAM,SAAS,GAAkC;YAC/C,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,UAAU;YACpB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,UAAU;YACpB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,SAAS;YACjB,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,OAAO;YACd,IAAI,EAAE,MAAM;SACb,CAAC;QAEF,oBAAoB;QACpB,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnD,MAAM,MAAM,GAAkB,WAAW;YACvC,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;YACvC,CAAC,CAAC,MAAM,CAAC;QAEX,sEAAsE;QACtE,qEAAqE;QACrE,uDAAuD;QACvD,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC7D,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnD,IAAI,IAAI,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,QAAQ,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;YACvD,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;gBACrB,IAAI,WAAW,KAAK,SAAS,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;oBACpE,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;gBACrC,CAAC;YACH,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM;YAC1E,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;YACvB,CAAC,CAAC,SAAS,CAAC;QAEd,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,2CAA2C,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { classifyComment, classifyIssueIsQuestion, extractGithubRefFromText } from "./classifier.js";
3
+ describe("extractGithubRefFromText", () => {
4
+ it("returns undefined when no github.com URL is present", () => {
5
+ expect(extractGithubRefFromText("hi there")).toBeUndefined();
6
+ expect(extractGithubRefFromText("check cliftonc/lastlight")).toBeUndefined();
7
+ });
8
+ it("extracts owner/repo from a bare github.com URL", () => {
9
+ expect(extractGithubRefFromText("https://github.com/cliftonc/lastlight")).toEqual({
10
+ repo: "cliftonc/lastlight",
11
+ });
12
+ });
13
+ it("extracts from a URL embedded in surrounding text", () => {
14
+ const input = "can you do a security review of https://github.com/cliftonc/lastlight";
15
+ expect(extractGithubRefFromText(input)).toEqual({ repo: "cliftonc/lastlight" });
16
+ });
17
+ it("strips a trailing slash", () => {
18
+ expect(extractGithubRefFromText("https://github.com/cliftonc/lastlight/")).toEqual({
19
+ repo: "cliftonc/lastlight",
20
+ });
21
+ });
22
+ it("strips a trailing punctuation (question mark, comma, period)", () => {
23
+ expect(extractGithubRefFromText("triage https://github.com/foo/bar?")).toEqual({
24
+ repo: "foo/bar",
25
+ });
26
+ expect(extractGithubRefFromText("review https://github.com/foo/bar, please")).toEqual({
27
+ repo: "foo/bar",
28
+ });
29
+ expect(extractGithubRefFromText("scan https://github.com/foo/bar.")).toEqual({
30
+ repo: "foo/bar",
31
+ });
32
+ });
33
+ it("strips a .git suffix", () => {
34
+ expect(extractGithubRefFromText("https://github.com/foo/bar.git")).toEqual({
35
+ repo: "foo/bar",
36
+ });
37
+ });
38
+ it("extracts an issue number from /issues/N URLs", () => {
39
+ expect(extractGithubRefFromText("please look at https://github.com/cliftonc/lastlight/issues/42")).toEqual({ repo: "cliftonc/lastlight", issueNumber: 42 });
40
+ });
41
+ it("extracts a PR number from /pull/N URLs", () => {
42
+ expect(extractGithubRefFromText("review https://github.com/foo/bar/pull/7 when you can")).toEqual({ repo: "foo/bar", issueNumber: 7 });
43
+ });
44
+ it("ignores trailing URL path segments beyond owner/repo when no issue/PR", () => {
45
+ expect(extractGithubRefFromText("https://github.com/foo/bar/tree/main/src")).toEqual({
46
+ repo: "foo/bar",
47
+ });
48
+ });
49
+ it("handles http:// in addition to https://", () => {
50
+ expect(extractGithubRefFromText("http://github.com/foo/bar")).toEqual({
51
+ repo: "foo/bar",
52
+ });
53
+ });
54
+ it("handles repo names with dots and hyphens", () => {
55
+ expect(extractGithubRefFromText("https://github.com/cliftonc/drizzle-cube")).toEqual({
56
+ repo: "cliftonc/drizzle-cube",
57
+ });
58
+ expect(extractGithubRefFromText("https://github.com/user/foo.bar")).toEqual({
59
+ repo: "user/foo.bar",
60
+ });
61
+ });
62
+ });
63
+ describe("classifyComment — injected chat", () => {
64
+ it("parses build intent responses", async () => {
65
+ const chat = vi.fn().mockResolvedValue("INTENT: BUILD\nREPO: cliftonc/lastlight\nISSUE: 96\nREASON: NONE");
66
+ const r = await classifyComment("@last-light can you build this?", { issueTitle: "Introduce one provider-agnostic chat() seam" }, { chat, defaultFastModel: () => "openai/test" });
67
+ expect(r).toMatchObject({ intent: "build", repo: "cliftonc/lastlight", issueNumber: 96 });
68
+ expect(chat).toHaveBeenCalledOnce();
69
+ expect(chat).toHaveBeenCalledWith("openai/test", expect.arrayContaining([
70
+ expect.objectContaining({ role: "system" }),
71
+ expect.objectContaining({ role: "user", content: expect.stringContaining("@last-light can you build this?") }),
72
+ ]), { maxTokens: 128 });
73
+ });
74
+ it("falls back to a GitHub URL when model output omits repo and issue", async () => {
75
+ const chat = vi.fn().mockResolvedValue("INTENT: SECURITY\nREPO: NONE\nISSUE: NONE\nREASON: NONE");
76
+ const r = await classifyComment("security review https://github.com/foo/bar/pull/7 please", {}, { chat, defaultFastModel: () => "openai/test" });
77
+ expect(r).toMatchObject({ intent: "security", repo: "foo/bar", issueNumber: 7 });
78
+ });
79
+ it("falls back to chat intent when chat rejects", async () => {
80
+ const chat = vi.fn().mockRejectedValue(new Error("network"));
81
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
82
+ const r = await classifyComment("@last-light can you build this?", {}, { chat, defaultFastModel: () => "openai/test" });
83
+ expect(r).toEqual({ intent: "chat" });
84
+ expect(errorSpy).toHaveBeenCalled();
85
+ errorSpy.mockRestore();
86
+ });
87
+ });
88
+ describe("classifyIssueIsQuestion — injected chat", () => {
89
+ it("returns true for a question issue", async () => {
90
+ const chat = vi.fn().mockResolvedValue("QUESTION");
91
+ const r = await classifyIssueIsQuestion("How is lastlight different to Vercel Eve?", "Keen on a comparison.", { chat, defaultFastModel: () => "openai/test" });
92
+ expect(r).toBe(true);
93
+ expect(chat).toHaveBeenCalledWith("openai/test", expect.arrayContaining([
94
+ expect.objectContaining({ role: "system" }),
95
+ expect.objectContaining({ role: "user", content: expect.stringContaining("How is lastlight different") }),
96
+ ]), { maxTokens: 16 });
97
+ });
98
+ it("returns false for a work item", async () => {
99
+ const chat = vi.fn().mockResolvedValue("WORK");
100
+ const r = await classifyIssueIsQuestion("Crash on startup", "App throws on boot.", {
101
+ chat,
102
+ defaultFastModel: () => "openai/test",
103
+ });
104
+ expect(r).toBe(false);
105
+ });
106
+ it("defaults to false (work → triage) when chat rejects", async () => {
107
+ const chat = vi.fn().mockRejectedValue(new Error("network"));
108
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => undefined);
109
+ const r = await classifyIssueIsQuestion("Anything", "body", { chat, defaultFastModel: () => "openai/test" });
110
+ expect(r).toBe(false);
111
+ expect(errorSpy).toHaveBeenCalled();
112
+ errorSpy.mockRestore();
113
+ });
114
+ });
115
+ //# sourceMappingURL=classifier.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.test.js","sourceRoot":"","sources":["../../src/engine/classifier.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { EventEnvelope } from "../connectors/types.js";
2
+ import type { SessionManager } from "../connectors/index.js";
3
+ import type { StateDb } from "../state/db.js";
4
+ import type { GitHubClient } from "./github.js";
5
+ import type { ChatResult } from "./chat.js";
6
+ import { type Route, type RouterDeps } from "./router.js";
7
+ /**
8
+ * Hand a workflow to the runner. Matches `dispatchWorkflow` in index.ts — the
9
+ * dispatcher names workflows and accumulates their outcome but owns none of the
10
+ * sandbox/runner plumbing.
11
+ */
12
+ export type DispatchWorkflowFn = (workflowName: string, context: Record<string, unknown>, onRunStart?: (runId: string) => Promise<void>) => Promise<{
13
+ success: boolean;
14
+ error?: string;
15
+ paused?: boolean;
16
+ }>;
17
+ /** Run one in-process chat turn. Injected so the chat branch is testable. */
18
+ export type RunChatFn = (message: string, messagingSessionId: string, sender: string, resumeAgentSessionId: string | undefined) => Promise<ChatResult>;
19
+ /**
20
+ * Everything the dispatcher needs, bundled so `main()` constructs it once and
21
+ * hands it over. `route` defaults to `routeEvent` but is injectable so a
22
+ * branch test names the exact Route it wants without mocking the classifier.
23
+ */
24
+ export interface DispatchDeps {
25
+ db: StateDb;
26
+ github: GitHubClient | null;
27
+ dispatchWorkflow: DispatchWorkflowFn;
28
+ sessionManager: SessionManager;
29
+ runChat: RunChatFn;
30
+ route?: (envelope: EventEnvelope, deps: RouterDeps) => Promise<Route>;
31
+ reviewPostsCheck: boolean;
32
+ publicUrl?: string;
33
+ }
34
+ /**
35
+ * The typed result of dispatching one event. Handlers return an outcome rather
36
+ * than only producing side effects, so each branch is assertable through the
37
+ * single `dispatch` seam.
38
+ */
39
+ export type DispatchOutcome = {
40
+ kind: "ignored";
41
+ reason: string;
42
+ } | {
43
+ kind: "replied";
44
+ message: string;
45
+ } | {
46
+ kind: "skipped";
47
+ reason: string;
48
+ } | {
49
+ kind: "handled";
50
+ handler: string;
51
+ } | {
52
+ kind: "dispatched";
53
+ workflow: string;
54
+ };
55
+ /**
56
+ * Turn an EventEnvelope into a workflow dispatch (or an in-process handler
57
+ * run). Classifies via `route`, then acts on the decision. Extracted from the
58
+ * former `registry.onEvent` closure so every event branch is testable.
59
+ */
60
+ export declare function dispatch(envelope: EventEnvelope, deps: DispatchDeps): Promise<DispatchOutcome>;