@loicngr/kobo 1.6.13 → 1.6.15

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 (183) hide show
  1. package/README.md +1 -0
  2. package/dist/server/routes/workspaces.js +51 -7
  3. package/dist/server/services/auto-loop-service.js +16 -9
  4. package/dist/server/services/settings-service.js +39 -2
  5. package/dist/server/services/usage/poller.js +4 -1
  6. package/dist/server/utils/git-ops.js +67 -5
  7. package/dist/shared/auto-loop-prompts.js +19 -7
  8. package/package.json +1 -1
  9. package/src/client/dist/spa/assets/ActivityFeed-BHdMJRwS.css +1 -0
  10. package/src/client/dist/spa/assets/ActivityFeed-D7MF6IK1.js +8 -0
  11. package/src/client/dist/spa/assets/ClosePopup-D7BBEcaf.js +1 -0
  12. package/src/client/dist/spa/assets/CreatePage-DJbZH8wp.css +1 -0
  13. package/src/client/dist/spa/assets/{CreatePage-Cdhkkx-X.js → CreatePage-rp-9_jOF.js} +2 -2
  14. package/src/client/dist/spa/assets/DiffViewer-BJZADilo.js +7 -0
  15. package/src/client/dist/spa/assets/DiffViewer-D1Sdu307.css +1 -0
  16. package/src/client/dist/spa/assets/HealthPage-CZQB2pvh.js +1 -0
  17. package/src/client/dist/spa/assets/MainLayout-CFHf3zKv.js +37 -0
  18. package/src/client/dist/spa/assets/MainLayout-Db3dwSTM.css +1 -0
  19. package/src/client/dist/spa/assets/QChip-D905z6BM.js +1 -0
  20. package/src/client/dist/spa/assets/QExpansionItem-CUXuOfeR.js +1 -0
  21. package/src/client/dist/spa/assets/QInput-6U0_avSY.js +1 -0
  22. package/src/client/dist/spa/assets/{QItemSection-CiY_LK5Y.js → QItemSection-Cloi4ErY.js} +1 -1
  23. package/src/client/dist/spa/assets/QMenu-BPzgTm2k.js +1 -0
  24. package/src/client/dist/spa/assets/{QPage-yqdKDG7-.js → QPage-C6h_ah5z.js} +1 -1
  25. package/src/client/dist/spa/assets/{QScrollArea-DpCqRRE0.js → QScrollArea-N10UpHIf.js} +1 -1
  26. package/src/client/dist/spa/assets/QTabPanels-PPompnxw.js +1 -0
  27. package/src/client/dist/spa/assets/QTooltip-DLT8jCHz.js +1 -0
  28. package/src/client/dist/spa/assets/SearchPage-CfYy4vGJ.js +1 -0
  29. package/src/client/dist/spa/assets/SearchPage-cVwt0DaQ.css +1 -0
  30. package/src/client/dist/spa/assets/SettingsPage-B8DhSZw7.css +1 -0
  31. package/src/client/dist/spa/assets/SettingsPage-ONWYC-Bn.js +1 -0
  32. package/src/client/dist/spa/assets/WorkspacePage-B2VAbf6l.js +4 -0
  33. package/src/client/dist/spa/assets/WorkspacePage-k2pgeRoy.css +1 -0
  34. package/src/client/dist/spa/assets/{build-path-tree-D-2LpB2J.js → build-path-tree-DETFP2lL.js} +1 -1
  35. package/src/client/dist/spa/assets/{cssMode-DVBmJp-B.js → cssMode-QQTtBrD_.js} +1 -1
  36. package/src/client/dist/spa/assets/documents-BMdAS6h8.js +1 -0
  37. package/src/client/dist/spa/assets/{editor.api-DgbPJaK4.js → editor.api-YqpktRoe.js} +1 -1
  38. package/src/client/dist/spa/assets/{editor.main-BqqoRfAU.js → editor.main-DDGqfxYm.js} +3 -3
  39. package/src/client/dist/spa/assets/expand-template-CZkefibF.js +1 -0
  40. package/src/client/dist/spa/assets/expand-template-D2yUa54D.css +1 -0
  41. package/src/client/dist/spa/assets/{formatters-CX2gvLFv.js → formatters-DWeOzSfw.js} +1 -1
  42. package/src/client/dist/spa/assets/{freemarker2-CgaW0Q0y.js → freemarker2-BC_Lt7t3.js} +1 -1
  43. package/src/client/dist/spa/assets/{handlebars-BSs5PdXe.js → handlebars-BphhRg2c.js} +1 -1
  44. package/src/client/dist/spa/assets/{html-C9wlJaMs.js → html-C84Ufc1n.js} +1 -1
  45. package/src/client/dist/spa/assets/{htmlMode-DaRssGJk.js → htmlMode-CIlyKZJ4.js} +1 -1
  46. package/src/client/dist/spa/assets/i18n-CNdSgNP6.js +1 -0
  47. package/src/client/dist/spa/assets/index-pGAaG7Rh.js +2 -0
  48. package/src/client/dist/spa/assets/{javascript-D0VYhsc-.js → javascript-D5LTZTWn.js} +1 -1
  49. package/src/client/dist/spa/assets/{jsonMode-B57EaUNS.js → jsonMode-YBOBMJNl.js} +1 -1
  50. package/src/client/dist/spa/assets/kobo-commands-DFflpxts.js +11 -0
  51. package/src/client/dist/spa/assets/{liquid-gP2gg7sw.js → liquid-UNCP2Jl6.js} +1 -1
  52. package/src/client/dist/spa/assets/marked.esm-D7ibHC_y.js +60 -0
  53. package/src/client/dist/spa/assets/{mdx-HhXcZn_S.js → mdx-CsHyBm_B.js} +1 -1
  54. package/src/client/dist/spa/assets/models-tXWASlTL.js +1 -0
  55. package/src/client/dist/spa/assets/{monaco.contribution-ChJg8bwd.js → monaco.contribution-Bv79M2zD.js} +2 -2
  56. package/src/client/dist/spa/assets/{python-DM6FfMV3.js → python-B3h-WTW0.js} +1 -1
  57. package/src/client/dist/spa/assets/{razor-XifsxhTG.js → razor-Cs79ULMl.js} +1 -1
  58. package/src/client/dist/spa/assets/settings-Cw4mtk9x.js +1 -0
  59. package/src/client/dist/spa/assets/{stats-C3n1k51k.js → stats-BrLStQKj.js} +1 -1
  60. package/src/client/dist/spa/assets/touch-DBLw8vQK.js +1 -0
  61. package/src/client/dist/spa/assets/{tsMode-B8gurPqG.js → tsMode-238NR35q.js} +1 -1
  62. package/src/client/dist/spa/assets/{typescript-CZKTCOjl.js → typescript-C93UakWa.js} +1 -1
  63. package/src/client/dist/spa/assets/use-checkbox-w-raiu10.js +1 -0
  64. package/src/client/dist/spa/assets/use-id-CDuXkR0Z.js +1 -0
  65. package/src/client/dist/spa/assets/{xml-CtZPkb7Q.js → xml-24CcVrVJ.js} +1 -1
  66. package/src/client/dist/spa/assets/{yaml-D5IEE5M-.js → yaml-BLhB8_OL.js} +1 -1
  67. package/src/client/dist/spa/index.html +9 -11
  68. package/src/client/dist/spa/assets/ActivityFeed-BHDJ5lUn.css +0 -1
  69. package/src/client/dist/spa/assets/ActivityFeed-BsY3-q5d.js +0 -8
  70. package/src/client/dist/spa/assets/ClosePopup-DkLittac.js +0 -1
  71. package/src/client/dist/spa/assets/CreatePage-PRvhol1N.css +0 -1
  72. package/src/client/dist/spa/assets/DiffViewer-BC81-2me.css +0 -1
  73. package/src/client/dist/spa/assets/DiffViewer-DXcoEtVq.js +0 -2
  74. package/src/client/dist/spa/assets/HealthPage-BSyGqDRu.js +0 -1
  75. package/src/client/dist/spa/assets/MainLayout-D2SfvksB.css +0 -1
  76. package/src/client/dist/spa/assets/MainLayout-EYaLqjJx.js +0 -37
  77. package/src/client/dist/spa/assets/QChip-bl3YRhax.js +0 -1
  78. package/src/client/dist/spa/assets/QExpansionItem-CWw6ZujM.js +0 -1
  79. package/src/client/dist/spa/assets/QSeparator-DNSiXYrN.js +0 -1
  80. package/src/client/dist/spa/assets/QTabPanels-C4bZGqml.js +0 -1
  81. package/src/client/dist/spa/assets/QTooltip-BIDjo2hJ.js +0 -1
  82. package/src/client/dist/spa/assets/SearchPage-Bgx02GOH.js +0 -1
  83. package/src/client/dist/spa/assets/SearchPage-DWglAeQv.css +0 -1
  84. package/src/client/dist/spa/assets/SettingsPage-BTSOovDV.js +0 -1
  85. package/src/client/dist/spa/assets/SettingsPage-CwLELxfl.css +0 -1
  86. package/src/client/dist/spa/assets/TouchPan-vsl78kxF.js +0 -1
  87. package/src/client/dist/spa/assets/WorkspacePage-C5MZx1sZ.css +0 -1
  88. package/src/client/dist/spa/assets/WorkspacePage-C8dJWu-n.js +0 -4
  89. package/src/client/dist/spa/assets/documents-Ck8VwvpQ.js +0 -60
  90. package/src/client/dist/spa/assets/expand-template-bkCTc78P.js +0 -1
  91. package/src/client/dist/spa/assets/expand-template-hbnn7St6.css +0 -1
  92. package/src/client/dist/spa/assets/i18n-BSNIShFg.js +0 -1
  93. package/src/client/dist/spa/assets/index-odgA9x8A.js +0 -2
  94. package/src/client/dist/spa/assets/kobo-commands-D-9dbM70.js +0 -11
  95. package/src/client/dist/spa/assets/models-CJC61gWE.js +0 -1
  96. package/src/client/dist/spa/assets/private.use-form-Dlb0iQZh.js +0 -1
  97. package/src/client/dist/spa/assets/scroll-CYWyxBdv.js +0 -1
  98. package/src/client/dist/spa/assets/settings-CAILUJXO.js +0 -1
  99. package/src/client/dist/spa/assets/touch-Bj_Fr4kC.js +0 -1
  100. package/src/client/dist/spa/assets/use-checkbox-B_o-iLG2.js +0 -1
  101. package/src/client/dist/spa/assets/use-id-C93QQwrt.js +0 -1
  102. /package/src/client/dist/spa/assets/{QBadge-DqtcDv8D.js → QBadge-BUkmTO0P.js} +0 -0
  103. /package/src/client/dist/spa/assets/{QBtn-DHwAb18J.js → QBtn-CyzfM9-_.js} +0 -0
  104. /package/src/client/dist/spa/assets/{QItemLabel-Codqjisk.js → QItemLabel-DwnV_S8y.js} +0 -0
  105. /package/src/client/dist/spa/assets/{QList-Bl9824vi.js → QList-DZfpUv3n.js} +0 -0
  106. /package/src/client/dist/spa/assets/{QSlideTransition-BQxI8l5r.js → QSlideTransition-BMX92yUu.js} +0 -0
  107. /package/src/client/dist/spa/assets/{QSpace-BNr0AftG.js → QSpace-PlDK6Fg3.js} +0 -0
  108. /package/src/client/dist/spa/assets/{QSpinnerDots-DEiRooBD.js → QSpinnerDots-D7bo_KgI.js} +0 -0
  109. /package/src/client/dist/spa/assets/{_plugin-vue_export-helper-r4mAJOHR.js → _plugin-vue_export-helper-CpNzZuug.js} +0 -0
  110. /package/src/client/dist/spa/assets/{abap-Bgec7Keq.js → abap-DrZwwXZX.js} +0 -0
  111. /package/src/client/dist/spa/assets/{apex-VBlPwEoQ.js → apex-CrCz0btt.js} +0 -0
  112. /package/src/client/dist/spa/assets/{azcli-DKqrEFBx.js → azcli-BapzKHay.js} +0 -0
  113. /package/src/client/dist/spa/assets/{bat-DdgQWy_0.js → bat-C_NRAiA1.js} +0 -0
  114. /package/src/client/dist/spa/assets/{bicep-CRMM43EB.js → bicep-C7pp2CNk.js} +0 -0
  115. /package/src/client/dist/spa/assets/{cameligo-UatALtML.js → cameligo-BhhK9vxZ.js} +0 -0
  116. /package/src/client/dist/spa/assets/{clojure-D8JU08RA.js → clojure-D0ujmUyE.js} +0 -0
  117. /package/src/client/dist/spa/assets/{coffee-C56wu358.js → coffee-DHEl7Jbb.js} +0 -0
  118. /package/src/client/dist/spa/assets/{cpp-CyZLvhJG.js → cpp-Iil-3nzZ.js} +0 -0
  119. /package/src/client/dist/spa/assets/{csharp-BJl3ixva.js → csharp-Dh0Ee7SY.js} +0 -0
  120. /package/src/client/dist/spa/assets/{csp-CxEKxmO-.js → csp-mwzjw0JL.js} +0 -0
  121. /package/src/client/dist/spa/assets/{css-B0t_muXd.js → css-COIa8ZTR.js} +0 -0
  122. /package/src/client/dist/spa/assets/{cypher-D1hqiMFD.js → cypher-GVc17FC4.js} +0 -0
  123. /package/src/client/dist/spa/assets/{dart-Bz550Pyv.js → dart-phiCaE7_.js} +0 -0
  124. /package/src/client/dist/spa/assets/{dockerfile-CIXgVAuA.js → dockerfile-BMaDhdim.js} +0 -0
  125. /package/src/client/dist/spa/assets/{ecl-D9qbvZoA.js → ecl-Cj47kvqp.js} +0 -0
  126. /package/src/client/dist/spa/assets/{elixir-b2M38fAy.js → elixir-DBbstcE1.js} +0 -0
  127. /package/src/client/dist/spa/assets/{flow9-Dq1UYMkt.js → flow9-ChHb1adO.js} +0 -0
  128. /package/src/client/dist/spa/assets/{fsharp-CFNadkg7.js → fsharp-CDI_AxQw.js} +0 -0
  129. /package/src/client/dist/spa/assets/{go-dSur1iB2.js → go-DmsC2k-Y.js} +0 -0
  130. /package/src/client/dist/spa/assets/{graphql-qyhAo11d.js → graphql-C8hjT6Ki.js} +0 -0
  131. /package/src/client/dist/spa/assets/{hcl-DFzjMyzm.js → hcl-C15cAQOZ.js} +0 -0
  132. /package/src/client/dist/spa/assets/{ini-TdzA8TIl.js → ini-CKrAe0ag.js} +0 -0
  133. /package/src/client/dist/spa/assets/{is-DUKatk8N.js → is-BbsvEMaT.js} +0 -0
  134. /package/src/client/dist/spa/assets/{java-CSGA9pkE.js → java-BVhjILyl.js} +0 -0
  135. /package/src/client/dist/spa/assets/{julia-9izz5OsY.js → julia-BzPDHDOG.js} +0 -0
  136. /package/src/client/dist/spa/assets/{kotlin-DuPK7AtF.js → kotlin-DQMAn-b6.js} +0 -0
  137. /package/src/client/dist/spa/assets/{less-B8d93iCg.js → less-428mfr1h.js} +0 -0
  138. /package/src/client/dist/spa/assets/{lexon-DWtEIyu7.js → lexon-B09dCO6A.js} +0 -0
  139. /package/src/client/dist/spa/assets/{lua-Ciq0OGgt.js → lua-CVQ0BJif.js} +0 -0
  140. /package/src/client/dist/spa/assets/{m3-Cki6JWj_.js → m3-CiPQ1ljw.js} +0 -0
  141. /package/src/client/dist/spa/assets/{markdown-Cu47xwU0.js → markdown--G0dqL-7.js} +0 -0
  142. /package/src/client/dist/spa/assets/{mips-BM8ui995.js → mips-BaboCM3T.js} +0 -0
  143. /package/src/client/dist/spa/assets/{msdax-DqLio0_c.js → msdax-DUaqkqre.js} +0 -0
  144. /package/src/client/dist/spa/assets/{mysql-v1wbjJOq.js → mysql-CUE6XF4r.js} +0 -0
  145. /package/src/client/dist/spa/assets/{objective-c-CQl3PGSB.js → objective-c-C4MUnzeT.js} +0 -0
  146. /package/src/client/dist/spa/assets/{pascal-D4iW0ZtD.js → pascal-CWMUMx__.js} +0 -0
  147. /package/src/client/dist/spa/assets/{pascaligo-BdC9CZdj.js → pascaligo-DLCVutek.js} +0 -0
  148. /package/src/client/dist/spa/assets/{perl-BL10m4XD.js → perl-JYoirQpx.js} +0 -0
  149. /package/src/client/dist/spa/assets/{pgsql-Be_oqVo3.js → pgsql-BqOy7sqx.js} +0 -0
  150. /package/src/client/dist/spa/assets/{php-BtvXSFRI.js → php-PZqsysO1.js} +0 -0
  151. /package/src/client/dist/spa/assets/{pla-B2vUy15C.js → pla-BiwqVlg6.js} +0 -0
  152. /package/src/client/dist/spa/assets/{postiats-CbmTTfXr.js → postiats-COxQtXCD.js} +0 -0
  153. /package/src/client/dist/spa/assets/{powerquery-DszLhJGx.js → powerquery-DdXUmaWa.js} +0 -0
  154. /package/src/client/dist/spa/assets/{powershell-B0dYktF6.js → powershell-D05yu9sz.js} +0 -0
  155. /package/src/client/dist/spa/assets/{protobuf-CZvaj1VX.js → protobuf-BDsm0ZB_.js} +0 -0
  156. /package/src/client/dist/spa/assets/{pug-CPDx1B3S.js → pug-3CmTiGoi.js} +0 -0
  157. /package/src/client/dist/spa/assets/{qsharp-CDP9TFLl.js → qsharp-C4eHfCpJ.js} +0 -0
  158. /package/src/client/dist/spa/assets/{r-8DbbFX2l.js → r-Decg_RIU.js} +0 -0
  159. /package/src/client/dist/spa/assets/{redis-DRWj9MtJ.js → redis-Cl3EBA4R.js} +0 -0
  160. /package/src/client/dist/spa/assets/{redshift-C6cElE_5.js → redshift-5ZsNLhOp.js} +0 -0
  161. /package/src/client/dist/spa/assets/{restructuredtext-W9pS9n3m.js → restructuredtext-BulNNF_e.js} +0 -0
  162. /package/src/client/dist/spa/assets/{ruby-BKnzWnk-.js → ruby-D3Axi_9w.js} +0 -0
  163. /package/src/client/dist/spa/assets/{rust-YPCclWwe.js → rust-Csys1Tos.js} +0 -0
  164. /package/src/client/dist/spa/assets/{sb-BgM4DTFb.js → sb-C_iBPphi.js} +0 -0
  165. /package/src/client/dist/spa/assets/{scala-fz1OPLMl.js → scala-Cg4p-EZ2.js} +0 -0
  166. /package/src/client/dist/spa/assets/{scheme-8Uz1RIbu.js → scheme-BlVnEL_j.js} +0 -0
  167. /package/src/client/dist/spa/assets/{scss-Djo3IYXr.js → scss-CmLW8ojr.js} +0 -0
  168. /package/src/client/dist/spa/assets/{shell-CINF5Tx_.js → shell-B1DV_gpl.js} +0 -0
  169. /package/src/client/dist/spa/assets/{solidity-GgiNEuUm.js → solidity-glFpNhe3.js} +0 -0
  170. /package/src/client/dist/spa/assets/{sophia-Culj97P9.js → sophia-D9j4cFkA.js} +0 -0
  171. /package/src/client/dist/spa/assets/{sparql-C2ZlpxOY.js → sparql-DV5Ux9cO.js} +0 -0
  172. /package/src/client/dist/spa/assets/{sql-BEf5Pg7Y.js → sql-K8tNKFcf.js} +0 -0
  173. /package/src/client/dist/spa/assets/{st-CT6UUoeH.js → st-BhIdE2hj.js} +0 -0
  174. /package/src/client/dist/spa/assets/{swift-B5g0xTG3.js → swift-B0pzSmmx.js} +0 -0
  175. /package/src/client/dist/spa/assets/{symbols-DCYodwb2.js → symbols-TAFELniU.js} +0 -0
  176. /package/src/client/dist/spa/assets/{systemverilog-CEgQz9DR.js → systemverilog-CeBgixbN.js} +0 -0
  177. /package/src/client/dist/spa/assets/{tcl-D0qL2L0I.js → tcl-B0Ji3IbZ.js} +0 -0
  178. /package/src/client/dist/spa/assets/{twig-BFUAVf1E.js → twig-KUgPCP41.js} +0 -0
  179. /package/src/client/dist/spa/assets/{typespec-CjVVcNKm.js → typespec-ryrhjid6.js} +0 -0
  180. /package/src/client/dist/spa/assets/{use-quasar-Cc4smfg5.js → use-quasar-Clv5nVxk.js} +0 -0
  181. /package/src/client/dist/spa/assets/{vb-CZJr-DQz.js → vb-Z68-YtMY.js} +0 -0
  182. /package/src/client/dist/spa/assets/{vue-i18n-BJlZEYnA.js → vue-i18n-BVrBmgZa.js} +0 -0
  183. /package/src/client/dist/spa/assets/{wgsl-ivoXUo2e.js → wgsl-bH-W-d_T.js} +0 -0
