@repo-os/repoos 0.5.54 → 0.5.56

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 (167) hide show
  1. package/README.md +12 -11
  2. package/dist/.build-info.json +2 -2
  3. package/dist/.build-stamp.json +1 -1
  4. package/dist/commands/check.js +18 -2
  5. package/dist/commands/init.js +19 -7
  6. package/dist/commands/tasks.d.ts +51 -31
  7. package/dist/commands/tasks.js +91 -31
  8. package/dist/core/config.js +2 -2
  9. package/dist/core/doctor.js +2 -2
  10. package/dist/core/input.d.ts +5 -1
  11. package/dist/core/input.js +34 -8
  12. package/dist/core/needs-input.d.ts +8 -0
  13. package/dist/core/needs-input.js +13 -0
  14. package/dist/core/repo-log.d.ts +78 -0
  15. package/dist/core/repo-log.js +322 -0
  16. package/dist/core/story-display.d.ts +17 -2
  17. package/dist/core/story-display.js +6 -0
  18. package/dist/core/types.d.ts +16 -3
  19. package/dist/core/types.js +4 -0
  20. package/dist/server/agents.d.ts +74 -2
  21. package/dist/server/agents.js +239 -55
  22. package/dist/server/cto-monitor.d.ts +3 -1
  23. package/dist/server/cto-monitor.js +16 -2
  24. package/dist/server/handoff-request.d.ts +29 -0
  25. package/dist/server/handoff-request.js +103 -0
  26. package/dist/server/handoff.d.ts +42 -8
  27. package/dist/server/handoff.js +272 -212
  28. package/dist/server/needs-input-dismiss.d.ts +6 -0
  29. package/dist/server/needs-input-dismiss.js +38 -0
  30. package/dist/server/review.js +5 -3
  31. package/dist/server/routes/index.d.ts +1 -0
  32. package/dist/server/routes/index.js +1 -0
  33. package/dist/server/routes/inputs.js +17 -4
  34. package/dist/server/routes/repo-log.d.ts +8 -0
  35. package/dist/server/routes/repo-log.js +67 -0
  36. package/dist/server/routes/tasks.d.ts +1 -0
  37. package/dist/server/routes/tasks.js +48 -10
  38. package/dist/server/routes/types.d.ts +18 -0
  39. package/dist/server/server.js +173 -55
  40. package/dist/server/task-watchdog.js +15 -11
  41. package/dist/ui/assets/AgentsView-CqkgzCze.js +1 -0
  42. package/dist/ui/assets/ChecksView-Doy8ZRcs.js +0 -0
  43. package/dist/ui/assets/ContextView-BKPMIh5Q.css +1 -0
  44. package/dist/ui/assets/ContextView-HTKy3bOH.js +4 -0
  45. package/dist/ui/assets/DashboardView-DP7D-o1S.js +1 -0
  46. package/dist/ui/assets/DeploymentsView-BRgF6h-S.js +1 -0
  47. package/dist/ui/assets/DiffView-B9-i6C6C.css +1 -0
  48. package/dist/ui/assets/DiffView-g587DbeS.js +4 -0
  49. package/dist/ui/assets/InputsView-BLhcZaQg.js +1 -0
  50. package/dist/ui/assets/InputsView-CAOnQPzO.css +1 -0
  51. package/dist/ui/assets/{LoginView-CV_1xAvv.js → LoginView-BKLriI-b.js} +1 -1
  52. package/dist/ui/assets/ReleasesView-BpUqzAYv.js +3 -0
  53. package/dist/ui/assets/SettingsView-DkZ64CDa.js +4 -0
  54. package/dist/ui/assets/StoriesView-BLX17KW4.css +1 -0
  55. package/dist/ui/assets/StoriesView-BqHkUyKa.js +1 -0
  56. package/dist/ui/assets/WorkView-Dpkonfs7.js +2 -0
  57. package/dist/ui/assets/{abnfDiagram-VCTEODGH-CTfjwe9G.js → abnfDiagram-VCTEODGH-C6WykvNa.js} +1 -1
  58. package/dist/ui/assets/architecture-7GRP2DOG-BobXoPbG.js +1 -0
  59. package/dist/ui/assets/{architectureDiagram-5GKGNRK7-CVfIBdXM.js → architectureDiagram-5GKGNRK7-roEGL8m0.js} +1 -1
  60. package/dist/ui/assets/{blockDiagram-I7D4REHJ-DHrovPE1.js → blockDiagram-I7D4REHJ-DJuucocN.js} +1 -1
  61. package/dist/ui/assets/button-DQcC_aLE.js +1 -0
  62. package/dist/ui/assets/{c4Diagram-7LVT6UL2-D1_IKyVU.js → c4Diagram-7LVT6UL2-6sUrKoSN.js} +1 -1
  63. package/dist/ui/assets/channel-Cy_U0xgO.js +1 -0
  64. package/dist/ui/assets/{chunk-4HAMMTFA-BQK2Qs9d.js → chunk-4HAMMTFA-J0_pcEVf.js} +1 -1
  65. package/dist/ui/assets/{chunk-75Z2AOVW-C560CV9I.js → chunk-75Z2AOVW-BxJ2TNvr.js} +1 -1
  66. package/dist/ui/assets/{chunk-DU6HZSFF-BX9aRA1h.js → chunk-DU6HZSFF-BX0XMIVw.js} +1 -1
  67. package/dist/ui/assets/{chunk-F27PBJKO-MId0AvWB.js → chunk-F27PBJKO-atSjCf8v.js} +1 -1
  68. package/dist/ui/assets/{chunk-GMAD6QVW-B8uV52pK.js → chunk-GMAD6QVW-Bos7znjM.js} +1 -1
  69. package/dist/ui/assets/{chunk-GVQU2GXP-BHRWhJBs.js → chunk-GVQU2GXP-Co0d8Tfs.js} +1 -1
  70. package/dist/ui/assets/{chunk-IMKFNOWR-C2p26tmJ.js → chunk-IMKFNOWR-DkOJUS_A.js} +1 -1
  71. package/dist/ui/assets/{chunk-L3NEJ4N5-CiNIjzhG.js → chunk-L3NEJ4N5-GBmbBw5A.js} +1 -1
  72. package/dist/ui/assets/{chunk-OSK3NFVY-DOZ8UOk-.js → chunk-OSK3NFVY-Pao3zNLe.js} +1 -1
  73. package/dist/ui/assets/{chunk-P2QGCYS3-CWkpd0sS.js → chunk-P2QGCYS3-NVMl_E1J.js} +1 -1
  74. package/dist/ui/assets/{chunk-POPQ4Y6H-C_jAEIPq.js → chunk-POPQ4Y6H-tzZL78RS.js} +1 -1
  75. package/dist/ui/assets/{chunk-PWAF6VOD-DlV-Z1d6.js → chunk-PWAF6VOD-DwrkqNeH.js} +1 -1
  76. package/dist/ui/assets/{chunk-SHT3W25Y-CDcQrQRH.js → chunk-SHT3W25Y-8I7NMlT4.js} +1 -1
  77. package/dist/ui/assets/{chunk-SVP7TREG-CXMcWUhi.js → chunk-SVP7TREG-BpweZcGw.js} +1 -1
  78. package/dist/ui/assets/{chunk-TICWLB2K-C9NkrBRa.js → chunk-TICWLB2K-B8ZvCXdY.js} +1 -1
  79. package/dist/ui/assets/classDiagram-ZZMXUADV-DentwTwE.js +1 -0
  80. package/dist/ui/assets/classDiagram-v2-VYDZK3BY-DentwTwE.js +1 -0
  81. package/dist/ui/assets/config-Cl3iDM_B.js +1 -0
  82. package/dist/ui/assets/{cynefin-OW5HDTMX-BOREGtlU.js → cynefin-OW5HDTMX-BHvKo-Aq.js} +1 -1
  83. package/dist/ui/assets/{cynefinDiagram-5FMLGOSQ-B4ry75uw.js → cynefinDiagram-5FMLGOSQ-3kuG-1VM.js} +1 -1
  84. package/dist/ui/assets/{dagre-GXQ25YYZ-DaFuaEbQ.js → dagre-GXQ25YYZ-2P8Yk_4G.js} +1 -1
  85. package/dist/ui/assets/{diagram-S7CK7UJ4-CWKov5PL.js → diagram-S7CK7UJ4-DRs3pJcK.js} +1 -1
  86. package/dist/ui/assets/{diagram-UQ7AKVKN-BIaDBoBe.js → diagram-UQ7AKVKN-B3pbA6Zl.js} +1 -1
  87. package/dist/ui/assets/{diagram-VSXAHHWV-BjTKaekf.js → diagram-VSXAHHWV-BmFgGjHb.js} +1 -1
  88. package/dist/ui/assets/{diagram-VX7I27RA-Yx3scpQA.js → diagram-VX7I27RA-D5jJPu_a.js} +1 -1
  89. package/dist/ui/assets/{diagram-Z3DM3KII-Zj02SBoH.js → diagram-Z3DM3KII-cCr1BFg7.js} +1 -1
  90. package/dist/ui/assets/{ebnfDiagram-PWID7BFC-0YAqIwpj.js → ebnfDiagram-PWID7BFC-Dg1VhPuY.js} +1 -1
  91. package/dist/ui/assets/{erDiagram-RLTQ6QDP-D1qzM_C2.js → erDiagram-RLTQ6QDP-CT4xW2dC.js} +1 -1
  92. package/dist/ui/assets/eventmodeling-NTZA5JFV-C-x4iElw.js +1 -0
  93. package/dist/ui/assets/flowDiagram-HODETNUW-BSzze3T9.js +1 -0
  94. package/dist/ui/assets/{ganttDiagram-EL5Y4UJY-BfFGqNAR.js → ganttDiagram-EL5Y4UJY-Hc1my0Jy.js} +1 -1
  95. package/dist/ui/assets/{gitGraph-4MIJSDKK-C8Hio8mK.js → gitGraph-4MIJSDKK-ClQZHGcg.js} +1 -1
  96. package/dist/ui/assets/{gitGraphDiagram-WWUBYQGX-zDzdEhVR.js → gitGraphDiagram-WWUBYQGX-BwlfrDKb.js} +1 -1
  97. package/dist/ui/assets/index-CWATVnBi.css +1 -0
  98. package/dist/ui/assets/index-ChI6cY4t.js +35 -0
  99. package/dist/ui/assets/{info-A6RAGUB7-Co-frmaH.js → info-A6RAGUB7-GHiNutT4.js} +1 -1
  100. package/dist/ui/assets/{infoDiagram-27XIBGKW-C8vJrt_c.js → infoDiagram-27XIBGKW-B5tfXwvj.js} +1 -1
  101. package/dist/ui/assets/{ishikawaDiagram-5VMMS53U-BGO19EHJ.js → ishikawaDiagram-5VMMS53U-B8G11EdJ.js} +1 -1
  102. package/dist/ui/assets/{journeyDiagram-3NMN7TZE-DduoU3Je.js → journeyDiagram-3NMN7TZE-ubCuNa3S.js} +1 -1
  103. package/dist/ui/assets/{kanban-definition-UXKFOSKX-D7pp8Jfx.js → kanban-definition-UXKFOSKX-RHlFG4is.js} +1 -1
  104. package/dist/ui/assets/{line-un3qDwwl.js → line-DMrn1eUc.js} +1 -1
  105. package/dist/ui/assets/{mermaid-parser.core-BbfKCEpr.js → mermaid-parser.core-D_ycqYZL.js} +3 -3
  106. package/dist/ui/assets/{mermaid.core-BN7qhUmq.js → mermaid.core-CaXJ86pu.js} +4 -4
  107. package/dist/ui/assets/{mindmap-definition-YA3MSWOX-BYD5kmA5.js → mindmap-definition-YA3MSWOX-kx1nN_cJ.js} +1 -1
  108. package/dist/ui/assets/{packet-AYTQ26CC-C4FJPnFC.js → packet-AYTQ26CC-L5erT19p.js} +1 -1
  109. package/dist/ui/assets/{pegDiagram-XKGWAZYB-DsBi7Fms.js → pegDiagram-XKGWAZYB-C_KTXpQM.js} +1 -1
  110. package/dist/ui/assets/{pie-WAS4IAKB-C79YZGPK.js → pie-WAS4IAKB-q2S3rGf9.js} +1 -1
  111. package/dist/ui/assets/{pieDiagram-E7YTZNPT-BkOWAtjO.js → pieDiagram-E7YTZNPT-BLHxbu3z.js} +1 -1
  112. package/dist/ui/assets/{quadrantDiagram-AXDQQJYC-4cKZyntK.js → quadrantDiagram-AXDQQJYC-DNxwy4on.js} +1 -1
  113. package/dist/ui/assets/{radar-RG4KPBEZ-BRRdMOI4.js → radar-RG4KPBEZ-5paFbTrA.js} +1 -1
  114. package/dist/ui/assets/{railroad-74A4TZTK-Bbhl18lT.js → railroad-74A4TZTK-C4zNgPiH.js} +1 -1
  115. package/dist/ui/assets/railroad-abnf-HS5TGJTU-BXfjNyx0.js +1 -0
  116. package/dist/ui/assets/railroad-ebnf-LZEXJU2U-jn0Yk9uc.js +1 -0
  117. package/dist/ui/assets/railroad-peg-WCYAUIDC-Cv8anGyJ.js +1 -0
  118. package/dist/ui/assets/{railroadDiagram-O6MQD6OU-Bbg60dv2.js → railroadDiagram-O6MQD6OU-VmXZ6m0T.js} +1 -1
  119. package/dist/ui/assets/{requirementDiagram-BXWQKSXE-DoLwPzhb.js → requirementDiagram-BXWQKSXE-BHuX0BwK.js} +1 -1
  120. package/dist/ui/assets/{sankeyDiagram-P5KCCOFB-BVBS_VZc.js → sankeyDiagram-P5KCCOFB-5kDwMhVC.js} +1 -1
  121. package/dist/ui/assets/{sequenceDiagram-WJ2MYXX4-BACpdW4n.js → sequenceDiagram-WJ2MYXX4-KSqKjCpq.js} +1 -1
  122. package/dist/ui/assets/{stateDiagram-D77RDMKH-DlGZE9N1.js → stateDiagram-D77RDMKH-A_P_Un-0.js} +1 -1
  123. package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BuGnvM9I.js +1 -0
  124. package/dist/ui/assets/{swimlanes-42K2YHIH-DLQ7Vteo.js → swimlanes-42K2YHIH-BMAnXKSs.js} +1 -1
  125. package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-DK-DtsW-.js +8 -0
  126. package/dist/ui/assets/{timeline-definition-24CTP7MA-b99HY5j-.js → timeline-definition-24CTP7MA-BWweJQmR.js} +1 -1
  127. package/dist/ui/assets/title-ComEuLCp.js +1 -0
  128. package/dist/ui/assets/{treeView-Q6P3EWNA-BKXQAE6F.js → treeView-Q6P3EWNA-DusMk7Rp.js} +1 -1
  129. package/dist/ui/assets/{treemap-WGGIJYW6-CZj1ym9X.js → treemap-WGGIJYW6-BoIcxm2V.js} +1 -1
  130. package/dist/ui/assets/vendor-ui-DFGj-ZXN.js +3 -0
  131. package/dist/ui/assets/{vennDiagram-4TSXK5OY-Syj4tlui.js → vennDiagram-4TSXK5OY-DENh7Izt.js} +1 -1
  132. package/dist/ui/assets/{wardley-WFR3VGLG-W_snBoVU.js → wardley-WFR3VGLG-D-IbkF5y.js} +1 -1
  133. package/dist/ui/assets/{wardleyDiagram-VM6X3IG4-BnpA19Oz.js → wardleyDiagram-VM6X3IG4-Cwsu4-ww.js} +1 -1
  134. package/dist/ui/assets/{xychartDiagram-S5SC5T6Z-g474RiA-.js → xychartDiagram-S5SC5T6Z-wZXsbj8s.js} +1 -1
  135. package/dist/ui/index.html +6 -3
  136. package/dist/ui/sw.js +2 -2
  137. package/package.json +1 -1
  138. package/dist/ui/assets/AgentsView-UghV_I0u.js +0 -1
  139. package/dist/ui/assets/ChecksView-CE9_UWEH.js +0 -0
  140. package/dist/ui/assets/ContextView-C3cJribS.js +0 -4
  141. package/dist/ui/assets/ContextView-TVoMpCso.css +0 -1
  142. package/dist/ui/assets/DashboardView-DydNm09c.js +0 -1
  143. package/dist/ui/assets/DeploymentsView-DVMkUs04.js +0 -1
  144. package/dist/ui/assets/DiffView-BpW0Agf3.css +0 -1
  145. package/dist/ui/assets/DiffView-CCOUztfU.js +0 -4
  146. package/dist/ui/assets/InputsView-CgH2DwWm.css +0 -1
  147. package/dist/ui/assets/InputsView-uzRKFx_8.js +0 -1
  148. package/dist/ui/assets/ReleasesView-CMTPaJGR.js +0 -3
  149. package/dist/ui/assets/SettingsView-3Y2d6VYS.js +0 -4
  150. package/dist/ui/assets/StoriesView-DGnU5Cy9.js +0 -1
  151. package/dist/ui/assets/StoriesView-DTqtvuk_.css +0 -1
  152. package/dist/ui/assets/TaskCard-hX9bZtBs.js +0 -1
  153. package/dist/ui/assets/WorkView-DpL4RTSC.js +0 -2
  154. package/dist/ui/assets/architecture-7GRP2DOG-iLDzG2hD.js +0 -1
  155. package/dist/ui/assets/channel-C2SsuvKM.js +0 -1
  156. package/dist/ui/assets/classDiagram-ZZMXUADV-DOc-KWik.js +0 -1
  157. package/dist/ui/assets/classDiagram-v2-VYDZK3BY-DOc-KWik.js +0 -1
  158. package/dist/ui/assets/eventmodeling-NTZA5JFV-DoMudewO.js +0 -1
  159. package/dist/ui/assets/flowDiagram-HODETNUW-COa-0qtC.js +0 -1
  160. package/dist/ui/assets/index-CxZVHdeX.js +0 -35
  161. package/dist/ui/assets/index-DCiwKRGz.css +0 -1
  162. package/dist/ui/assets/railroad-abnf-HS5TGJTU-DP10zylu.js +0 -1
  163. package/dist/ui/assets/railroad-ebnf-LZEXJU2U-CD0qyNXa.js +0 -1
  164. package/dist/ui/assets/railroad-peg-WCYAUIDC-B4QvCxtr.js +0 -1
  165. package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BAXOtgDm.js +0 -1
  166. package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-BBsmj1pm.js +0 -8
  167. package/dist/ui/assets/vendor-ui-Cke7Wpgg.js +0 -3
