issue-flow 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +43 -97
  2. package/dist/agent-4DHRKZ2Z.js +32 -0
  3. package/dist/{analyze-Q4ONIWRZ.js → analyze-UEJ2R3H5.js} +42 -41
  4. package/dist/analyze-UEJ2R3H5.js.map +1 -0
  5. package/dist/{apply-EEFPY2W4.js → apply-QDYNQMUX.js} +22 -24
  6. package/dist/apply-QDYNQMUX.js.map +1 -0
  7. package/dist/artifacts-AMJQJFFV.js +82 -0
  8. package/dist/artifacts-AMJQJFFV.js.map +1 -0
  9. package/dist/{bench-WWWXLTFH.js → bench-RNRXVEFK.js} +42 -42
  10. package/dist/{chunk-RYOUZ6EK.js → chunk-23VHVVKW.js} +3 -3
  11. package/dist/chunk-2EYLOS5I.js +99 -0
  12. package/dist/chunk-2EYLOS5I.js.map +1 -0
  13. package/dist/{chunk-C5TL7T4V.js → chunk-2YM2VH6R.js} +2 -2
  14. package/dist/{chunk-XUEMATVY.js → chunk-37O2FMAN.js} +28 -25
  15. package/dist/chunk-37O2FMAN.js.map +1 -0
  16. package/dist/chunk-44RF3VFG.js +717 -0
  17. package/dist/chunk-44RF3VFG.js.map +1 -0
  18. package/dist/chunk-6AOZCWN2.js +21 -0
  19. package/dist/chunk-6AOZCWN2.js.map +1 -0
  20. package/dist/chunk-7MJGYPHF.js +1571 -0
  21. package/dist/chunk-7MJGYPHF.js.map +1 -0
  22. package/dist/{chunk-SA6HXL4Y.js → chunk-7WBUEBD5.js} +9 -9
  23. package/dist/chunk-7WBUEBD5.js.map +1 -0
  24. package/dist/chunk-7ZZUSMBJ.js +35 -0
  25. package/dist/chunk-7ZZUSMBJ.js.map +1 -0
  26. package/dist/{chunk-QJ44VG5G.js → chunk-ADH6RJNX.js} +3 -3
  27. package/dist/{chunk-34OCSKOB.js → chunk-AKL5UPR6.js} +159 -114
  28. package/dist/chunk-AKL5UPR6.js.map +1 -0
  29. package/dist/{chunk-7F7LGMVO.js → chunk-BTBJ5UOO.js} +12 -12
  30. package/dist/{chunk-7F7LGMVO.js.map → chunk-BTBJ5UOO.js.map} +1 -1
  31. package/dist/{chunk-HOAHMERN.js → chunk-CLZ6RRZL.js} +21 -21
  32. package/dist/chunk-DBLXLKGU.js +1201 -0
  33. package/dist/chunk-DBLXLKGU.js.map +1 -0
  34. package/dist/{chunk-DUS5ZNKL.js → chunk-DIMDAYZS.js} +7 -6
  35. package/dist/chunk-DZ3DZ3EN.js +278 -0
  36. package/dist/chunk-DZ3DZ3EN.js.map +1 -0
  37. package/dist/{chunk-CRGIQFUQ.js → chunk-E2H3JV2E.js} +3 -1
  38. package/dist/chunk-E2H3JV2E.js.map +1 -0
  39. package/dist/{chunk-45WZ6GMI.js → chunk-EORDSKWZ.js} +17 -17
  40. package/dist/chunk-EORDSKWZ.js.map +1 -0
  41. package/dist/{chunk-LTH6XYZ3.js → chunk-EYKPWHT7.js} +40 -30
  42. package/dist/chunk-EYKPWHT7.js.map +1 -0
  43. package/dist/{chunk-OODXFCQV.js → chunk-FU2FQ4JE.js} +14 -16
  44. package/dist/{chunk-OODXFCQV.js.map → chunk-FU2FQ4JE.js.map} +1 -1
  45. package/dist/{chunk-V7QOIQYC.js → chunk-G442RDFX.js} +9 -23
  46. package/dist/{chunk-V7QOIQYC.js.map → chunk-G442RDFX.js.map} +1 -1
  47. package/dist/{chunk-QPGEFLMY.js → chunk-I64N6RL6.js} +7 -7
  48. package/dist/{chunk-KRAGAUKD.js → chunk-JBYXJOJJ.js} +201 -64
  49. package/dist/chunk-JBYXJOJJ.js.map +1 -0
  50. package/dist/{chunk-SOM4QA6C.js → chunk-JCR5C5A7.js} +93 -18
  51. package/dist/chunk-JCR5C5A7.js.map +1 -0
  52. package/dist/{chunk-RNFUVKC3.js → chunk-K5NM26NV.js} +30 -13
  53. package/dist/chunk-K5NM26NV.js.map +1 -0
  54. package/dist/{chunk-YVPZUN6X.js → chunk-KUA6KBBD.js} +335 -38
  55. package/dist/chunk-KUA6KBBD.js.map +1 -0
  56. package/dist/{chunk-KMCS76ZW.js → chunk-KUL64XUF.js} +44 -12
  57. package/dist/chunk-KUL64XUF.js.map +1 -0
  58. package/dist/{chunk-HAKDLR6N.js → chunk-MAJ6QDRL.js} +65 -78
  59. package/dist/chunk-MAJ6QDRL.js.map +1 -0
  60. package/dist/{chunk-DGRTJD5R.js → chunk-MML2G7IC.js} +7 -7
  61. package/dist/chunk-NP47HRX2.js +136 -0
  62. package/dist/chunk-NP47HRX2.js.map +1 -0
  63. package/dist/{chunk-3R27MJ5R.js → chunk-PFWHHYEO.js} +3 -3
  64. package/dist/{chunk-ECU7YO7S.js → chunk-PM5YWJYO.js} +22 -6
  65. package/dist/chunk-PM5YWJYO.js.map +1 -0
  66. package/dist/{chunk-E4I6MMHD.js → chunk-PXPTIXJF.js} +154 -101
  67. package/dist/chunk-PXPTIXJF.js.map +1 -0
  68. package/dist/{chunk-CWSQVHVV.js → chunk-QJHZ3HYJ.js} +248 -102
  69. package/dist/chunk-QJHZ3HYJ.js.map +1 -0
  70. package/dist/{chunk-LI35PIIS.js → chunk-RDPK7LYK.js} +20 -117
  71. package/dist/chunk-RDPK7LYK.js.map +1 -0
  72. package/dist/{chunk-PMLE7KAQ.js → chunk-RMIBLFG3.js} +30 -14
  73. package/dist/chunk-RMIBLFG3.js.map +1 -0
  74. package/dist/{chunk-62GVVZQO.js → chunk-T736DSZI.js} +32 -316
  75. package/dist/chunk-T736DSZI.js.map +1 -0
  76. package/dist/{chunk-TZYINKCC.js → chunk-UDUUUFQF.js} +57 -29
  77. package/dist/chunk-UDUUUFQF.js.map +1 -0
  78. package/dist/{chunk-NEWNXZ65.js → chunk-V7GETODQ.js} +36 -28
  79. package/dist/{chunk-NEWNXZ65.js.map → chunk-V7GETODQ.js.map} +1 -1
  80. package/dist/{chunk-PPZ3K4YM.js → chunk-VHQMMY2D.js} +2 -2
  81. package/dist/{chunk-ZVZMWNCG.js → chunk-VJ3PF6B7.js} +50 -16
  82. package/dist/chunk-VJ3PF6B7.js.map +1 -0
  83. package/dist/{chunk-L6HSKTQE.js → chunk-W7LZLJAF.js} +4 -4
  84. package/dist/{chunk-6K2LCFG6.js → chunk-WB4FA2EH.js} +70 -81
  85. package/dist/chunk-WB4FA2EH.js.map +1 -0
  86. package/dist/{chunk-LGHND47K.js → chunk-WQSGL5LX.js} +22 -16
  87. package/dist/chunk-WQSGL5LX.js.map +1 -0
  88. package/dist/{chunk-ICM463EO.js → chunk-XNK5UN2D.js} +5 -649
  89. package/dist/chunk-XNK5UN2D.js.map +1 -0
  90. package/dist/{chunk-7UMMFOL2.js → chunk-Y2KOWABB.js} +39 -24
  91. package/dist/chunk-Y2KOWABB.js.map +1 -0
  92. package/dist/{chunk-QF7VEL3M.js → chunk-YC24E66P.js} +568 -158
  93. package/dist/chunk-YC24E66P.js.map +1 -0
  94. package/dist/{chunk-EG2MGDP5.js → chunk-Z6RCKCVB.js} +52 -46
  95. package/dist/chunk-Z6RCKCVB.js.map +1 -0
  96. package/dist/{chunk-YUNBOUQH.js → chunk-ZPGNVBWY.js} +8 -8
  97. package/dist/cli.js +132 -62
  98. package/dist/cli.js.map +1 -1
  99. package/dist/compat-OJG76J4I.js +24 -0
  100. package/dist/{config-LEBZZCJP.js → config-6YTWPDBK.js} +20 -17
  101. package/dist/{contract-JH2WBIO6.js → contract-HWLFFL3F.js} +2 -2
  102. package/dist/{conventions-XOT6YW5L.js → conventions-5UPOTU42.js} +20 -16
  103. package/dist/{conventions-XOT6YW5L.js.map → conventions-5UPOTU42.js.map} +1 -1
  104. package/dist/db-QAZG4LQD.js +264 -0
  105. package/dist/db-QAZG4LQD.js.map +1 -0
  106. package/dist/diagnostics-YEFB42E5.js +25 -0
  107. package/dist/execute-QCT64D37.js +38 -0
  108. package/dist/{generate-JGW5JSKB.js → generate-QATMDKOK.js} +46 -43
  109. package/dist/generate-QATMDKOK.js.map +1 -0
  110. package/dist/{git-A2IP32PQ.js → git-XGOECXWG.js} +2 -3
  111. package/dist/history-NBPUZVGQ.js +101 -0
  112. package/dist/history-NBPUZVGQ.js.map +1 -0
  113. package/dist/init-4LB2YKWP.js +28 -0
  114. package/dist/{operations-O4FORFIH.js → operations-RUQP2Y2F.js} +78 -52
  115. package/dist/operations-RUQP2Y2F.js.map +1 -0
  116. package/dist/{permissions-G4XCAESF.js → permissions-QZPUUBSO.js} +5 -6
  117. package/dist/plan-2NMSHTMD.js +38 -0
  118. package/dist/{policy-U5NBFAO5.js → policy-RKKFF5A2.js} +17 -15
  119. package/dist/{policy-U5NBFAO5.js.map → policy-RKKFF5A2.js.map} +1 -1
  120. package/dist/pr-QKFO5HI3.js +42 -0
  121. package/dist/pr-review-H2BFD6F2.js +33 -0
  122. package/dist/prd-ET6XKYRS.js +37 -0
  123. package/dist/{ps-76L6ZAZL.js → ps-UIKSOUAQ.js} +26 -15
  124. package/dist/ps-UIKSOUAQ.js.map +1 -0
  125. package/dist/{recorder-2BAKPBRA.js → recorder-LOU3PTIE.js} +11 -7
  126. package/dist/registry-HV7M6MAH.js +17 -0
  127. package/dist/repository-RCRR6RDE.js +80 -0
  128. package/dist/resume-BWJZGBLZ.js +278 -0
  129. package/dist/resume-BWJZGBLZ.js.map +1 -0
  130. package/dist/review-2MTZLNSS.js +38 -0
  131. package/dist/routing-RM5ABHHD.js +35 -0
  132. package/dist/run-XMACQBRJ.js +61 -0
  133. package/dist/{session-publisher-KAJQRHNZ.js → session-publisher-Q5TNEZPX.js} +3 -3
  134. package/dist/session-publisher-Q5TNEZPX.js.map +1 -0
  135. package/dist/{state-manager-UPHO2EA2.js → state-manager-SRN4VJSX.js} +9 -4
  136. package/dist/state-manager-SRN4VJSX.js.map +1 -0
  137. package/dist/{usage-KTMYXAPF.js → usage-JOODVE5O.js} +69 -63
  138. package/dist/usage-JOODVE5O.js.map +1 -0
  139. package/dist/web-TLMWJ2BM.js +205 -0
  140. package/dist/web-TLMWJ2BM.js.map +1 -0
  141. package/package.json +22 -8
  142. package/prompts/analyze.md +16 -19
  143. package/prompts/execute.md +73 -144
  144. package/prompts/generate.md +23 -10
  145. package/prompts/plan.md +32 -60
  146. package/prompts/pr-review.md +58 -40
  147. package/prompts/pr.md +50 -16
  148. package/prompts/prd.md +26 -19
  149. package/prompts/review.md +41 -18
  150. package/web/public/app.js +111 -112
  151. package/dist/agent-HMZTXCRT.js +0 -30
  152. package/dist/analyze-Q4ONIWRZ.js.map +0 -1
  153. package/dist/apply-EEFPY2W4.js.map +0 -1
  154. package/dist/chunk-34OCSKOB.js.map +0 -1
  155. package/dist/chunk-45WZ6GMI.js.map +0 -1
  156. package/dist/chunk-62GVVZQO.js.map +0 -1
  157. package/dist/chunk-6K2LCFG6.js.map +0 -1
  158. package/dist/chunk-7UMMFOL2.js.map +0 -1
  159. package/dist/chunk-CRGIQFUQ.js.map +0 -1
  160. package/dist/chunk-CWSQVHVV.js.map +0 -1
  161. package/dist/chunk-E4I6MMHD.js.map +0 -1
  162. package/dist/chunk-ECU7YO7S.js.map +0 -1
  163. package/dist/chunk-EG2MGDP5.js.map +0 -1
  164. package/dist/chunk-HAKDLR6N.js.map +0 -1
  165. package/dist/chunk-ICM463EO.js.map +0 -1
  166. package/dist/chunk-KMCS76ZW.js.map +0 -1
  167. package/dist/chunk-KRAGAUKD.js.map +0 -1
  168. package/dist/chunk-LGHND47K.js.map +0 -1
  169. package/dist/chunk-LI35PIIS.js.map +0 -1
  170. package/dist/chunk-LTH6XYZ3.js.map +0 -1
  171. package/dist/chunk-PMLE7KAQ.js.map +0 -1
  172. package/dist/chunk-QF7VEL3M.js.map +0 -1
  173. package/dist/chunk-RNFUVKC3.js.map +0 -1
  174. package/dist/chunk-SA6HXL4Y.js.map +0 -1
  175. package/dist/chunk-SOM4QA6C.js.map +0 -1
  176. package/dist/chunk-TZYINKCC.js.map +0 -1
  177. package/dist/chunk-WCCZJL7W.js +0 -141
  178. package/dist/chunk-WCCZJL7W.js.map +0 -1
  179. package/dist/chunk-XEYH6ZZI.js +0 -289
  180. package/dist/chunk-XEYH6ZZI.js.map +0 -1
  181. package/dist/chunk-XUEMATVY.js.map +0 -1
  182. package/dist/chunk-XZUEFTUP.js +0 -243
  183. package/dist/chunk-XZUEFTUP.js.map +0 -1
  184. package/dist/chunk-YVPZUN6X.js.map +0 -1
  185. package/dist/chunk-ZVZMWNCG.js.map +0 -1
  186. package/dist/diagnostics-HTCWF7F4.js +0 -23
  187. package/dist/execute-LELOSR5T.js +0 -37
  188. package/dist/generate-JGW5JSKB.js.map +0 -1
  189. package/dist/init-TO6MUXO4.js +0 -26
  190. package/dist/operations-O4FORFIH.js.map +0 -1
  191. package/dist/plan-OXAT344Q.js +0 -34
  192. package/dist/pr-UZHNKKZC.js +0 -39
  193. package/dist/pr-review-Q4ACAWDM.js +0 -32
  194. package/dist/prd-6CYEMLDY.js +0 -33
  195. package/dist/ps-76L6ZAZL.js.map +0 -1
  196. package/dist/registry-XLHHYLTR.js +0 -15
  197. package/dist/resume-URCU6CIJ.js +0 -244
  198. package/dist/resume-URCU6CIJ.js.map +0 -1
  199. package/dist/review-LVZT2TMW.js +0 -35
  200. package/dist/routing-EK6RBZM4.js +0 -33
  201. package/dist/run-2RXJCJKT.js +0 -56
  202. package/dist/usage-KTMYXAPF.js.map +0 -1
  203. package/dist/web-B5UPHHR5.js +0 -172
  204. package/dist/web-B5UPHHR5.js.map +0 -1
  205. /package/dist/{agent-HMZTXCRT.js.map → agent-4DHRKZ2Z.js.map} +0 -0
  206. /package/dist/{bench-WWWXLTFH.js.map → bench-RNRXVEFK.js.map} +0 -0
  207. /package/dist/{chunk-RYOUZ6EK.js.map → chunk-23VHVVKW.js.map} +0 -0
  208. /package/dist/{chunk-C5TL7T4V.js.map → chunk-2YM2VH6R.js.map} +0 -0
  209. /package/dist/{chunk-QJ44VG5G.js.map → chunk-ADH6RJNX.js.map} +0 -0
  210. /package/dist/{chunk-HOAHMERN.js.map → chunk-CLZ6RRZL.js.map} +0 -0
  211. /package/dist/{chunk-DUS5ZNKL.js.map → chunk-DIMDAYZS.js.map} +0 -0
  212. /package/dist/{chunk-QPGEFLMY.js.map → chunk-I64N6RL6.js.map} +0 -0
  213. /package/dist/{chunk-DGRTJD5R.js.map → chunk-MML2G7IC.js.map} +0 -0
  214. /package/dist/{chunk-3R27MJ5R.js.map → chunk-PFWHHYEO.js.map} +0 -0
  215. /package/dist/{chunk-PPZ3K4YM.js.map → chunk-VHQMMY2D.js.map} +0 -0
  216. /package/dist/{chunk-L6HSKTQE.js.map → chunk-W7LZLJAF.js.map} +0 -0
  217. /package/dist/{chunk-YUNBOUQH.js.map → chunk-ZPGNVBWY.js.map} +0 -0
  218. /package/dist/{config-LEBZZCJP.js.map → compat-OJG76J4I.js.map} +0 -0
  219. /package/dist/{contract-JH2WBIO6.js.map → config-6YTWPDBK.js.map} +0 -0
  220. /package/dist/{diagnostics-HTCWF7F4.js.map → contract-HWLFFL3F.js.map} +0 -0
  221. /package/dist/{execute-LELOSR5T.js.map → diagnostics-YEFB42E5.js.map} +0 -0
  222. /package/dist/{git-A2IP32PQ.js.map → execute-QCT64D37.js.map} +0 -0
  223. /package/dist/{init-TO6MUXO4.js.map → git-XGOECXWG.js.map} +0 -0
  224. /package/dist/{permissions-G4XCAESF.js.map → init-4LB2YKWP.js.map} +0 -0
  225. /package/dist/{plan-OXAT344Q.js.map → permissions-QZPUUBSO.js.map} +0 -0
  226. /package/dist/{pr-UZHNKKZC.js.map → plan-2NMSHTMD.js.map} +0 -0
  227. /package/dist/{pr-review-Q4ACAWDM.js.map → pr-QKFO5HI3.js.map} +0 -0
  228. /package/dist/{prd-6CYEMLDY.js.map → pr-review-H2BFD6F2.js.map} +0 -0
  229. /package/dist/{recorder-2BAKPBRA.js.map → prd-ET6XKYRS.js.map} +0 -0
  230. /package/dist/{registry-XLHHYLTR.js.map → recorder-LOU3PTIE.js.map} +0 -0
  231. /package/dist/{review-LVZT2TMW.js.map → registry-HV7M6MAH.js.map} +0 -0
  232. /package/dist/{routing-EK6RBZM4.js.map → repository-RCRR6RDE.js.map} +0 -0
  233. /package/dist/{run-2RXJCJKT.js.map → review-2MTZLNSS.js.map} +0 -0
  234. /package/dist/{session-publisher-KAJQRHNZ.js.map → routing-RM5ABHHD.js.map} +0 -0
  235. /package/dist/{state-manager-UPHO2EA2.js.map → run-XMACQBRJ.js.map} +0 -0