package/README.md CHANGED
@@ -275,6 +275,7 @@ Kōbō reads settings from `~/.config/kobo/settings.json` (or falls back to defa
275
275
  - `gitConventions` — markdown-formatted git conventions written to `.ai/.git-conventions.md` in every workspace so the agent follows them when committing
276
276
  - `devServer` — per-project `startCommand` / `stopCommand` for launching workspace-scoped dev servers
277
277
  - `e2e` — per-project E2E test framework (`cypress`, `playwright`, `jest`, `vitest`, `other`, or none) plus an optional skill name and prompt; consumed by the auto-loop grooming step to inject `[E2E] ` test sub-tasks alongside parent tasks
278
+ - `finalization` — per-project free-form prompt that runs as the very last auto-loop iteration. The grooming step injects a `[FINAL]`-prefixed task at the end of the list whose iteration block is replaced by this prompt. Default content asks the agent to run linters, type-checkers, and tests. Empty string disables the feature.
278
279
 
279
280
  ## Contributing
280
281
 
@@ -197,9 +197,12 @@ app.post('/', migrationGuard, async (c) => {
197
197
  }
198
198
  }
199
199
  // Update workspace name with Sentry issue title if the user did not provide
200
- // a custom name and Notion hasn't already filled it.
200
+ // a custom name and Notion hasn't already filled it. Prefix with the Sentry
201
+ // short-id (e.g. "SEKUR-IOS-9 | TypeError: …") so the workspace stays
202
+ // identifiable in the sidebar without opening the panel.
201
203
  if (sentryContent?.title && !notionContent?.title && workspace.name === 'workspace') {
202
- workspace = workspaceService.updateWorkspaceName(workspace.id, sentryContent.title);
204
+ const prefix = sentryContent.issueId ? `${sentryContent.issueId} | ` : '';
205
+ workspace = workspaceService.updateWorkspaceName(workspace.id, `${prefix}${sentryContent.title}`);
203
206
  }
204
207
  // Create tasks from extracted Notion data
205
208
  if (notionContent) {
@@ -218,9 +221,13 @@ app.post('/', migrationGuard, async (c) => {
218
221
  sortOrder: sortOrder++,
219
222
  });
220
223
  }
221
- // Update workspace name with Notion page title only if user didn't provide a custom name
224
+ // Update workspace name with Notion page title only if user didn't
225
+ // provide a custom name. Prefix with the Notion unique-id (e.g.
226
+ // "TK-123 | …") when the page has one — it makes the workspace
227
+ // immediately scannable in the sidebar.
222
228
  if (notionContent.title && workspace.name === 'workspace') {
223
- workspace = workspaceService.updateWorkspaceName(workspace.id, notionContent.title);
229
+ const prefix = notionContent.ticketId ? `${notionContent.ticketId} | ` : '';
230
+ workspace = workspaceService.updateWorkspaceName(workspace.id, `${prefix}${notionContent.title}`);
224
231
  }
225
232
  }
