lastlight 0.1.8 → 0.2.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 (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 +423 -437
  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 +31 -12
  272. package/dist/setup.js +516 -174
  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 -7
  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
@@ -1,5 +1,5 @@
1
- import { execSync } from "child_process";
2
- import { existsSync, rmSync } from "fs";
1
+ import { execFileSync } from "child_process";
2
+ import { existsSync, mkdirSync, rmSync } from "fs";
3
3
  import { join, resolve } from "path";
4
4
  const WORKTREE_DIR = ".worktrees";
5
5
  /**
@@ -14,8 +14,8 @@ export class WorktreeManager {
14
14
  constructor(baseDir) {
15
15
  this.baseDir = baseDir || resolve(WORKTREE_DIR);
16
16
  }
17
- exec(cmd, opts) {
18
- return execSync(cmd, { encoding: "utf-8", stdio: "pipe", ...opts });
17
+ execGit(args, opts) {
18
+ return execFileSync("git", args, { encoding: "utf-8", stdio: "pipe", ...opts });
19
19
  }
20
20
  /**
21
21
  * Create a new worktree for a task.
@@ -29,7 +29,7 @@ export class WorktreeManager {
29
29
  const worktreePath = join(this.baseDir, taskId);
30
30
  // Ensure base directory exists
31
31
  if (!existsSync(this.baseDir)) {
32
- execSync(`mkdir -p ${this.baseDir}`);
32
+ mkdirSync(this.baseDir, { recursive: true });
33
33
  }
34
34
  // We need a bare/normal repo to create worktrees from.
35
35
  // Use a shared bare clone per repo to avoid re-cloning each time.
@@ -37,11 +37,11 @@ export class WorktreeManager {
37
37
  const bareDir = join(this.baseDir, `.bare-${repoName}`);
38
38
  if (!existsSync(bareDir)) {
39
39
  console.log(`[worktree] Bare clone: ${repoUrl}`);
40
- this.exec(`git clone --bare ${repoUrl} ${bareDir}`);
40
+ this.execGit(["clone", "--bare", repoUrl, bareDir]);
41
41
  }
42
42
  else {
43
43
  // Fetch latest
44
- this.exec(`git -C ${bareDir} fetch --all --prune`);
44
+ this.execGit(["-C", bareDir, "fetch", "--all", "--prune"]);
45
45
  }
46
46
  // Create worktree with new branch from base
47
47
  const base = baseBranch || "main";
@@ -49,7 +49,7 @@ export class WorktreeManager {
49
49
  // Check if branch already exists remotely
50
50
  let branchExists = false;
51
51
  try {
52
- this.exec(`git -C ${bareDir} rev-parse --verify origin/${branch}`);
52
+ this.execGit(["-C", bareDir, "rev-parse", "--verify", `origin/${branch}`]);
53
53
  branchExists = true;
54
54
  }
55
55
  catch {
@@ -58,13 +58,13 @@ export class WorktreeManager {
58
58
  if (branchExists) {
59
59
  // Resume existing branch
60
60
  console.log(`[worktree] Resuming branch: ${branch}`);
61
- this.exec(`git -C ${bareDir} worktree add ${worktreePath} origin/${branch}`);
62
- this.exec(`git -C ${worktreePath} checkout -B ${branch} origin/${branch}`);
61
+ this.execGit(["-C", bareDir, "worktree", "add", worktreePath, `origin/${branch}`]);
62
+ this.execGit(["-C", worktreePath, "checkout", "-B", branch, `origin/${branch}`]);
63
63
  }
64
64
  else {
65
65
  // Create new branch from base
66
66
  console.log(`[worktree] New branch: ${branch} from ${baseRef}`);
67
- this.exec(`git -C ${bareDir} worktree add -b ${branch} ${worktreePath} ${baseRef}`);
67
+ this.execGit(["-C", bareDir, "worktree", "add", "-b", branch, worktreePath, baseRef]);
68
68
  }
69
69
  const info = {
70
70
  taskId,
@@ -94,7 +94,7 @@ export class WorktreeManager {
94
94
  try {
95
95
  // Remove worktree
96
96
  if (existsSync(info.path)) {
97
- this.exec(`git -C ${bareDir} worktree remove --force ${info.path}`);
97
+ this.execGit(["-C", bareDir, "worktree", "remove", "--force", info.path]);
98
98
  }
99
99
  }
100
100
  catch (err) {
@@ -102,7 +102,7 @@ export class WorktreeManager {
102
102
  if (existsSync(info.path)) {
103
103
  rmSync(info.path, { recursive: true, force: true });
104
104
  try {
105
- this.exec(`git -C ${bareDir} worktree prune`);
105
+ this.execGit(["-C", bareDir, "worktree", "prune"]);
106
106
  }
107
107
  catch { /* best effort */ }