package/web/public/app.js CHANGED
@@ -177,7 +177,7 @@
177
177
 
178
178
  // Ícone textual (○ ● ✓ ✗): a cor e o rótulo ao lado já dizem o estado.
179
179
  function statusIcon(status, symbol) {
180
- const icon = el('span', 'item-icon icon-' + status, symbol || PHASE_ICONS[status] || '○');
180
+ const icon = el('span', `item-icon icon-${status}`, symbol || PHASE_ICONS[status] || '○');
181
181
  icon.setAttribute('aria-hidden', 'true');
182
182
  return icon;
183
183
  }
@@ -203,17 +203,17 @@
203
203
  function formatDuration(totalSeconds) {
204
204
  if (totalSeconds === null || totalSeconds === undefined || totalSeconds < 0) return '—';
205
205
  const seconds = Math.round(totalSeconds);
206
- if (seconds < 60) return seconds + 's';
206
+ if (seconds < 60) return `${seconds}s`;
207
207
  const minutes = Math.floor(seconds / 60);
208
- if (minutes < 60) return minutes + 'min ' + String(seconds % 60).padStart(2, '0') + 's';
208
+ if (minutes < 60) return `${minutes}min ${String(seconds % 60).padStart(2, '0')}s`;
209
209
  const hours = Math.floor(minutes / 60);
210
- return hours + 'h ' + String(minutes % 60).padStart(2, '0') + 'min';
210
+ return `${hours}h ${String(minutes % 60).padStart(2, '0')}min`;
211
211
  }