226
233
  // Create manual tasks/criteria if no Notion content was extracted
@@ -522,11 +529,12 @@ app.post('/', migrationGuard, async (c) => {
522
529
  // been registered yet, the empty default below is correct.
523
530
  const projectSettingsForE2e = settingsService.getProjectSettings(body.projectPath);
524
531
  const e2eSettings = projectSettingsForE2e?.e2e ?? { framework: '', skill: '', prompt: '' };
532
+ const finalizationSettings = projectSettingsForE2e?.finalization ?? { prompt: '' };
525
533
  brainstormPrompt += `\n\nThen brainstorm the implementation approach. Explore the codebase to understand the existing structure. Ask clarifying questions if needed. When you have a clear plan, create a plan file.
526
534
 
527
535
  Auto-loop mode is active for this workspace. After the plan is ready, DO NOT implement anything. Instead:
528
536
 
529
- ${buildAutoLoopGroomingSteps(e2eSettings)}
537
+ ${buildAutoLoopGroomingSteps(e2eSettings, finalizationSettings)}
530
538
 
531
539
  When the steps above are complete, output [BRAINSTORM_COMPLETE] on its own line and end your turn cleanly.
532
540
 
@@ -983,9 +991,10 @@ app.get('/:id/prep-autoloop-prompt', (c) => {
983
991
  return c.json({ error: `Workspace '${id}' not found` }, 404);
984
992
  const projectSettings = settingsService.getProjectSettings(workspace.projectPath);
985
993
  const e2eSettings = projectSettings?.e2e ?? { framework: '', skill: '', prompt: '' };
994
+ const finalizationSettings = projectSettings?.finalization ?? { prompt: '' };
986
995
  const prompt = `${PREP_AUTOLOOP_INTRO}
987
996
 
988
- ${buildAutoLoopGroomingSteps(e2eSettings)}
997
+ ${buildAutoLoopGroomingSteps(e2eSettings, finalizationSettings)}
989
998
 
990
999
  ${AUTO_LOOP_HARD_RULES}`;
991
1000
  return c.json({ prompt });
@@ -1439,6 +1448,9 @@ app.get('/:id/diff', (c) => {
1439
1448
  try {
1440
1449
  const id = c.req.param('id');
1441
1450
  const mode = c.req.query('mode') === 'unpushed' ? 'unpushed' : 'branch';
1451
+ // Opt-in flag from the diff viewer toggle. Only meaningful in `branch`
1452
+ // mode — `unpushed` is committed-only by definition.
1453
+ const includeUntracked = c.req.query('includeUntracked') === '1';
1442
1454
  const workspace = workspaceService.getWorkspace(id);
1443
1455
  if (!workspace) {
1444
1456
  return c.json({ error: `Workspace '${id}' not found` }, 404);
@@ -1446,7 +1458,7 @@ app.get('/:id/diff', (c) => {
1446
1458
  const worktreePath = workspace.worktreePath;
1447
1459
  const files = mode === 'unpushed'
1448
1460
  ? gitOps.getUnpushedChangedFiles(worktreePath, workspace.workingBranch)
1449
- : gitOps.getChangedFiles(worktreePath, workspace.sourceBranch);
1461
+ : gitOps.getChangedFiles(worktreePath, workspace.sourceBranch, includeUntracked);
1450
1462
  c.header('Cache-Control', 'no-store');
1451
1463
  return c.json({
1452
1464
  files,
@@ -1489,6 +1501,38 @@ app.get('/:id/diff-file', (c) => {
1489
1501
  return c.json({ error: message }, 500);
1490
1502
  }
1491
1503
  });
1504
+ // POST /api/workspaces/:id/rollback-file { path }
1505
+ // Reset a single file to its `origin/<workingBranch>` version (overwrites
1506
+ // working tree + index). Used by the right-click menu in the diff viewer.
1507
+ // Returns 422 when the branch has never been pushed (no remote ref to
1508
+ // rollback to) so the UI can disable the action gracefully.
1509
+ app.post('/:id/rollback-file', async (c) => {
1510
+ try {
1511
+ const id = c.req.param('id');
1512
+ const workspace = workspaceService.getWorkspace(id);
1513
+ if (!workspace) {
1514
+ return c.json({ error: `Workspace '${id}' not found` }, 404);
1515
+ }
1516
+ const body = await c.req.json();
1517
+ const filePath = typeof body?.path === 'string' ? body.path.trim() : '';
1518
+ if (!filePath) {
1519
+ return c.json({ error: 'Missing or invalid `path` field' }, 400);
1520
+ }
1521
+ let target;
1522
+ try {
1523
+ target = gitOps.rollbackFile(workspace.worktreePath, workspace.workingBranch, filePath);
1524
+ }
1525
+ catch (err) {
1526
+ const message = err instanceof Error ? err.message : String(err);
1527
+ return c.json({ error: message }, 422);
1528
+ }
1529
+ return c.json({ ok: true, path: filePath, target });
1530
+ }
1531
+ catch (err) {
1532
+ const message = err instanceof Error ? err.message : String(err);
1533
+ return c.json({ error: message }, 500);
1534
+ }
1535
+ });
1492
1536
  // GET /api/workspaces/:id/commits?limit=50 — list commits between sourceBranch
1493
1537
  // and HEAD, each tagged with whether it's already pushed to origin/<branch>.
1494
1538
  app.get('/:id/commits', (c) => {
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { buildE2eIterationBlock } from '../../shared/auto-loop-prompts.js';
3
+ import { buildE2eIterationBlock, buildFinalizationIterationBlock } from '../../shared/auto-loop-prompts.js';
4
4
  import { getDb } from '../db/index.js';
5
5
  import * as orchestrator from './agent/orchestrator.js';
6
6
  import * as settingsService from './settings-service.js';
@@ -172,7 +172,7 @@ Current pending task (highest priority, non-acceptance-criterion first):
172
172
  - Task ID: {taskId}
173
173
  - Title: {taskTitle}
174
174
  - Is acceptance criterion: {isAcceptanceCriterion}
175
- {e2eBlock}
175
+ {overrideBlock}
176
176
  Your job this iteration:
177
177
  1. Read \`kobo__list_tasks\` to see all tasks and the big picture.
178
178
  2. Implement the SINGLE task above and nothing else. Do not pick a different task.
@@ -225,19 +225,26 @@ function spawnNextIteration(workspaceId, opts = {}) {
225
225
  return;
226
226
  }
227
227
  const iterationNumber = computeIterationNumber(workspaceId);
228
- // E2E iteration block: only injected for tasks whose title starts with the
229
- // exact `[E2E] ` prefix (case-sensitive, trailing space required) AND when
230
- // the project has an E2E framework configured. Empty string otherwise so
231
- // the placeholder collapses cleanly in PROMPT_TEMPLATE.
228
+ // Override block: replaces the standard iteration prompt body when the task
229
+ // title carries a recognized prefix (case-sensitive, trailing space required).
230
+ // Empty string otherwise so the placeholder collapses cleanly in PROMPT_TEMPLATE.
231
+ // A title cannot literally start with both prefixes, so the order of these
232
+ // branches is purely cosmetic.
232
233
  const projectSettings = settingsService.getProjectSettings(row.project_path);
233
234
  const e2eSettings = projectSettings?.e2e ?? { framework: '', skill: '', prompt: '' };
234
- const isE2eTask = task.title.startsWith('[E2E] ');
235
- const e2eBlock = isE2eTask && e2eSettings.framework ? buildE2eIterationBlock(e2eSettings) : '';
235
+ const finalizationSettings = projectSettings?.finalization ?? { prompt: '' };
236
+ let overrideBlock = '';
237
+ if (task.title.startsWith('[FINAL] ')) {
238
+ overrideBlock = buildFinalizationIterationBlock(finalizationSettings);
239
+ }
240
+ else if (task.title.startsWith('[E2E] ') && e2eSettings.framework) {
241
+ overrideBlock = buildE2eIterationBlock(e2eSettings);
242
+ }
236
243
  const prompt = PROMPT_TEMPLATE.replaceAll('{n}', String(iterationNumber))
237
244
  .replaceAll('{taskId}', task.id)
238
245
  .replaceAll('{taskTitle}', task.title)
239
246
  .replaceAll('{isAcceptanceCriterion}', String(task.isAcceptanceCriterion))
240
- .replaceAll('{e2eBlock}', e2eBlock);
247
+ .replaceAll('{overrideBlock}', overrideBlock);
241
248
  const worktreePath = row.worktree_path ?? path.join(row.project_path, '.worktrees', row.working_branch);
242
249
  // Auto-loop iterations always run in auto-accept mode. Plan mode blocks MCP
243
250
  // tools (kobo__mark_task_done, etc.) and Edit/Write/Bash — everything the
@@ -53,6 +53,12 @@ Please:
53
53
  3. Post a comment on the PR summarizing what was done and any follow-up items
54
54
  4. Do NOT add a "Generated with Claude Code" footer or any AI attribution to the PR description
55
55
  `;
56
+ export const DEFAULT_FINALIZATION_PROMPT = `Run final quality checks before closing the workspace:
57
+
58
+ 1. Verify all other tasks are marked \`done\`. If any remain \`pending\`, stop and report.
59
+ 2. Run the project's linters, type-checkers, and tests (see CLAUDE.md or package.json scripts).
60
+ 3. If any check fails, create a new regular task at the end of the list with a title like \`Fix lint failure in X\` (NO \`[FINAL]\` or \`[E2E]\` prefix — it must use the default iteration prompt) and mark this \`[FINAL]\` task as \`done\`. The auto-loop will pick up the fix on the next iteration. The finalization mechanism is single-shot per grooming pass; if you want quality checks to re-run after the fix, mark the fix task \`done\` and re-trigger grooming manually.
61
+ 4. If everything passes, mark this task as \`done\`.`;
56
62
  /** Default workspace tags seeded on fresh install and on settings upgrade. */
57
63
  export const DEFAULT_WORKSPACE_TAGS = [
58
64
  'bug',
@@ -170,6 +176,21 @@ const settingsMigrations = [
170
176
  }
171
177
  },
172
178
  },
179
+ {
180
+ version: 10,
181
+ name: 'add-project-finalization',
182
+ migrate({ projects }) {
183
+ for (const p of projects) {
184
+ if (!p.finalization || typeof p.finalization !== 'object') {
185
+ p.finalization = { prompt: DEFAULT_FINALIZATION_PROMPT };
186
+ }
187
+ else if (typeof p.finalization.prompt !== 'string') {
188
+ ;
189
+ p.finalization.prompt = DEFAULT_FINALIZATION_PROMPT;
190
+ }
191
+ }
192
+ },
193
+ },
173
194
  ];
174
195
  /** Current settings schema version — always equals the highest migration version. */
175
196
  export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
@@ -233,6 +254,9 @@ function defaultProjectSettings(projectPath) {
233
254
  skill: '',
234
255
  prompt: '',
235
256
  },
257
+ finalization: {
258
+ prompt: DEFAULT_FINALIZATION_PROMPT,
259
+ },
236
260
  };
237
261
  }