package/README.md CHANGED
@@ -298,21 +298,22 @@ no API tokens leave your machine unless you configure them.
298
298
 
299
299
  ## RepoOS Hub for macOS
300
300
 
301
- A native Mac app (macOS 13+; sessions persist across relaunches on macOS 14+)
302
- that keeps tabs on all your RepoOS servers from
303
- one window: pin tasks across servers, see live attention counts in the sidebar
304
- and Dock badge, and jump between servers and tasks with Cmd-K. Each server
305
- loads in its own isolated WebKit session — sessions are never shared between
306
- servers, and server pages get no bridge into your files or Keychain. Local
307
- loopback servers need zero setup; remote servers use short-lived, revocable
308
- capability tokens (created over HTTPS on the server, stored in the Keychain)
309
- for their attention and search endpoints.
301
+ A native Mac companion app (macOS 13+; sessions persist across relaunches on
302
+ macOS 14+) that connects to the RepoOS servers you run locally or remotely.
303
+ From one window, pin tasks across servers, see live attention counts in the
304
+ sidebar and Dock badge, and jump between servers and tasks with Cmd-K. Each
305
+ server loads in its own isolated WebKit session — sessions are never shared
306
+ between servers, and server pages get no bridge into your files or Keychain.
307
+ Local loopback servers need zero setup; remote servers use short-lived,
308
+ revocable capability tokens (created over HTTPS on the server, stored in the
309
+ Keychain) for their attention and search endpoints.
310
310
 