212
212
 
213
213
  function formatAgo(iso) {
214
214
  const ms = parseIso(iso);
215
215
  if (ms === null) return '';
216
- return '' + formatDuration((Date.now() - ms) / 1000);
216
+ return `${formatDuration((Date.now() - ms) / 1000)}`;
217
217
  }
218
218
 
219
219
  function formatClock(iso) {
@@ -233,8 +233,8 @@
233
233
  // 1523 → 1.5k, 2400000 → 2.4M (mesma regra do resumo no terminal).
234
234
  function compactTokens(value) {
235
235
  const abs = Math.abs(value);
236
- if (abs >= 1000000) return (value / 1000000).toFixed(1) + 'M';
237
- if (abs >= 1000) return (value / 1000).toFixed(1) + 'k';
236
+ if (abs >= 1000000) return `${(value / 1000000).toFixed(1)}M`;
237
+ if (abs >= 1000) return `${(value / 1000).toFixed(1)}k`;
238
238
  return String(Math.round(value));
239
239
  }
240
240
 
@@ -253,14 +253,14 @@
253
253
  const input = metric(usage.inputTokens);
254
254
  const output = metric(usage.outputTokens);
255
255
  const io = [];
256
- if (input !== null) io.push(compactTokens(input) + ' in');
257
- if (output !== null) io.push(compactTokens(output) + ' out');
256
+ if (input !== null) io.push(`${compactTokens(input)} in`);
257
+ if (output !== null) io.push(`${compactTokens(output)} out`);
258
258
  if (io.length > 0) segments.push(io.join(' / '));
259
259
 
260
260
  const cacheRead = metric(usage.cacheReadTokens);
261
261
  const cacheCreation = metric(usage.cacheCreationTokens);
262
262
  if (cacheRead !== null || cacheCreation !== null) {
263
- segments.push(compactTokens((cacheRead || 0) + (cacheCreation || 0)) + ' cache');
263
+ segments.push(`${compactTokens((cacheRead || 0) + (cacheCreation || 0))} cache`);
264
264
  }
265
265
 
266
266
  const cost = metric(usage.costUsd);
@@ -289,7 +289,7 @@
289
289
 
290
290
  // URL do repositório derivada da URL da issue (…/issues/N → raiz do repo).
291
291
  function repoUrl(snapshot) {
292
- const url = snapshot.issue && snapshot.issue.url;
292
+ const url = snapshot.issue?.url;
293
293
  if (!url) return null;
294
294
  const match = url.match(/^(https?:\/\/\S+?)\/issues\/\d+\/?$/);
295
295
  return match ? match[1] : null;
@@ -357,7 +357,7 @@
357
357
  if (panel) panel.hidden = !active;
358
358
  if (active) focused = tab;
359
359
  }
360
- if (options && options.focus && focused) focused.focus();
360
+ if (options?.focus && focused) focused.focus();
361
361
  }