238
262
  function pickKnownKeys(data, allowedKeys) {
@@ -439,7 +463,7 @@ export function updateGlobalSettings(data) {
439
463
  writeSettings(settings, { backup: true });
440
464
  return settings.global;
441
465
  }
442
- /** Create or update project-specific settings. Merges devServer and e2e fields on update. */
466
+ /** Create or update project-specific settings. Merges devServer, e2e, and finalization fields on update. */
443
467
  export function upsertProject(projectPath, data) {
444
468
  const allowedProjectKeys = [
445
469
  'displayName',
@@ -451,9 +475,11 @@ export function upsertProject(projectPath, data) {
451
475
  'setupScript',
452
476
  'devServer',
453
477
  'e2e',
478
+ 'finalization',
454
479
  ];
455
480
  const allowedDevServerKeys = ['startCommand', 'stopCommand'];
456
481
  const allowedE2eKeys = ['framework', 'skill', 'prompt'];
482
+ const allowedFinalizationKeys = ['prompt'];
457
483
  const filtered = pickKnownKeys(data, allowedProjectKeys);
458
484
  if (filtered.devServer) {
459
485
  filtered.devServer = pickKnownKeys(filtered.devServer, allowedDevServerKeys);
@@ -461,20 +487,28 @@ export function upsertProject(projectPath, data) {
461
487
  if (filtered.e2e) {
462
488
  filtered.e2e = pickKnownKeys(filtered.e2e, allowedE2eKeys);
463
489
  }
490
+ if (filtered.finalization) {
491
+ filtered.finalization = pickKnownKeys(filtered.finalization, allowedFinalizationKeys);
492
+ }
464
493
  const settings = readSettings();
465
494
  const idx = settings.projects.findIndex((p) => p.path === projectPath);
466
495
  if (idx >= 0) {
467
- // Update existing project — merge devServer and e2e separately to allow partial updates
496
+ // Update existing project — merge devServer, e2e, and finalization separately to allow partial updates
468
497
  const existing = settings.projects[idx];
469
498
  const updatedDevServer = filtered.devServer ? { ...existing.devServer, ...filtered.devServer } : existing.devServer;
470
499
  const existingE2e = existing.e2e ?? defaultProjectSettings(projectPath).e2e;
471
500
  const updatedE2e = filtered.e2e ? { ...existingE2e, ...filtered.e2e } : existingE2e;
501
+ const existingFinalization = existing.finalization ?? defaultProjectSettings(projectPath).finalization;
502
+ const updatedFinalization = filtered.finalization
503
+ ? { ...existingFinalization, ...filtered.finalization }
504
+ : existingFinalization;
472
505
  settings.projects[idx] = {
473
506
  ...existing,
474
507
  ...filtered,
475
508
  path: projectPath,
476
509
  devServer: updatedDevServer,
477
510
  e2e: updatedE2e,
511
+ finalization: updatedFinalization,
478
512
  };
479
513
  }
480
514
  else {
@@ -490,6 +524,9 @@ export function upsertProject(projectPath, data) {
490
524
  if (filtered.e2e) {
491
525
  newProject.e2e = { ...defaultProjectSettings(projectPath).e2e, ...filtered.e2e };
492
526
  }
527
+ if (filtered.finalization) {
528
+ newProject.finalization = { ...defaultProjectSettings(projectPath).finalization, ...filtered.finalization };
529
+ }
493
530
  settings.projects.push(newProject);
494
531
  }
495
532
  writeSettings(settings, { backup: true });
@@ -1,7 +1,10 @@
1
1
  import { broadcastAll } from '../websocket-service.js';
2
2
  import { upsertUsageSnapshot } from './db.js';
3
3
  import { createClaudeCodeProvider } from './providers/claude-code.js';
4
- export const POLL_INTERVAL_MS = 60_000;
4
+ // 5 minutes the Anthropic `five_hour` / `seven_day` buckets are slow-moving
5
+ // so a tighter cadence just burns rate limit (429s) without surfacing fresher
6
+ // data to the UI. The user can still trigger a manual refresh via `refreshNow`.
7
+ export const POLL_INTERVAL_MS = 5 * 60_000;
5
8
  const DEFAULT_PROVIDERS = [createClaudeCodeProvider()];
6
9
  let providers = DEFAULT_PROVIDERS;
7
10
  let intervalHandle = null;
@@ -1,5 +1,5 @@
1
1
  import { execFile as execFileCb, execFileSync } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
5
  const execFileAsync = promisify(execFileCb);
@@ -410,7 +410,7 @@ export function moveWorktree(projectPath, oldPath, newPath) {
410
410
  git(projectPath, ['worktree', 'move', oldPath, newPath]);
411
411
  }
412
412
  /** List files changed between base and HEAD (committed), plus working tree changes. */
413
- export function getChangedFiles(repoPath, base) {
413
+ export function getChangedFiles(repoPath, base, includeUntracked = false) {
414
414
  const ref = resolveBase(repoPath, base);
415
415
  const files = [];
416
416
  const seen = new Set();
@@ -443,9 +443,14 @@ export function getChangedFiles(repoPath, base) {
443
443
  catch {
444
444
  // No commits yet
445
445
  }
446
- // Working tree changes (uncommitted)
446
+ // Working tree changes (uncommitted). Default to `-uno` to skip pure
447
+ // untracked files: they have never been `git add`-ed and won't ship in
448
+ // the next commit/PR, so showing them in the diff viewer is misleading.
449
+ // When `includeUntracked` is true (user opt-in via the diff viewer toggle)
450
+ // we use `-uall` and surface them with status='added'.
447
451
  try {
448
- const output = git(repoPath, ['status', '--porcelain', '-uall']);
452
+ const flag = includeUntracked ? '-uall' : '-uno';
453
+ const output = git(repoPath, ['status', '--porcelain', flag]);
449
454
  for (const line of output.split('\n')) {
450
455
  if (!line)
451
456
  continue;
@@ -456,7 +461,7 @@ export function getChangedFiles(repoPath, base) {
456
461
  const y = line[1];
457
462
  let status = 'modified';
458
463
  if (x === '?' && y === '?')
459
- status = 'added';
464
+ status = 'untracked';
460
465
  else if (x === 'A' || y === 'A')
461
466
  status = 'added';
462
467
  else if (x === 'D' || y === 'D')
@@ -523,6 +528,63 @@ export function getFileAtRef(repoPath, ref, filePath) {
523
528
  return null;
524
529
  }
525
530
  }
531
+ /**
532
+ * Reset a single file in the worktree to a sensible baseline. Cascade:
533
+ * 1. `origin/<branchName>` if the remote ref AND the file exist there
534
+ * (typical: branch is pushed, user wants to undo all local changes).
535
+ * 2. `HEAD` if the file exists at the last local commit (typical: branch
536
+ * not yet pushed, or file was added in commits that aren't on remote
537
+ * yet — discards just the uncommitted edits, keeps the commits).
538
+ * 3. **Delete** the file from disk when it's untracked (not on remote AND
539
+ * not in HEAD): there's nothing to "rollback to", so the only sensible
540
+ * undo is to remove the local-only file. Caller MUST surface this to
541
+ * the user with an explicit confirmation message — the action is
542
+ * permanent.
543
+ *
544
+ * Throws on filesystem errors (permission denied, etc.). Returns the
545
+ * target that was actually used so the caller can surface the right
546
+ * feedback in the UI.
547
+ */
548
+ export function rollbackFile(repoPath, branchName, filePath, remote = 'origin') {
549
+ const remoteRef = `${remote}/${branchName}`;
550
+ let remoteRefExists = false;
551
+ try {
552
+ git(repoPath, ['rev-parse', '--verify', remoteRef]);
553
+ remoteRefExists = true;
554
+ }
555
+ catch {
556
+ // Branch never pushed — fall through to HEAD.
557
+ }
558
+ if (remoteRefExists) {
559
+ try {
560
+ git(repoPath, ['cat-file', '-e', `${remoteRef}:${filePath}`]);
561
+ git(repoPath, ['checkout', remoteRef, '--', filePath]);
562
+ return 'remote';
563
+ }
564
+ catch {
565
+ // File doesn't exist at origin/<branch> (added locally) — fall through.
566
+ }
567
+ }
568
+ try {
569
+ git(repoPath, ['cat-file', '-e', `HEAD:${filePath}`]);
570
+ git(repoPath, ['checkout', 'HEAD', '--', filePath]);
571
+ return 'head';
572
+ }
573
+ catch {
574
+ // File is untracked OR has already been rolled back — delete it from
575
+ // disk if still present. Idempotent: if the file is already gone (race
576
+ // with a previous rollback, stale UI list, manual rm), we still return
577
+ // 'deleted' since the end state matches the user's intent. `rmSync`
578
+ // over `git clean -f` keeps the action narrow to one file.
579
+ const absPath = join(repoPath, filePath);
580
+ if (existsSync(absPath)) {
581
+ rmSync(absPath, { force: true });
582
+ }
583
+ return 'deleted';
584
+ }
585
+ }
586
+ /** @deprecated kept for backwards-compat with older imports — use `rollbackFile`. */
587
+ export const rollbackFileToRemote = rollbackFile;
526
588
  /** Get the current content of a file in the worktree. Returns null if the file doesn't exist. */
527
589
  export function getFileContent(repoPath, filePath) {
528
590
  try {
@@ -17,7 +17,7 @@
17
17
  * `AUTO_LOOP_HARD_RULES` is the trailing hard-rules block, same for both.
18
18
  */
19
19
  export const PREP_AUTOLOOP_INTRO = `You are preparing this workspace for Kōbō auto-loop mode. This is a GROOMING session only — DO NOT implement anything, DO NOT write or edit code, DO NOT run tests or builds, DO NOT invoke \`superpowers:executing-plans\` or any implementation skill. Your ONLY job is to curate the Kōbō task list via MCP tools.`;
20
- export function buildAutoLoopGroomingSteps(e2e) {
20
+ export function buildAutoLoopGroomingSteps(e2e, finalization) {
21
21
  const steps = [
22
22
  `1. Call \`kobo__list_tasks\` FIRST to inspect any pre-existing tasks (they may have been seeded from Notion, a template, or the CreatePage form).`,
23
23
  `2. If tasks already exist: DO NOT delete or recreate them from scratch. Read each one, judge whether it is atomic and implementable in one session with clear completion criteria. Improve them in place:
@@ -29,19 +29,31 @@ export function buildAutoLoopGroomingSteps(e2e) {
29
29
  - If no plan exists, ask the user what the workspace goal is and propose tasks accordingly.
30
30
  - Create the tasks via \`kobo__create_task\`. For each task, decide \`is_acceptance_criterion\` appropriately.`,
31
31
  ];
32
+ let nextNum = 4;
32
33
  if (e2e.framework) {
33
34
  const skillHint = e2e.skill ? `Use the \`${e2e.skill}\` skill for this task. ` : '';
34
35
  const promptHint = e2e.prompt ? `Additional guidance: ${e2e.prompt}` : '';
35
- steps.push(`4. **E2E review**: walk the task list and identify which tasks produce user-visible behavior (UI flows, form submissions, page renders, etc.). For each one that warrants regression coverage, INSERT a follow-up sub-task with title prefixed \`[E2E] \` describing the test to write. Place it in \`sort_order\` directly after the parent task. Skip tasks that don't produce user-visible behavior (refactors, infra, internal services) and briefly justify your choices in chat. The project uses \`${e2e.framework}\`. ${skillHint}${promptHint}`.trim());
36
- steps.push(`5. Call \`kobo__mark_auto_loop_ready\`. This will automatically start the auto-loop, which will pick up the tasks one by one in fresh sessions.`);
36
+ steps.push(`${nextNum}. **E2E review**: walk the task list and identify which tasks produce user-visible behavior (UI flows, form submissions, page renders, etc.). For each one that warrants regression coverage, INSERT a follow-up sub-task with title prefixed \`[E2E] \` describing the test to write. Place it in \`sort_order\` directly after the parent task. Skip tasks that don't produce user-visible behavior (refactors, infra, internal services) and briefly justify your choices in chat. The project uses \`${e2e.framework}\`. ${skillHint}${promptHint}`.trim());
37
+ nextNum++;
37
38
  }
38
- else {
39
- steps.push(`4. Call \`kobo__mark_auto_loop_ready\`. This will automatically start the auto-loop, which will pick up the tasks one by one in fresh sessions.`);
39
+ if (finalization.prompt) {
40
+ steps.push(`${nextNum}. **Finalization task**: create ONE task with title prefixed \`[FINAL] <descriptive title>\`. Place it at the END of the task list (sort_order = max + 1, AFTER any [E2E] tasks). The agent will execute this task using the project's finalization prompt do NOT inline the prompt content into the task title or description.`);
41
+ nextNum++;
40
42
  }
43
+ steps.push(`${nextNum}. Call \`kobo__mark_auto_loop_ready\`. This will automatically start the auto-loop, which will pick up the tasks one by one in fresh sessions.`);
41
44
  return steps.join('\n');
42
45
  }
43
- /** @deprecated Use buildAutoLoopGroomingSteps({ framework: '', skill: '', prompt: '' }) instead. */
44
- export const AUTO_LOOP_GROOMING_STEPS = buildAutoLoopGroomingSteps({ framework: '', skill: '', prompt: '' });
46
+ /** @deprecated Use buildAutoLoopGroomingSteps({ framework: '', skill: '', prompt: '' }, { prompt: '' }) instead. */
47
+ export const AUTO_LOOP_GROOMING_STEPS = buildAutoLoopGroomingSteps({ framework: '', skill: '', prompt: '' }, { prompt: '' });
48
+ export function buildFinalizationIterationBlock(finalization) {
49
+ if (!finalization.prompt)
50
+ return '';
51
+ return `
52
+ This is the **finalization task**. Follow this prompt instead of the standard 8 steps below:
53
+
54
+ ${finalization.prompt}
55
+ `;
56
+ }
45
57
  export function buildE2eIterationBlock(e2e) {
46
58
  if (!e2e.framework)
47
59
  return '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loicngr/kobo",
3
- "version": "1.6.13",
3
+ "version": "1.6.15",
4
4
  "description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-or-later",
@@ -0,0 +1 @@
1
+ .message-choices[data-v-29c0dc7a]{flex-wrap:wrap;align-items:stretch;display:flex}.message-choice-btn[data-v-29c0dc7a]{background:#818cf814;border:1px solid #818cf859;border-radius:6px;max-width:360px;font-size:12px}.message-choice-btn[data-v-29c0dc7a] .q-btn__content{flex-wrap:nowrap;justify-content:flex-start}.message-choice-btn[data-v-29c0dc7a]:hover{background:#818cf82e}.choice-label[data-v-29c0dc7a]{text-overflow:ellipsis;white-space:nowrap;max-width:320px;overflow:hidden}.markdown-message[data-v-8382750f]{color:#e0e0e0;word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-8382750f] *{max-width:100%}.markdown-message[data-v-8382750f] p{margin:0 0 .5em}.markdown-message[data-v-8382750f] p:last-child{margin-bottom:0}.markdown-message[data-v-8382750f] pre{background:#00000059;border-radius:4px;margin:.5em 0;padding:.5em .75em;overflow-x:auto}.markdown-message[data-v-8382750f] code{word-break:break-all;background:#0000004d;border-radius:3px;padding:.1em .3em;font-size:.9em}.markdown-message[data-v-8382750f] pre code{background:0 0;padding:0}.markdown-message[data-v-8382750f] ul,.markdown-message[data-v-8382750f] ol{margin:.25em 0 .5em;padding-left:1.5em}.markdown-message[data-v-8382750f] li{margin:.15em 0}.markdown-message[data-v-8382750f] a{color:#7986cb;text-decoration:underline}.markdown-message[data-v-8382750f] .document-link{color:#9fa8da;cursor:pointer;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.markdown-message[data-v-8382750f] .document-link:hover{color:#c5cae9;-webkit-text-decoration:underline;text-decoration:underline}.markdown-message[data-v-8382750f] h1,.markdown-message[data-v-8382750f] h2,.markdown-message[data-v-8382750f] h3,.markdown-message[data-v-8382750f] h4,.markdown-message[data-v-8382750f] h5,.markdown-message[data-v-8382750f] h6{margin:.5em 0 .3em;font-weight:600;line-height:1.3}.markdown-message[data-v-8382750f] h1{font-size:1.25em}.markdown-message[data-v-8382750f] h2{font-size:1.15em}.markdown-message[data-v-8382750f] h3{font-size:1.08em}.markdown-message[data-v-8382750f] h4,.markdown-message[data-v-8382750f] h5,.markdown-message[data-v-8382750f] h6{font-size:1em}.markdown-message[data-v-8382750f] blockquote{color:#ffffffb3;border-left:3px solid #fff3;margin:.5em 0;padding-left:.75em}.markdown-message[data-v-8382750f] table{border-collapse:collapse;margin:.5em 0}.markdown-message[data-v-8382750f] th,.markdown-message[data-v-8382750f] td{border:1px solid #ffffff26;padding:.25em .5em}.markdown-thinking[data-v-4e64694c] p{margin:0 0 .4em}.markdown-thinking[data-v-4e64694c] p:last-child{margin-bottom:0}.markdown-thinking[data-v-4e64694c] code{background:#ffffff14;border-radius:3px;padding:.1em .3em}.tool-row[data-v-b1fcd20d]{border-radius:4px;margin:0;font-size:12px}.tool-header[data-v-b1fcd20d]{color:#bbb;cursor:default;align-items:center;gap:10px;min-width:0;padding:5px 10px;display:flex}.tool-row:not(.tool-row-generic) .tool-header[data-v-b1fcd20d],.tool-row--toggleable .tool-header[data-v-b1fcd20d]{cursor:pointer}.tool-row:has(.tool-diff) .tool-header[data-v-b1fcd20d]{cursor:pointer}.tool-row:not(.tool-row-generic) .tool-header[data-v-b1fcd20d]:hover,.tool-row--toggleable .tool-header[data-v-b1fcd20d]:hover{background:#ffffff08}.tool-icon[data-v-b1fcd20d]{color:#9fbce0;flex-shrink:0}.tool-name[data-v-b1fcd20d]{color:#d0d0d0;flex-shrink:0;font-weight:600}.tool-arg[data-v-b1fcd20d],.tool-path[data-v-b1fcd20d]{color:#999;text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11.5px;overflow:hidden}.tool-path[data-v-b1fcd20d],.tool-arg[data-v-b1fcd20d]{flex:1}.tool-stat-add[data-v-b1fcd20d]{color:#66bb6a;flex-shrink:0;font-size:11px;font-weight:600}.tool-stat-del[data-v-b1fcd20d]{color:#ef5350;flex-shrink:0;font-size:11px;font-weight:600}.tool-diff[data-v-b1fcd20d]{background:#0003;border-radius:4px;max-height:400px;margin-top:4px;padding:8px 0;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;line-height:1.5;overflow:auto}.diff-line[data-v-b1fcd20d]{white-space:pre;color:#bbb;padding:0 12px}.diff-sign[data-v-b1fcd20d]{color:#555;-webkit-user-select:none;user-select:none;width:14px;display:inline-block}.diff-add[data-v-b1fcd20d]{color:#c8e6c9;background:#66bb6a1a}.diff-add .diff-sign[data-v-b1fcd20d]{color:#66bb6a}.diff-del[data-v-b1fcd20d]{color:#ffcdd2;background:#ef53501a}.diff-del .diff-sign[data-v-b1fcd20d]{color:#ef5350}.tool-output[data-v-b1fcd20d]{color:#aaa;white-space:pre-wrap;background:#00000026;border-radius:4px;max-height:8em;margin-top:4px;padding:6px 10px;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px;overflow:auto}.markdown-message[data-v-c2e7c407]{color:#e0e0e0;word-break:break-word;overflow-wrap:anywhere;min-width:0;max-width:100%;font-size:13px;line-height:1.55}.markdown-message[data-v-c2e7c407] *{max-width:100%}.markdown-message[data-v-c2e7c407] img{-o-object-fit:contain;object-fit:contain;cursor:zoom-in;background:#0003;border-radius:4px;max-width:180px;max-height:100px;margin:.3em 0;display:block}.markdown-message[data-v-c2e7c407] code{word-break:break-all}.markdown-message[data-v-c2e7c407] p{margin:0 0 .4em}.markdown-message[data-v-c2e7c407] p:last-child{margin-bottom:0}.markdown-message[data-v-c2e7c407] code{background:#00000040;border-radius:3px;padding:.1em .3em}.markdown-message[data-v-c2e7c407] h1,.markdown-message[data-v-c2e7c407] h2,.markdown-message[data-v-c2e7c407] h3,.markdown-message[data-v-c2e7c407] h4,.markdown-message[data-v-c2e7c407] h5,.markdown-message[data-v-c2e7c407] h6{margin:.4em 0 .25em;font-weight:600;line-height:1.3}.markdown-message[data-v-c2e7c407] h1{font-size:1.25em}.markdown-message[data-v-c2e7c407] h2{font-size:1.15em}.markdown-message[data-v-c2e7c407] h3{font-size:1.08em}.markdown-message[data-v-c2e7c407] h4,.markdown-message[data-v-c2e7c407] h5,.markdown-message[data-v-c2e7c407] h6{font-size:1em}.markdown-user-prompt[data-v-c2e7c407]{color:#aaa;font-size:12px;font-style:italic}.markdown-user-prompt[data-v-c2e7c407] p{margin:0 0 .4em}.markdown-user-prompt[data-v-c2e7c407] code{background:#ffffff14;border-radius:3px;padding:.1em .3em;font-style:normal}.image-lightbox-img{-o-object-fit:contain;object-fit:contain;cursor:zoom-out;background:#0000004d;border-radius:4px;max-width:92vw;max-height:92vh;display:block}.turn-card[data-v-813aff2b]{border:1px solid #ffffff14;border-left:3px solid var(--turn-accent);background:#ffffff05;border-radius:6px;min-width:0;max-width:100%;margin:14px 0;overflow:hidden}.turn-header[data-v-813aff2b]{color:#888;background:#ffffff08;border-bottom:1px solid #ffffff0d;align-items:center;gap:8px;padding:8px 14px;font-size:11px;display:flex}.turn-badge[data-v-813aff2b]{letter-spacing:.3px;border-radius:3px;padding:2px 8px;font-size:11px;font-weight:700}.turn-badge-user[data-v-813aff2b]{color:#ce93d8;background:#ce93d826}.turn-badge-agent[data-v-813aff2b]{color:#7986cb;background:#7986cb26}.turn-badge-system[data-v-813aff2b]{color:#bdbdbd;background:#75757533;font-style:italic}.turn-badge-session[data-v-813aff2b]{color:#9e9e9e;background:#61616133}.turn-time[data-v-813aff2b]{color:#666;font-family:SF Mono,Menlo,Consolas,monospace;font-size:11px}.turn-time-arrow[data-v-813aff2b]{opacity:.7;margin:0 -2px}.turn-time-updated[data-v-813aff2b]{color:#8891a3}.turn-actions[data-v-813aff2b]{color:#777;font-size:11px}.turn-body[data-v-813aff2b]{flex-direction:column;gap:12px;min-width:0;padding:14px 18px;display:flex}.turn-body[data-v-813aff2b]>*{min-width:0;max-width:100%}.turn-body[data-v-813aff2b] .tool-row+.tool-row{margin-top:-8px}.turn-scroll-top[data-v-813aff2b]{justify-content:flex-start;padding:0 8px 6px;display:flex}.turn-scroll-top-btn[data-v-813aff2b]{opacity:.5;transition:opacity .15s}.turn-scroll-top-btn[data-v-813aff2b]:hover{opacity:1}.activity-feed-wrap[data-v-989e89a0]{width:100%;height:100%;position:relative}.activity-feed-scroll[data-v-989e89a0]{width:100%;height:100%}.activity-feed-nav-cluster[data-v-989e89a0]{z-index:2;align-items:center;gap:8px;display:flex;position:absolute;bottom:14px;right:14px}.activity-feed-nav-btn[data-v-989e89a0]{opacity:.8;transition:opacity .12s}.activity-feed-nav-btn[data-v-989e89a0]:hover{opacity:1}.content-origin-marker[data-v-989e89a0]{pointer-events:none;width:0;height:0;margin:0;padding:0}.activity-feed-scroll[data-v-989e89a0] .q-scrollarea__content{max-width:100%;overflow-x:hidden}.activity-feed-switching[data-v-989e89a0]{justify-content:center;align-items:center;width:100%;height:100%;display:flex}
@@ -0,0 +1,8 @@
1
+ import{E as e,F as t,H as n,L as r,M as i,Q as a,U as o,_t as s,bt as c,d as l,f as u,g as d,h as f,l as p,p as m,r as h,rt as g,u as _,v,yt as y}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{L as b,l as x,t as S}from"./QIcon-B0-pH3Qs.js";import{t as C}from"./settings-Cw4mtk9x.js";import{t as w}from"./QBtn-CyzfM9-_.js";import{n as T}from"./vue-i18n-BVrBmgZa.js";import{d as E,h as D,l as O,p as k,v as A}from"./index-pGAaG7Rh.js";import{t as j}from"./QSpinnerDots-D7bo_KgI.js";import{t as M}from"./QExpansionItem-CUXuOfeR.js";import{t as ee}from"./QScrollArea-N10UpHIf.js";import{n as N,t as P}from"./marked.esm-D7ibHC_y.js";import{t as F}from"./QTooltip-DLT8jCHz.js";import{t as I}from"./_plugin-vue_export-helper-CpNzZuug.js";import{t as L}from"./documents-BMdAS6h8.js";function te(e,t,n=!0){let r=[],i=new Map,a=new Map;for(let n=0;n<e.length;n++){let o=e[n],s=t?.[n];switch(o.kind){case`message:text`:{let e=i.get(o.messageId);if(e)e.text+=o.text,e.streaming=o.streaming;else{let e={type:`text`,messageId:o.messageId,text:o.text,streaming:o.streaming,ts:s};i.set(o.messageId,e),r.push(e)}break}case`message:end`:{let e=i.get(o.messageId);e&&(e.streaming=!1);break}case`message:thinking`:r.push({type:`thinking`,messageId:o.messageId,text:o.text,ts:s});break;case`tool:call`:{let e={type:`tool`,toolCallId:o.toolCallId,name:o.name,input:o.input,ts:s};a.set(o.toolCallId,e),r.push(e);break}case`tool:result`:{let e=a.get(o.toolCallId);e&&(e.result={output:o.output,isError:o.isError});break}case`session:started`:r.push({type:`session`,kind:`started`,detail:{engineSessionId:o.engineSessionId,model:o.model},ts:s});break;case`session:ended`:r.push({type:`session`,kind:`ended`,detail:{reason:o.reason,exitCode:o.exitCode},ts:s});break;case`session:compacted`:r.push({type:`session`,kind:`compacted`,ts:s});break;case`session:brainstorm-complete`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let o=null;for(let e of r)e.type===`text`&&e.streaming&&(o&&(o.streaming=!1),o=e);return o&&!n&&(o.streaming=!1),r}function ne(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts})),r=[...e,...n];r.sort((e,t)=>{let n=e.ts??``,r=t.ts??``;return n===r?0:n?r?n<r?-1:1:-1:1});let i;for(let e of r)e.type===`user`&&e.sender!==`system-prompt`&&e.ts&&(!i||e.ts>i)&&(i=e.ts);if(i)for(let e of r)e.type===`text`&&e.streaming&&(!e.ts||e.ts<i)&&(e.streaming=!1);return r}function R(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:`user`;case`session`:return`session`;default:return`agent`}}function re(e){let t=[],n=null;for(let r of e){let e=R(r),i=e===`session`||e===`system-prompt`;!n||n.speaker!==e||i?(n={speaker:e,ts:r.ts,items:[r]},t.push(n),i&&(n=null)):n.items.push(r)}return t}var z={class:`text-caption text-grey-6`},B=v({__name:`SessionEventItem`,props:{item:{}},setup(e){let n=e,r=p(()=>{switch(n.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,n)=>(t(),m(`span`,z,c(e.$t(r.value)),1))}}),V={key:0,class:`message-choices q-mt-sm`},ie={class:`choice-key text-weight-medium`},ae={class:`choice-label q-ml-xs ellipsis`},H=I(v({__name:`MessageChoices`,props:{choices:{},active:{type:Boolean}},setup(e){let n=e,i=O(),a=E();function s(e){if(!n.active)return;let t=a.selectedWorkspaceId;if(!t)return;let r=`${e.key}. ${e.label}`,o=a.selectedSessionId??void 0;i.sendChatMessage(t,r,o),a.addActivityItem(t,{id:`user-${Date.now()}`,type:`text`,content:r,timestamp:new Date().toISOString(),sessionId:o,meta:{sender:`user`,pending:!0}})}return(n,i)=>e.choices.length>0&&e.active?(t(),m(`div`,V,[(t(!0),m(h,null,r(e.choices,e=>(t(),l(w,{key:e.key,flat:``,dense:``,"no-caps":``,size:`sm`,color:`indigo-3`,class:`message-choice-btn q-mr-xs q-mb-xs`,onClick:t=>s(e)},{default:o(()=>[_(`span`,ie,c(e.key)+`.`,1),_(`span`,ae,c(e.label),1)]),_:2},1032,[`onClick`]))),128))])):u(``,!0)}}),[[`__scopeId`,`data-v-29c0dc7a`]]),oe=5,U=/^\s*(?:[-*]|\d+\.)\s+(?:\*\*([A-Za-z0-9])\.?\*\*|([A-Za-z])\.)\s+(.+?)\s*$/;function se(e){if(!e)return null;let t=e.split(`
2
+ `),n=0;for(;n<t.length;){let e=t[n].match(U);if(!e){n++;continue}let r=n,i=[{key:e[1]??e[2],label:e[3]}],a=n+1;for(;a<t.length;){let e=t[a];if(e.trim()===``){if(a+1<t.length&&t[a+1].trim()===``)break;a++;continue}let n=e.match(U);if(!n)break;i.push({key:n[1]??n[2],label:n[3]}),a++}if(i.length>=2){let e=Math.max(0,r-oe);if(t.slice(e,r).join(` `).includes(`?`))return{choices:i}}n=a}return null}function ce(e,t){if(t.length===0||e.length===0)return e;let n=[...t].sort((e,t)=>t.length-e.length),r=new DOMParser().parseFromString(`<div>${e}</div>`,`text/html`),i=r.body.firstChild;if(!i)return e;function a(e){if(e.nodeType===Node.TEXT_NODE){W(e,n,r);return}if(e.nodeName===`A`)return;let t=Array.from(e.childNodes);for(let e of t)a(e)}return a(i),i.innerHTML}function W(e,t,n){let r=e.textContent??``;if(!t.some(e=>r.includes(e)))return;let i=n.createDocumentFragment(),a=0;for(;a<r.length;){let e=le(r,a,t);if(!e){i.appendChild(n.createTextNode(r.slice(a)));break}e.index>a&&i.appendChild(n.createTextNode(r.slice(a,e.index)));let o=n.createElement(`a`);o.className=`document-link`,o.setAttribute(`data-document-path`,e.path),o.setAttribute(`href`,`#`),o.textContent=e.path,i.appendChild(o),a=e.index+e.path.length}e.parentNode?.replaceChild(i,e)}function le(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var G=[`innerHTML`],K=I(v({__name:`TextMessageItem`,props:{item:{},isLatestTurn:{type:Boolean}},setup(e){let n=e,r=L(),i=E(),a=p(()=>{let e=i.selectedWorkspaceId;return e?r.documentsFor(e).map(e=>e.path):[]}),o=p(()=>{let e=ce(P.parse(n.item.text,{async:!1,breaks:!0,gfm:!0}),a.value);return N.sanitize(e,{ADD_ATTR:[`data-document-path`]})}),s=p(()=>se(n.item.text));function c(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let n=t.getAttribute(`data-document-path`),a=i.selectedWorkspaceId;!n||!a||r.openDocumentByPath(a,n)}return(n,r)=>(t(),m(`div`,{class:`markdown-message`,onClick:c},[_(`div`,{innerHTML:o.value},null,8,G),e.item.streaming?(t(),l(x,{key:0,size:`xs`,class:`q-ml-xs`})):u(``,!0),s.value?(t(),l(H,{key:1,choices:s.value.choices,active:!!e.isLatestTurn&&!e.item.streaming},null,8,[`choices`,`active`])):u(``,!0)]))}}),[[`__scopeId`,`data-v-8382750f`]]),ue={key:0,class:`text-caption text-grey-5`,style:{"font-style":`italic`}},q=[`innerHTML`],de={key:1,style:{"white-space":`pre-wrap`}},J=I(v({__name:`ThinkingItem`,props:{item:{}},setup(e){let n=e,r=p(()=>n.item.text.trim().slice(0,100)),i=p(()=>n.item.text.trim().length>0),a=p(()=>n.item.text.trim().length>100),s=p(()=>{let e=P.parse(n.item.text,{async:!1,breaks:!0,gfm:!0});return N.sanitize(e)});return(n,d)=>i.value?(t(),m(`div`,ue,[a.value?(t(),l(M,{key:0,dense:``,"dense-toggle":``,label:r.value,"header-class":`text-grey-5 text-caption`,style:{"font-style":`italic`}},{default:o(()=>[_(`div`,{class:`q-py-xs markdown-thinking`,innerHTML:s.value},null,8,q)]),_:1},8,[`label`])):(t(),m(`span`,de,c(e.item.text),1))])):u(``,!0)}}),[[`__scopeId`,`data-v-4e64694c`]]);function Y(e,t){let n=e.split(`
3
+ `),r=t.split(`
4
+ `),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)n[e]===r[t]?o[e][t]=o[e+1][t+1]+1:o[e][t]=Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push({type:`context`,content:n[c]}),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push({type:`del`,content:n[c]}),c++):(s.push({type:`add`,content:r[l]}),l++);for(;c<i;)s.push({type:`del`,content:n[c++]});for(;l<a;)s.push({type:`add`,content:r[l++]});return s}function fe(e,t){if(!t||typeof t!=`object`)return null;let n=t;if(e===`Edit`){let e=n.file_path;if(!e)return null;let t=n.old_string??``,r=n.new_string??``;return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
5
+ `).length:0,deletions:t?t.split(`
6
+ `).length:0}}if(e===`Write`){let e=n.file_path;if(!e)return null;let t=n.content??``;return{toolName:`Write`,filePath:e,content:t,additions:t?t.split(`
7
+ `).length:0,deletions:0}}if(e===`Bash`){let e=(n.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function X(e,t){if(!e||!t?.projectPath)return e;let n=Z(e,`${t.projectPath}/.worktrees/${t.workingBranch}`);return n===e&&(n=Z(e,t.projectPath)),n}function Z(e,t){if(!t)return e;let n=t.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`);return e.replace(RegExp(`${n}/`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`)}var Q={class:`tool-name`},$=[`title`],pe={key:0,class:`tool-stat-add`},me={key:1,class:`tool-stat-del`},he={class:`diff-sign`},ge={class:`tool-name`},_e=[`title`],ve=I(v({__name:`ToolCallItem`,props:{item:{}},setup(e){let i=e,o=a(!1),g=E(),v=p(()=>fe(i.item.name,i.item.input)),y=p(()=>v.value?X(v.value.filePath,g.selectedWorkspace):``),x={Bash:`terminal`,Read:`description`,Edit:`edit`,Write:`edit_note`,MultiEdit:`edit`,Glob:`folder_open`,Grep:`manage_search`,LS:`list`,Skill:`auto_awesome`,Task:`hub`,Agent:`hub`,TodoWrite:`checklist`,TodoRead:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},C=p(()=>x[i.item.name]??`build`),w=p(()=>{if(v.value)return``;let e=i.item.input,t=T(e);return t?X(t,g.selectedWorkspace):``});function T(e){if(!e||typeof e!=`object`)return typeof e==`string`?e:``;let t=e;for(let e of[`file_path`,`path`,`command`,`pattern`,`query`,`url`,`skill`,`description`,`subject`,`prompt`]){let n=t[e];if(typeof n==`string`&&n.length>0)return n}for(let e of Object.values(t))if(typeof e==`string`&&e.length>0)return e;return``}let D=p(()=>{let e=v.value;return e?e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?Y(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
8
+ `).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),O=p(()=>{let e=i.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),k=new Set([`Read`]),A=p(()=>!!(i.item.result&&O.value)&&(!k.has(i.item.name)||i.item.result?.isError===!0));function j(){o.value=!o.value}return n(()=>i.item.result?.isError===!0,e=>{e&&(o.value=!0)},{immediate:!0}),(n,i)=>v.value?(t(),m(`div`,{key:0,class:s([`tool-row`,{"tool-row-expanded":o.value}])},[_(`div`,{class:`tool-header`,onClick:j},[d(S,{name:C.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,Q,c(v.value.toolName===`Bash:rm`?`Bash`:v.value.toolName),1),_(`span`,{class:`tool-path`,title:v.value.filePath},c(y.value),9,$),v.value.additions>0?(t(),m(`span`,pe,`+`+c(v.value.additions),1)):u(``,!0),v.value.deletions>0?(t(),m(`span`,me,`-`+c(v.value.deletions),1)):u(``,!0),e.item.result?.isError?(t(),l(S,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):e.item.result?(t(),l(S,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):u(``,!0),d(S,{name:o.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-grey-6`},null,8,[`name`])]),o.value&&D.value?(t(),m(`div`,{key:0,class:`tool-diff`,onClick:i[0]||=b(()=>{},[`stop`])},[(t(!0),m(h,null,r(D.value,(e,n)=>(t(),m(`div`,{key:n,class:s([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[_(`span`,he,c(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),f(c(e.content),1)],2))),128))])):u(``,!0)],2)):(t(),m(`div`,{key:1,class:s([`tool-row tool-row-generic`,{"tool-row-expanded":o.value,"tool-row--toggleable":A.value}])},[_(`div`,{class:`tool-header`,onClick:i[1]||=e=>A.value&&j()},[d(S,{name:C.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,ge,c(e.item.name),1),w.value?(t(),m(`span`,{key:0,class:`tool-arg`,title:T(e.item.input)||w.value},c(w.value),9,_e)):u(``,!0),e.item.result?.isError?(t(),l(S,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):e.item.result?(t(),l(S,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):u(``,!0),A.value?(t(),l(S,{key:3,name:o.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-grey-6`},null,8,[`name`])):u(``,!0)]),o.value&&A.value?(t(),m(`div`,{key:0,class:`tool-output`,onClick:i[2]||=b(()=>{},[`stop`])},c(O.value),1)):u(``,!0)],2))}}),[[`__scopeId`,`data-v-b1fcd20d`]]);function ye(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`![${r}](${`/api/workspaces/${encodeURIComponent(t)}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var be=[`innerHTML`],xe=[`innerHTML`],Se=[`src`],Ce=I(v({__name:`UserMessageItem`,props:{item:{}},setup(e){let n=e,r=E(),i=p(()=>n.item.sender===`system-prompt`),s=p(()=>{let e=ye(n.item.content,r.selectedWorkspaceId??``),t=P.parse(e,{async:!1,breaks:!0,gfm:!0});return N.sanitize(t)}),c=a(null),f=a(!1);function g(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(c.value=n.src,f.value=!0)}return(e,n)=>(t(),m(h,null,[i.value?(t(),l(M,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-grey-5 text-caption`},{default:o(()=>[_(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:s.value},null,8,be)]),_:1},8,[`label`])):(t(),m(`div`,{key:1,class:`markdown-message`,onClick:g},[_(`div`,{innerHTML:s.value},null,8,xe)])),d(A,{modelValue:f.value,"onUpdate:modelValue":n[1]||=e=>f.value=e},{default:o(()=>[c.value?(t(),m(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>f.value=!1},null,8,Se)):u(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-c2e7c407`]]),we={class:`turn-header`},Te={key:0,class:`turn-time`},Ee={class:`turn-time turn-time-updated`},De={key:2,class:`turn-actions`},Oe={class:`turn-body`},ke={key:0,class:`turn-scroll-top`},Ae=I(v({__name:`TurnCard`,props:{turn:{},isLatestTurn:{type:Boolean}},emits:[`scrollTo`],setup(e,{emit:n}){let i=e,v=n,{t:b}=T(),x=a(null);function C(){let e=x.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;v(`scrollTo`,Math.max(0,n-8))}let E=p(()=>{switch(i.turn.speaker){case`user`:return{label:b(`chat.you`),accent:`#ce93d8`,badgeClass:`turn-badge-user`};case`agent`:return{label:b(`chat.agent`),accent:`#7986cb`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:b(`chat.systemPrompt`),accent:`#757575`,badgeClass:`turn-badge-system`};case`session`:return{label:b(`chat.session`),accent:`#616161`,badgeClass:`turn-badge-session`}}});function D(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let O=p(()=>D(i.turn.ts)),k=p(()=>{let e=i.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),A=p(()=>{let e=i.turn.ts,t=k.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:D(t,r-n<6e4)}),j=p(()=>A.value!==``),M=p(()=>i.turn.items.filter(e=>e.type===`tool`).length);return(n,i)=>(t(),m(`div`,{ref_key:`cardEl`,ref:x,class:s([`turn-card`,{"turn-card--user":e.turn.speaker===`user`}]),style:y({"--turn-accent":E.value.accent})},[_(`div`,we,[_(`span`,{class:s([`turn-badge`,E.value.badgeClass])},c(E.value.label),3),O.value?(t(),m(`span`,Te,c(O.value),1)):u(``,!0),j.value?(t(),m(h,{key:1},[d(S,{name:`arrow_forward`,size:`10px`,color:`grey-7`,class:`turn-time-arrow`}),_(`span`,Ee,[f(c(A.value)+` `,1),d(F,null,{default:o(()=>[f(c(g(b)(`chat.lastUpdatedAt`,{time:A.value})),1)]),_:1})])],64)):u(``,!0),M.value>0?(t(),m(`span`,De,` · `+c(g(b)(`chat.nActions`,{n:M.value})),1)):u(``,!0)]),_(`div`,Oe,[(t(!0),m(h,null,r(e.turn.items,(n,r)=>(t(),m(h,{key:r},[n.type===`text`?(t(),l(K,{key:0,item:n,"is-latest-turn":e.isLatestTurn},null,8,[`item`,`is-latest-turn`])):n.type===`thinking`?(t(),l(J,{key:1,item:n},null,8,[`item`])):n.type===`tool`?(t(),l(ve,{key:2,item:n},null,8,[`item`])):n.type===`user`?(t(),l(Ce,{key:3,item:n},null,8,[`item`])):n.type===`session`?(t(),l(B,{key:4,item:n},null,8,[`item`])):u(``,!0)],64))),128))]),e.turn.items.length>4?(t(),m(`div`,ke,[d(w,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`grey-6`,class:`turn-scroll-top-btn`,onClick:C},{default:o(()=>[d(F,null,{default:o(()=>[f(c(g(b)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):u(``,!0)],6))}}),[[`__scopeId`,`data-v-813aff2b`]]),je={key:0,class:`activity-feed-switching`},Me={key:1,class:`activity-feed-wrap`},Ne={key:0,class:`text-center q-py-sm text-caption text-grey-6`},Pe={class:`q-pa-md`},Fe={key:1,class:`q-px-md q-pb-md`},Ie={class:`activity-feed-nav-cluster`},Le=60,Re=200,ze=200,Be=400,Ve=200,He=I(v({__name:`ActivityFeed`,props:{workspaceId:{}},setup(s){let g=s,v=D(),y=C(),b=E(),S=p(()=>b.selectedSessionId),T=p(()=>b.sessions.find(e=>e.id===S.value)?.engineSessionId??null),O=p(()=>{let e=b.sessions;return e.length===0?!1:S.value===e[e.length-1].id});function A(e){return S.value?e?e===S.value||e===T.value:O.value:!0}let N=p(()=>(b.activityFeeds[g.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&A(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId}))),P=p(()=>k(b.workspaces.find(e=>e.id===g.workspaceId)?.status)),F=p(()=>{let e=v.eventsFor(g.workspaceId),t=v.timestampsFor(g.workspaceId),n=v.sessionIdsFor(g.workspaceId),r=[],i=[];for(let a=0;a<e.length;a++)A(n[a])&&(r.push(e[a]),i.push(t[a]));let a=ne(te(r,i,P.value),N.value);return re(y.showVerboseSystemMessages?a:a.filter(e=>e.type!==`session`))}),I=p(()=>y.showVerboseSystemMessages?v.eventsFor(g.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),L=a(null),R=a(!0),z=a(!1),B=!1,V=a(!0),ie=a(new Map);function ae(e){R.value=e.verticalSize-e.verticalPosition-e.verticalContainerSize<=Le,B&&e.verticalPosition<=Re&&!z.value&&oe()&&ce()}function H(e,t){return`${e}:${t}`}function oe(){let e=S.value;return e?ie.value.get(H(g.workspaceId,e))??!0:v.hasMoreOlderFor(g.workspaceId)}function U(e,t,n){ie.value.set(H(e,t),n)}function se(e){if(!S.value)return v.oldestIdFor(e);let t=v.eventIdsFor(e),n=v.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!A(n[e]))continue;let r=t[e];if(r)return r}}async function ce(){let t=g.workspaceId,n=S.value,r=se(t);if(!r)return;z.value=!0;let i=Date.now();try{let i=L.value,a=i?.getScroll().verticalSize??0,o=i?.getScroll().verticalPosition??0,s=new URLSearchParams({before:r,limit:`200`});n&&s.set(`session`,n);let c=fetch(`/api/workspaces/${t}/events?${s.toString()}`),l=new Promise(e=>setTimeout(e,ze)),[u]=await Promise.all([c,l]);if(!u.ok){n?U(t,n,!1):v.prepend(t,[],[],{oldestId:r,hasMoreOlder:!1});return}let d=await u.json(),f=d.events??[],p=f.filter(e=>e.type===`agent:event`&&e.workspaceId===t),m=f.filter(e=>e.type===`user:message`&&e.workspaceId===t),h=p.map(e=>e.payload),g=p.map(e=>e.createdAt),_=p.map(e=>e.sessionId??null),y=p.map(e=>e.id),x=f.length>0?f[0].id:r;n&&U(t,n,d.hasMore),v.prepend(t,h,g,{oldestId:x,hasMoreOlder:n?v.hasMoreOlderFor(t):d.hasMore,sessionIds:_,eventIds:y});for(let e of m){let n=e.payload;typeof n.content==`string`&&b.addActivityItem(t,{id:e.id,type:`text`,content:n.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await e(),i){let e=i.getScroll().verticalSize-a,t=Math.max(o+e,Re+50);i.setScrollPosition(`vertical`,t,0)}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-i,t=Math.max(0,ze-e);await new Promise(e=>setTimeout(e,t+Be)),z.value=!1}}async function W(t=0){await e();let n=L.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,t)}function le(e){let t=L.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}let G=a([]),K=a(null);function ue(){let e=F.value,t=G.value,n=[];if(t.length===e.length){for(let r=0;r<e.length;r++){if(e[r].speaker!==`user`)continue;let i=t[r]?.$el;i&&n.push(i)}if(n.length>0)return n}let r=K.value?.parentElement;if(r){let e=r.querySelectorAll(`.turn-card--user`);for(let t of e)n.push(t)}return n}function q(){let e=L.value;if(!e)return null;let t=K.value;if(!t)return null;let n=e.getScroll().verticalPosition,r=t.getBoundingClientRect().top,i=null;for(let e of ue()){let t=e.getBoundingClientRect().top-r;if(t<n-40)i=t;else break}return i}async function de(){let t=L.value;if(!t)return;let n=q();if(n===null)for(let t=0;t<15&&oe();t++){for(;z.value;)await new Promise(e=>setTimeout(e,50));if(await ce(),await e(),n=q(),n!==null)break}n!==null&&t.setScrollPosition(`vertical`,Math.max(0,n-12),250)}async function J(){B=!1,await e(),await W(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{B=!0})})}let Y=p(()=>{let e=v.sessionIdsFor(g.workspaceId);if(!S.value)return e.length;let t=0;for(let n of e)A(n)&&t++;return t}),fe=p(()=>v.eventsFor(g.workspaceId).length);async function X(){V.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,Ve));let t=e+5e3;for(;fe.value===0&&Date.now()<t;)await new Promise(e=>setTimeout(e,50));V.value=!1}n(V,async e=>{!e&&Y.value>0&&await J(),!e&&Y.value===0&&S.value&&$()}),i(()=>{X(),Y.value>0&&J(),S.value&&$()});let Z=!1;n(Y,async(e,t)=>{if(!Z&&e>0){Z=!0,await J();return}e>t&&R.value&&!z.value&&await W(180)}),n(()=>g.workspaceId,()=>{R.value=!0,Z=!1,B=!1,X(),Y.value>0&&J()}),n(()=>b.selectedSessionId,async()=>{R.value=!0,B=!1,await J(),$()});let Q=new Set;async function $(){let t=S.value;if(!t||Y.value>0)return;let n=H(g.workspaceId,t);if(!Q.has(n)){Q.add(n);try{let n=await fetch(`/api/workspaces/${g.workspaceId}/events?session=${encodeURIComponent(t)}&limit=500`);if(!n.ok)return;let r=await n.json(),i=r.events??[];if(i.length===0)return;let a=i.filter(e=>e.type===`agent:event`&&e.workspaceId===g.workspaceId),o=i.filter(e=>e.type===`user:message`&&e.workspaceId===g.workspaceId),s=a.map(e=>e.payload),c=a.map(e=>e.createdAt),l=a.map(e=>e.sessionId??null),u=a.map(e=>e.id);U(g.workspaceId,t,r.hasMore),s.length>0&&v.prepend(g.workspaceId,s,c,{oldestId:i[0].id,hasMoreOlder:v.hasMoreOlderFor(g.workspaceId),sessionIds:l,eventIds:u});for(let e of o){let t=e.payload;typeof t.content==`string`&&b.addActivityItem(g.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await e(),await W(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Q.delete(n)}}}n(p(()=>N.value.filter(e=>e.sender!==`system-prompt`).length),async(e,t)=>{e>t&&(R.value=!0,await W(180))});async function pe(){R.value=!0,await W(250)}return(e,n)=>V.value?(t(),m(`div`,je,[d(j,{size:`40px`,color:`indigo-4`})])):(t(),m(`div`,Me,[d(ee,{ref_key:`scrollRef`,ref:L,class:`activity-feed-scroll`,onScroll:ae},{default:o(()=>[_(`div`,{ref_key:`contentOriginRef`,ref:K,class:`content-origin-marker`},null,512),z.value?(t(),m(`div`,Ne,[d(x,{size:`sm`}),f(` `+c(e.$t(`activity.loading_older`)),1)])):u(``,!0),_(`div`,Pe,[(t(!0),m(h,null,r(F.value,(e,n)=>(t(),l(Ae,{key:n,ref_for:!0,ref_key:`turnRefs`,ref:G,turn:e,"is-latest-turn":n===F.value.length-1,onScrollTo:le},null,8,[`turn`,`is-latest-turn`]))),128))]),I.value.length?(t(),m(`div`,Fe,[d(M,{label:e.$t(`activity.raw_lines`,{n:I.value.length}),dense:``},{default:o(()=>[(t(!0),m(h,null,r(I.value,(e,n)=>(t(),m(`div`,{key:n,class:`text-caption text-grey q-pa-xs`},c(e),1))),128))]),_:1},8,[`label`])])):u(``,!0)]),_:1},512),_(`div`,Ie,[R.value?u(``,!0):(t(),l(w,{key:0,round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),onClick:pe},null,8,[`title`])),d(w,{round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),onClick:de},null,8,[`title`])])]))}}),[[`__scopeId`,`data-v-989e89a0`]]);export{He as default};
@@ -0,0 +1 @@
1
+ import{k as e,m as t}from"./QIcon-B0-pH3Qs.js";import{_ as n,g as r}from"./settings-Cw4mtk9x.js";function i(e){if(e===!1)return 0;if(e===!0||e===void 0)return 1;let t=parseInt(e,10);return isNaN(t)?0:t}var a=e({name:`close-popup`,beforeMount(e,{value:a}){let o={depth:i(a),handler(t){o.depth!==0&&setTimeout(()=>{let i=n(e);i!==void 0&&r(i,t,o.depth)})},handlerKey(e){t(e,13)===!0&&o.handler(e)}};e.__qclosepopup=o,e.addEventListener(`click`,o.handler),e.addEventListener(`keyup`,o.handlerKey)},updated(e,{value:t,oldValue:n}){t!==n&&(e.__qclosepopup.depth=i(t))},beforeUnmount(e){let t=e.__qclosepopup;e.removeEventListener(`click`,t.handler),e.removeEventListener(`keyup`,t.handlerKey),delete e.__qclosepopup}});export{a as t};
@@ -0,0 +1 @@
1
+ .create-page[data-v-f098e4b1]{background-color:#1a1a2e;min-height:100%;padding:48px 24px}.create-inner[data-v-f098e4b1]{width:100%;max-width:700px}.create-title[data-v-f098e4b1]{font-size:24px;line-height:1.3}.create-card[data-v-f098e4b1]{background:#224;border:1px solid #444;overflow:hidden}.card-top-bar[data-v-f098e4b1]{background:#1e1e3a;min-height:36px}.card-name-wrap[data-v-f098e4b1]{background:#224;padding:8px 16px 4px}.card-name-wrap[data-v-f098e4b1] .q-field__control{height:32px;min-height:32px;padding:0}.card-name-wrap[data-v-f098e4b1] input{color:#e0e0e0;font-size:15px;font-weight:500}.card-name-wrap[data-v-f098e4b1] input::placeholder{color:#555}.card-textarea-wrap[data-v-f098e4b1]{background:#224;position:relative}.create-slash-popup[data-v-f098e4b1]{z-index:9999;position:absolute;top:calc(100% + 4px);left:12px;right:12px}.repo-select[data-v-f098e4b1]{min-width:160px;max-width:260px}.repo-select[data-v-f098e4b1] .q-field__prepend{align-items:center;height:auto;padding-top:0}.create-textarea[data-v-f098e4b1]{color:#d0d0d0;width:100%;padding:12px 16px 4px}.create-textarea[data-v-f098e4b1] .q-field__control{padding:0}.create-textarea[data-v-f098e4b1] textarea{color:#d0d0d0;resize:none;min-height:100px;font-size:14px;line-height:1.6}.create-textarea[data-v-f098e4b1] textarea::placeholder{color:#666}.notion-toggle-btn[data-v-f098e4b1]{background:#333;padding:2px 10px}.notion-url-wrap[data-v-f098e4b1]{background:#1e1e3a;padding:8px 0 0}.notion-url-input[data-v-f098e4b1]{padding:0 12px}.notion-url-input[data-v-f098e4b1] .q-field__control{height:36px;min-height:36px;padding:0}.notion-url-input[data-v-f098e4b1] input{color:#d0d0d0;font-size:13px}.notion-url-input[data-v-f098e4b1] input::placeholder{color:#555;font-size:12px}.notion-error[data-v-f098e4b1],.notion-valid[data-v-f098e4b1]{padding-bottom:6px}.notion-peek-choice[data-v-f098e4b1]{padding-top:4px}.peek-card[data-v-f098e4b1]{cursor:pointer;text-align:left;color:#e0e0e0;font-family:inherit;font-size:inherit;background:#ffffff08;border:1px solid #ffffff14;border-radius:8px;align-items:center;gap:10px;padding:10px 12px;transition:background .15s,border-color .15s,transform .1s;display:flex;position:relative}.peek-card[data-v-f098e4b1]:hover{background:#ffffff0f;border-color:#6c63ff66}.peek-card[data-v-f098e4b1]:active{transform:scale(.99)}.peek-card--active[data-v-f098e4b1]{background:#6c63ff1f;border-color:#6c63ffd9;box-shadow:0 0 0 1px #6c63ff66}.peek-card--active .peek-card-icon[data-v-f098e4b1]{color:#8a82ff}.peek-card--active .peek-card-title[data-v-f098e4b1]{color:#fff}.peek-card-icon[data-v-f098e4b1]{color:#999;flex-shrink:0}.peek-card-text[data-v-f098e4b1]{flex:1;min-width:0;line-height:1.25}.peek-card-title[data-v-f098e4b1]{color:#d0d0d0;font-size:12px;font-weight:600}.peek-card-desc[data-v-f098e4b1]{color:#888;margin-top:2px;font-size:10.5px}.peek-card-check[data-v-f098e4b1]{flex-shrink:0}.sentry-toggle-btn[data-v-f098e4b1]{background:#333;padding:2px 10px}.sentry-url-wrap[data-v-f098e4b1]{background:#1e1e3a;padding:8px 0 0}.sentry-url-input[data-v-f098e4b1]{padding:0 12px}.sentry-url-input[data-v-f098e4b1] .q-field__control{height:36px;min-height:36px;padding:0}.sentry-url-input[data-v-f098e4b1] input{color:#d0d0d0;font-size:13px}.sentry-url-input[data-v-f098e4b1] input::placeholder{color:#555;font-size:12px}.sentry-error[data-v-f098e4b1],.sentry-valid[data-v-f098e4b1]{padding-bottom:6px}.slide-enter-active[data-v-f098e4b1],.slide-leave-active[data-v-f098e4b1]{transition:all .2s;overflow:hidden}.slide-enter-from[data-v-f098e4b1],.slide-leave-to[data-v-f098e4b1]{opacity:0;max-height:0}.slide-enter-to[data-v-f098e4b1],.slide-leave-from[data-v-f098e4b1]{opacity:1;max-height:120px}.card-bottom-bar[data-v-f098e4b1]{background:#1e1e3a}.skip-setup-btn[data-v-f098e4b1]{min-height:28px;padding:2px 10px;font-size:11px}.skip-setup-btn[data-v-f098e4b1] .q-btn__content{gap:4px}.skip-setup-btn[data-v-f098e4b1] .q-icon{font-size:14px}.bottom-row-git .bottom-select-label[data-v-f098e4b1]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.bottom-row-git .bottom-select.repo-select[data-v-f098e4b1] input{color:#bbb;padding:0 4px;font-size:11px}.bottom-row-git .bottom-select.repo-select[data-v-f098e4b1] input::placeholder{color:#666;font-style:italic}.bottom-select[data-v-f098e4b1]{background:#333;height:28px;padding:0 6px}.bottom-select[data-v-f098e4b1] .q-field__control{height:28px;min-height:28px;padding:0}.bottom-select[data-v-f098e4b1] .q-field__native{min-height:unset;padding:0}.bottom-select-label[data-v-f098e4b1]{color:#bbb;gap:2px;font-size:11px}.bottom-sep[data-v-f098e4b1]{color:#555;padding:0 2px;font-size:12px;line-height:1}.repo-path-wrap[data-v-f098e4b1]{background:#333;border-radius:6px;height:28px;padding:0 8px}.repo-input[data-v-f098e4b1]{min-width:140px}.repo-input[data-v-f098e4b1] .q-field__control{height:28px;min-height:28px;padding:0}.repo-input[data-v-f098e4b1] input{color:#bbb;font-size:11px}.repo-input[data-v-f098e4b1] input::placeholder{color:#666;font-size:11px}.branch-select[data-v-f098e4b1]{min-width:80px}.create-btn[data-v-f098e4b1]{color:#fff;background:#4f46e5;min-width:220px;height:32px;padding:0 32px;font-size:13px}.create-btn[data-v-f098e4b1] .q-btn__content{height:32px}.create-hint[data-v-f098e4b1]{line-height:1.5}.fade-enter-active[data-v-f098e4b1],.fade-leave-active[data-v-f098e4b1]{transition:opacity .2s}.fade-enter-from[data-v-f098e4b1],.fade-leave-to[data-v-f098e4b1]{opacity:0}.manual-hint[data-v-f098e4b1]{background:#1e1e3a;line-height:1.4}.manual-expansion[data-v-f098e4b1]{background:#1e1e3a;border:1px solid #333;border-radius:4px;margin-top:6px;overflow:hidden}.manual-expansion[data-v-f098e4b1] .manual-expansion-header{min-height:32px;padding:4px 10px;font-size:12px}.manual-expansion[data-v-f098e4b1] .q-expansion-item__content,.manual-section-body[data-v-f098e4b1]{background:#1a1a2e}.manual-input[data-v-f098e4b1] .q-field__control{height:26px;min-height:26px;padding:0}.manual-input[data-v-f098e4b1] input{color:#e0e0e0;font-size:12px}.manual-input[data-v-f098e4b1] input::placeholder{color:#555}.manual-item[data-v-f098e4b1]{border-top:1px solid #ffffff0a}.manual-item[data-v-f098e4b1]:first-child{border-top:none}