108
108
  }
@@ -110,7 +110,7 @@ export class WorktreeManager {
110
110
  // Optionally delete the branch
111
111
  if (opts?.deleteBranch) {
112
112
  try {
113
- this.exec(`git -C ${bareDir} branch -D ${info.branch}`);
113
+ this.execGit(["-C", bareDir, "branch", "-D", info.branch]);
114
114
  }
115
115
  catch { /* branch may not exist locally */ }
116
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/worktree/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/worktree/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAwB,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,YAAY,GAAG,YAAY,CAAC;AAUlC;;;;;GAKG;AACH,MAAM,OAAO,eAAe;IAClB,OAAO,CAAS;IAChB,MAAM,GAA8B,IAAI,GAAG,EAAE,CAAC;IAEtD,YAAY,OAAgB;QAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAEO,OAAO,CAAC,IAAc,EAAE,IAAsB;QACpD,OAAO,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,CAAW,CAAC;IAC5F,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,IAKZ;QACC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;QAErD,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;QAClC,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAEhD,+BAA+B;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;QAED,uDAAuD;QACvD,kEAAkE;QAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,yBAAyB,EAAE,IAAI,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,eAAe;YACf,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,4CAA4C;QAC5C,MAAM,IAAI,GAAG,UAAU,IAAI,MAAM,CAAC;QAClC,MAAM,OAAO,GAAG,UAAU,IAAI,EAAE,CAAC;QAEjC,0CAA0C;QAC1C,IAAI,YAAY,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC;YAC3E,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;QAAC,MAAM,CAAC;YACP,8CAA8C;QAChD,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YACjB,yBAAyB;YACzB,OAAO,CAAC,GAAG,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;YACrD,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC;YACnF,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,MAAM,EAAE,CAAC,CAAC,CAAC;QACnF,CAAC;aAAM,CAAC;YACN,8BAA8B;YAC9B,OAAO,CAAC,GAAG,CAAC,0BAA0B,MAAM,SAAS,OAAO,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,IAAI,GAAiB;YACzB,MAAM;YACN,IAAI,EAAE,YAAY;YAClB,MAAM;YACN,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,IAAiC;QAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,IAAI,CAAC,CAAC;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC;YACH,kBAAkB;YAClB,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,6CAA6C;YAC7C,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpD,IAAI,CAAC;oBACH,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;gBACrD,CAAC;gBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,+BAA+B;QAC/B,IAAI,IAAI,EAAE,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7D,CAAC;YAAC,MAAM,CAAC,CAAC,kCAAkC,CAAC,CAAC;QAChD,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,CAAC;CACF"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ vi.mock("child_process", () => ({
3
+ execSync: vi.fn(),
4
+ execFileSync: vi.fn().mockReturnValue(""),
5
+ }));
6
+ vi.mock("fs", async (importOriginal) => {
7
+ const actual = await importOriginal();
8
+ return {
9
+ ...actual,
10
+ existsSync: vi.fn().mockReturnValue(false),
11
+ mkdirSync: vi.fn(),
12
+ rmSync: vi.fn(),
13
+ };
14
+ });
15
+ import { execSync, execFileSync } from "child_process";
16
+ import { existsSync, mkdirSync } from "fs";
17
+ import { WorktreeManager } from "./manager.js";
18
+ const mockExecSync = vi.mocked(execSync);
19
+ const mockExecFileSync = vi.mocked(execFileSync);
20
+ const mockExistsSync = vi.mocked(existsSync);
21
+ const mockMkdirSync = vi.mocked(mkdirSync);
22
+ describe("WorktreeManager — no shell injection via execFileSync", () => {
23
+ let manager;
24
+ beforeEach(() => {
25
+ vi.clearAllMocks();
26
+ manager = new WorktreeManager("/tmp/test-worktrees");
27
+ // baseDir doesn't exist → triggers mkdir and clone
28
+ mockExistsSync.mockReturnValue(false);
29
+ });
30
+ it("create() does not use execSync", async () => {
31
+ await manager.create({
32
+ taskId: "task-001",
33
+ repoUrl: "https://github.com/cliftonc/lastlight.git",
34
+ branch: "feature/test",
35
+ });
36
+ expect(mockExecSync).not.toHaveBeenCalled();
37
+ });
38
+ it("create() calls execFileSync with 'git' as the command", async () => {
39
+ await manager.create({
40
+ taskId: "task-002",
41
+ repoUrl: "https://github.com/cliftonc/lastlight.git",
42
+ branch: "feature/test",
43
+ });
44
+ expect(mockExecFileSync).toHaveBeenCalled();
45
+ for (const call of mockExecFileSync.mock.calls) {
46
+ expect(call[0]).toBe("git");
47
+ expect(Array.isArray(call[1])).toBe(true);
48
+ }
49
+ });
50
+ it("create() uses mkdirSync instead of execSync for directory creation", async () => {
51
+ await manager.create({
52
+ taskId: "task-003",
53
+ repoUrl: "https://github.com/cliftonc/lastlight.git",
54
+ branch: "feature/test",
55
+ });
56
+ expect(mockMkdirSync).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ recursive: true }));
57
+ // execSync must not have been used for mkdir
58
+ const mkdirCalls = mockExecSync.mock.calls.filter((c) => String(c[0]).includes("mkdir"));
59
+ expect(mkdirCalls).toHaveLength(0);
60
+ });
61
+ it("create() passes repoUrl as a separate argument (not shell-interpolated)", async () => {
62
+ const repoUrl = "https://github.com/cliftonc/lastlight.git";
63
+ await manager.create({
64
+ taskId: "task-004",
65
+ repoUrl,
66
+ branch: "feature/test",
67
+ });
68
+ const cloneCall = mockExecFileSync.mock.calls.find((c) => Array.isArray(c[1]) && c[1].includes("clone"));
69
+ expect(cloneCall).toBeDefined();
70
+ const args = cloneCall[1];
71
+ expect(args).toContain(repoUrl);
72
+ });
73
+ it("cleanup() does not use execSync", async () => {
74
+ // First create so cleanup has something to work with
75
+ mockExistsSync.mockReturnValue(true);
76
+ await manager.create({
77
+ taskId: "task-005",
78
+ repoUrl: "https://github.com/cliftonc/lastlight.git",
79
+ branch: "feature/test",
80
+ });
81
+ vi.clearAllMocks();
82
+ mockExistsSync.mockReturnValue(true);
83
+ await manager.cleanup("task-005", { deleteBranch: true });
84
+ expect(mockExecSync).not.toHaveBeenCalled();
85
+ });
86
+ });
87
+ //# sourceMappingURL=manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.test.js","sourceRoot":"","sources":["../../src/worktree/manager.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE9D,EAAE,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9B,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE;IACjB,YAAY,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;CAC1C,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;IACrC,MAAM,MAAM,GAAG,MAAM,cAAc,EAAuB,CAAC;IAC3D,OAAO;QACL,GAAG,MAAM;QACT,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;QAC1C,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE;QAClB,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;KAChB,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,YAAY,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AACzC,MAAM,gBAAgB,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AACjD,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAC7C,MAAM,aAAa,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;AAE3C,QAAQ,CAAC,uDAAuD,EAAE,GAAG,EAAE;IACrE,IAAI,OAAwB,CAAC;IAE7B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,GAAG,IAAI,eAAe,CAAC,qBAAqB,CAAC,CAAC;QACrD,mDAAmD;QACnD,cAAc,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,OAAO,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,2CAA2C;YACpD,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,OAAO,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,2CAA2C;YACpD,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,gBAAgB,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC5B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,OAAO,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,2CAA2C;YACpD,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC;QACH,MAAM,CAAC,aAAa,CAAC,CAAC,oBAAoB,CACxC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAClB,MAAM,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAC7C,CAAC;QACF,6CAA6C;QAC7C,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAC/C,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CACtC,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,OAAO,GAAG,2CAA2C,CAAC;QAC5D,MAAM,OAAO,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,UAAU;YAClB,OAAO;YACP,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC;QACH,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAChD,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAK,CAAC,CAAC,CAAC,CAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CACnE,CAAC;QACF,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,SAAU,CAAC,CAAC,CAAa,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,qDAAqD;QACrD,cAAc,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,MAAM,CAAC;YACnB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,2CAA2C;YACpD,MAAM,EAAE,cAAc;SACvB,CAAC,CAAC;QACH,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,cAAc,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -8,11 +8,35 @@ services:
8
8
  - 8.8.8.8