362
362
 
363
363
  // Padrão ARIA de tablist: setas movem entre as abas, Home/End vão às pontas.
@@ -386,7 +386,7 @@
386
386
  let raw = null;
387
387
  try {
388
388
  raw = window.localStorage.getItem(THEME_STORAGE_KEY);
389
- } catch (err) {
389
+ } catch (_err) {
390
390
  // localStorage indisponível (ex.: bloqueado) — segue no modo sistema.
391
391
  }
392
392
  return raw === 'light' || raw === 'dark' ? raw : 'system';
@@ -395,7 +395,7 @@
395
395
  function storeTheme(value) {
396
396
  try {
397
397
  window.localStorage.setItem(THEME_STORAGE_KEY, value);
398
- } catch (err) {
398
+ } catch (_err) {
399
399
  // Persistência é conveniência; falha é ignorada. Sem gravar, a escolha
400
400
  // continua valendo nesta aba — só não sobrevive ao reload.
401
401
  }
@@ -463,7 +463,7 @@
463
463
  let raw = null;
464
464
  try {
465
465
  raw = window.localStorage.getItem(STORAGE_KEY);
466
- } catch (err) {
466
+ } catch (_err) {
467
467
  // localStorage indisponível (ex.: bloqueado) — segue com o default.
468
468
  }
469
469
  if (raw === null) return null;
@@ -474,7 +474,7 @@
474
474
  function storeRefresh(value) {
475
475
  try {
476
476
  window.localStorage.setItem(STORAGE_KEY, String(value));
477
- } catch (err) {
477
+ } catch (_err) {
478
478
  // Persistência é conveniência; falha é ignorada.
479
479
  }
480
480
  }
@@ -488,7 +488,7 @@
488
488
  }
489
489
  clear(select);
490
490
  for (const value of values) {
491
- const option = el('option', null, value + 's');
491
+ const option = el('option', null, `${value}s`);
492
492
  option.value = String(value);
493
493
  select.appendChild(option);
494
494
  }
@@ -561,14 +561,14 @@
561
561
  }
562
562
 
