specrails-desktop 2.36.0 → 2.42.2

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 (510) hide show
  1. package/README.md +94 -247
  2. package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
  3. package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
  6. package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
  7. package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
  8. package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
  9. package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
  10. package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
  11. package/client/dist/assets/{DocsDialog-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
  12. package/client/dist/assets/{DocsPage-ziMlj6B6.js → DocsPage-24yWglNI.js} +1 -1
  13. package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
  14. package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
  15. package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
  16. package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
  17. package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
  18. package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
  19. package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
  20. package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
  21. package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
  22. package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
  23. package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
  24. package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
  25. package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
  26. package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
  27. package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
  28. package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
  29. package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
  30. package/client/dist/assets/agent-BdT0SB1D.js +1 -0
  31. package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
  32. package/client/dist/assets/agent-C4ibdFk9.js +1 -0
  33. package/client/dist/assets/agent-CSFY22Zo.js +1 -0
  34. package/client/dist/assets/agent-DAnqxUn8.js +1 -0
  35. package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
  36. package/client/dist/assets/agent-T54WuR_Y.js +1 -0
  37. package/client/dist/assets/agent-i75WDZOA.js +1 -0
  38. package/client/dist/assets/browser-05vRDg_y.js +1 -0
  39. package/client/dist/assets/browser-Bulb-cB1.js +1 -0
  40. package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
  41. package/client/dist/assets/browser-CG0PE1j_.js +1 -0
  42. package/client/dist/assets/browser-CMzaqAjo.js +1 -0
  43. package/client/dist/assets/browser-DMajYdNX.js +1 -0
  44. package/client/dist/assets/browser-DxcOHrdP.js +1 -0
  45. package/client/dist/assets/browser-ObE7xVo3.js +1 -0
  46. package/client/dist/assets/builder-765NeYTp.js +1 -0
  47. package/client/dist/assets/builder-8MG3V6ea.js +1 -0
  48. package/client/dist/assets/builder-B-1UBs5K.js +1 -0
  49. package/client/dist/assets/builder-BTiGnPT0.js +1 -0
  50. package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
  51. package/client/dist/assets/builder-DKFMELBl.js +1 -0
  52. package/client/dist/assets/builder-OcmKrC0h.js +1 -0
  53. package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
  54. package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
  55. package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
  56. package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
  57. package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
  58. package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
  59. package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
  60. package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
  61. package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
  62. package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
  63. package/client/dist/assets/commands-0XjAftla.js +1 -0
  64. package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
  65. package/client/dist/assets/commands-BBwowgiE.js +1 -0
  66. package/client/dist/assets/commands-BacjIZLN.js +1 -0
  67. package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
  68. package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
  69. package/client/dist/assets/commands-DlbFzgrp.js +1 -0
  70. package/client/dist/assets/commands-LjPSsi5F.js +1 -0
  71. package/client/dist/assets/common-B5AZNYMH.js +1 -0
  72. package/client/dist/assets/common-BFZ0c_vN.js +1 -0
  73. package/client/dist/assets/common-BrlwEnL7.js +1 -0
  74. package/client/dist/assets/common-BttQx3z4.js +1 -0
  75. package/client/dist/assets/common-CM65k_-e.js +1 -0
  76. package/client/dist/assets/common-D4mvFFlu.js +1 -0
  77. package/client/dist/assets/common-TZ9Ny96S.js +1 -0
  78. package/client/dist/assets/common-nFpSF4GC.js +1 -0
  79. package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
  80. package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
  81. package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
  82. package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
  83. package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
  84. package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
  85. package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
  86. package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
  87. package/client/dist/assets/dialog-SRw01_QU.js +45 -0
  88. package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
  89. package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
  90. package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
  91. package/client/dist/assets/index-DwAmL7m5.css +2 -0
  92. package/client/dist/assets/index-EzrkUb8B.js +76 -0
  93. package/client/dist/assets/{jira-api-B1-MUehg.js → jira-api-CSCqGr-Q.js} +1 -1
  94. package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
  95. package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
  96. package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
  97. package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
  98. package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
  99. package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
  100. package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
  101. package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
  102. package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
  103. package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
  104. package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
  105. package/client/dist/assets/mcp-CCpSelOh.js +1 -0
  106. package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
  107. package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
  108. package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
  109. package/client/dist/assets/mcp-DUVQamDz.js +1 -0
  110. package/client/dist/assets/narration-B-ynWTpn.js +1 -0
  111. package/client/dist/assets/narration-Bj_laZwg.js +1 -0
  112. package/client/dist/assets/narration-BxHpmMv_.js +1 -0
  113. package/client/dist/assets/narration-C1Px0-0j.js +1 -0
  114. package/client/dist/assets/narration-CAXhGbzM.js +1 -0
  115. package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
  116. package/client/dist/assets/narration-XISXwqZd.js +1 -0
  117. package/client/dist/assets/narration-kwzfglQo.js +1 -0
  118. package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
  119. package/client/dist/assets/nav-B34QkESm.js +1 -0
  120. package/client/dist/assets/nav-Bap_UXAq.js +1 -0
  121. package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
  122. package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
  123. package/client/dist/assets/nav-CygC_WN-.js +1 -0
  124. package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
  125. package/client/dist/assets/nav-m00uxYUG.js +1 -0
  126. package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
  127. package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
  128. package/client/dist/assets/packet-C3Ievb9R.js +1 -0
  129. package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
  130. package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
  131. package/client/dist/assets/packet-DUTGX52M.js +1 -0
  132. package/client/dist/assets/packet-DhsKtUPB.js +1 -0
  133. package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
  134. package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
  135. package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
  136. package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
  137. package/client/dist/assets/settings-BJB9RKJj.js +1 -0
  138. package/client/dist/assets/settings-CHwQR2rB.js +1 -0
  139. package/client/dist/assets/settings-CrMi3kSi.js +1 -0
  140. package/client/dist/assets/settings-DKyhDGD1.js +1 -0
  141. package/client/dist/assets/settings-Deupji9c.js +1 -0
  142. package/client/dist/assets/settings-DqRMmwAM.js +1 -0
  143. package/client/dist/assets/settings-J4rj6vo-.js +1 -0
  144. package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
  145. package/client/dist/assets/setup-Ba11l2JA.js +1 -0
  146. package/client/dist/assets/setup-BzW4kMok.js +1 -0
  147. package/client/dist/assets/setup-COzwYhkJ.js +1 -0
  148. package/client/dist/assets/setup-CRVULhtF.js +1 -0
  149. package/client/dist/assets/setup-DgvlxwpE.js +1 -0
  150. package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
  151. package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
  152. package/client/dist/assets/spending-BqbdPm_4.js +1 -0
  153. package/client/dist/assets/square-Ci-PFcFe.js +1 -0
  154. package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
  155. package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
  156. package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
  157. package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
  158. package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
  159. package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
  160. package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
  161. package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
  162. package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
  163. package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
  164. package/client/dist/index.html +34 -35
  165. package/docs/README.md +3 -0
  166. package/docs/agent-live-steering.md +57 -0
  167. package/docs/ci-cd.md +57 -0
  168. package/docs/code-explorer.md +48 -0
  169. package/docs/codex.md +12 -1
  170. package/docs/features/detachable-mission-windows.md +59 -0
  171. package/docs/guide/de/integrations/5-mcp-server.md +13 -9
  172. package/docs/guide/de/integrations/6-agent-chat.md +1 -0
  173. package/docs/guide/en/integrations/5-mcp-server.md +13 -9
  174. package/docs/guide/en/integrations/6-agent-chat.md +1 -0
  175. package/docs/guide/es/integrations/5-mcp-server.md +13 -9
  176. package/docs/guide/es/integrations/6-agent-chat.md +1 -0
  177. package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
  178. package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
  179. package/docs/guide/it/integrations/5-mcp-server.md +13 -9
  180. package/docs/guide/it/integrations/6-agent-chat.md +1 -0
  181. package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
  182. package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
  183. package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
  184. package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
  185. package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
  186. package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
  187. package/docs/internals/README.md +1 -0
  188. package/docs/internals/browser-capture-performance.md +55 -3
  189. package/docs/internals/browser-login-popups.md +45 -0
  190. package/docs/internals/browser-native-retina-audit.md +39 -0
  191. package/docs/internals/ci-cd-audit.md +39 -0
  192. package/docs/internals/core-runtime-updates.md +68 -0
  193. package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
  194. package/docs/internals/implementation-reliability-audit.md +93 -0
  195. package/docs/internals/interactive-jobs.md +110 -6
  196. package/docs/internals/mcp-mission-audit.md +76 -0
  197. package/docs/internals/project-builder.md +404 -42
  198. package/docs/internals/safe-pr-review-flow.md +52 -10
  199. package/docs/internals/startup-recovery-audit.md +58 -0
  200. package/docs/mcp.md +74 -41
  201. package/docs/mission-processes.md +59 -0
  202. package/docs/multi-repo-projects.md +51 -0
  203. package/docs/platforms/windows-parity.md +70 -0
  204. package/docs/platforms/windows.md +13 -11
  205. package/docs/running-pipelines.md +14 -0
  206. package/mcp-bridge/dist/specrails-mcp.js +7186 -0
  207. package/package.json +9 -4
  208. package/server/dist/agent-chat-manager.js +537 -68
  209. package/server/dist/agent-chat-router.js +87 -6
  210. package/server/dist/agent-context-resolver.js +129 -25
  211. package/server/dist/agent-defaults.js +19 -11
  212. package/server/dist/agent-input-store.js +206 -0
  213. package/server/dist/agent-mcp-config.js +54 -15
  214. package/server/dist/agent-operator-prompt.js +255 -65
  215. package/server/dist/agent-spec-framing.js +202 -0
  216. package/server/dist/agent-steering.js +218 -0
  217. package/server/dist/agent-store.js +153 -0
  218. package/server/dist/api-not-found.js +8 -0
  219. package/server/dist/background-process-control.js +164 -0
  220. package/server/dist/background-process-service.js +61 -0
  221. package/server/dist/background-process-store.js +242 -0
  222. package/server/dist/background-windows-bootstrap.js +238 -0
  223. package/server/dist/blueprint-chat-manager.js +476 -62
  224. package/server/dist/blueprint-commit.js +21 -1
  225. package/server/dist/blueprint-draft-parser.js +122 -17
  226. package/server/dist/blueprint-generation.js +238 -0
  227. package/server/dist/blueprint-operator-prompt.js +91 -63
  228. package/server/dist/blueprint-router.js +55 -3
  229. package/server/dist/blueprint-spec-fixtures.js +85 -0
  230. package/server/dist/blueprint-spec-quality.js +57 -16
  231. package/server/dist/blueprint-store.js +92 -1
  232. package/server/dist/browser-capture-manager.js +86 -30
  233. package/server/dist/browser-playwright.js +168 -45
  234. package/server/dist/browser-viewport.js +26 -0
  235. package/server/dist/chat-manager.js +23 -15
  236. package/server/dist/chromium-archive.cjs +83 -0
  237. package/server/dist/chromium-resolver.js +72 -64
  238. package/server/dist/code-activity.js +145 -0
  239. package/server/dist/code-explorer-router.js +714 -114
  240. package/server/dist/core-compat.js +28 -1
  241. package/server/dist/core-execution.js +111 -0
  242. package/server/dist/core-package.js +4 -16
  243. package/server/dist/core-runtime.js +202 -0
  244. package/server/dist/core-update-manager.js +161 -60
  245. package/server/dist/core-update-state.js +29 -0
  246. package/server/dist/db.js +66 -35
  247. package/server/dist/delivery-evidence.js +142 -39
  248. package/server/dist/desktop-db.js +239 -43
  249. package/server/dist/desktop-router.js +109 -9
  250. package/server/dist/dev-ports.js +8 -3
  251. package/server/dist/file-provenance.js +34 -22
  252. package/server/dist/file-story-manager.js +62 -64
  253. package/server/dist/file-story.js +118 -35
  254. package/server/dist/file-summary-generator.js +46 -5
  255. package/server/dist/file-summary-manager.js +355 -106
  256. package/server/dist/framework-manager.js +98 -110
  257. package/server/dist/framework-reseed.js +76 -10
  258. package/server/dist/git-diagnostics.js +2 -2
  259. package/server/dist/headroom-manager.js +38 -3
  260. package/server/dist/host-control.js +52 -0
  261. package/server/dist/index.js +43 -2
  262. package/server/dist/interactive-job-session.js +149 -16
  263. package/server/dist/internal-api.js +48 -0
  264. package/server/dist/jira/jira-materializer.js +1 -0
  265. package/server/dist/json-tolerant.js +174 -0
  266. package/server/dist/legacy-migration.js +4 -2
  267. package/server/dist/loop-command-catalog.js +83 -21
  268. package/server/dist/loop-constants.js +6 -1
  269. package/server/dist/loop-decider.js +11 -1
  270. package/server/dist/loop-executors.js +194 -45
  271. package/server/dist/loop-factory.js +11 -4
  272. package/server/dist/loop-graph.js +90 -4
  273. package/server/dist/loop-run-manager.js +301 -69
  274. package/server/dist/loop-shell-invocation.js +21 -0
  275. package/server/dist/loop-step-idle.js +55 -0
  276. package/server/dist/loop-templates.js +31 -7
  277. package/server/dist/mcp/agent-capability.js +37 -4
  278. package/server/dist/mcp/guide.js +149 -36
  279. package/server/dist/mcp/mcp-admin-router.js +20 -4
  280. package/server/dist/mcp/mcp-server.js +89 -43
  281. package/server/dist/mcp/mcp-tiers.js +6 -5
  282. package/server/dist/mcp/mcp-token.js +3 -1
  283. package/server/dist/mcp/resources.js +5 -4
  284. package/server/dist/mcp/tools/catalog.js +4 -0
  285. package/server/dist/mcp/tools/code.js +125 -14
  286. package/server/dist/mcp/tools/context.js +153 -0
  287. package/server/dist/mcp/tools/git.js +15 -5
  288. package/server/dist/mcp/tools/jobs.js +84 -71
  289. package/server/dist/mcp/tools/loops.js +17 -6
  290. package/server/dist/mcp/tools/meta.js +74 -27
  291. package/server/dist/mcp/tools/mission.js +29 -0
  292. package/server/dist/mcp/tools/projects.js +22 -6
  293. package/server/dist/mcp/tools/rails.js +48 -11
  294. package/server/dist/mcp/tools/specs.js +86 -14
  295. package/server/dist/mcp/tools/support.js +1 -1
  296. package/server/dist/mcp/tools/types.js +117 -43
  297. package/server/dist/mcp/tools/watch.js +144 -51
  298. package/server/dist/milestone-chain-store.js +164 -0
  299. package/server/dist/milestone-chain.js +487 -0
  300. package/server/dist/milestone-progress.js +348 -0
  301. package/server/dist/mobile/mobile-devices.js +10 -7
  302. package/server/dist/mobile/mobile-gateway.js +2 -0
  303. package/server/dist/mobile/mobile-missions.js +214 -0
  304. package/server/dist/mobile/mobile-redact.js +17 -3
  305. package/server/dist/mobile/mobile-router.js +46 -4
  306. package/server/dist/mobile/mobile-ws.js +49 -4
  307. package/server/dist/multi-repo-bases.js +56 -0
  308. package/server/dist/multi-repo-checkout.js +101 -0
  309. package/server/dist/multi-repo-delivery.js +100 -0
  310. package/server/dist/multi-repo-execution-store.js +65 -0
  311. package/server/dist/multi-repo-execution.js +453 -0
  312. package/server/dist/offline-assemble.js +10 -8
  313. package/server/dist/openspec-runtime-plugin-commands.json +18 -0
  314. package/server/dist/openspec-runtime-plugin.js +120 -0
  315. package/server/dist/plugins/codex-spawn.js +49 -0
  316. package/server/dist/plugins/serena/manifest.js +2 -2
  317. package/server/dist/plugins/serena/templates/instructions.md +16 -0
  318. package/server/dist/pr-publisher.js +1 -1
  319. package/server/dist/profiles-router.js +3 -18
  320. package/server/dist/project-code-discovery.js +142 -0
  321. package/server/dist/project-git.js +19 -21
  322. package/server/dist/project-registry.js +272 -20
  323. package/server/dist/project-repositories.js +157 -0
  324. package/server/dist/project-repository-provenance.js +31 -0
  325. package/server/dist/project-router-background-processes.js +105 -0
  326. package/server/dist/project-router-git.js +75 -16
  327. package/server/dist/project-router-jobs.js +2 -126
  328. package/server/dist/project-router-loop-runs.js +72 -2
  329. package/server/dist/project-router-tickets.js +53 -8
  330. package/server/dist/project-router.js +239 -38
  331. package/server/dist/provider-detection.js +18 -6
  332. package/server/dist/provider-limit.js +64 -0
  333. package/server/dist/providers/claude-adapter.js +26 -1
  334. package/server/dist/providers/claude-background-tasks.js +18 -0
  335. package/server/dist/providers/claude-live-session.js +383 -0
  336. package/server/dist/providers/codex-adapter.js +19 -0
  337. package/server/dist/providers/codex-live-session.js +545 -0
  338. package/server/dist/providers/gemini-adapter.js +1 -0
  339. package/server/dist/providers/live-session-types.js +12 -0
  340. package/server/dist/providers/live-session.js +13 -0
  341. package/server/dist/providers/terminal-result.js +24 -0
  342. package/server/dist/queue-manager.js +2 -0
  343. package/server/dist/rail-isolated-launch.js +141 -16
  344. package/server/dist/rail-pr-decision.js +364 -99
  345. package/server/dist/rail-pr-store.js +24 -10
  346. package/server/dist/rail-worktree-release.js +34 -2
  347. package/server/dist/rail-worktrees-store.js +4 -4
  348. package/server/dist/rails-router.js +249 -55
  349. package/server/dist/repo-lock.js +92 -17
  350. package/server/dist/review-packet.js +23 -13
  351. package/server/dist/schemas/file-summary.v1.json +59 -59
  352. package/server/dist/schemas/profile.v1.json +137 -137
  353. package/server/dist/setup-manager.js +52 -8
  354. package/server/dist/shell-integration/bash-shim.bash +45 -0
  355. package/server/dist/shell-integration/fish-shim.fish +36 -0
  356. package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
  357. package/server/dist/shell-integration/zsh-shim.zsh +47 -0
  358. package/server/dist/smash-runner.js +1 -0
  359. package/server/dist/spawn-lifecycle.js +44 -27
  360. package/server/dist/spec-contract-prompt.js +143 -0
  361. package/server/dist/spec-draft-parser.js +6 -1
  362. package/server/dist/spec-models.js +1 -0
  363. package/server/dist/stuck-run-detector.js +1 -0
  364. package/server/dist/terminal-manager.js +21 -1
  365. package/server/dist/terminal-shell-integration.js +4 -1
  366. package/server/dist/transient-children.js +619 -230
  367. package/server/dist/util/cli-prompt.js +78 -5
  368. package/server/dist/util/sqlite-migrations.js +20 -0
  369. package/server/dist/util/win-spawn.js +18 -3
  370. package/server/dist/util/windows-provider-shell.js +29 -0
  371. package/server/dist/verification-sentinel.js +14 -0
  372. package/server/dist/vitest-setup.js +2 -0
  373. package/server/dist/windows-job-supervisor.js +133 -0
  374. package/server/dist/workspace-manager.js +9 -30
  375. package/server/dist/worktree-manager.js +17 -2
  376. package/server/dist/worktree-overlay.js +3 -1
  377. package/client/dist/assets/ActivityFeedPage-tAQcWs1R.js +0 -1
  378. package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
  379. package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
  380. package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
  381. package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
  382. package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
  383. package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
  384. package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
  385. package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
  386. package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
  387. package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
  388. package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
  389. package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
  390. package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
  391. package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
  392. package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
  393. package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
  394. package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
  395. package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
  396. package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
  397. package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
  398. package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
  399. package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
  400. package/client/dist/assets/Trans-Bsz9_gyq.js +0 -1
  401. package/client/dist/assets/agent-BQgKv_2F.js +0 -1
  402. package/client/dist/assets/agent-C1NRDJqL.js +0 -1
  403. package/client/dist/assets/agent-CLNleiy6.js +0 -1
  404. package/client/dist/assets/agent-CmceArkQ.js +0 -1
  405. package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
  406. package/client/dist/assets/agent-DB1JeE64.js +0 -1
  407. package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
  408. package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
  409. package/client/dist/assets/auth-D_5bQv5W.js +0 -1
  410. package/client/dist/assets/ban-E4EaZe3a.js +0 -1
  411. package/client/dist/assets/bot-DzuuuC5R.js +0 -1
  412. package/client/dist/assets/browser-BgqHDMXP.js +0 -1
  413. package/client/dist/assets/browser-CsKJtrpv.js +0 -1
  414. package/client/dist/assets/browser-Cu58EpZU.js +0 -1
  415. package/client/dist/assets/browser-CvXOJm-j.js +0 -1
  416. package/client/dist/assets/browser-DOrFh1c7.js +0 -1
  417. package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
  418. package/client/dist/assets/browser-LPgxvJu2.js +0 -1
  419. package/client/dist/assets/browser-eL-3MIEI.js +0 -1
  420. package/client/dist/assets/builder-0IWtosUm.js +0 -1
  421. package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
  422. package/client/dist/assets/builder-BnqaaBkT.js +0 -1
  423. package/client/dist/assets/builder-BxxfGgAO.js +0 -1
  424. package/client/dist/assets/builder-C5IIgWsq.js +0 -1
  425. package/client/dist/assets/builder-CHuahItu.js +0 -1
  426. package/client/dist/assets/builder-CjWosXL_.js +0 -1
  427. package/client/dist/assets/builder-CpCIOoKj.js +0 -1
  428. package/client/dist/assets/code-CHPkOkDS.js +0 -1
  429. package/client/dist/assets/commands-6tYQih5m.js +0 -1
  430. package/client/dist/assets/commands-BK2QinXN.js +0 -1
  431. package/client/dist/assets/commands-C2Nca76h.js +0 -1
  432. package/client/dist/assets/commands-CKdLihNX.js +0 -1
  433. package/client/dist/assets/commands-CYIBTAlc.js +0 -1
  434. package/client/dist/assets/commands-CjUtYJIM.js +0 -1
  435. package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
  436. package/client/dist/assets/commands-D_ZHYQug.js +0 -1
  437. package/client/dist/assets/common-6MLbD-jM.js +0 -1
  438. package/client/dist/assets/common-BKGEu4rm.js +0 -1
  439. package/client/dist/assets/common-BNbnv8cE.js +0 -1
  440. package/client/dist/assets/common-BqGWtDbt.js +0 -1
  441. package/client/dist/assets/common-DBtTzeYm.js +0 -1
  442. package/client/dist/assets/common-DEW3Nr8n.js +0 -1
  443. package/client/dist/assets/common-DHsv9N5d.js +0 -1
  444. package/client/dist/assets/common-mkrEcW-Y.js +0 -1
  445. package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
  446. package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
  447. package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
  448. package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
  449. package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
  450. package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
  451. package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
  452. package/client/dist/assets/dialog-CdWCb02j.js +0 -45
  453. package/client/dist/assets/dist-bVqfYmby.js +0 -1
  454. package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
  455. package/client/dist/assets/formatDistanceToNow-DQCvMgUc.js +0 -1
  456. package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
  457. package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
  458. package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
  459. package/client/dist/assets/index-DIhMEAVw.js +0 -66
  460. package/client/dist/assets/index-DwygcU7s.css +0 -2
  461. package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
  462. package/client/dist/assets/mcp-C2AojZZN.js +0 -1
  463. package/client/dist/assets/mcp-D8BT78_L.js +0 -1
  464. package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
  465. package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
  466. package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
  467. package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
  468. package/client/dist/assets/mcp-mKFpTSRb.js +0 -1
  469. package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
  470. package/client/dist/assets/narration-BGuiEDj3.js +0 -1
  471. package/client/dist/assets/narration-BuSoqRmv.js +0 -1
  472. package/client/dist/assets/narration-CB9t29fy.js +0 -1
  473. package/client/dist/assets/narration-CU3x8FHD.js +0 -1
  474. package/client/dist/assets/narration-CWeXhfP5.js +0 -1
  475. package/client/dist/assets/narration-DRaDnhT5.js +0 -1
  476. package/client/dist/assets/narration-DtEbQgCw.js +0 -1
  477. package/client/dist/assets/nav-ApJVg3xu.js +0 -1
  478. package/client/dist/assets/nav-BKNky1Bg.js +0 -1
  479. package/client/dist/assets/nav-C6vWWKYx.js +0 -1
  480. package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
  481. package/client/dist/assets/nav-CsmFBIDf.js +0 -1
  482. package/client/dist/assets/nav-DjkmQJjd.js +0 -1
  483. package/client/dist/assets/packet-B1IxaL5f.js +0 -1
  484. package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
  485. package/client/dist/assets/packet-By8fqYxW.js +0 -1
  486. package/client/dist/assets/packet-CZdvA44E.js +0 -1
  487. package/client/dist/assets/packet-CrjHRQCP.js +0 -1
  488. package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
  489. package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
  490. package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
  491. package/client/dist/assets/play-DNlrawbS.js +0 -1
  492. package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
  493. package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
  494. package/client/dist/assets/settings-C1-7cXzC.js +0 -1
  495. package/client/dist/assets/settings-CAFaT38b.js +0 -1
  496. package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
  497. package/client/dist/assets/settings-DHh7_IF5.js +0 -1
  498. package/client/dist/assets/settings-DQXAmdcv.js +0 -1
  499. package/client/dist/assets/settings-guzlhpup.js +0 -1
  500. package/client/dist/assets/settings-sZfTPMEH.js +0 -1
  501. package/client/dist/assets/setup-3jWaoVUO.js +0 -1
  502. package/client/dist/assets/setup-BjgRneb2.js +0 -1
  503. package/client/dist/assets/setup-BzFIjv-f.js +0 -1
  504. package/client/dist/assets/setup-C4VfQsxE.js +0 -1
  505. package/client/dist/assets/setup-DfUkpDbN.js +0 -1
  506. package/client/dist/assets/setup-ECe90Pmu.js +0 -1
  507. package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
  508. package/client/dist/assets/setup-jwfLMiM2.js +0 -1
  509. package/client/dist/assets/spending-EC9xqio-.js +0 -1
  510. package/client/dist/assets/useDesktop-BQVt8lFd.js +0 -1
@@ -26,7 +26,7 @@ const ai_invocations_1 = require("./ai-invocations");
26
26
  const crypto_1 = require("crypto");
27
27
  const artifact_registry_1 = require("./artifact-registry");
28
28
  const install_config_path_1 = require("./install-config-path");
29
- const bundled_core_1 = require("./bundled-core");
29
+ const core_runtime_1 = require("./core-runtime");
30
30
  const path_resolver_1 = require("./path-resolver");
31
31
  const offline_assemble_1 = require("./offline-assemble");
32
32
  const framework_manager_1 = require("./framework-manager");
@@ -627,9 +627,10 @@ async function validateCoreContract() {
627
627
  return;
628
628
  }
629
629
  if (contract.checkpoints) {
630
- const missingCheckpoints = contract.checkpoints.filter((c) => !exports.CHECKPOINTS.some((cp) => cp.key === c));
631
- const extraCheckpoints = exports.CHECKPOINTS
632
- .filter((cp) => !contract.checkpoints.includes(cp.key))
630
+ const keys = Array.isArray(contract.checkpoints) ? contract.checkpoints : Object.keys(contract.checkpoints);
631
+ const missingCheckpoints = keys.filter((c) => !exports.CHECKPOINTS.some((cp) => cp.key === c));
632
+ const extraCheckpoints = Number(contract.schemaVersion?.split('.')[0] ?? 0) >= 4 ? [] : exports.CHECKPOINTS
633
+ .filter((cp) => !keys.includes(cp.key))
633
634
  .map((cp) => cp.key);
634
635
  if (missingCheckpoints.length > 0 || extraCheckpoints.length > 0) {
635
636
  console.warn('[Specrails] ⚠️ specrails-core contract checkpoint mismatch:');
@@ -771,7 +772,13 @@ class SetupManager {
771
772
  providers: list,
772
773
  continueOnError: true,
773
774
  onProviderStart: (p) => emit(p, 'running'),
774
- onProviderResult: (r) => emit(r.provider, r.ok ? 'done' : 'failed', r.error),
775
+ onProviderResult: (r) => {
776
+ // The WS event carries the reason to the app; the server log must too,
777
+ // or a packaged-app failure (installer smoke, support bundle) is blind.
778
+ if (!r.ok)
779
+ console.warn(`[SetupManager] silent assemble: ${r.provider} failed for ${projectId}: ${r.error ?? 'unknown error'}`);
780
+ emit(r.provider, r.ok ? 'done' : 'failed', r.error);
781
+ },
775
782
  })
776
783
  .then((results) => {
777
784
  const failed = results.filter((r) => !r.ok).map((r) => r.provider);
@@ -874,7 +881,12 @@ class SetupManager {
874
881
  // assemble by symlink; openspec init is the only remaining network step) —
875
882
  // NO `npx specrails-core` round-trip. When NO bundled core is present we fall
876
883
  // through to the legacy npx probe + spawn, byte-identical to today.
877
- const useBundledCore = (0, bundled_core_1.getBundledCoreCli)() !== null;
884
+ const selectedCore = (0, core_runtime_1.getCoreRuntimeStatus)();
885
+ if (selectedCore.error) {
886
+ this._broadcast({ type: 'setup_error', projectId, error: selectedCore.error });
887
+ return;
888
+ }
889
+ const useBundledCore = selectedCore.runtime !== null;
878
890
  if (useBundledCore) {
879
891
  // Materialize the framework for the selected provider (idempotent). The
880
892
  // subsequent bundled `init` finds it already present (ensureFramework
@@ -886,7 +898,7 @@ class SetupManager {
886
898
  const fm = new framework_manager_1.FrameworkManager({
887
899
  broadcast: (msg) => this._broadcast(msg),
888
900
  });
889
- const mat = fm.materialize((0, bundled_core_1.getBundledCoreVersion)() ?? undefined, [provider]);
901
+ const mat = fm.materialize(undefined, [provider]);
890
902
  if (mat.ran && mat.errors.length > 0) {
891
903
  console.warn(`[SetupManager] framework materialize had errors: ${mat.errors.map((e) => `${e.provider}: ${e.message}`).join('; ')}`);
892
904
  }
@@ -935,7 +947,7 @@ class SetupManager {
935
947
  // entry realpath) is diagnosable without server-console access.
936
948
  const diagHeader = [];
937
949
  if (useBundledCore) {
938
- diagHeader.push(`[diag] node=${(0, path_resolver_1.resolveBundledNodeExe)() ?? process.execPath}`, `[diag] cli=${(0, bundled_core_1.getBundledCoreCli)() ?? '<none>'}`, `[diag] cwd=${projectPath}`, `[diag] args=${initArgs.join(' ')}`, `[diag] SPECRAILS_BUNDLED_RUNTIMES_PATH=${process.env.SPECRAILS_BUNDLED_RUNTIMES_PATH ?? '<unset>'}`, `[diag] SPECRAILS_BUNDLED_CORE_PATH=${process.env.SPECRAILS_BUNDLED_CORE_PATH ?? '<unset>'}`);
950
+ diagHeader.push(`[diag] node=${(0, path_resolver_1.resolveBundledNodeExe)() ?? process.execPath}`, `[diag] cli=${selectedCore.runtime?.cli ?? '<none>'}`, `[diag] cwd=${projectPath}`, `[diag] args=${initArgs.join(' ')}`, `[diag] SPECRAILS_BUNDLED_RUNTIMES_PATH=${process.env.SPECRAILS_BUNDLED_RUNTIMES_PATH ?? '<unset>'}`, `[diag] SPECRAILS_BUNDLED_CORE_PATH=${process.env.SPECRAILS_BUNDLED_CORE_PATH ?? '<unset>'}`);
939
951
  }
940
952
  // Bundled core (offline, node <cli> init) when available, else legacy npx.
941
953
  const child = useBundledCore
@@ -1041,8 +1053,38 @@ class SetupManager {
1041
1053
  }
1042
1054
  });
1043
1055
  }
1056
+ _completeDeterministicSetup(projectId, projectPath, provider) {
1057
+ const root = this._artifactRoot(projectId, projectPath);
1058
+ let installedVersion = null;
1059
+ try {
1060
+ const marker = (0, path_1.join)(root, '.specrails', 'specrails-version');
1061
+ if ((0, fs_1.existsSync)(marker))
1062
+ installedVersion = (0, fs_1.readFileSync)(marker, 'utf8').trim();
1063
+ }
1064
+ catch { /* not installed */ }
1065
+ const runtime = (0, core_runtime_1.getCoreRuntimeStatus)().runtime;
1066
+ if (!(Number((installedVersion ?? runtime?.version ?? '0').split('.')[0]) >= 5))
1067
+ return false;
1068
+ const validation = validateInstalledCore(root);
1069
+ const selectedProvider = provider ?? this._projectProviders.get(projectId) ?? 'claude';
1070
+ const summary = computeSummary(root, 'quick', selectedProvider);
1071
+ if (!validation.ok || !installedVersion || summary.agents < 3 || summary.specrailsCommands < 1) {
1072
+ this._broadcast({ type: 'setup_error', projectId, error: 'Core 5 uses deterministic installation. Run project installation to repair its framework; enrichment is no longer supported.' });
1073
+ return true;
1074
+ }
1075
+ if (provider)
1076
+ this._projectProviders.set(projectId, provider);
1077
+ this._projectTiers.set(projectId, 'quick');
1078
+ this._initCheckpoints(projectId);
1079
+ this._syncFilesystemCheckpoints(projectId, projectPath);
1080
+ this._broadcast({ type: 'setup_complete', projectId, summary });
1081
+ this._onSetupDone?.(projectId);
1082
+ return true;
1083
+ }
1044
1084
  // ─── Enrich: claude -p "/specrails:enrich --from-config" ────────────────────
1045
1085
  startEnrich(projectId, projectPath, provider, projectName) {
1086
+ if (this._completeDeterministicSetup(projectId, projectPath, provider))
1087
+ return;
1046
1088
  this._abortedProjects.delete(projectId); // fresh run — clear any prior abort flag
1047
1089
  if (this._setupProcesses.has(projectId)) {
1048
1090
  console.warn(`[SetupManager] enrich already running for ${projectId}`);
@@ -1092,6 +1134,8 @@ class SetupManager {
1092
1134
  return this.startEnrich(projectId, projectPath, provider);
1093
1135
  }
1094
1136
  resumeEnrich(projectId, projectPath, sessionId, userMessage, provider) {
1137
+ if (this._completeDeterministicSetup(projectId, projectPath, provider))
1138
+ return;
1095
1139
  if (this._setupProcesses.has(projectId)) {
1096
1140
  console.warn(`[SetupManager] enrich already running for ${projectId}`);
1097
1141
  return;
@@ -0,0 +1,45 @@
1
+ # Specrails — bash shell-integration shim
2
+ # Loaded via --rcfile when the user spawns a terminal in the app.
3
+ # Sources the user's real ~/.bashrc, then layers OSC 133 / OSC 1337 hooks on top.
4
+
5
+ export SPECRAILS_SHELL_INTEGRATION_LOADED=1
6
+
7
+ if [[ -r "$HOME/.bashrc" ]]; then
8
+ # shellcheck disable=SC1091
9
+ source "$HOME/.bashrc"
10
+ fi
11
+
12
+ # OSC helpers (\a = BEL terminator)
13
+ __sr_osc_prompt_start() { printf '\e]133;A\a'; }
14
+ __sr_osc_prompt_end() { printf '\e]133;B\a'; }
15
+ __sr_osc_pre_exec() { printf '\e]133;C\a'; }
16
+ __sr_osc_post_exec() { printf '\e]133;D;%d\a' "${1:-0}"; }
17
+ __sr_osc_cwd() { printf '\e]1337;CurrentDir=%s\a' "$PWD"; }
18
+
19
+ # DEBUG trap fires before each simple command.
20
+ # Skip when inside the prompt itself (BASH_COMMAND == PROMPT_COMMAND) to avoid
21
+ # treating the prompt as a user command.
22
+ __sr_in_prompt=0
23
+ __sr_debug_trap() {
24
+ if [[ "$__sr_in_prompt" == "1" ]]; then return; fi
25
+ __sr_osc_prompt_end
26
+ __sr_osc_pre_exec
27
+ }
28
+ trap '__sr_debug_trap' DEBUG
29
+
30
+ # PROMPT_COMMAND wraps the user's existing PROMPT_COMMAND.
31
+ __sr_prompt_command() {
32
+ local exit=$?
33
+ __sr_in_prompt=1
34
+ __sr_osc_post_exec "$exit"
35
+ __sr_osc_cwd
36
+ __sr_osc_prompt_start
37
+ __sr_in_prompt=0
38
+ }
39
+
40
+ if [[ -n "$PROMPT_COMMAND" ]]; then
41
+ PROMPT_COMMAND="__sr_prompt_command; $PROMPT_COMMAND"
42
+ else
43
+ PROMPT_COMMAND="__sr_prompt_command"
44
+ fi
45
+ export PROMPT_COMMAND
@@ -0,0 +1,36 @@
1
+ # Specrails — fish shell-integration shim
2
+ # Sourced via `fish -C "source <this-file>"`, which runs after the user's
3
+ # config.fish / conf.d, so the user's real fish config is preserved.
4
+
5
+ set -gx SPECRAILS_SHELL_INTEGRATION_LOADED 1
6
+
7
+ function __sr_osc_prompt_start
8
+ printf '\e]133;A\a'
9
+ end
10
+
11
+ function __sr_osc_prompt_end
12
+ printf '\e]133;B\a'
13
+ end
14
+
15
+ function __sr_osc_pre_exec
16
+ printf '\e]133;C\a'
17
+ end
18
+
19
+ function __sr_osc_post_exec
20
+ printf '\e]133;D;%d\a' $argv[1]
21
+ end
22
+
23
+ function __sr_osc_cwd
24
+ printf '\e]1337;CurrentDir=%s\a' (pwd)
25
+ end
26
+
27
+ function __sr_preexec --on-event fish_preexec
28
+ __sr_osc_prompt_end
29
+ __sr_osc_pre_exec
30
+ end
31
+
32
+ function __sr_postexec --on-event fish_postexec
33
+ __sr_osc_post_exec $status
34
+ __sr_osc_cwd
35
+ __sr_osc_prompt_start
36
+ end
@@ -0,0 +1,48 @@
1
+ # Specrails — PowerShell shell-integration shim
2
+ # Loaded via -NoLogo -NoExit -File <shim.ps1>. Dot-sources the user's real
3
+ # $PROFILE if it exists, then wraps the prompt function with OSC 133 marks.
4
+
5
+ $env:SPECRAILS_SHELL_INTEGRATION_LOADED = '1'
6
+
7
+ # Dot-source the user's real profile so their environment, aliases, modules load.
8
+ $__srUserProfile = $PROFILE.CurrentUserAllHosts
9
+ if (Test-Path -LiteralPath $__srUserProfile) {
10
+ . $__srUserProfile
11
+ }
12
+
13
+ # Capture the user's existing prompt function so we can chain to it.
14
+ $__srOriginalPrompt = $function:prompt
15
+
16
+ function global:__sr-OscEmit([string]$seq) {
17
+ [Console]::Write($seq)
18
+ }
19
+
20
+ # Override prompt with our wrapper.
21
+ function global:prompt {
22
+ $exit = if ($global:LASTEXITCODE) { [int]$global:LASTEXITCODE } else { 0 }
23
+
24
+ # post-exec for the *previous* command, then cwd, then prompt-start.
25
+ __sr-OscEmit ("`e]133;D;{0}`a" -f $exit)
26
+ __sr-OscEmit ("`e]1337;CurrentDir={0}`a" -f (Get-Location).Path)
27
+ __sr-OscEmit "`e]133;A`a"
28
+
29
+ if ($__srOriginalPrompt) {
30
+ & $__srOriginalPrompt
31
+ } else {
32
+ "PS $((Get-Location).Path)> "
33
+ }
34
+
35
+ __sr-OscEmit "`e]133;B`a"
36
+ }
37
+
38
+ # Trace pre-exec via PSReadLine if available.
39
+ if (Get-Module -Name PSReadLine -ListAvailable) {
40
+ try {
41
+ Set-PSReadLineKeyHandler -Key Enter -ScriptBlock {
42
+ [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
43
+ __sr-OscEmit "`e]133;C`a"
44
+ }
45
+ } catch {
46
+ # PSReadLine version may not support custom handlers; ignore.
47
+ }
48
+ }
@@ -0,0 +1,47 @@
1
+ # Specrails — zsh shell-integration shim
2
+ # Loaded via ZDOTDIR override when the user spawns a terminal in the app.
3
+ # Sources the user's real login files, then layers OSC 133 / OSC 1337 hooks on top.
4
+
5
+ # Sentinel so the server can detect bootstrap success.
6
+ export SPECRAILS_SHELL_INTEGRATION_LOADED=1
7
+
8
+ # The ZDOTDIR override makes zsh read its startup files from the shim dir, so the
9
+ # user's own ~/.zshenv/.zprofile/.zshrc/.zlogin are skipped — losing PATH,
10
+ # Homebrew, nvm/fnm/asdf, etc. Source them ourselves from the user's real ZDOTDIR
11
+ # (default $HOME), in zsh's normal login order, before installing our hooks so we
12
+ # don't stomp on the user's precmd/preexec arrays.
13
+ __sr_uz="${SPECRAILS_REAL_ZDOTDIR:-$HOME}"
14
+ [[ -r "$__sr_uz/.zshenv" ]] && source "$__sr_uz/.zshenv"
15
+ [[ -r "$__sr_uz/.zprofile" ]] && source "$__sr_uz/.zprofile"
16
+ [[ -r "$__sr_uz/.zshrc" ]] && source "$__sr_uz/.zshrc"
17
+ [[ -r "$__sr_uz/.zlogin" ]] && source "$__sr_uz/.zlogin"
18
+ unset __sr_uz
19
+
20
+ # Helper: print OSC 133 sequences. \a is the BEL (ST) terminator.
21
+ __sr_osc_prompt_start() { print -n -- $'\e]133;A\a' }
22
+ __sr_osc_prompt_end() { print -n -- $'\e]133;B\a' }
23
+ __sr_osc_pre_exec() { print -n -- $'\e]133;C\a' }
24
+ __sr_osc_post_exec() { print -n -- $'\e]133;D;'"${1:-0}"$'\a' }
25
+ __sr_osc_cwd() { print -n -- $'\e]1337;CurrentDir='"$PWD"$'\a' }
26
+
27
+ # Track exit code of the last command for OSC 133;D.
28
+ __sr_last_exit=0
29
+
30
+ # precmd: runs before every prompt. Emits prompt-start and CWD.
31
+ __sr_precmd() {
32
+ __sr_last_exit=$?
33
+ __sr_osc_post_exec "$__sr_last_exit"
34
+ __sr_osc_cwd
35
+ __sr_osc_prompt_start
36
+ }
37
+
38
+ # preexec: runs just before a command executes. Emits pre-exec and prompt-end.
39
+ __sr_preexec() {
40
+ __sr_osc_prompt_end
41
+ __sr_osc_pre_exec
42
+ }
43
+
44
+ # Append (do not overwrite) so user-defined hooks are preserved.
45
+ typeset -ga precmd_functions preexec_functions
46
+ precmd_functions+=("__sr_precmd")
47
+ preexec_functions+=("__sr_preexec")
@@ -301,6 +301,7 @@ function applySmashToStore(filePath, epicId, children, nowIso, createdBy) {
301
301
  const id = s.next_id++;
302
302
  const description = composeChildDescription(child);
303
303
  const ticket = {
304
+ ...(target.repositoryIds ? { repositoryIds: [...target.repositoryIds] } : {}),
304
305
  id,
305
306
  title: child.title,
306
307
  description,
@@ -11,13 +11,10 @@
11
11
  // manages crash-respawn / idle-kill) stay with the caller via hooks + the
12
12
  // returned result. Adopting it is behaviour-preserving: the caller keeps every
13
13
  // post-close decision it had, just without re-implementing the plumbing.
14
- var __importDefault = (this && this.__importDefault) || function (mod) {
15
- return (mod && mod.__esModule) ? mod : { "default": mod };
16
- };
17
14
  Object.defineProperty(exports, "__esModule", { value: true });
18
15
  exports.runAiCliInvocation = runAiCliInvocation;
19
16
  const node_readline_1 = require("node:readline");
20
- const tree_kill_1 = __importDefault(require("tree-kill"));
17
+ const win_spawn_1 = require("./util/win-spawn");
21
18
  const cli_prompt_1 = require("./util/cli-prompt");
22
19
  const runtime_1 = require("./providers/runtime");
23
20
  const STDERR_TAIL_CAP = 64 * 1024;
@@ -42,6 +39,40 @@ function runAiCliInvocation(hooks) {
42
39
  return new Promise((resolve) => {
43
40
  let settled = false;
44
41
  let timer = null;
42
+ let inactivityTimer = null;
43
+ let child;
44
+ const terminateForTimeout = () => {
45
+ if (child.pid) {
46
+ (0, win_spawn_1.treeKillSafe)(child.pid, 'SIGTERM');
47
+ const pid = child.pid;
48
+ const killTimer = setTimeout(() => {
49
+ try {
50
+ (0, win_spawn_1.treeKillSafe)(pid, 'SIGKILL', () => { });
51
+ }
52
+ catch { /* gone */ }
53
+ }, 2000);
54
+ killTimer.unref?.();
55
+ child.once('close', () => clearTimeout(killTimer));
56
+ }
57
+ else {
58
+ try {
59
+ child.kill('SIGTERM');
60
+ }
61
+ catch { /* already gone */ }
62
+ }
63
+ };
64
+ const armInactivityTimer = () => {
65
+ if (hooks.inactivityTimeoutMs == null || hooks.inactivityTimeoutMs <= 0 || settled)
66
+ return;
67
+ if (inactivityTimer)
68
+ clearTimeout(inactivityTimer);
69
+ inactivityTimer = setTimeout(() => {
70
+ hooks.onInactivityTimeout?.();
71
+ terminateForTimeout();
72
+ settle({ code: null, timedOut: true, spawnFailed: false, child });
73
+ }, hooks.inactivityTimeoutMs);
74
+ inactivityTimer.unref?.();
75
+ };
45
76
  const settle = (partial) => {
46
77
  if (settled)
47
78
  return;
@@ -50,9 +81,12 @@ function runAiCliInvocation(hooks) {
50
81
  clearTimeout(timer);
51
82
  timer = null;
52
83
  }
84
+ if (inactivityTimer) {
85
+ clearTimeout(inactivityTimer);
86
+ inactivityTimer = null;
87
+ }
53
88
  resolve({ ...partial, events, lastResultEvent, sessionId, stderrTail });
54
89
  };
55
- let child;
56
90
  try {
57
91
  child = spawn(binary, args, {
58
92
  env,
@@ -66,6 +100,7 @@ function runAiCliInvocation(hooks) {
66
100
  return;
67
101
  }
68
102
  hooks.onSpawn?.(child);
103
+ armInactivityTimer();
69
104
  if (hooks.timeoutMs != null && hooks.timeoutMs > 0) {
70
105
  timer = setTimeout(() => {
71
106
  hooks.onTimeout?.();
@@ -73,31 +108,13 @@ function runAiCliInvocation(hooks) {
73
108
  // then escalate to SIGKILL after a grace window if the child ignores
74
109
  // SIGTERM — otherwise a hung, signal-swallowing CLI is orphaned for the
75
110
  // host's lifetime. (Mirrors QueueManager._kill.)
76
- if (child.pid) {
77
- (0, tree_kill_1.default)(child.pid, 'SIGTERM');
78
- const pid = child.pid;
79
- const killTimer = setTimeout(() => {
80
- try {
81
- (0, tree_kill_1.default)(pid, 'SIGKILL', () => { });
82
- }
83
- catch { /* gone */ }
84
- }, 2000);
85
- killTimer.unref?.();
86
- child.once('close', () => clearTimeout(killTimer));
87
- }
88
- else {
89
- try {
90
- child.kill('SIGTERM');
91
- }
92
- catch { /* already gone */ }
93
- }
111
+ terminateForTimeout();
94
112
  settle({ code: null, timedOut: true, spawnFailed: false, child });
95
113
  }, hooks.timeoutMs);
96
114
  }
97
115
  if (child.stdout) {
98
116
  const reader = (0, node_readline_1.createInterface)({ input: child.stdout, crlfDelay: Infinity });
99
- if (hooks.onData)
100
- child.stdout.on('data', () => hooks.onData('stdout'));
117
+ child.stdout.on('data', () => { armInactivityTimer(); hooks.onData?.('stdout'); });
101
118
  reader.on('line', (line) => {
102
119
  // A malformed line or a buggy adapter parser must NEVER crash the whole
103
120
  // server process — a throw in this readline handler is otherwise an
@@ -127,12 +144,12 @@ function runAiCliInvocation(hooks) {
127
144
  if (child.stderr) {
128
145
  if (hooks.onStderrLine) {
129
146
  const errReader = (0, node_readline_1.createInterface)({ input: child.stderr, crlfDelay: Infinity });
130
- if (hooks.onData)
131
- child.stderr.on('data', () => hooks.onData('stderr'));
147
+ child.stderr.on('data', () => { armInactivityTimer(); hooks.onData?.('stderr'); });
132
148
  errReader.on('line', (line) => hooks.onStderrLine(line));
133
149
  }
134
150
  else {
135
151
  child.stderr.on('data', (chunk) => {
152
+ armInactivityTimer();
136
153
  if (hooks.onData)
137
154
  hooks.onData('stderr');
138
155
  if (stderrTail.length < STDERR_TAIL_CAP)
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ // ─── The ONE premium spec contract (premium-milestone-progress D6) ─────────
3
+ //
4
+ // Every AI author of detailed specs derives its spec-content instructions
5
+ // from here: the day-0 Project Builder (blueprint-operator-prompt.ts), M2+
6
+ // milestone generation (chat-manager.ts _buildMilestoneSystemPrompt) and the
7
+ // agent's super-spec mode (agent-operator-prompt.ts). The DEPTH bar is shared;
8
+ // only the grounding rule differs per author (`day0` = everything introduced
9
+ // is labelled *planned*, nothing existing may be claimed; `verified` = only
10
+ // paths/identifiers actually read may be named).
11
+ //
12
+ // BYTE-STABILITY: every export is a static string / object (no timestamps,
13
+ // no live data) so provider prompt caching keeps working across turns.
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.PREMIUM_SCAFFOLD_EXAMPLE = exports.SPEC_DEPTH_FLOORS = void 0;
16
+ exports.premiumSpecContract = premiumSpecContract;
17
+ exports.premiumSpecContractCompact = premiumSpecContractCompact;
18
+ /** Deterministic floors the quality gate enforces (server + client mirror). */
19
+ exports.SPEC_DEPTH_FLOORS = {
20
+ problemMinChars: 200,
21
+ solutionMinChars: 500,
22
+ outOfScopeMinBullets: 3,
23
+ technicalMinBullets: 5,
24
+ criteriaMin: 6,
25
+ criteriaMax: 10,
26
+ criterionMinChars: 20,
27
+ };
28
+ const GROUNDING = {
29
+ day0: {
30
+ intro: 'No repository exists yet. Ground every technical detail in the selected stack, the planned components, the intended contracts and data shapes, the known risks and the dependencies between specs.',
31
+ modules: 'name the modules, components, routes, tables and files this spec will CREATE, each explicitly marked as planned (for example "src/features/pantry/PantryList.tsx (planned)") with one line of responsibility — never claim that any of them already exists',
32
+ technical: 'anchor bullets on the planned artifacts above and on the selected stack; never invent or imply an existing repository path, module, function, migration or test file',
33
+ },
34
+ verified: {
35
+ intro: 'Ground every technical detail in the real codebase you read during this conversation, never in plausible-sounding memory.',
36
+ modules: 'name the REAL modules, components, routes, tables and files the change builds on or extends — only ones you actually opened — plus the new ones it will create, each marked as planned',
37
+ technical: 'anchor bullets on EXACT file paths and identifiers you verified; if you did not read it, do not name it',
38
+ },
39
+ };
40
+ /**
41
+ * The full contract, as a markdown section body (no leading heading) — for
42
+ * instruction files and prompt bodies.
43
+ */
44
+ function premiumSpecContract(mode) {
45
+ const g = GROUNDING[mode];
46
+ const f = exports.SPEC_DEPTH_FLOORS;
47
+ return `${g.intro} Write every spec as if a senior engineer who has never spoken to the user must implement it unattended and a non-technical reviewer must understand what they are approving: no section is a restated title, every section carries decisions.
48
+
49
+ Every detailed spec MUST satisfy all of these rules:
50
+
51
+ - \`kind\` is exactly \`scaffold\`, \`feature\`, or \`verification\`.
52
+ - \`title\` is concise, imperative, unique in the batch, and in English.
53
+ - \`shortSummary\` is one useful English sentence, no more than 240 characters.
54
+ - \`description\` is English markdown with exactly these five \`##\` headings, once each and in this order (sub-blocks use \`###\`, never \`##\`):
55
+ 1. \`## Problem Statement\` — 3–5 sentences (at least ${f.problemMinChars} characters): the persona, the moment the pain happens, what hurts today and why, why this belongs in THIS milestone, and what a good outcome looks like. A narrative, not a restated title.
56
+ 2. \`## Proposed Solution\` — at least ${f.solutionMinChars} characters. Open with a numbered user journey (what the user does and sees, step by step), then these \`###\` sub-blocks:
57
+ - \`### User experience\` — the screens/commands involved and their states: empty, loading, error, success; copy tone; what is remembered between visits.
58
+ - \`### Data model\` — the entities with their fields, types, constraints and relationships (as planned).
59
+ - \`### Interfaces & contracts\` — routes/commands/events with request and response shapes, validation rules and error responses.
60
+ - \`### Planned modules\` — ${g.modules}.
61
+ - \`### Key decisions\` — 2–3 decisions with the alternative considered and why it was rejected.
62
+ 3. \`## Out of Scope\` — at least ${f.outOfScopeMinBullets} bullets; each names what is deferred, why, and where it lands (a later spec or milestone by title).
63
+ 4. \`## Technical Considerations\` — at least ${f.technicalMinBullets} labelled bullets chosen from: **Architecture**, **Data & contracts**, **Failure handling & edge cases**, **Security & privacy**, **Performance & limits**, **Observability**, **Testing strategy** (unit / integration / end-to-end with named scenarios), **Dependencies** (earlier specs by title), **Risks & mitigations**; ${g.technical}.
64
+ 5. \`## Estimated Complexity\` — Low, Medium, High, or Very High, then 1–2 sentences on what drives the estimate and the main uncertainty.
65
+ - Do NOT put an \`## Acceptance Criteria\` heading in \`description\`. Criteria live only in the separate \`acceptanceCriteria\` array; the app folds them into the ticket deterministically at commit time.
66
+ - \`acceptanceCriteria\` contains ${f.criteriaMin}–${f.criteriaMax} independent, testable outcomes (each at least ${f.criterionMinChars} characters), written as observable behaviour — "Given … when … then …" or "When X, the system Y" — never implementation steps. Across the set cover: the happy path, at least one failure or edge case (invalid input, empty state, permission denied, offline, timeout), at least one automated verification (the unit/integration/e2e test that proves it), and for user-facing specs an empty-state or accessibility outcome.
67
+ - \`priority\` is exactly \`low\`, \`medium\`, \`high\`, or \`critical\` and reflects delivery urgency/risk rather than implementation complexity.
68
+ - \`labels\` includes the milestone label plus at least one concise domain label such as \`frontend\`, \`api\`, \`data\`, \`auth\`, \`testing\`, or \`infra\`.`;
69
+ }
70
+ /** One-paragraph form for \`--system-prompt\` channels (same rules, no markdown). */
71
+ function premiumSpecContractCompact(mode) {
72
+ const g = GROUNDING[mode];
73
+ const f = exports.SPEC_DEPTH_FLOORS;
74
+ return `Spec depth bar (premium): ${g.intro} Each spec has kind (scaffold|feature|verification), a unique imperative English title, a one-sentence shortSummary <=240 characters, and a description with exactly these ## headings once and in order: ## Problem Statement (3-5 sentences, >=${f.problemMinChars} chars: persona, trigger, pain, why this milestone, what good looks like), ## Proposed Solution (>=${f.solutionMinChars} chars: a numbered user journey, then ### User experience with empty/loading/error/success states, ### Data model with fields and types, ### Interfaces & contracts with request/response shapes and errors, ### Planned modules — ${g.modules} — and ### Key decisions with the rejected alternative), ## Out of Scope (>=${f.outOfScopeMinBullets} bullets: what, why deferred, where it lands), ## Technical Considerations (>=${f.technicalMinBullets} labelled bullets from Architecture, Data & contracts, Failure handling & edge cases, Security & privacy, Performance & limits, Observability, Testing strategy with named scenarios, Dependencies by spec title, Risks & mitigations; ${g.technical}), ## Estimated Complexity (level plus 1-2 sentences and the main uncertainty). Never an ## Acceptance Criteria section in description. A separate acceptanceCriteria array of ${f.criteriaMin}-${f.criteriaMax} observable outcomes (>=${f.criterionMinChars} chars each, Given/When/Then style) covering the happy path, at least one failure/edge case, at least one automated verification and an empty-state/accessibility outcome for user-facing specs. priority low|medium|high|critical by urgency/risk; labels include the milestone label plus a domain label.`;
75
+ }
76
+ /**
77
+ * A complete premium example (the mandatory M1 scaffold) — rendered into the
78
+ * Builder prompt so the model mirrors the DEPTH, not just the shape.
79
+ */
80
+ exports.PREMIUM_SCAFFOLD_EXAMPLE = {
81
+ kind: 'scaffold',
82
+ title: 'Scaffold the runnable project foundation',
83
+ shortSummary: 'Turn the README-only repository into an installable, runnable, testable application shell with CI, so every later slice lands on a verified foundation.',
84
+ description: [
85
+ '## Problem Statement',
86
+ 'A contributor who clones the repository today finds a README and nothing else: there is no dependency manifest, no command that starts the app, no test to prove anything works and no CI to catch a broken change. Every Milestone-1 slice would otherwise begin by improvising its own setup, producing three incompatible foundations by week one. The walking skeleton needs this first because each later spec is defined as "runs on top of the scaffold". A good outcome is a clean checkout that installs, starts, tests and passes CI with documented commands, with zero product behaviour beyond a visible placeholder.',
87
+ '',
88
+ '## Proposed Solution',
89
+ '1. The contributor runs the documented install command and gets a reproducible dependency tree.',
90
+ '2. They run the development command and see the placeholder home screen respond on the documented port.',
91
+ '3. They run the test command and one smoke test passes.',
92
+ '4. They open a pull request and CI runs the same install, lint, build and test steps.',
93
+ '',
94
+ '### User experience',
95
+ 'A single placeholder route/screen showing the product name and a short "foundation ready" message; no other UI. States: success (page renders), error (the process exits with a clear message when the port is taken or a dependency is missing). Nothing is remembered between visits.',
96
+ '',
97
+ '### Data model',
98
+ 'None yet. The persistence layer is initialised (connection/config only) so later specs add entities without re-scaffolding.',
99
+ '',
100
+ '### Interfaces & contracts',
101
+ '`GET /health` → `{ "status": "ok", "version": "<package version>" }` (200). Development, build and test commands documented in the README with their exit-code semantics.',
102
+ '',
103
+ '### Planned modules',
104
+ '- `package manifest + lockfile (planned)` — pinned runtime and dependency versions.',
105
+ '- `src/app entry (planned)` — process bootstrap, port/config loading, the placeholder route.',
106
+ '- `src/config (planned)` — typed environment loading with defaults.',
107
+ '- `tests/smoke (planned)` — one end-to-end smoke test hitting `/health`.',
108
+ '- `CI workflow (planned)` — install → lint → build → test on every push and pull request.',
109
+ '',
110
+ '### Key decisions',
111
+ '- Keep the placeholder minimal instead of seeding a real feature: a feature here would blur the boundary later specs build on. Rejected: scaffolding the first product screen now.',
112
+ '- Pin exact dependency versions from day one; floating ranges were rejected because the first CI break would be a dependency drift, not a product bug.',
113
+ '',
114
+ '## Out of Scope',
115
+ '- Any product feature beyond the placeholder — every slice of the core flow lands in the later Milestone-1 specs.',
116
+ '- Production deployment, hosting and environment provisioning — deferred to a later milestone once the skeleton runs end to end.',
117
+ '- Authentication, authorization and user accounts — a dedicated Milestone-1 or Milestone-2 spec owns them.',
118
+ '',
119
+ '## Technical Considerations',
120
+ '- **Architecture**: one process, one entry module, configuration read once at boot; later specs add modules under the planned layout rather than reshaping it.',
121
+ '- **Data & contracts**: `/health` is the only contract; its response shape is frozen so CI and later monitoring can rely on it.',
122
+ '- **Failure handling & edge cases**: a missing runtime or an occupied port fails fast with an actionable message; the smoke test fails when `/health` is not 200.',
123
+ '- **Security & privacy**: no secrets committed; `.env.example` documents variables; the health endpoint exposes no internal detail beyond the version.',
124
+ '- **Performance & limits**: cold start under a few seconds on a laptop; no performance work beyond that.',
125
+ '- **Observability**: structured startup log line with port and version; CI logs are the audit trail.',
126
+ '- **Testing strategy**: unit — config loading with and without overrides; e2e — the smoke test starts the app and asserts `/health`; CI runs both on every change.',
127
+ '- **Dependencies**: none (first spec); every later Milestone-1 spec depends on this one.',
128
+ '- **Risks & mitigations**: toolchain drift between contributors — mitigated by the pinned lockfile and CI running from a clean checkout.',
129
+ '',
130
+ '## Estimated Complexity',
131
+ 'Medium — the foundation must align local development, automated tests and CI without implementing product behaviour; the main uncertainty is CI runtime setup for the chosen stack.',
132
+ ].join('\n'),
133
+ acceptanceCriteria: [
134
+ 'Given a clean checkout, when the contributor runs the documented install command, then it completes without errors and produces the pinned dependency tree.',
135
+ 'When the development command runs, the placeholder screen renders and GET /health returns 200 with { status: "ok", version } within a few seconds.',
136
+ 'When the test command runs from a clean checkout, the smoke test passes and the process exits with code 0.',
137
+ 'When the configured port is already in use, the process exits non-zero with a message naming the port instead of hanging.',
138
+ 'When a pull request is opened, CI runs install, lint, build and test and reports a red status on any failure.',
139
+ 'The README documents the install, run and test commands and a contributor can follow them without asking anyone.',
140
+ ],
141
+ priority: 'high',
142
+ labels: ['M1', 'foundation', 'infra'],
143
+ };
@@ -29,6 +29,9 @@ function validateBlock(parsed) {
29
29
  partial.title = obj.title;
30
30
  if (typeof obj.description === 'string')
31
31
  partial.description = obj.description;
32
+ if (Array.isArray(obj.repositoryIds) && obj.repositoryIds.length > 0 && obj.repositoryIds.every((id) => typeof id === 'string' && id.trim()) && new Set(obj.repositoryIds).size === obj.repositoryIds.length) {
33
+ partial.repositoryIds = [...obj.repositoryIds];
34
+ }
32
35
  const labels = coerceStringArray(obj.labels);
33
36
  if (labels)
34
37
  partial.labels = labels;
@@ -87,6 +90,8 @@ function mergeDraft(prev, next) {
87
90
  out.title = next.title;
88
91
  if (typeof next.description === 'string' && next.description !== '')
89
92
  out.description = next.description;
93
+ if (Array.isArray(next.repositoryIds) && next.repositoryIds.length > 0 && next.repositoryIds.every((id) => typeof id === 'string' && id.trim()) && new Set(next.repositoryIds).size === next.repositoryIds.length)
94
+ out.repositoryIds = next.repositoryIds.slice();
90
95
  if (Array.isArray(next.labels))
91
96
  out.labels = next.labels.slice();
92
97
  if (Array.isArray(next.acceptanceCriteria))
@@ -108,7 +113,7 @@ function applyBlocks(prev, blocks) {
108
113
  for (const block of blocks) {
109
114
  const before = nextDraft;
110
115
  nextDraft = mergeDraft(before, block.partial);
111
- for (const key of ['title', 'description', 'labels', 'priority', 'acceptanceCriteria']) {
116
+ for (const key of ['title', 'description', 'labels', 'priority', 'acceptanceCriteria', 'repositoryIds']) {
112
117
  if (!shallowEqual(before[key], nextDraft[key]))
113
118
  changed.add(key);
114
119
  }
@@ -12,6 +12,7 @@ exports.CLAUDE_MODELS = [
12
12
  { value: 'haiku', label: 'Claude Haiku' },
13
13
  ];
14
14
  exports.CODEX_MODELS = [
15
+ { value: 'gpt-6-astra', label: 'GPT-6 Astra' },
15
16
  { value: 'gpt-5.6-sol', label: 'GPT-5.6 Sol' },
16
17
  { value: 'gpt-5.6-terra', label: 'GPT-5.6 Terra' },
17
18
  { value: 'gpt-5.6-luna', label: 'GPT-5.6 Luna' },
@@ -117,6 +117,7 @@ class StuckRunDetector {
117
117
  jobId: candidate.runId,
118
118
  stepKey: candidate.stepKey,
119
119
  staleMs: candidate.staleMs,
120
+ actions: ['stop'],
120
121
  timestamp: new Date(this.io.now?.() ?? Date.now()).toISOString(),
121
122
  };
122
123
  try {