311
311
  Download the latest [RepoOS Hub DMG](https://github.com/repo-os/repoos/releases/latest/download/RepoOSHub.dmg),
312
312
  or browse [GitHub Releases](https://github.com/repo-os/repoos/releases). Setup,
313
313
  pairing, and limitation details are in the [Hub guide](https://docs.repoos.org/macos-hub).
314
- Current builds are ad-hoc signed, so macOS requires Control-click **Open** once;
315
- the guide has the exact steps. Developer ID signing and notarization are planned.
314
+ Current builds are ad-hoc signed and not notarized. After a first blocked launch,
315
+ use **System Settings → Privacy & Security → Open Anyway**; the guide has the
316
+ exact steps. Developer ID signing and notarization are planned.
316
317
 
317
318
  ## Roadmap
318
319
 
@@ -1,4 +1,4 @@
1
1
  {
2
- "hash": "c59ba0faf030004be937d5bd9fb28afc6fdd9156857ae68454cba1c959f0f979",
3
- "version": "0.5.54"
2
+ "hash": "f30fcce6f5c1d53000699830d693eabb1921532ae40f9462a44af88519527c92",
3
+ "version": "0.5.56"
4
4
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "generatedAt": "2026-09-23T11:41:25.327Z"
2
+ "generatedAt": "2026-09-26T09:59:53.699Z"
3
3
  }
@@ -499,6 +499,22 @@ function resolveVar(value, map, depth = 0) {
499
499
  const next = map[`--${m[1]}`];
500
500
  return next !== undefined ? resolveVar(next, map, depth + 1) : value;
501
501
  }
502
+ // KNOWN LIMITATION (fix tracked in #0504, found while registering the gruvbox
503
+ // theme in #0503): the rgb()/rgba() branches below tolerate no whitespace between
504
+ // components, but oxfmt writes `rgba(110, 157, 106, 0.22)` with spaces, so every
505
+ // spaced literal fails to parse and returns null. A pair whose fg or bg yields no
506
+ // candidates is SKIPPED rather than failed, so a `[check] contrastPairs` bg token
507
+ // written as a spaced `rgba()` is silently never checked — as are the spaced
508
+ // `rgba()` stops inside a gradient, which drop out of `colorCandidates`. Measured
509
+ // on this repo's own stylesheet: 425 spaced literals, leaving 4-6 of the 9
510
+ // configured pairs unchecked in every theme. A whitespace-tolerant regex (`\s*`
511
+ // after each separator) was measured to produce ZERO new offenders across all five
512
+ // themes, so the fix is a one-liner that only strengthens the gate. It is its own
513
+ // task rather than folded into a theme change because it alters the guard's
514
+ // behaviour repo-wide and for every project that configures
515
+ // `[check] contrastPairs`. Until it lands, do not rely on this guard to catch a
516
+ // contrast problem in a token written as `rgba(r, g, b, a)`; gruvbox deliberately
517
+ // uses 8-digit hex for its checked alpha tokens to stay verifiable.
502
518
  function parseColor(v) {
503
519
  const s = v.trim().toLowerCase();
504
520
  if (!s || s === "none")
@@ -521,7 +537,7 @@ function parseColor(v) {
521
537
  }
522
538
  return null;
523
539
  }
524
- const m = s.match(/^rgba?\(([\d.]+),([\d.]+),([\d.]+)(?:,([\d.]+))?\)$/);
540
+ const m = s.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)$/);
525
541
  if (m) {
526
542
  return {
527
543
  r: parseInt(m[1], 10),
@@ -556,7 +572,7 @@ function colorCandidates(raw, map, bg) {
556
572
  const resolved = resolveVar(raw, map);
557
573
  const out = [];
558
574
  if (resolved.includes("gradient(")) {
559
- const re = /#[0-9a-fA-F]{3,8}|rgba?\([^)]*\)|transparent/g;
575
+ const re = /#[0-9a-fA-F]{3,8}|rgba?\(\s*[\d.]+\s*,\s*[\d.]+\s*,\s*[\d.]+(?:\s*,\s*[\d.]+)?\s*\)|transparent/g;
560
576
  let m;
561
577
  while ((m = re.exec(resolved))) {
562
578
  const c = parseColor(m[0]);
@@ -101,8 +101,9 @@ ${description
101
101
  architecture note. Keep them specific to this project.
102
102
  4. Break the result into a handful of concrete tasks with
103
103
  \`repoos new "<title>"\`, and move the ones that are ready into \`ready\`.
104
- 5. Record the docs you wrote here, then move this task to \`review\` (or
105
- \`done\` if there is genuinely nothing left to capture).
104
+ 5. Record the docs you wrote here, then hand this task off with
105
+ \`repoos mv <id> review\` and stop — RepoOS runs the checks and moves the
106
+ status (or \`done\` if there is genuinely nothing left to capture).
106
107
 
107
108
  ## Notes for AI
108
109
 
@@ -140,8 +141,9 @@ then turn what's missing into work.
140
141
  conventions anyone working here must follow.
141
142
  3. Draft a small starter backlog of real tasks with \`repoos new "<title>"\`,
142
143
  each concrete enough to work on its own.
143
- 4. Record what you wrote here, then move this task to \`review\` (or \`done\` if
144
- there is nothing left to capture).
144
+ 4. Record what you wrote here, then hand this task off with
145
+ \`repoos mv <id> review\` and stop — RepoOS runs the checks and moves the
146
+ status (or \`done\` if there is nothing left to capture).
145
147
 
146
148
  ## Notes for AI
147
149
 
@@ -160,14 +162,24 @@ repo itself is the source of truth. This file tells AI agents how to operate.
160
162
  2. Pick a task from \`${workDir}/\` whose \`status: ready\`.
161
163
  3. Set its \`status: active\` (edit the frontmatter; do not move the file).
162
164
  4. Create a worktree on the branch named in the task's \`branch:\` field, or set one.
163
- 5. Implement → test → if the repo has a git remote, open an MR/PR against main.
164
- 6. Set \`status: review\` when ready for human sign-off. **Leave the worktree open; do NOT merge its branch.**
165
+ 5. Run \`repoos check\` and confirm it passes, then implement → test → if the repo
166
+ has a git remote, open an MR/PR against main.
167
+ 6. When ready for human sign-off, run \`repoos mv <id> review\` and stop. **That
168
+ records a handoff request; it does not set the status.** RepoOS re-runs the
169
+ check, commits the branch and moves the task to \`review\`. The task stays
170
+ \`active\` until then, and stays \`active\` with the failure shown if the check
171
+ fails. **Leave the worktree open; do NOT merge its branch.**
165
172
 
166
173
  ## Review and sign-off
167
174
 
168
175
  A \`review\` task stays in its open worktree until a human (or another AI) signs
169
176
  off; the implementer never merges to \`main\` at \`review\` time.
170
177
 
178
+ Every route into \`review\` — the Review button, a board drag, a
179
+ \`PATCH status: review\`, \`repoos mv\`, or an agent's handoff signal — runs the
180
+ same finalization: scoped \`repoos check\`, then the commit guard, then
181
+ \`review\`. Nothing reaches \`review\` on the commit guard alone.
182
+
171
183
  - No git remote: on approval, the reviewer says **"move task <id> to done"**. The
172
184
  implementer then sets \`status: done\` (commit \`docs(<id>): set status done\`),
173
185
  fast-forward merges the branch to \`main\`, removes the worktree, and deletes
@@ -177,7 +189,7 @@ off; the implementer never merges to \`main\` at \`review\` time.
177
189
  <id> to done"), then remote + local branches are deleted and \`status\` is set
178
190
  to \`done\`.
179
191
  - Requested changes are fixed on the same worktree, tests re-run, and the task
180
- re-set to \`review\`.
192
+ handed off again.
181
193
  - If the \`reviewer\` agent is enabled on the Agents page, RepoOS reviews the
182
194
  task automatically when it lands in \`review\` and shows a short report (bugs,
183
195
  edge cases, suggestions) in the task drawer. It is advisory: the human still
@@ -2,41 +2,61 @@
2
2
  export declare function cmdList(statusArg?: string): void;
3
3
  /** `repoos show <id>` — full task detail. */
4
4
  export declare function cmdShow(id?: string): void;
5
+ /**
6
+ * True when this process is a managed agent working on `id` — the runner
7
+ * injects `REPOOS_AGENT=1` into every managed agent process, and
8
+ * `REPOOS_TASK_ID` / `REPOOS_RUN_ID` into a task turn (agents.ts). Both must
9
+ * match: without the run id the recorded request could not be tied to a live
10
+ * turn, and without the task id this would hijack `repoos mv` calls an agent
11
+ * makes about some OTHER task. A human in their own shell has neither, so they
12
+ * keep the plain status edit (which the server still runs through the same
13
+ * finalization via the file-watch route).
14
+ */
15
+ export declare function isRunnerSessionForTask(id: string): boolean;
5
16
  /**
6
17
  * `repoos mv <id> <status>` — change status (frontmatter edit).
7
18
  *
8
- * Deliberately does NOT run this transition through `guardReviewTransition`
9
- * (#0263), even for a move into `review`. That gate lives in the HTTP PATCH
10
- * path because it operates on the task's OWN branch worktree — staging and
11
- * committing pending implementation changes there and rejecting a vacuous
12
- * (zero-source-change) transition — which is an agent-handoff guarantee, not
13
- * a board-write guarantee. `repoos mv` is the low-level, generic "edit this
14
- * frontmatter field" tool (a human or script can move ANY task to ANY status,
15
- * with or without a branch or worktree at all); forcing every review move
16
- * through worktree resolution would turn a one-line status edit into a hard
17
- * dependency on handoff plumbing that plenty of legitimate `mv` calls don't
18
- * have. The bug this task fixes is narrower and applies to every status
19
- * change here regardless of target: `updateStatus` (via `rewrite()` in
20
- * core/repoos.ts) now always commits the task file in the main checkout, so
21
- * the write itself is never left as an untrusted dirty file. Agents that want
22
- * the full handoff guarantee (implementation committed + non-vacuous) should
23
- * go through the trusted handoff/PATCH path, not this CLI shortcut.
19
+ * Two transitions are special-cased, because both are ones this command
20
+ * cannot perform on its own.
21
+ *
22
+ * **`review` from inside the runner session for that same task (#0507)** does
23
+ * NOT flip `status:`. It records a handoff request in the board's cache dir
24
+ * (`.repoos/handoff-requests/<id>.json`) and exits 0; the runner picks that up
25
+ * exactly like the `::repoos-handoff-ready::` signal and finalizes when the
26
+ * turn ends — scoped `repoos check`, then the commit/vacuity gate, then
27
+ * `review`. This is the single most-used route in practice: engineers reach for
28
+ * it because the CTO nudge says "hand off to review" and the operating loop
29
+ * says "set `status: review`". A bare frontmatter write is not a handoff — it
30
+ * skips the check, and because the task leaves `active` the server stops the
31
+ * very agent that asked, which `cleanup()` then books as a dev error (#0505,
32
+ * #0499: "needs input" next to "waiting for human" on a task that passed
33
+ * review). Detection is the runner's own marker — `REPOOS_AGENT=1` plus a
34
+ * `REPOOS_TASK_ID` equal to the id being moved — so a human running the same
35
+ * command in their own shell still gets a plain status edit, picked up by the
36
+ * server and run through the same finalization via the file-watch route.
37
+ *
38
+ * `done` gets a narrow guard for the mirror-image reason (confirmed live,
39
+ * 2026-09-17 — see #0399 and the #0185/#0389 incidents in this session):
40
+ * unlike every other status, `done` claims the task's code is actually on
41
+ * `main`. The server's own HTTP PATCH route already refuses a bare
42
+ * `status: "done"` for exactly this reason (routes/tasks.ts) and forces callers
43
+ * through `POST /api/tasks/:id/done`, the real close-out pipeline — but this CLI
44
+ * command never went through that route to begin with, so it silently flips the
45
+ * flag with zero merge awareness. If the task has a `branch` that still exists
46
+ * locally and is NOT an ancestor of main, that branch's code has not landed;
47
+ * refuse rather than mark it done from under the user. This intentionally fails
48
+ * OPEN (allows the move) whenever it can't tell for sure — no branch recorded,
49
+ * the branch was already deleted (the normal post-merge cleanup), or git can't
50
+ * answer the ancestry question — so it never blocks the many legitimate `mv`
51
+ * calls that have nothing to do with code at all.
24
52
  *
25
- * `done` gets one narrow exception to the "generic, no side checks" rule
26
- * above (confirmed live, 2026-09-17 — see #0399 and the #0185/#0389 incidents
27
- * in this session): unlike every other status, `done` claims the task's code
28
- * is actually on `main`. The server's own HTTP PATCH route already refuses a
29
- * bare `status: "done"` for exactly this reason (routes/tasks.ts) and forces
30
- * callers through `POST /api/tasks/:id/done`, the real close-out pipeline —
31
- * but this CLI command never went through that route to begin with, so it
32
- * silently flips the flag with zero merge awareness. If the task has a
33
- * `branch` that still exists locally and is NOT an ancestor of main, that
34
- * branch's code has not landed; refuse rather than mark it done from under
35
- * the user. This intentionally fails OPEN (allows the move) whenever it
36
- * can't tell for sure — no branch recorded, the branch was already deleted
37
- * (the normal post-merge cleanup), or git can't answer the ancestry question
38
- * — so it never blocks the many legitimate `mv` calls that have nothing to
39
- * do with code at all.
53
+ * Every other status is a plain frontmatter edit. `updateStatus` (via
54
+ * `rewrite()` in core/repoos.ts) always commits the task file in the main
55
+ * checkout, so the write itself is never left as an untrusted dirty file. Note
56
+ * that a move into `review` from OUTSIDE a runner session still goes through
57
+ * the full server-side finalization — the server intercepts the file write and
58
+ * runs the same check the handoff signal does, keeping the task `active` until
59
+ * it passes.
40
60
  */
41
61
  export declare function cmdMv(id?: string, status?: string, note?: string, opts?: {
42
62
  force?: boolean;
@@ -13,6 +13,7 @@ import { boardRoot, loadConfig, resolveColumnLabels } from "../core/config.js";
13
13
  import { STATUSES } from "../core/types.js";
14
14
  import { c, statusColor, priorityColor } from "../cli/colors.js";
15
15
  import { patchTaskFile } from "../server/write.js";
16
+ import { writeHandoffRequest } from "../server/handoff-request.js";
16
17
  import { isAncestor } from "../core/git.js";
17
18
  /**
18
19
  * RepoOS facade rooted at the LIVE BOARD's checkout (the main checkout), even
@@ -136,41 +137,98 @@ function detectMainBranch(root) {
136
137
  }
137
138
  return null;
138
139
  }
140
+ /**
141
+ * True when this process is a managed agent working on `id` — the runner
142
+ * injects `REPOOS_AGENT=1` into every managed agent process, and
143
+ * `REPOOS_TASK_ID` / `REPOOS_RUN_ID` into a task turn (agents.ts). Both must
144
+ * match: without the run id the recorded request could not be tied to a live
145
+ * turn, and without the task id this would hijack `repoos mv` calls an agent
146
+ * makes about some OTHER task. A human in their own shell has neither, so they
147
+ * keep the plain status edit (which the server still runs through the same
148
+ * finalization via the file-watch route).
149
+ */
150
+ export function isRunnerSessionForTask(id) {
151
+ return (process.env.REPOOS_AGENT === "1" &&
152
+ Boolean(process.env.REPOOS_RUN_ID) &&
153
+ process.env.REPOOS_TASK_ID === id);
154
+ }
155
+ /**
156
+ * #0507: record a handoff request instead of flipping `status: review`, when
157
+ * the task's own agent is the one asking. Returns true when the request was
158
+ * recorded and `cmdMv` must stop there. On failure it returns false so the
159
+ * caller falls through to the ordinary status edit — worse, but never a dead
160
+ * end for the agent mid-turn.
161
+ */
162
+ function handoffRequestFromRunner(id) {
163
+ if (!isRunnerSessionForTask(id))
164
+ return false;
165
+ const { root } = boardRoot();
166
+ const request = {
167
+ taskId: id,
168
+ runId: process.env.REPOOS_RUN_ID,
169
+ at: new Date().toISOString(),
170
+ source: "repoos-mv",
171
+ };
172
+ if (!writeHandoffRequest(root, loadConfig(root).cacheDir, request)) {
173
+ console.error(c.red(` Could not record a handoff request for #${id} — `) +
174
+ c.dim("RepoOS could not write its handoff-request file."));
175
+ console.error(c.dim(" Falling back to a plain status edit, which SKIPS the checks and stops this " +
176
+ "agent mid-turn. Prefer ending your turn and letting the reviewer pick it up."));
177
+ return false;
178
+ }
179
+ console.log(" " +
180
+ c.green("handoff requested ") +
181
+ c.dim("#" + id) +
182
+ c.dim(" —") +
183
+ "\n" +
184
+ c.dim(" RepoOS will run `repoos check`, pass the commit gate, and move this to review."));
185
+ console.log(c.dim(" `status:` was left alone — end your turn now; finalization runs on exit."));
186
+ return true;
187
+ }
139
188
  /**
140
189
  * `repoos mv <id> <status>` — change status (frontmatter edit).
141
190
  *
142
- * Deliberately does NOT run this transition through `guardReviewTransition`
143
- * (#0263), even for a move into `review`. That gate lives in the HTTP PATCH
144
- * path because it operates on the task's OWN branch worktree — staging and
145
- * committing pending implementation changes there and rejecting a vacuous
146
- * (zero-source-change) transition — which is an agent-handoff guarantee, not
147
- * a board-write guarantee. `repoos mv` is the low-level, generic "edit this
148
- * frontmatter field" tool (a human or script can move ANY task to ANY status,
149
- * with or without a branch or worktree at all); forcing every review move
150
- * through worktree resolution would turn a one-line status edit into a hard
151
- * dependency on handoff plumbing that plenty of legitimate `mv` calls don't
152
- * have. The bug this task fixes is narrower and applies to every status
153
- * change here regardless of target: `updateStatus` (via `rewrite()` in
154
- * core/repoos.ts) now always commits the task file in the main checkout, so
155
- * the write itself is never left as an untrusted dirty file. Agents that want
156
- * the full handoff guarantee (implementation committed + non-vacuous) should
157
- * go through the trusted handoff/PATCH path, not this CLI shortcut.
191
+ * Two transitions are special-cased, because both are ones this command
192
+ * cannot perform on its own.
193
+ *
194
+ * **`review` from inside the runner session for that same task (#0507)** does
195
+ * NOT flip `status:`. It records a handoff request in the board's cache dir
196
+ * (`.repoos/handoff-requests/<id>.json`) and exits 0; the runner picks that up
197
+ * exactly like the `::repoos-handoff-ready::` signal and finalizes when the
198
+ * turn ends — scoped `repoos check`, then the commit/vacuity gate, then
199
+ * `review`. This is the single most-used route in practice: engineers reach for
200
+ * it because the CTO nudge says "hand off to review" and the operating loop
201
+ * says "set `status: review`". A bare frontmatter write is not a handoff — it
202
+ * skips the check, and because the task leaves `active` the server stops the
203
+ * very agent that asked, which `cleanup()` then books as a dev error (#0505,
204
+ * #0499: "needs input" next to "waiting for human" on a task that passed
205
+ * review). Detection is the runner's own marker — `REPOOS_AGENT=1` plus a
206
+ * `REPOOS_TASK_ID` equal to the id being moved — so a human running the same
207
+ * command in their own shell still gets a plain status edit, picked up by the
208
+ * server and run through the same finalization via the file-watch route.
158
209
  *
159
- * `done` gets one narrow exception to the "generic, no side checks" rule
160
- * above (confirmed live, 2026-09-17 — see #0399 and the #0185/#0389 incidents
161
- * in this session): unlike every other status, `done` claims the task's code
162
- * is actually on `main`. The server's own HTTP PATCH route already refuses a
163
- * bare `status: "done"` for exactly this reason (routes/tasks.ts) and forces
164
- * callers through `POST /api/tasks/:id/done`, the real close-out pipeline —
165
- * but this CLI command never went through that route to begin with, so it
166
- * silently flips the flag with zero merge awareness. If the task has a
167
- * `branch` that still exists locally and is NOT an ancestor of main, that
168
- * branch's code has not landed; refuse rather than mark it done from under
169
- * the user. This intentionally fails OPEN (allows the move) whenever it
170
- * can't tell for sure — no branch recorded, the branch was already deleted
171
- * (the normal post-merge cleanup), or git can't answer the ancestry question
172
- * — so it never blocks the many legitimate `mv` calls that have nothing to
173
- * do with code at all.
210
+ * `done` gets a narrow guard for the mirror-image reason (confirmed live,
211
+ * 2026-09-17 — see #0399 and the #0185/#0389 incidents in this session):
212
+ * unlike every other status, `done` claims the task's code is actually on
213
+ * `main`. The server's own HTTP PATCH route already refuses a bare
214
+ * `status: "done"` for exactly this reason (routes/tasks.ts) and forces callers
215
+ * through `POST /api/tasks/:id/done`, the real close-out pipeline — but this CLI
216
+ * command never went through that route to begin with, so it silently flips the
217
+ * flag with zero merge awareness. If the task has a `branch` that still exists
218
+ * locally and is NOT an ancestor of main, that branch's code has not landed;
219
+ * refuse rather than mark it done from under the user. This intentionally fails
220
+ * OPEN (allows the move) whenever it can't tell for sure — no branch recorded,
221
+ * the branch was already deleted (the normal post-merge cleanup), or git can't
222
+ * answer the ancestry question — so it never blocks the many legitimate `mv`
223
+ * calls that have nothing to do with code at all.
224
+ *
225
+ * Every other status is a plain frontmatter edit. `updateStatus` (via
226
+ * `rewrite()` in core/repoos.ts) always commits the task file in the main
227
+ * checkout, so the write itself is never left as an untrusted dirty file. Note
228
+ * that a move into `review` from OUTSIDE a runner session still goes through
229
+ * the full server-side finalization — the server intercepts the file write and
230
+ * runs the same check the handoff signal does, keeping the task `active` until
231
+ * it passes.
174
232
  */
175
233
  export function cmdMv(id, status, note, opts = {}) {
176
234
  if (!id || !status) {
@@ -186,6 +244,8 @@ export function cmdMv(id, status, note, opts = {}) {
186
244
  // .git) is a silent no-op from the board's perspective.
187
245
  const repoos = boardRepoOS();
188
246
  try {
247
+ if (status === "review" && handoffRequestFromRunner(id))
248
+ return;
189
249
  if (status === "done" && !opts.force) {
190
250
  const existing = repoos.getTask(id);
191
251
  if (existing && existing.status !== "done" && existing.branch) {
@@ -1482,12 +1482,12 @@ export function getConfigSchema() {
1482
1482
  // [board.columns] — display-only label overrides (#0396).
1483
1483
  ...STATUSES.map((status) => ({
1484
1484
  key: `board.columns.${status}`,
1485
- label: `Column label: ${status}`,
1485
+ label: `${status.charAt(0).toUpperCase() + status.slice(1)} column`,
1486
1486
  type: "string",
1487
1487
  tier: "live",
1488
1488
  restartRequired: false,
1489
1489
  default: DEFAULT_COLUMN_LABELS[status],
1490
- description: `Display label for the "${status}" column (${DEFAULT_COLUMN_LABELS[status]})`,
1490
+ description: `Display label only — the "${status}" status ID never changes (default: ${DEFAULT_COLUMN_LABELS[status]})`,
1491
1491
  })),
1492
1492
  ];
1493
1493
  }
@@ -19,7 +19,7 @@
19
19
  import { accessSync, constants, existsSync, readdirSync, readFileSync, statSync } from "node:fs";
20
20
  import { extname, isAbsolute, join, relative, resolve, sep } from "node:path";
21
21
  import { findRepoRoot, isLinkedWorktreeRoot, loadConfig, mainCheckoutRoot, parseFlatToml, projectDisplayName, resolveServePort, SUPPORTED_TOML_KEYS, worktreesDir, } from "./config.js";
22
- import { STATUSES } from "./types.js";
22
+ import { STATUSES, UI_THEMES } from "./types.js";
23
23
  import { checkBuildForRoot } from "./build.js";
24
24
  import { detectRepoMarkers, hasBinary, installHint, missingBinaries, prereqDetail, } from "./check-runner.js";
25
25
  import { resolveCheckPlan } from "./check-plan.js";
@@ -187,7 +187,7 @@ export function findConfigValueProblems(parsed) {
187
187
  enumCheck("defaultAssignee", ["ai", "human", "unassigned"]);
188
188
  enumCheck("defaultTaskMode", ["freeform", "manual"]);
189
189
  enumCheck("theme", ["dark", "light", "system"]);
190
- enumCheck("uiTheme", ["classic", "clear", "gen z", "jelly"]);
190
+ enumCheck("uiTheme", UI_THEMES);
191
191
  const boolCheck = (key) => {
192
192
  const v = get(key);
193
193
  if (v !== undefined && typeof v !== "boolean") {
@@ -47,7 +47,11 @@ export interface Input {
47
47
  export declare function ensureInputNumbers(c: RepoOSConfig): Input[];
48
48
  export declare function listInputs(c: RepoOSConfig): Input[];
49
49
  export declare function createInput(c: RepoOSConfig, body: string, type?: string, createdBy?: string): Input;
50
- export declare function updateInput(c: RepoOSConfig, id: string, status: InputStatus): Input;
50
+ export type InputPatch = {
51
+ status?: InputStatus;
52
+ text?: string;
53
+ };
54
+ export declare function updateInput(c: RepoOSConfig, id: string, patch: InputPatch): Input;
51
55
  /**
52
56
  * Record how an input was resolved and move it to `processed`. `task` stores
53
57
  * the created task's id in `resolved_task`; `none` clears it. Both keys are
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- import { parseDocument } from "./frontmatter.js";
3
+ import { parseDocument, serializeDocument } from "./frontmatter.js";
4
4
  const q = (v) => `"${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
5
5
  /**
6
6
  * Set a frontmatter key in a file's raw content, adding it to the block if it
@@ -12,6 +12,19 @@ function setField(content, key, value) {
12
12
  const re = new RegExp(`^${key}:.*$`, "m");
13
13
  return re.test(content) ? content.replace(re, line) : content.replace("---\n", `---\n${line}\n`);
14
14
  }
15
+ const INPUT_KEY_ORDER = [
16
+ "id",
17
+ "number",
18
+ "title",
19
+ "status",
20
+ "type",
21
+ "area",
22
+ "created_by",
23
+ "created_at",
24
+ "updated_at",
25
+ "resolution",
26
+ "resolved_task",
27
+ ];
15
28
  const slug = (v) => v
16
29
  .toLowerCase()
17
30
  .replace(/[^a-z0-9]+/g, "-")
@@ -135,22 +148,35 @@ export function createInput(c, body, type = "other", createdBy = "") {
135
148
  ].join("\n"));
136
149
  return listInputs(c).find((i) => i.id === id);
137
150
  }
138
- export function updateInput(c, id, status) {
151
+ export function updateInput(c, id, patch) {
152
+ if (patch.status === undefined && patch.text === undefined)
153
+ throw new Error("nothing to update");
139
154
  const item = listInputs(c).find((i) => i.id === id);
140
155
  if (!item)
141
156
  throw new Error("input not found");
142
157
  const file = join(c.root, item.path);
143
- let content = readFileSync(file, "utf8")
144
- .replace(/^status:.*$/m, `status: ${status}`)
145
- .replace(/^updated_at:.*$/m, `updated_at: ${q(new Date().toISOString())}`);
158
+ const parsed = parseDocument(readFileSync(file, "utf8"));
159
+ const status = patch.status ?? item.status;
160
+ let body = parsed.body.trim();
161
+ let title = String(parsed.data.title ?? item.title);
162
+ if (patch.text !== undefined) {
163
+ body = patch.text.trim();
164
+ title = body.split(/\n/)[0].replace(/^#\s*/, "").slice(0, 100) || "Untitled input";
165
+ }
166
+ const data = {
167
+ ...parsed.data,
168
+ status,
169
+ title,
170
+ updated_at: new Date().toISOString(),
171
+ };
146
172
  // A manual move out of `processed` invalidates any recorded resolution —
147
173
  // leaving it behind would re-display a stale outcome if the input is later
148
174
  // marked processed again without a real resolve action.
149
175
  if (status !== "processed") {
150
- content = setField(content, "resolution", "");
151
- content = setField(content, "resolved_task", "");
176
+ data.resolution = "";
177
+ data.resolved_task = "";
152
178
  }
153
- writeFileSync(file, content);
179
+ writeFileSync(file, serializeDocument(data, `${body}\n`, INPUT_KEY_ORDER));
154
180
  return listInputs(c).find((i) => i.id === id);
155
181
  }
156
182
  /**
@@ -0,0 +1,8 @@
1
+ import type { Task } from "./types.js";
2
+ /**
3
+ * Stale `dev-error` on a `review` task: the engineer reached review but a
4
+ * handoff-signal glitch left the flag set (see TaskDrawer banner note).
5
+ */
6
+ export declare function needsInputSuppressedOnReview(task: Pick<Task, "status" | "needsInput" | "needsInputReason">): boolean;
7
+ /** A successful review run clears these reviewer-episode flags (#0511). */
8
+ export declare function needsInputClearsOnSuccessfulReview(task: Pick<Task, "status" | "needsInputReason">): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Stale `dev-error` on a `review` task: the engineer reached review but a
3
+ * handoff-signal glitch left the flag set (see TaskDrawer banner note).
4
+ */
5
+ export function needsInputSuppressedOnReview(task) {
6
+ return Boolean(task.needsInput && task.status === "review" && task.needsInputReason === "dev-error");
7
+ }
8
+ /** A successful review run clears these reviewer-episode flags (#0511). */
9
+ export function needsInputClearsOnSuccessfulReview(task) {
10
+ if (task.needsInputReason === "review-failed")
11
+ return true;
12
+ return task.needsInputReason === "watchdog-stuck" && task.status === "review";
13
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Read-only git history for the Context page History tab.
3
+ *
4
+ * Every git invocation is argv (never a shell string). Inputs are validated
5
+ * before they reach `git`. Output is a machine format with unit-separator
6
+ * fields and a NUL record terminator — never parsed from human `git log`
7
+ * pretty-print.
8
+ */
9
+ import { type DiffResult } from "./git.js";
10
+ export declare const DEFAULT_LOG_LIMIT = 50;
11
+ export declare const MAX_LOG_LIMIT = 100;
12
+ export interface RepoCommit {
13
+ sha: string;
14
+ shortSha: string;
15
+ subject: string;
16
+ body: string;
17
+ authorName: string;
18
+ authorEmail: string;
19
+ date: string;
20
+ refs: string[];
21
+ parents: string[];
22
+ taskId: string | null;
23
+ }
24
+ export interface RepoCommitFile {
25
+ path: string;
26
+ additions: number;
27
+ deletions: number;
28
+ status: "added" | "deleted" | "modified" | "binary";
29
+ }
30
+ export interface RepoCommitDetail extends RepoCommit {
31
+ files: RepoCommitFile[];
32
+ patch: string;
33
+ truncated: boolean;
34
+ }
35
+ export interface RepoLogPage {
36
+ ok: true;
37
+ commits: RepoCommit[];
38
+ nextCursor: string | null;
39
+ branch: string;
40
+ defaultBranch: string;
41
+ }
42
+ export interface RepoBranchList {
43
+ ok: true;
44
+ defaultBranch: string;
45
+ branches: string[];
46
+ }
47
+ export type RepoLogError = {
48
+ ok: false;
49
+ error: string;
50
+ code: "not-git" | "invalid" | "missing";
51
+ };
52
+ export declare function isValidSha(value: string): boolean;
53
+ export declare function isValidBranch(value: string): boolean;
54
+ /** Strip trailing slashes so `src/ui-app/` is the same filter as `src/ui-app`. */
55
+ export declare function normalizePathFilter(value: string): string;
56
+ export declare function isValidPathFilter(value: string): boolean;
57
+ /** `type(NNNN): subject` — the RepoOS commit-message convention. */
58
+ export declare function extractTaskId(subject: string): string | null;
59
+ export declare function parseDecorations(raw: string): string[];
60
+ /** Parse NUL-terminated `--format` records from `git log`. Exported for tests. */
61
+ export declare function parseGitLogOutput(stdout: string): RepoCommit[];
62
+ export declare function resolveDefaultBranch(root: string): Promise<string>;
63
+ export declare function listRepoBranches(root: string): Promise<RepoBranchList | RepoLogError>;
64
+ export declare function listRepoLog(root: string, opts?: {
65
+ branch?: string;
66
+ path?: string;
67
+ limit?: number;
68
+ before?: string;
69
+ }): Promise<RepoLogPage | RepoLogError>;
70
+ export declare function truncatePatch(stdout: string): DiffResult;
71
+ export declare function getRepoCommit(root: string, sha: string): Promise<RepoCommitDetail | RepoLogError>;
72
+ export declare function getCommitFileContents(root: string, sha: string, filePath: string): Promise<{
73
+ ok: true;
74
+ before: string;
75
+ after: string;
76
+ existsBefore: boolean;
77
+ existsAfter: boolean;
78
+ } | RepoLogError>;