9
9
  - 1.1.1.1
10
10
  volumes:
11
- - ./secrets:/app/secrets:ro
11
+ # Single private deployment folder: overlay config.yaml + asset overrides
12
+ # plus a gitignored secrets/ subdir (.env, *.pem). LASTLIGHT_OVERLAY_DIR
13
+ # points the harness at /app/instance; the entrypoint reads secrets from
14
+ # /app/instance/secrets. This is the only deployment-specific input, and
15
+ # the harness reads it at startup — edit it and `docker compose restart
16
+ # agent` to apply, no image rebuild. Built-in assets (config/, workflows/,
17
+ # skills/, agent-context/) stay baked into the image; they're lastlight
18
+ # source and change via a normal rebuild.
19
+ - ./instance:/app/instance:ro
12
20
  - agent-data:/app/data
13
21
  - /var/run/docker.sock:/var/run/docker.sock # spawn sandbox containers
22
+ # Env comes from the consolidated overlay (instance/secrets/.env). The
23
+ # entrypoint also sources it inside the container; this env_file is the
24
+ # belt-and-suspenders copy. required: false so a fresh checkout (no
25
+ # instance/ yet) doesn't error. No repo-root .env is needed.
14
26
  env_file:
15
- - .env
27
+ - path: ./instance/secrets/.env
28
+ required: false
29
+ environment:
30
+ # V8's default old-space cap (~1.5 GB on 64-bit) is well below the 2g
31
+ # cgroup limit, so Node would OOM before docker did. Raise it close to
32
+ # the container limit so transient bursts have somewhere to land.
33
+ - NODE_OPTIONS=--max-old-space-size=1800
34
+ # Steady-state ~150 MB; bursts during webhook fan-out, the in-process
35
+ # chat skill (full Agent SDK), and the dispatch screening call. 2 GB
36
+ # gives headroom for those bursts while still preventing a runaway leak
37
+ # from taking the host down.
38
+ mem_limit: 2g
39
+ memswap_limit: 2g
16
40
  networks:
17
41
  - internal
18
42
 
@@ -25,6 +49,150 @@ services:
25
49
  profiles:
26
50
  - build-only # never started as a service, just built
27
51
 
52
+ # Build the enriched browser-QA sandbox image (Tier B). Separate + heavier
53
+ # (Playwright + a baked Chromium), built only when QA is enabled. Its FROM
54
+ # depends on lastlight-sandbox:latest existing first, so build in that order:
55
+ # docker compose --profile build-only build sandbox sandbox-qa
56
+ sandbox-qa:
57
+ build:
58
+ context: .
59
+ dockerfile: sandbox-qa.Dockerfile
60
+ image: lastlight-sandbox-qa:latest
61
+ profiles:
62
+ - build-only # never started as a service, just built
63
+
64
+ # ── Egress firewall (replaces the earlier tinyproxy CONNECT-proxy) ────
65
+ #
66
+ # See src/sandbox/egress-firewall-config.ts for the architecture rationale.
67
+ # Two pairs: (coredns-strict + nginx-egress-strict) and
68
+ # (coredns-open + nginx-egress-open). Sandbox containers spawn with
69
+ # `--dns <coredns-ip>` and dial allowlisted hosts directly — coredns
70
+ # resolves the hostname to the nginx IP, nginx peeks SNI and tunnels.
71
+ #
72
+ # The sandbox sees no proxy at all. Works for any SDK regardless of
73
+ # whether it honours HTTP(S)_PROXY env vars.
74
+
75
+ # One-shot init that writes nginx-strict.conf / nginx-open.conf /
76
+ # Corefile.strict / Corefile.open into the agent-data volume before
77
+ # coredns + nginx try to read them. Without this, the firewall
78
+ # services boot first, fail to find the configs, and run with empty
79
+ # state (returning NXDOMAIN for everything) — even after the harness
80
+ # later writes the files, the running processes never re-read them.
81
+ # `condition: service_completed_successfully` on the firewalls below
82
+ # makes them wait for this to exit 0.
83
+ egress-init:
84
+ image: lastlight-agent
85
+ restart: "no"
86
+ # Mounts the same overlay + secrets the agent does and runs the SAME
87
+ # config loader, so the collector config it pre-writes is computed from
88
+ # the effective config (env AND instance/config.yaml) — identical to what
89
+ # the harness writes at boot. This is what keeps the running collector's
90
+ # forwarding gate from diverging from the harness when OTEL is enabled via
91
+ # YAML rather than env.
92
+ env_file:
93
+ - path: ./instance/secrets/.env
94
+ required: false
95
+ volumes:
96
+ - ./instance:/app/instance:ro
97
+ - agent-data:/app/data
98
+ command:
99
+ ["node", "-e",
100
+ "const c=require('/app/dist/config.js').loadConfig();const m=require('/app/dist/sandbox/egress-firewall-config.js');const d=c.stateDir;m.writeEgressFirewallConfigs(d);m.writeOtelCollectorConfig(d,{active:c.otel.enabled&&c.otel.forwardToSandbox})"]
101
+ depends_on:
102
+ - agent # ensures the image has been built when init runs
103
+
104
+ coredns-strict:
105
+ image: coredns/coredns:1.11.3
106
+ restart: unless-stopped
107
+ command: ["-conf", "/app/data/proxy/Corefile.strict"]
108
+ volumes:
109
+ - agent-data:/app/data:ro
110
+ depends_on:
111
+ egress-init:
112
+ condition: service_completed_successfully
113
+ networks:
114
+ sandbox-egress:
115
+ ipv4_address: 172.30.0.10
116
+
117
+ coredns-open:
118
+ image: coredns/coredns:1.11.3
119
+ restart: unless-stopped
120
+ command: ["-conf", "/app/data/proxy/Corefile.open"]
121
+ volumes:
122
+ - agent-data:/app/data:ro
123
+ depends_on:
124
+ egress-init:
125
+ condition: service_completed_successfully
126
+ networks:
127
+ sandbox-egress:
128
+ ipv4_address: 172.30.0.11
129
+
130
+ nginx-egress-strict:
131
+ image: nginx:alpine
132
+ restart: unless-stopped
133
+ command:
134
+ ["nginx", "-c", "/app/data/proxy/nginx-strict.conf", "-g", "daemon off;"]
135
+ volumes:
136
+ - agent-data:/app/data:ro
137
+ depends_on:
138
+ egress-init:
139
+ condition: service_completed_successfully
140
+ # Deliberately NOT on `internal`. Splitting ingress (sandbox-egress)
141
+ # and egress (proxy-egress) onto separate networks that don't include
142
+ # harness services keeps compose-internal hostnames unresolvable to
143
+ # the firewall — so an `unrestricted_egress` phase can't ask nginx to
144
+ # tunnel to `agent:8644` or similar.
145
+ networks:
146
+ sandbox-egress:
147
+ ipv4_address: 172.30.0.20
148
+ proxy-egress: {}
149
+
150
+ nginx-egress-open:
151
+ image: nginx:alpine
152
+ restart: unless-stopped
153
+ command:
154
+ ["nginx", "-c", "/app/data/proxy/nginx-open.conf", "-g", "daemon off;"]
155
+ volumes:
156
+ - agent-data:/app/data:ro
157
+ depends_on:
158
+ egress-init:
159
+ condition: service_completed_successfully
160
+ networks:
161
+ sandbox-egress:
162
+ ipv4_address: 172.30.0.21
163
+ proxy-egress: {}
164
+
165
+ # In-network OTEL collector. Sandboxes export telemetry here (by IP, on
166
+ # sandbox-egress) and it re-exports to the configured backend over
167
+ # proxy-egress. This is why the strict SNI firewall doesn't need to know
168
+ # about collector hosts or non-443 ports: the sandbox only ever dials this
169
+ # fixed internal endpoint, and the backend hop runs from a trusted,
170
+ # harness-controlled service that can use any port/scheme/headers. Backend
171
+ # auth headers live in the generated config (mode 0600 on the host volume)
172
+ # and are never forwarded into a sandbox.
173
+ #
174
+ # Dual-homed like the nginx firewalls — sandbox-egress (ingress) +
175
+ # proxy-egress (outbound). Deliberately NOT on `internal`, so it can't
176
+ # bridge a sandbox into harness services.
177
+ otel-collector:
178
+ image: otel/opentelemetry-collector:0.115.0
179
+ restart: unless-stopped
180
+ # Runs as the image's default non-root UID (10001). The harness `lastlight`
181
+ # user is pinned to that same UID (Dockerfile `useradd -u 10001`), so the
182
+ # collector reads the mode-0600 config the harness writes as its owner —
183
+ # no root, no world-readable secret. The image tag and that pinned UID must
184
+ # move together if a future image changes its user.
185
+ command: ["--config=/app/data/proxy/otel-collector.yaml"]
186
+ volumes:
187
+ - agent-data:/app/data:ro
188
+ depends_on:
189
+ egress-init:
190
+ condition: service_completed_successfully
191
+ networks:
192
+ sandbox-egress:
193
+ ipv4_address: 172.30.0.30
194
+ proxy-egress: {}
195
+
28
196
  caddy:
29
197
  image: caddy:2-alpine
30
198
  restart: unless-stopped
@@ -35,15 +203,48 @@ services:
35
203
  - ./Caddyfile:/etc/caddy/Caddyfile:ro
36
204
  - caddy-data:/data
37
205
  - caddy-config:/config
38
- environment:
39
- - DOMAIN=${DOMAIN:-localhost}
206
+ # Caddyfile uses `{$DOMAIN:localhost}` (Caddy's own env substitution), so
207
+ # DOMAIN just needs to be in the container env. Read it from the overlay
208
+ # secrets file directly — no repo-root .env / ${DOMAIN} interpolation.
209
+ env_file:
210
+ - path: ./instance/secrets/.env
211
+ required: false
212
+ # Caddy's memory footprint is tiny (typically <50 MB even under load).
213
+ mem_limit: 256m
214
+ memswap_limit: 256m
40
215
  networks:
41
216
  - internal
42
217
 
43
218
  volumes:
44
- agent-data: # contains: lastlight.db, claude-home/, sandboxes/, logs/
219
+ agent-data: # contains: lastlight.db, agent-sessions/, sandboxes/, secrets/, logs/, proxy/
220
+ # Shared package-manager download cache (npm/pnpm/yarn), mounted at /cache in
221
+ # every dynamically-created sandbox container by src/sandbox/docker.ts so
222
+ # repeated installs reuse already-fetched tarballs (issue #107). Declared
223
+ # here so the stack manages its lifecycle; the agent service does not mount
224
+ # it. Override the name with LASTLIGHT_PKG_CACHE_VOLUME (defaults to the
225
+ # compose-prefixed `lastlight_pkg-cache`).
226
+ pkg-cache:
45
227
  caddy-data:
46
228
  caddy-config:
47
229
 
48
230
  networks:
49
231
  internal:
232
+ # Sandbox containers (created dynamically by src/sandbox/docker.ts) attach
233
+ # to this network only with `--dns 172.30.0.10` (or .11 for open mode).
234
+ # `internal: true` disables direct host egress, so the only path to the
235
+ # public internet is through the nginx firewalls, which receive sandbox
236
+ # traffic on this network. Static subnet so the coredns + nginx IPs
237
+ # baked into the generated configs are stable.
238
+ sandbox-egress:
239
+ internal: true
240
+ ipam:
241
+ config:
242
+ - subnet: 172.30.0.0/24
243
+ # Egress-only network for the nginx firewalls. Regular bridge (has a
244
+ # default route to the host's network, so nginx can reach the public
245
+ # internet). Critically: NO other compose services attach here. That way
246
+ # docker's embedded DNS on this network resolves only public names — the
247
+ # harness service names (`agent`, `caddy`) are not reachable from the
248
+ # firewall's perspective, so an unrestricted sandbox can't ask nginx to
249
+ # bridge into the compose-internal services.
250
+ proxy-egress:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lastlight",
3
- "version": "0.1.8",
3
+ "version": "0.2.0",
4
4
  "description": "GitHub repository maintenance agent — Agent SDK harness",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,12 +9,10 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
+ "config",
12
13
  "workflows",
13
14
  "skills",
14
15
  "agent-context",
15
- "mcp-github-app/src",
16
- "mcp-github-app/package.json",
17
- "mcp-github-app/tsconfig.json",
18
16
  "deploy",
19
17
  "sandbox.Dockerfile",
20
18
  "docker-compose.yml"
@@ -50,13 +48,27 @@
50
48
  "test:watch": "vitest"
51
49
  },