563
563
  function statusUrlFor(session) {
564
- if (!session || !session.statusUrl) return 'api/status';
564
+ if (!session?.statusUrl) return 'api/status';
565
565
  // statusUrl vem com barra inicial (/api/status?session=…); o fetch relativo
566
566
  // ao path do painel precisa da forma sem a barra absoluta do host.
567
567
  return session.statusUrl.replace(/^\//, '');
568
568
  }
569
569
 
570
570
  function eventsUrlFor(session) {
571
- if (!session || !session.eventsUrl) return null;
571
+ if (!session?.eventsUrl) return null;
572
572
  return session.eventsUrl.replace(/^\//, '');
573
573
  }
574
574
 
@@ -586,7 +586,7 @@
586
586
  // então é ela que explica o que está na tela. As duas aparecem juntas no card
587
587
  // de configuração quando divergem.
588
588
  function renderMonitorVersion(version) {
589
- const label = typeof version === 'string' && version !== '' ? 'v' + version : null;
589
+ const label = typeof version === 'string' && version !== '' ? `v${version}` : null;
590
590
  state.monitorVersion = label === null ? null : version;
591
591
  for (const node of [els.appVersion, els.appVersionDashboard]) {
592
592
  if (!node) continue;
@@ -643,7 +643,7 @@
643
643
  try {
644
644
  const sessionsRes = await fetch('api/sessions', { cache: 'no-store' });
645
645
  if (serverInstanceChanged(sessionsRes)) return;
646
- if (!sessionsRes.ok) throw new Error('HTTP ' + sessionsRes.status);
646
+ if (!sessionsRes.ok) throw new Error(`HTTP ${sessionsRes.status}`);
647
647
  const sessions = await sessionsRes.json();
648
648
  if (!Array.isArray(sessions)) throw new Error('sessions payload invalid');
649
649
 
@@ -701,12 +701,12 @@
701
701
  return;
702
702
  }
703
703
  if (res.status !== 304) {
704
- if (!res.ok) throw new Error('HTTP ' + res.status);
704
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
705
705
  state.etag = res.headers.get('ETag');
706
706
  state.snapshot = await res.json();
707
707
  render();
708
708
  }
709
- const configUrl = 'api/config?session=' + encodeURIComponent(state.detailSessionId || '');
709
+ const configUrl = `api/config?session=${encodeURIComponent(state.detailSessionId || '')}`;
710
710
  const configRes = await fetch(configUrl, { cache: 'no-store' });
711
711
  if (configRes.ok) {
712
712
  state.configData = await configRes.json();
@@ -714,11 +714,10 @@
714
714
  }
715
715
  if (state.eventsUrl) {
716
716
  const eventsRes = await fetch(state.eventsUrl, { cache: 'no-store' });
717
- if (!eventsRes.ok) throw new Error('HTTP ' + eventsRes.status);
717
+ if (!eventsRes.ok) throw new Error(`HTTP ${eventsRes.status}`);
718
718
  const entries = await eventsRes.json();
719
719
  state.events = Array.isArray(entries) ? entries : [];
720
- const diagnosticUrl =
721
- 'api/diagnostics?session=' + encodeURIComponent(state.detailSessionId || '');
720
+ const diagnosticUrl = `api/diagnostics?session=${encodeURIComponent(state.detailSessionId || '')}`;
722
721
  const diagnosticsRes = await fetch(diagnosticUrl, { cache: 'no-store' });
723
722
  if (diagnosticsRes.ok) {
724
723
  const diagnostics = await diagnosticsRes.json();
@@ -730,7 +729,7 @@
730
729
  setViewMode('detail');
731
730
  state.failures = 0;
732
731
  els.banner.hidden = true;
733
- } catch (err) {
732
+ } catch (_err) {
734
733
  state.failures += 1;
735
734
  els.banner.hidden = false;
736
735
  } finally {
@@ -761,7 +760,7 @@
761
760
  if (!text) return '';
762
761
  const normalized = String(text).replace(/\s+/g, ' ').trim();
763
762
  if (normalized.length <= max) return normalized;
764
- return normalized.slice(0, max - 1).trimEnd() + '…';
763
+ return `${normalized.slice(0, max - 1).trimEnd()}…`;
765
764
  }
766
765
 
767
766
  // Linha secundária do header do dashboard: quantas execuções e em que
@@ -777,16 +776,16 @@
777
776
  const count = counts[status];
778
777
  if (!count) continue;
779
778
  const label = SUMMARY_STATUS_LABELS[status];
780
- parts.push(count + ' ' + (count === 1 ? label[0] : label[1]));
779
+ parts.push(`${count} ${count === 1 ? label[0] : label[1]}`);
781
780
  }
782
781
  const total = sessions.length + (sessions.length === 1 ? ' execução' : ' execuções');
783
- els.dashboardSummary.textContent = parts.length > 0 ? total + ' · ' + parts.join(' · ') : total;
782
+ els.dashboardSummary.textContent = parts.length > 0 ? `${total} · ${parts.join(' · ')}` : total;
784
783
  }
785
784
 
786
785
  function renderDashboard(sessions) {
787
786
  const active = document.activeElement;
788
787
  const focusedId =
789
- active && active.dataset && els.dashboard.contains(active) && active.dataset.sessionId
788
+ active?.dataset && els.dashboard.contains(active) && active.dataset.sessionId
790
789
  ? active.dataset.sessionId
791
790
  : null;
792
791
 
@@ -815,7 +814,7 @@
815
814
  'dashboard-project',
816
815
  session.repositoryName || 'Projeto desconhecido',
817
816
  );
818
- const badge = el('span', 'badge status-' + (session.status || 'idle'));
817
+ const badge = el('span', `badge status-${session.status || 'idle'}`);
819
818
  badge.textContent = STATUS_LABELS[session.status] || session.status || '—';
820
819
  head.appendChild(project);
821
820
  head.appendChild(badge);
@@ -823,7 +822,7 @@
823
822
 
824
823
  const titleRow = el('span', 'dashboard-title-row');
825
824
  if (session.issueNumber !== null && session.issueNumber !== undefined) {
826
- titleRow.appendChild(el('span', 'dashboard-issue', '#' + session.issueNumber));
825
+ titleRow.appendChild(el('span', 'dashboard-issue', `#${session.issueNumber}`));
827
826
  }
828
827
  titleRow.appendChild(el('span', 'dashboard-title', session.issueTitle || 'Sem título'));
829
828
  card.appendChild(titleRow);
@@ -832,9 +831,9 @@
832
831
  card.appendChild(el('span', 'dashboard-summary muted', summary || 'Sem descrição'));
833
832
 
834
833
  const meta = el('span', 'dashboard-meta-row');
835
- meta.appendChild(el('span', null, 'Fase: ' + (session.currentPhase || '—')));
834
+ meta.appendChild(el('span', null, `Fase: ${session.currentPhase || '—'}`));
836
835
  const percent = typeof session.progressPercent === 'number' ? session.progressPercent : 0;
837
- meta.appendChild(el('span', null, percent + '%'));
836
+ meta.appendChild(el('span', null, `${percent}%`));
838
837
  const elapsed =
839
838
  typeof session.elapsedSeconds === 'number'
840
839
  ? formatDuration(session.elapsedSeconds)
@@ -846,25 +845,25 @@
846
845
  // A card that only shows a percentage cannot tell a run that is
847
846
  // progressing from one that has been retrying for twenty minutes.
848
847
  if (typeof session.retries === 'number' && session.retries > 0) {
849
- meta.appendChild(el('span', null, session.retries + ' retry(s)'));
848
+ meta.appendChild(el('span', null, `${session.retries} retry(s)`));
850
849
  }
851
850
  if (typeof session.correctionCycle === 'number' && session.correctionCycle > 0) {
852
- meta.appendChild(el('span', null, 'correção ' + session.correctionCycle));
851
+ meta.appendChild(el('span', null, `correção ${session.correctionCycle}`));
853
852
  }
854
853
  if (session.updatedAt) {
855
854
  meta.appendChild(
856
- el('span', null, 'atividade ' + formatAgo(session.lastActivityAt || session.updatedAt)),
855
+ el('span', null, `atividade ${formatAgo(session.lastActivityAt || session.updatedAt)}`),
857
856
  );
858
857
  }
859
- if (session.provider) meta.appendChild(el('span', null, 'provider ' + session.provider));
858
+ if (session.provider) meta.appendChild(el('span', null, `provider ${session.provider}`));
860
859
  if (typeof session.attempt === 'number' && session.attempt > 0) {
861
- meta.appendChild(el('span', null, 'tentativa ' + session.attempt));
860
+ meta.appendChild(el('span', null, `tentativa ${session.attempt}`));
862
861
  }
863
862
  card.appendChild(meta);
864
863
 
865
864
  const progress = el('span', 'dashboard-progress');
866
865
  const bar = el('span', 'dashboard-progress-bar');
867
- bar.style.width = Math.max(0, Math.min(100, percent)) + '%';
866
+ bar.style.width = `${Math.max(0, Math.min(100, percent))}%`;
868
867
  progress.appendChild(bar);
869
868
  card.appendChild(progress);
870
869
 
@@ -884,11 +883,11 @@
884
883
 
885
884
  els.dashboardMeta.textContent =
886
885
  sessions.length + (sessions.length === 1 ? ' execução' : ' execuções');
887
- document.title = sessions.length + ' execuções · issue-flow';
886
+ document.title = `${sessions.length} execuções · issue-flow`;
888
887
 
889
888
  if (focusedId) {
890
889
  const card = els.dashboard.querySelector(
891
- '[data-session-id="' + focusedId.replace(/"/g, '') + '"]',
890
+ `[data-session-id="${focusedId.replace(/"/g, '')}"]`,
892
891
  );
893
892
  if (card) card.focus();
894
893
  }
@@ -922,12 +921,12 @@
922
921
  }
923
922
 
924
923
  function renderTitle(snapshot) {
925
- const issue = snapshot.issue.number !== null ? '#' + snapshot.issue.number : '';
924
+ const issue = snapshot.issue.number !== null ? `#${snapshot.issue.number}` : '';
926
925
  let prefix = '';
927
- if (snapshot.status === 'running') prefix = snapshot.progress.percent + '% · ';
926
+ if (snapshot.status === 'running') prefix = `${snapshot.progress.percent}% · `;
928
927
  else if (snapshot.status === 'completed') prefix = '✓ ';
929
928
  else if (snapshot.status === 'failed') prefix = '✗ ';
930
- document.title = (prefix + issue + ' · issue-flow').replace(/^ · /, '');
929
+ document.title = `${prefix + issue} · issue-flow`.replace(/^ · /, '');
931
930
  }
932
931
 
933
932
  // O h1 é a execução — número, título, e ao lado status, tempo decorrido e
@@ -936,7 +935,7 @@
936
935
  const hasIssue = snapshot.issue.number !== null && snapshot.issue.number !== undefined;
937
936
  if (hasIssue) {
938
937
  els.issueLink.hidden = false;
939
- els.issueLink.textContent = '#' + snapshot.issue.number;
938
+ els.issueLink.textContent = `#${snapshot.issue.number}`;
940
939
  if (snapshot.issue.url) els.issueLink.href = snapshot.issue.url;
941
940
  else els.issueLink.removeAttribute('href');
942
941
  } else {
@@ -956,11 +955,11 @@
956
955
  ? 'criada pelo Issue Flow'
957
956
  : 'origem da branch não informada';
958
957
  els.branchLine.textContent = branch
959
- ? (base ? branch + ' ' + base : branch) + ' · ' + branchMode
958
+ ? `${base ? `${branch}${base}` : branch} · ${branchMode}`
960
959
  : '';
961
960
 
962
961
  els.statusBadge.textContent = STATUS_LABELS[snapshot.status] || snapshot.status;
963
- els.statusBadge.className = 'badge status-' + snapshot.status;
962
+ els.statusBadge.className = `badge status-${snapshot.status}`;
964
963
  }
965
964
 
966
965
  function renderAlerts(snapshot) {
@@ -973,8 +972,8 @@
973
972
  if (!any) return;
974
973
 
975
974
  const counts = [];
976
- if (errors.length > 0) counts.push(errors.length + ' erro(s)');
977
- if (warnings.length > 0) counts.push(warnings.length + ' aviso(s)');
975
+ if (errors.length > 0) counts.push(`${errors.length} erro(s)`);
976
+ if (warnings.length > 0) counts.push(`${warnings.length} aviso(s)`);
978
977
  if (counts.length > 0) {
979
978
  els.alertsBody.appendChild(el('p', 'alert-count', counts.join(' · ')));
980
979
  }
@@ -987,13 +986,13 @@
987
986
  }
988
987
  for (const log of errors.slice(-ALERT_PREVIEW)) {
989
988
  const entry = el('div', 'alert-entry level-error');
990
- entry.appendChild(el('span', 'mono', formatClock(log.at) + ' '));
989
+ entry.appendChild(el('span', 'mono', `${formatClock(log.at)} `));
991
990
  entry.appendChild(document.createTextNode(log.message));
992
991
  els.alertsBody.appendChild(entry);
993
992
  }
994
993
  for (const log of warnings.slice(-ALERT_PREVIEW)) {
995
994
  const entry = el('div', 'alert-entry level-warn');
996
- entry.appendChild(el('span', 'mono', formatClock(log.at) + ' '));
995
+ entry.appendChild(el('span', 'mono', `${formatClock(log.at)} `));
997
996
  entry.appendChild(document.createTextNode(log.message));
998
997
  els.alertsBody.appendChild(entry);
999
998
  }
@@ -1012,7 +1011,7 @@
1012
1011
  const state = issue.state || null;
1013
1012
  const stateClass =
1014
1013
  state === 'open' ? 'state-open' : state === 'closed' ? 'state-closed' : 'state-unknown';
1015
- meta.appendChild(el('span', 'badge ' + stateClass, state || 'estado desconhecido'));
1014
+ meta.appendChild(el('span', `badge ${stateClass}`, state || 'estado desconhecido'));
1016
1015
  els.issueSummary.appendChild(meta);
1017
1016
 
1018
1017
  const labels = issue.labels || [];
@@ -1054,7 +1053,7 @@
1054
1053
  function renderProgress(snapshot) {
1055
1054
  const progress = snapshot.progress;
1056
1055
  els.progressBar.value = progress.percent;
1057
- els.progressPercent.textContent = progress.percent + '%';
1056
+ els.progressPercent.textContent = `${progress.percent}%`;
1058
1057
  const counters =
1059
1058
  'Fases ' +
1060
1059
  progress.phasesCompleted +
@@ -1065,7 +1064,7 @@
1065
1064
  '/' +
1066
1065
  progress.storiesTotal;
1067
1066
  const totals = formatTotals(snapshot.metrics);
1068
- els.progressCounters.textContent = totals ? counters + ' · ' + totals : counters;
1067
+ els.progressCounters.textContent = totals ? `${counters} · ${totals}` : counters;
1069
1068
  }
1070
1069
 
1071
1070
  function nowRow(grid, label, value) {
@@ -1123,7 +1122,7 @@
1123
1122
  resilience.cooldownUntil ? formatClock(resilience.cooldownUntil) : '—',
1124
1123
  );
1125
1124
  const activity = resilience.lastActivityAt
1126
- ? formatClock(resilience.lastActivityAt) + ' (' + formatAgo(resilience.lastActivityAt) + ')'
1125
+ ? `${formatClock(resilience.lastActivityAt)} (${formatAgo(resilience.lastActivityAt)})`
1127
1126
  : '—';
1128
1127
  nowRow(grid, 'Última atividade', activity);
1129
1128
  els.resilience.appendChild(grid);
@@ -1149,16 +1148,16 @@
1149
1148
  // estar em versões diferentes; este é o único lugar onde as duas aparecem
1150
1149
  // lado a lado, então a divergência é dita aqui.
1151
1150
  const env = snapshot.environment;
1152
- const runVersion = env && env.cliVersion ? env.cliVersion : null;
1151
+ const runVersion = env?.cliVersion ? env.cliVersion : null;
1153
1152
  const runtime = el('dl', 'now-grid');
1154
- const runtimeBits = [runVersion ? 'v' + runVersion : 'versão não registrada'];
1155
- if (env && env.node) runtimeBits.push(env.node);
1156
- if (env && env.platform) runtimeBits.push(env.platform);
1153
+ const runtimeBits = [runVersion ? `v${runVersion}` : 'versão não registrada'];
1154
+ if (env?.node) runtimeBits.push(env.node);
1155
+ if (env?.platform) runtimeBits.push(env.platform);
1157
1156
  nowRow(runtime, 'Issue Flow (execução)', runtimeBits.join(' · '));
1158
1157
  nowRow(
1159
1158
  runtime,
1160
1159
  'Monitor (este painel)',
1161
- state.monitorVersion ? 'v' + state.monitorVersion : '—',
1160
+ state.monitorVersion ? `v${state.monitorVersion}` : '—',
1162
1161
  );
1163
1162
  els.configuration.appendChild(runtime);
1164
1163
  if (runVersion !== null && state.monitorVersion && runVersion !== state.monitorVersion) {
@@ -1197,10 +1196,10 @@
1197
1196
  nowRow(summary, 'Precedência', list(config.precedence).join(' → '));
1198
1197
  els.configuration.appendChild(summary);
1199
1198
 
1200
- const liveRouting = state.configData && state.configData.routing;
1199
+ const liveRouting = state.configData?.routing;
1201
1200
  if (liveRouting) {
1202
1201
  const routingSummary = el('dl', 'now-grid config-routing-summary');
1203
- nowRow(routingSummary, 'Routing', liveRouting.mode + ' · perfil ' + liveRouting.profile);
1202
+ nowRow(routingSummary, 'Routing', `${liveRouting.mode} · perfil ${liveRouting.profile}`);
1204
1203
  nowRow(
1205
1204
  routingSummary,
1206
1205
  'Política',
@@ -1266,10 +1265,10 @@
1266
1265
  phaseButton.addEventListener('click', () => openDrawer('phase', phase.phase));
1267
1266
  row.appendChild(phaseButton);
1268
1267
  const provider = el('select');
1269
- provider.setAttribute('aria-label', 'Harness para ' + phase.phase);
1268
+ provider.setAttribute('aria-label', `Harness para ${phase.phase}`);
1270
1269
  fillProviderSelect(provider, phase.provider.value || 'claude');
1271
1270
  const model = el('select');
1272
- model.setAttribute('aria-label', 'Tier e modelo para ' + phase.phase);
1271
+ model.setAttribute('aria-label', `Tier e modelo para ${phase.phase}`);
1273
1272
  fillModelSelect(model, provider.value, phase.model.value || '');
1274
1273
  provider.addEventListener('change', () => fillModelSelect(model, provider.value, ''));
1275
1274
  const save = el('button', 'config-save', 'Salvar');
@@ -1324,7 +1323,7 @@
1324
1323
  const form = el('form', 'config-form');
1325
1324
  const provider = el('select');
1326
1325
  provider.setAttribute('aria-label', 'Harness padrão para execuções futuras');
1327
- for (const id of ['claude', 'codex', 'cursor', 'antigravity']) {
1326
+ for (const id of ['claude', 'codex', 'cursor', 'antigravity', 'opencode']) {
1328
1327
  const option = el('option', null, id);
1329
1328
  option.value = id;
1330
1329
  provider.appendChild(option);
@@ -1365,7 +1364,7 @@
1365
1364
  }
1366
1365
 
1367
1366
  function catalog() {
1368
- return list(state.configData && state.configData.catalog);
1367
+ return list(state.configData?.catalog);
1369
1368
  }
1370
1369
 
1371
1370
  function fillProviderSelect(select, selected) {
@@ -1380,7 +1379,7 @@
1380
1379
  });
1381
1380
  const providers = entries.length
1382
1381
  ? entries.map((entry) => entry.provider)
1383
- : ['claude', 'codex', 'cursor', 'antigravity'];
1382
+ : ['claude', 'codex', 'cursor', 'antigravity', 'opencode'];
1384
1383
  if (!providers.includes(selected)) providers.push(selected);
1385
1384
  for (const provider of providers) {
1386
1385
  const option = el('option', null, provider);
@@ -1396,9 +1395,9 @@
1396
1395
  defaultOption.value = '';
1397
1396
  select.appendChild(defaultOption);
1398
1397
  const entry = catalog().find((candidate) => candidate.provider === provider);
1399
- for (const model of list(entry && entry.models)) {
1398
+ for (const model of list(entry?.models)) {
1400
1399
  if (!model.id) continue;
1401
- const option = el('option', null, model.tier + ' · ' + model.id);
1400
+ const option = el('option', null, `${model.tier} · ${model.id}`);
1402
1401
  option.value = model.id;
1403
1402
  select.appendChild(option);
1404
1403
  }
@@ -1406,7 +1405,7 @@
1406
1405
  selected &&
1407
1406
  !Array.prototype.some.call(select.options, (option) => option.value === selected)
1408
1407
  ) {
1409
- const current = el('option', null, 'configurado · ' + selected);
1408
+ const current = el('option', null, `configurado · ${selected}`);
1410
1409
  current.value = selected;
1411
1410
  select.appendChild(current);
1412
1411
  }
@@ -1505,13 +1504,13 @@
1505
1504
  meta.appendChild(
1506
1505
  el(
1507
1506
  'span',
1508
- 'badge story-status-' + storyStatus,
1507
+ `badge story-status-${storyStatus}`,
1509
1508
  STORY_STATUS_LABELS[storyStatus] || storyStatus,
1510
1509
  ),
1511
1510
  );
1512
1511
  const dependencies = story.dependencies || [];
1513
1512
  if (dependencies.length > 0) {
1514
- meta.appendChild(el('span', 'muted story-deps', 'depende de: ' + dependencies.join(', ')));
1513
+ meta.appendChild(el('span', 'muted story-deps', `depende de: ${dependencies.join(', ')}`));
1515
1514
  }
1516
1515
  main.appendChild(meta);
1517
1516
 
@@ -1519,11 +1518,11 @@
1519
1518
  const duration = metric(story.durationSeconds);
1520
1519
  const stageLabel = STORY_STAGE_LABELS[storyStage] || storyStage;
1521
1520
  const stageText = story.stageSince
1522
- ? stageLabel + ' ' + formatAgo(story.stageSince)
1521
+ ? `${stageLabel} ${formatAgo(story.stageSince)}`
1523
1522
  : stageLabel;
1524
1523
  const side = itemSideText([
1525
1524
  stageText,
1526
- story.completedAt ? 'concluída ' + formatClock(story.completedAt) : '',
1525
+ story.completedAt ? `concluída ${formatClock(story.completedAt)}` : '',
1527
1526
  duration !== null ? formatDuration(duration) : '',
1528
1527
  formatUsage(story),
1529
1528
  ]);
@@ -1556,7 +1555,7 @@
1556
1555
  card.appendChild(el('span', 'kanban-card-title', story.title));
1557
1556
  if (story.description) card.appendChild(el('span', 'kanban-card-desc', story.description));
1558
1557
  card.appendChild(
1559
- el('span', 'badge story-status-' + story.status, STORY_STATUS_LABELS[story.status]),
1558
+ el('span', `badge story-status-${story.status}`, STORY_STATUS_LABELS[story.status]),
1560
1559
  );
1561
1560
  card.addEventListener('click', () => openDrawer('story', story.id));
1562
1561
  return card;
@@ -1601,10 +1600,10 @@
1601
1600
  if (cost.status === 'reported' || cost.status === 'estimated') {
1602
1601
  return formatCost(cost.amount, cost.status === 'estimated');
1603
1602
  }
1604
- return cost.reason ? 'não informado (' + cost.reason + ')' : 'não informado';
1603
+ return cost.reason ? `não informado (${cost.reason})` : 'não informado';
1605
1604
  }
1606
1605
 
1607
- function renderExecutionHistory(snapshot, executions) {
1606
+ function renderExecutionHistory(_snapshot, executions) {
1608
1607
  drawerSection('Tentativas, revisões e correções', (body) => {
1609
1608
  if (executions.length === 0) {
1610
1609
  body.appendChild(el('p', 'empty', 'Nenhuma invocação associada.'));
@@ -1617,7 +1616,7 @@
1617
1616
  title.appendChild(
1618
1617
  el(
1619
1618
  'span',
1620
- 'badge status-' + (execution.status === 'completed' ? 'completed' : execution.status),
1619
+ `badge status-${execution.status === 'completed' ? 'completed' : execution.status}`,
1621
1620
  execution.status,
1622
1621
  ),
1623
1622
  );
@@ -1625,7 +1624,7 @@
1625
1624
  el(
1626
1625
  'strong',
1627
1626
  null,
1628
- execution.purpose + ' · tentativa ' + execution.attempt + ' · ' + execution.trigger,
1627
+ `${execution.purpose} · tentativa ${execution.attempt} · ${execution.trigger}`,
1629
1628
  ),
1630
1629
  );
1631
1630
  item.appendChild(title);
@@ -1650,7 +1649,7 @@
1650
1649
  nowRow(grid, 'Tokens', formatUsage(execution.usage || {}) || '—');
1651
1650
  nowRow(grid, 'Custo', executionCost(execution));
1652
1651
  if (execution.correctionCycle)
1653
- nowRow(grid, 'Correção', 'ciclo ' + execution.correctionCycle);
1652
+ nowRow(grid, 'Correção', `ciclo ${execution.correctionCycle}`);
1654
1653
  if (execution.verdict?.status) nowRow(grid, 'Veredito', execution.verdict.status);
1655
1654
  item.appendChild(grid);
1656
1655
  if (execution.failure?.message)
@@ -1668,7 +1667,7 @@
1668
1667
  );
1669
1668
  drawerSection('Saída do processo', (body) => {
1670
1669
  const details = el('details', 'process-output');
1671
- const summary = el('summary', null, logs.length + ' linha(s) sanitizada(s)');
1670
+ const summary = el('summary', null, `${logs.length} linha(s) sanitizada(s)`);
1672
1671
  details.appendChild(summary);
1673
1672
  if (logs.length === 0) {
1674
1673
  details.appendChild(el('p', 'empty', 'Nenhuma saída capturada.'));
@@ -1676,7 +1675,7 @@
1676
1675
  const output = el('pre', 'process-output-body');
1677
1676
  output.textContent = logs
1678
1677
  .slice(-200)
1679
- .map((entry) => formatClock(entry.at) + ' ' + entry.message)
1678
+ .map((entry) => `${formatClock(entry.at)} ${entry.message}`)
1680
1679
  .join('\n');
1681
1680
  details.appendChild(output);
1682
1681
  }
@@ -1695,12 +1694,12 @@
1695
1694
  drawerSection('Diagnóstico global persistente', (body) => {
1696
1695
  const details = el('details', 'process-output');
1697
1696
  details.appendChild(
1698
- el('summary', null, entries.length + ' registro(s) em ~/.issue-flow/logs'),
1697
+ el('summary', null, `${entries.length} registro(s) em ~/.issue-flow/logs`),
1699
1698
  );
1700
1699
  const output = el('pre', 'process-output-body');
1701
1700
  output.textContent = entries
1702
1701
  .slice(0, 200)
1703
- .map((entry) => formatClock(entry.timestamp) + ' ' + entry.level + ' ' + entry.message)
1702
+ .map((entry) => `${formatClock(entry.timestamp)} ${entry.level} ${entry.message}`)
1704
1703
  .join('\n');
1705
1704
  details.appendChild(output);
1706
1705
  body.appendChild(details);
@@ -1722,8 +1721,8 @@
1722
1721
 
1723
1722
  clear(els.drawerBody);
1724
1723
  if (kind === 'phase') {
1725
- els.drawerTitle.textContent = 'Fase · ' + phase.name;
1726
- els.drawerBody.appendChild(el('span', 'badge status-' + phase.status, phase.status));
1724
+ els.drawerTitle.textContent = `Fase · ${phase.name}`;
1725
+ els.drawerBody.appendChild(el('span', `badge status-${phase.status}`, phase.status));
1727
1726
  const grid = el('dl', 'now-grid drawer-summary-grid');
1728
1727
  nowRow(grid, 'Início', phase.startedAt ? formatClock(phase.startedAt) : '—');
1729
1728
  nowRow(grid, 'Fim', phase.endedAt ? formatClock(phase.endedAt) : '—');
@@ -1750,13 +1749,13 @@
1750
1749
  return;
1751
1750
  }
1752
1751
 
1753
- els.drawerTitle.textContent = story.id + ' · ' + story.title;
1752
+ els.drawerTitle.textContent = `${story.id} · ${story.title}`;
1754
1753
  els.drawerBody.appendChild(
1755
- el('span', 'badge story-status-' + story.status, STORY_STATUS_LABELS[story.status]),
1754
+ el('span', `badge story-status-${story.status}`, STORY_STATUS_LABELS[story.status]),
1756
1755
  );
1757
1756
 
1758
1757
  const timing = itemSideText([
1759
- story.completedAt ? 'concluída ' + formatClock(story.completedAt) : '',
1758
+ story.completedAt ? `concluída ${formatClock(story.completedAt)}` : '',
1760
1759
  metric(story.durationSeconds) !== null ? formatDuration(story.durationSeconds) : '',
1761
1760
  ]);
1762
1761
  if (timing) els.drawerBody.appendChild(el('p', 'muted drawer-timing', timing));
@@ -1804,7 +1803,7 @@
1804
1803
  item.appendChild(
1805
1804
  document.createTextNode(
1806
1805
  (STORY_STAGE_LABELS[entry.stage] || entry.stage || '') +
1807
- (entry.detail ? ' · ' + entry.detail : ''),
1806
+ (entry.detail ? ` · ${entry.detail}` : ''),
1808
1807
  ),
1809
1808
  );
1810
1809
  entries.appendChild(item);
@@ -1845,7 +1844,7 @@
1845
1844
  // referência guardada na abertura.
1846
1845
  const card =
1847
1846
  selected && selected.kind === 'story'
1848
- ? els.kanban.querySelector('[data-story-id="' + selected.id + '"]')
1847
+ ? els.kanban.querySelector(`[data-story-id="${selected.id}"]`)
1849
1848
  : null;
1850
1849
  if (card) card.focus();
1851
1850
  }
@@ -1860,7 +1859,7 @@
1860
1859
  for (const commit of snapshot.git.commits) {
1861
1860
  const item = el('li');
1862
1861
  const hash = repo
1863
- ? link(repo + '/commit/' + commit.hash, commit.hash, 'mono commit-hash')
1862
+ ? link(`${repo}/commit/${commit.hash}`, commit.hash, 'mono commit-hash')
1864
1863
  : el('span', 'mono commit-hash', commit.hash);
1865
1864
  item.appendChild(hash);
1866
1865
  const subject = el('div', 'item-main item-title', commit.subject);
@@ -1880,7 +1879,7 @@
1880
1879
  } else {
1881
1880
  for (const pr of snapshot.pullRequests) {
1882
1881
  const item = el('li');
1883
- item.appendChild(link(pr.url, '#' + pr.number));
1882
+ item.appendChild(link(pr.url, `#${pr.number}`));
1884
1883
  item.appendChild(el('div', 'item-main item-title', pr.title));
1885
1884
  els.pullRequests.appendChild(item);
1886
1885
  }
@@ -1897,7 +1896,7 @@
1897
1896
  }
1898
1897
  // Mais recentes primeiro: monitoramento lê o topo, sem gerenciar scroll.
1899
1898
  for (const entry of entries.slice().reverse()) {
1900
- const item = el('li', 'level-' + entry.level);
1899
+ const item = el('li', `level-${entry.level}`);
1901
1900
  item.appendChild(el('span', 'log-time', formatClock(entry.at)));
1902
1901
  item.appendChild(el('span', 'log-level', entry.level));
1903
1902
  item.appendChild(el('span', 'log-message', entry.message));
@@ -1918,27 +1917,27 @@
1918
1917
  case 'session:start':
1919
1918
  return 'Sessão iniciada';
1920
1919
  case 'session:end':
1921
- return 'Sessão encerrada: ' + event.status;
1920
+ return `Sessão encerrada: ${event.status}`;
1922
1921
  case 'phase:start':
1923
- return 'Fase iniciada: ' + event.phase;
1922
+ return `Fase iniciada: ${event.phase}`;
1924
1923
  case 'phase:end':
1925
- return 'Fase encerrada: ' + event.phase + (event.success ? ' (ok)' : ' (falhou)');
1924
+ return `Fase encerrada: ${event.phase}${event.success ? ' (ok)' : ' (falhou)'}`;
1926
1925
  case 'iteration:start':
1927
- return 'Iteração ' + event.iteration + ' iniciada';
1926
+ return `Iteração ${event.iteration} iniciada`;
1928
1927
  case 'iteration:end':
1929
- return 'Iteração ' + event.iteration + ' encerrada';
1928
+ return `Iteração ${event.iteration} encerrada`;
1930
1929
  case 'retry':
1931
- return 'Retry ' + event.attempt + (event.kind ? ': ' + event.kind : '');
1930
+ return `Retry ${event.attempt}${event.kind ? `: ${event.kind}` : ''}`;
1932
1931
  case 'agent:attempt':
1933
- return 'Tentativa ' + event.attempt + ' com ' + event.provider;
1932
+ return `Tentativa ${event.attempt} com ${event.provider}`;
1934
1933
  case 'agent:activity':
1935
- return 'Atividade recebida de ' + event.provider;
1934
+ return `Atividade recebida de ${event.provider}`;
1936
1935
  case 'agent:result':
1937
1936
  return (
1938
1937
  event.provider +
1939
1938
  (event.success
1940
1939
  ? ' concluiu a tentativa'
1941
- : ' falhou: ' + (event.failureKind || 'desconhecida'))
1940
+ : ` falhou: ${event.failureKind || 'desconhecida'}`)
1942
1941
  );
1943
1942
  case 'failover':
1944
1943
  return (
@@ -1946,10 +1945,10 @@
1946
1945
  event.from +
1947
1946
  ' para ' +
1948
1947
  event.to +
1949
- (event.reason ? ': ' + event.reason : '')
1948
+ (event.reason ? `: ${event.reason}` : '')
1950
1949
  );
1951
1950
  case 'correction:cycle':
1952
- return 'Ciclo de correção ' + event.cycle + '/' + event.maxCycles;
1951
+ return `Ciclo de correção ${event.cycle}/${event.maxCycles}`;
1953
1952
  case 'log':
1954
1953
  return event.message;
1955
1954
  default:
@@ -1960,7 +1959,7 @@
1960
1959
  function renderHistory() {
1961
1960
  clear(els.history);
1962
1961
  const entries = state.events.filter((entry) => {
1963
- const type = entry && entry.event && entry.event.type;
1962
+ const type = entry?.event?.type;
1964
1963
  if (!type) return false;
1965
1964
  if (state.historyFilter === 'resilience') return RESILIENCE_EVENTS.has(type);
1966
1965
  if (state.historyFilter === 'pipeline') return !RESILIENCE_EVENTS.has(type);
@@ -1982,8 +1981,8 @@
1982
1981
 
1983
1982
  function renderMeta(snapshot) {
1984
1983
  const parts = [];
1985
- if (snapshot.sessionId) parts.push('execução ' + snapshot.sessionId);
1986
- if (snapshot.updatedAt) parts.push('atualizado ' + formatClock(snapshot.updatedAt));
1984
+ if (snapshot.sessionId) parts.push(`execução ${snapshot.sessionId}`);
1985
+ if (snapshot.updatedAt) parts.push(`atualizado ${formatClock(snapshot.updatedAt)}`);
1987
1986
  parts.push('somente leitura');
1988
1987
  els.sessionMeta.textContent = parts.join(' · ');
1989
1988
  }
@@ -2006,7 +2005,7 @@
2006
2005
  const estimate = snapshot.estimatedRemainingSeconds;
2007
2006
  if (snapshot.status === 'running' && estimate !== null) {
2008
2007
  els.estimate.hidden = false;
2009
- els.estimate.textContent = '~' + formatDuration(estimate) + ' restantes (estimativa)';
2008
+ els.estimate.textContent = `~${formatDuration(estimate)} restantes (estimativa)`;
2010
2009
  } else {
2011
2010
  els.estimate.hidden = true;
2012
2011
  }
@@ -2087,7 +2086,7 @@
2087
2086
  if (stored === null && Number.isFinite(suggested) && suggested > 0) {
2088
2087
  state.refreshSeconds = suggested;
2089
2088
  }
2090
- } catch (err) {
2089
+ } catch (_err) {
2091
2090
  // Sem /api/health segue o default local (5s).
2092
2091
  }
2093
2092
  if (stored !== null) state.refreshSeconds = stored;