52
50
  "dependencies": {
53
- "@anthropic-ai/claude-agent-sdk": "^0.1.0",
54
- "@hono/node-server": "^1.0.0",
51
+ "@clack/prompts": "^1.2.0",
52
+ "@earendil-works/pi-ai": "^0.79.8",
53
+ "@hono/node-server": "^1.19.13",
54
+ "@opentelemetry/api": "^1.9.1",
55
+ "@opentelemetry/exporter-logs-otlp-http": "^0.219.0",
56
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.219.0",
57
+ "@opentelemetry/exporter-trace-otlp-http": "^0.219.0",
58
+ "@opentelemetry/resources": "^2.8.0",
59
+ "@opentelemetry/sdk-logs": "^0.219.0",
60
+ "@opentelemetry/sdk-metrics": "^2.8.0",
61
+ "@opentelemetry/sdk-node": "^0.219.0",
62
+ "@opentelemetry/semantic-conventions": "^1.41.1",
63
+ "@sinclair/typebox": "^0.34.49",
55
64
  "@slack/bolt": "^4.6.0",
65
+ "agentic-pi": "^0.2.7",
56
66
  "arctic": "^3.7.0",
57
67
  "better-sqlite3": "^11.0.0",
68
+ "chalk": "^5.6.2",
69
+ "cli-table3": "^0.6.5",
58
70
  "croner": "^9.0.0",
59
- "hono": "^4.0.0",
71
+ "hono": "^4.12.14",
60
72
  "octokit": "^5.0.5",
61
73
  "yaml": "^2.8.3",
62
74
  "zod": "^4.3.6"
@@ -1,36 +1,108 @@
1
- # Sandbox image for Last Light agent tasks.
1
+ # Sandbox image for Last Light agent tasks (LASTLIGHT_SANDBOX=docker
2
+ # fallback only — the default production sandbox is gondolin, invoked
3
+ # in-process by agentic-pi without docker).
4
+ #
2
5
  # Immutable assets baked at /app/. Entrypoint wires them into the workspace
3
6
  # after volumes are mounted, then drops to the runtime user.
4
7
  FROM node:20-slim
5
8
 
6
9
  RUN apt-get update && apt-get install -y --no-install-recommends \
7
10
  git ripgrep curl jq ca-certificates gettext-base gosu \
11
+ build-essential pkg-config python3 unzip \
8
12
  && rm -rf /var/lib/apt/lists/*
9
13
 
10
- # Create non-root agent user
11
- RUN useradd -m -s /bin/bash agent
14
+ # fnm + multiple Node versions so repos pinning a specific Node via .nvmrc /
15
+ # package.json#engines just work. System node from the base image stays at
16
+ # /usr/local/bin; fnm-managed versions are pre-installed under FNM_DIR and
17
+ # selected per-shell by the bashrc hook below.
18
+ ENV FNM_DIR=/usr/local/share/fnm
19
+ ENV PATH=$FNM_DIR/aliases/default/bin:$PATH
20
+ RUN curl -fsSL https://fnm.vercel.app/install \
21
+ | bash -s -- --install-dir "$FNM_DIR" --skip-shell \
22
+ && ln -s "$FNM_DIR/fnm" /usr/local/bin/fnm \
23
+ && fnm install 22 \
24
+ && fnm install 24 \
25
+ && fnm default 22 \
26
+ && chmod -R a+rX "$FNM_DIR" \
27
+ && mkdir -p "$FNM_DIR/multishells" \
28
+ && chmod 1777 "$FNM_DIR/multishells"
12
29
 
13
- # Install Claude Code CLI (binary goes to ~/.local/bin/)
14
- # Remove ~/.claude/ after installentrypoint recreates it at runtime.
15
- USER agent
16
- RUN curl -fsSL https://claude.ai/install.sh | bash && rm -rf /home/agent/.claude
17
- USER root
18
- ENV PATH="/home/agent/.local/bin:${PATH}"
30
+ # Source fnm in every bash invocation (interactive or not). BASH_ENV makes
31
+ # non-interactive `bash -c` read this file that's how opencode's bash tool
32
+ # inherits the right node version when it runs `npm ci` inside a repo with
33
+ # an .nvmrc pinning Node 24.
34
+ RUN printf '%s\n' \
35
+ 'export FNM_DIR=/usr/local/share/fnm' \
36
+ 'export PATH="$FNM_DIR:$PATH"' \
37
+ '# --shell bash is required: when sourced via BASH_ENV the parent process' \
38
+ '# is not a shell so fnm cannot auto-detect.' \
39
+ 'eval "$(fnm env --shell bash --use-on-cd --version-file-strategy=recursive)"' \
40
+ '# The cd hook fires only on cd. Tools often launch `bash -c "..."` with' \
41
+ '# cwd already set via the spawn options — no cd happens — so also' \
42
+ '# auto-switch on shell start when the cwd has a version file.' \
43
+ 'if [ -f "$PWD/.nvmrc" ] || [ -f "$PWD/.node-version" ]; then' \
44
+ ' fnm use --silent-if-unchanged 2>/dev/null \' \
45
+ ' || { fnm install 2>/dev/null && fnm use --silent-if-unchanged 2>/dev/null; } \' \
46
+ ' || true' \
47
+ 'fi' \
48
+ > /etc/bash.bashrc.fnm \
49
+ && printf '\n[ -r /etc/bash.bashrc.fnm ] && . /etc/bash.bashrc.fnm\n' >> /etc/bash.bashrc \
50
+ && ln -s /etc/bash.bashrc.fnm /etc/profile.d/fnm.sh
51
+ # Source the fnm file directly — Debian's /etc/bash.bashrc bails early on
52
+ # non-interactive shells (`[ -z "$PS1" ] && return`), so pointing BASH_ENV
53
+ # at it would skip our setup for `bash -c` invocations (which is the path
54
+ # the agent's bash tool uses).
55
+ ENV BASH_ENV=/etc/bash.bashrc.fnm
19
56
 
20
- # MCP server (baked at /app/)
21
- COPY mcp-github-app/package.json /app/mcp-github-app/package.json
22
- RUN cd /app/mcp-github-app && npm install --prefer-offline --no-audit && npm cache clean --force
23
- COPY mcp-github-app/ /app/mcp-github-app/
57
+ RUN apt-get update && apt-get install -y --no-install-recommends \
58
+ python3 python3-pip pipx \
59
+ && PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install semgrep \
60
+ && curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz \
61
+ | tar -xz -C /usr/local/bin gitleaks \
62
+ && apt-get purge -y python3-pip \
63
+ && rm -rf /var/lib/apt/lists/*
24
64
 
25
- # Skills (baked at /app/ entrypoint symlinks into ~/.claude/skills/)
26
- COPY skills/ /app/skills/
65
+ # Create non-root agent user, UID/GID pinned to 10001 to MATCH the harness
66
+ # `lastlight` user (see Dockerfile: `useradd -u 10001 lastlight`). The
67
+ # entrypoint chowns the bind-mounted workspace to `agent`; per-PR workspaces
68
+ # are persistent and reused across runs (issue #107), so the harness re-stages
69
+ # AGENTS.md + skills into them on the next run. If `agent` lands on the default
70
+ # uid (1001) those files become unwritable by the harness (10001) on reuse —
71
+ # EACCES, and the review silently runs with no skill/AGENTS.md. Sharing the uid
72
+ # keeps the reused workspace mutually writable by both the agent (in-container)
73
+ # and the harness (host). Also self-heals dirs left at 1001 by older images:
74
+ # the entrypoint's `chown -R agent:agent` re-owns them to 10001 on next reuse.
75
+ RUN groupadd -g 10001 agent && useradd -m -s /bin/bash -u 10001 -g 10001 agent
27
76
 
28
- # Agent context (baked at /app/ entrypoint cats into workspace/CLAUDE.md)
77
+ # Install agentic-pi globally so the harness can `docker exec ...
78
+ # agentic-pi run ...` against this container. The version + integrity
79
+ # come straight from lastlight's `package-lock.json` so `npm i agentic-pi@…`
80
+ # in the harness automatically flows into the sandbox image — no
81
+ # manual Dockerfile bumps. `npm install -g <tarball>` itself doesn't
82
+ # consult a lockfile, hence the explicit integrity verification.
83
+ COPY package-lock.json /tmp/lastlight-lock.json
84
+ RUN node -e "const p=require('/tmp/lastlight-lock.json').packages['node_modules/agentic-pi']; \
85
+ if(!p) throw new Error('agentic-pi missing from lockfile'); \
86
+ process.stdout.write(p.version + '\n' + p.integrity + '\n')" > /tmp/agentic-pi.pin \
87
+ && version=$(sed -n '1p' /tmp/agentic-pi.pin) \
88
+ && expected=$(sed -n '2p' /tmp/agentic-pi.pin) \
89
+ && echo "Installing agentic-pi@${version} (${expected})" \
90
+ && curl -fsSL "https://registry.npmjs.org/agentic-pi/-/agentic-pi-${version}.tgz" -o /tmp/agentic-pi.tgz \
91
+ && actual="sha512-$(node -e "const c=require('crypto'),f=require('fs');process.stdout.write(c.createHash('sha512').update(f.readFileSync('/tmp/agentic-pi.tgz')).digest('base64'))")" \
92
+ && if [ "$actual" != "$expected" ]; then \
93
+ echo "agentic-pi tarball integrity mismatch:" >&2; \
94
+ echo " expected: $expected" >&2; \
95
+ echo " actual: $actual" >&2; \
96
+ exit 1; \
97
+ fi \
98
+ && npm install -g --no-audit --no-fund /tmp/agentic-pi.tgz \
99
+ && rm /tmp/agentic-pi.tgz /tmp/agentic-pi.pin /tmp/lastlight-lock.json
100
+
101
+ # Agent context (baked at /app/ — entrypoint cats into workspace/AGENTS.md)
29
102
  COPY agent-context/ /app/agent-context/
30
103
 
31
- # Entrypoint + MCP config template
104
+ # Entrypoint
32
105
  COPY deploy/sandbox-entrypoint.sh /app/sandbox-entrypoint.sh
33
- COPY deploy/mcp-config.tmpl.json /app/mcp-config.tmpl.json
34
106
  RUN chmod +x /app/sandbox-entrypoint.sh
35
107
 
36
108
  # Own app dir for agent user
@@ -38,6 +110,13 @@ RUN chown -R agent:agent /app /home/agent
38
110
 
39
111
  WORKDIR /home/agent/workspace
40
112
 
113
+ # Image-level env so every `docker exec` (the entrypoint just runs once at
114
+ # container start) sees these. Exporting them in sandbox-entrypoint.sh only
115
+ # affects PID 1 — subsequent `docker exec agentic-pi run …` calls get a
116
+ # fresh environment and would otherwise miss these paths.
117
+ ENV LASTLIGHT_WORKSPACE=/home/agent/workspace
118
+ ENV LASTLIGHT_GIT_CREDENTIALS=/home/agent/.lastlight-git-credentials
119
+
41
120
  # Entrypoint runs as root, fixes permissions, then drops to agent via gosu
42
121
  ENTRYPOINT ["/app/sandbox-entrypoint.sh"]
43
122
  CMD ["sleep", "infinity"]