@loicngr/kobo 1.6.1 → 1.6.3

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 (152) hide show
  1. package/README.md +17 -16
  2. package/dist/mcp-server/kobo-tasks-handlers.js +145 -0
  3. package/dist/mcp-server/kobo-tasks-server.js +144 -54
  4. package/dist/server/index.js +2 -2
  5. package/dist/server/routes/documents.js +113 -0
  6. package/dist/server/services/agent/engines/claude-code/args-builder.js +21 -0
  7. package/package.json +1 -1
  8. package/src/client/dist/spa/assets/ActivityFeed-BCPHgr3p.css +1 -0
  9. package/src/client/dist/spa/assets/ActivityFeed-DPg8lZZP.js +7 -0
  10. package/src/client/dist/spa/assets/{ClosePopup-DqhgFbQo.js → ClosePopup-DhM1C4Zw.js} +1 -1
  11. package/src/client/dist/spa/assets/{CreatePage-DENfwzPL.js → CreatePage-C2mUAcwE.js} +1 -1
  12. package/src/client/dist/spa/assets/DiffViewer-BVU58ujc.css +1 -0
  13. package/src/client/dist/spa/assets/DiffViewer-DdLwXeB3.js +2 -0
  14. package/src/client/dist/spa/assets/{HealthPage-Cjc79NaA.js → HealthPage-CJDF2_4D.js} +1 -1
  15. package/src/client/dist/spa/assets/MainLayout-BiBJtDTk.css +1 -0
  16. package/src/client/dist/spa/assets/{MainLayout-CFbMw65L.js → MainLayout-K2u9uIOb.js} +17 -17
  17. package/src/client/dist/spa/assets/QBtn-CyzfM9-_.js +1 -0
  18. package/src/client/dist/spa/assets/QChip-KJoHYE6F.js +1 -0
  19. package/src/client/dist/spa/assets/{QDialog-D42GLa1i.js → QDialog-DQeAxY3-.js} +1 -1
  20. package/src/client/dist/spa/assets/QExpansionItem-DCRks-Ra.js +1 -0
  21. package/src/client/dist/spa/assets/{QItemSection-GlMrLmz3.js → QItemSection-CGpX7GcL.js} +1 -1
  22. package/src/client/dist/spa/assets/{QScrollArea-L6wUiA20.js → QScrollArea-e5qTqwcb.js} +1 -1
  23. package/src/client/dist/spa/assets/QSlideTransition-BQxI8l5r.js +1 -0
  24. package/src/client/dist/spa/assets/{QTabPanels-Crs-ujNO.js → QTabPanels--6cYe2US.js} +1 -1
  25. package/src/client/dist/spa/assets/{QTooltip-Cg9E3Dvw.js → QTooltip-C4CPesBX.js} +1 -1
  26. package/src/client/dist/spa/assets/{SearchPage-Bf-iZnyE.js → SearchPage-Drjd-r_Y.js} +1 -1
  27. package/src/client/dist/spa/assets/{SettingsPage-BdcH3BSs.js → SettingsPage-DvqT-ViK.js} +1 -1
  28. package/src/client/dist/spa/assets/{TouchPan-DFx22dM3.js → TouchPan-BT6phK1f.js} +1 -1
  29. package/src/client/dist/spa/assets/{WorkspacePage-UUE0pPCR.js → WorkspacePage-Bk_SslTv.js} +3 -3
  30. package/src/client/dist/spa/assets/build-path-tree-BE2FoPWg.js +1 -0
  31. package/src/client/dist/spa/assets/{cssMode-BYtqFZtm.js → cssMode-0GRgcPCL.js} +1 -1
  32. package/src/client/dist/spa/assets/documents-BJW_8dHZ.js +60 -0
  33. package/src/client/dist/spa/assets/{editor.api-D6ZaO4A_.js → editor.api-C1-xuJKd.js} +1 -1
  34. package/src/client/dist/spa/assets/{editor.main-Cc_RDKsq.js → editor.main-C2QDgca3.js} +3 -3
  35. package/src/client/dist/spa/assets/{freemarker2-CBm--bBd.js → freemarker2-Dgc_0Q23.js} +1 -1
  36. package/src/client/dist/spa/assets/{handlebars-whX2mkV5.js → handlebars-DkfsYZsi.js} +1 -1
  37. package/src/client/dist/spa/assets/{html-D7ga_o6c.js → html-CbSyHFvE.js} +1 -1
  38. package/src/client/dist/spa/assets/{htmlMode-BXImjcsv.js → htmlMode-BKR6h-2d.js} +1 -1
  39. package/src/client/dist/spa/assets/i18n-C03q300x.js +1 -0
  40. package/src/client/dist/spa/assets/index-9vZWx9Bu.js +2 -0
  41. package/src/client/dist/spa/assets/{javascript-BwmzNMn5.js → javascript-QJqA2E1u.js} +1 -1
  42. package/src/client/dist/spa/assets/{jsonMode-CN5Z5bK_.js → jsonMode-D8CXYQiy.js} +1 -1
  43. package/src/client/dist/spa/assets/{liquid-CzMNAPor.js → liquid-DLssQpNB.js} +1 -1
  44. package/src/client/dist/spa/assets/{mdx-DC_P05Da.js → mdx-4u6l2_x4.js} +1 -1
  45. package/src/client/dist/spa/assets/{models-DMQoi09X.js → models-BtywKe_m.js} +1 -1
  46. package/src/client/dist/spa/assets/{monaco.contribution-BsBaFOOD.js → monaco.contribution-DFCuvXsm.js} +2 -2
  47. package/src/client/dist/spa/assets/{python-9DTZ8C3K.js → python-BFIl5AC4.js} +1 -1
  48. package/src/client/dist/spa/assets/{razor-B1LfM20o.js → razor-BrKtxN6e.js} +1 -1
  49. package/src/client/dist/spa/assets/{scroll-Dh2g7BwR.js → scroll-JVVkg2Ng.js} +1 -1
  50. package/src/client/dist/spa/assets/{tsMode-DI2bWo8r.js → tsMode-B7FS-eG8.js} +1 -1
  51. package/src/client/dist/spa/assets/{typescript-BZ9QJ2_N.js → typescript-CpUDqcsF.js} +1 -1
  52. package/src/client/dist/spa/assets/use-checkbox-DYiZQsbF.js +1 -0
  53. package/src/client/dist/spa/assets/{use-portal-mhLq4Rqk.js → use-portal-DBe4lcC2.js} +1 -1
  54. package/src/client/dist/spa/assets/{xml-D6qm6rp0.js → xml-_L0I3U6m.js} +1 -1
  55. package/src/client/dist/spa/assets/{yaml-D2dUr_wY.js → yaml-CY3U_Y-0.js} +1 -1
  56. package/src/client/dist/spa/index.html +6 -5
  57. package/src/mcp-server/kobo-tasks-handlers.ts +191 -0
  58. package/src/mcp-server/kobo-tasks-server.ts +158 -53
  59. package/dist/server/routes/plans.js +0 -89
  60. package/src/client/dist/spa/assets/ActivityFeed-DYtAK49y.js +0 -7
  61. package/src/client/dist/spa/assets/ActivityFeed-DiwnrdKX.css +0 -1
  62. package/src/client/dist/spa/assets/DiffViewer-C6q11kmw.js +0 -2
  63. package/src/client/dist/spa/assets/DiffViewer-DiHFLSk4.css +0 -1
  64. package/src/client/dist/spa/assets/MainLayout-B5poKEy_.css +0 -1
  65. package/src/client/dist/spa/assets/QBtn-p1aZtrJH.js +0 -1
  66. package/src/client/dist/spa/assets/QExpansionItem-5ekmpO-2.js +0 -1
  67. package/src/client/dist/spa/assets/QMenu-Q69oVX7b.js +0 -1
  68. package/src/client/dist/spa/assets/i18n-BxLBrD1J.js +0 -1
  69. package/src/client/dist/spa/assets/index-D997aY4Y.js +0 -2
  70. package/src/client/dist/spa/assets/marked.esm-DW0ulF0a.js +0 -60
  71. /package/src/client/dist/spa/assets/{QBadge-BUkmTO0P.js → QBadge-DqtcDv8D.js} +0 -0
  72. /package/src/client/dist/spa/assets/{QItemLabel-Czw5g0px.js → QItemLabel-Codqjisk.js} +0 -0
  73. /package/src/client/dist/spa/assets/{QList-DNzlynsS.js → QList-B-MkPF7n.js} +0 -0
  74. /package/src/client/dist/spa/assets/{QPage-B09NY4Nf.js → QPage-yqdKDG7-.js} +0 -0
  75. /package/src/client/dist/spa/assets/{QSpace-PlDK6Fg3.js → QSpace-BNr0AftG.js} +0 -0
  76. /package/src/client/dist/spa/assets/{QSpinnerDots-By20ptst.js → QSpinnerDots-DEiRooBD.js} +0 -0
  77. /package/src/client/dist/spa/assets/{_plugin-vue_export-helper-CEhRWsKN.js → _plugin-vue_export-helper-r4mAJOHR.js} +0 -0
  78. /package/src/client/dist/spa/assets/{abap-DiwvWnMr.js → abap-Bgec7Keq.js} +0 -0
  79. /package/src/client/dist/spa/assets/{apex-CmtZjKlf.js → apex-VBlPwEoQ.js} +0 -0
  80. /package/src/client/dist/spa/assets/{azcli-DL2My_i-.js → azcli-DKqrEFBx.js} +0 -0
  81. /package/src/client/dist/spa/assets/{bat-B-nC98wG.js → bat-DdgQWy_0.js} +0 -0
  82. /package/src/client/dist/spa/assets/{bicep-Ju5MwOgh.js → bicep-CRMM43EB.js} +0 -0
  83. /package/src/client/dist/spa/assets/{cameligo-8Eu1TyBr.js → cameligo-UatALtML.js} +0 -0
  84. /package/src/client/dist/spa/assets/{clojure-u-RpMkH3.js → clojure-D8JU08RA.js} +0 -0
  85. /package/src/client/dist/spa/assets/{coffee-CdA7bbTe.js → coffee-C56wu358.js} +0 -0
  86. /package/src/client/dist/spa/assets/{cpp-CzNFP8ks.js → cpp-CyZLvhJG.js} +0 -0
  87. /package/src/client/dist/spa/assets/{csharp-j1LThmcE.js → csharp-BJl3ixva.js} +0 -0
  88. /package/src/client/dist/spa/assets/{csp-CLRC61y6.js → csp-CxEKxmO-.js} +0 -0
  89. /package/src/client/dist/spa/assets/{css-r6rC_7P2.js → css-B0t_muXd.js} +0 -0
  90. /package/src/client/dist/spa/assets/{cypher-CW08XVUh.js → cypher-D1hqiMFD.js} +0 -0
  91. /package/src/client/dist/spa/assets/{dart-Cs9aL5T_.js → dart-Bz550Pyv.js} +0 -0
  92. /package/src/client/dist/spa/assets/{dockerfile-BWM0M184.js → dockerfile-CIXgVAuA.js} +0 -0
  93. /package/src/client/dist/spa/assets/{ecl-MJJuer5P.js → ecl-D9qbvZoA.js} +0 -0
  94. /package/src/client/dist/spa/assets/{elixir-D2AIuXqn.js → elixir-b2M38fAy.js} +0 -0
  95. /package/src/client/dist/spa/assets/{flow9-B2H24giC.js → flow9-Dq1UYMkt.js} +0 -0
  96. /package/src/client/dist/spa/assets/{format-uvONOeL4.js → format-Bttc9ToS.js} +0 -0
  97. /package/src/client/dist/spa/assets/{formatters-DiJ12fKd.js → formatters-BDadphwz.js} +0 -0
  98. /package/src/client/dist/spa/assets/{fsharp-CMk2OIJN.js → fsharp-CFNadkg7.js} +0 -0
  99. /package/src/client/dist/spa/assets/{go-BrMkuJg0.js → go-dSur1iB2.js} +0 -0
  100. /package/src/client/dist/spa/assets/{graphql-PSR1UKGv.js → graphql-qyhAo11d.js} +0 -0
  101. /package/src/client/dist/spa/assets/{hcl-DAQrbDOW.js → hcl-DFzjMyzm.js} +0 -0
  102. /package/src/client/dist/spa/assets/{ini-0TG5BxW0.js → ini-TdzA8TIl.js} +0 -0
  103. /package/src/client/dist/spa/assets/{java-rgorz17v.js → java-CSGA9pkE.js} +0 -0
  104. /package/src/client/dist/spa/assets/{julia-C8VMdHm8.js → julia-9izz5OsY.js} +0 -0
  105. /package/src/client/dist/spa/assets/{kotlin-CllWo3gX.js → kotlin-DuPK7AtF.js} +0 -0
  106. /package/src/client/dist/spa/assets/{less-Cgca25AP.js → less-B8d93iCg.js} +0 -0
  107. /package/src/client/dist/spa/assets/{lexon-D0GHdBaw.js → lexon-DWtEIyu7.js} +0 -0
  108. /package/src/client/dist/spa/assets/{lua-DmRsNG-P.js → lua-Ciq0OGgt.js} +0 -0
  109. /package/src/client/dist/spa/assets/{m3-BgL5dNKT.js → m3-Cki6JWj_.js} +0 -0
  110. /package/src/client/dist/spa/assets/{markdown-BuJfycGS.js → markdown-Cu47xwU0.js} +0 -0
  111. /package/src/client/dist/spa/assets/{mips-C9m_93PR.js → mips-BM8ui995.js} +0 -0
  112. /package/src/client/dist/spa/assets/{msdax-CpFHC9OI.js → msdax-DqLio0_c.js} +0 -0
  113. /package/src/client/dist/spa/assets/{mysql-qFvltsqN.js → mysql-v1wbjJOq.js} +0 -0
  114. /package/src/client/dist/spa/assets/{objective-c-Bnmr858J.js → objective-c-CQl3PGSB.js} +0 -0
  115. /package/src/client/dist/spa/assets/{pascal-WP0_D5AO.js → pascal-D4iW0ZtD.js} +0 -0
  116. /package/src/client/dist/spa/assets/{pascaligo-Blom4Rij.js → pascaligo-BdC9CZdj.js} +0 -0
  117. /package/src/client/dist/spa/assets/{perl-B-vk8g64.js → perl-BL10m4XD.js} +0 -0
  118. /package/src/client/dist/spa/assets/{pgsql-Cgvz6v67.js → pgsql-Be_oqVo3.js} +0 -0
  119. /package/src/client/dist/spa/assets/{php-8a3Lrw9m.js → php-BtvXSFRI.js} +0 -0
  120. /package/src/client/dist/spa/assets/{pla-DuFqEZ8V.js → pla-B2vUy15C.js} +0 -0
  121. /package/src/client/dist/spa/assets/{postiats-DkLtSgkp.js → postiats-CbmTTfXr.js} +0 -0
  122. /package/src/client/dist/spa/assets/{powerquery-BJ1aNepW.js → powerquery-DszLhJGx.js} +0 -0
  123. /package/src/client/dist/spa/assets/{powershell-rE98k687.js → powershell-B0dYktF6.js} +0 -0
  124. /package/src/client/dist/spa/assets/{protobuf-CUheFacr.js → protobuf-CZvaj1VX.js} +0 -0
  125. /package/src/client/dist/spa/assets/{pug-LDcAMD8w.js → pug-CPDx1B3S.js} +0 -0
  126. /package/src/client/dist/spa/assets/{qsharp-DUKSQoR1.js → qsharp-CDP9TFLl.js} +0 -0
  127. /package/src/client/dist/spa/assets/{r-D-QApv87.js → r-8DbbFX2l.js} +0 -0
  128. /package/src/client/dist/spa/assets/{rate-limit-labels-BvYERsho.js → rate-limit-labels-BoDORKFj.js} +0 -0
  129. /package/src/client/dist/spa/assets/{redis-SXdDyWR9.js → redis-DRWj9MtJ.js} +0 -0
  130. /package/src/client/dist/spa/assets/{redshift-Y6lsCryn.js → redshift-C6cElE_5.js} +0 -0
  131. /package/src/client/dist/spa/assets/{restructuredtext-edObr9a8.js → restructuredtext-W9pS9n3m.js} +0 -0
  132. /package/src/client/dist/spa/assets/{ruby-CNnUfF-8.js → ruby-BKnzWnk-.js} +0 -0
  133. /package/src/client/dist/spa/assets/{rust-IHUZWzBr.js → rust-YPCclWwe.js} +0 -0
  134. /package/src/client/dist/spa/assets/{sb-DrUvY44N.js → sb-BgM4DTFb.js} +0 -0
  135. /package/src/client/dist/spa/assets/{scala-B4hbXGLM.js → scala-fz1OPLMl.js} +0 -0
  136. /package/src/client/dist/spa/assets/{scheme-BGrd12j3.js → scheme-8Uz1RIbu.js} +0 -0
  137. /package/src/client/dist/spa/assets/{scss-x5G1ES4U.js → scss-Djo3IYXr.js} +0 -0
  138. /package/src/client/dist/spa/assets/{shell-DOehe2Y8.js → shell-CINF5Tx_.js} +0 -0
  139. /package/src/client/dist/spa/assets/{solidity-BeRvcwWV.js → solidity-GgiNEuUm.js} +0 -0
  140. /package/src/client/dist/spa/assets/{sophia-DZbkUNjy.js → sophia-Culj97P9.js} +0 -0
  141. /package/src/client/dist/spa/assets/{sparql-B7_oi5-h.js → sparql-C2ZlpxOY.js} +0 -0
  142. /package/src/client/dist/spa/assets/{sql-CTlsFWVE.js → sql-BEf5Pg7Y.js} +0 -0
  143. /package/src/client/dist/spa/assets/{st-DJVEJdPE.js → st-CT6UUoeH.js} +0 -0
  144. /package/src/client/dist/spa/assets/{swift-CwhT3fYa.js → swift-B5g0xTG3.js} +0 -0
  145. /package/src/client/dist/spa/assets/{systemverilog-BQN63pkN.js → systemverilog-CEgQz9DR.js} +0 -0
  146. /package/src/client/dist/spa/assets/{tcl-DqwfpskA.js → tcl-D0qL2L0I.js} +0 -0
  147. /package/src/client/dist/spa/assets/{touch-D_A29lik.js → touch-CBLrR6_z.js} +0 -0
  148. /package/src/client/dist/spa/assets/{twig-BiyenUgc.js → twig-BFUAVf1E.js} +0 -0
  149. /package/src/client/dist/spa/assets/{typespec-CWOJribt.js → typespec-CjVVcNKm.js} +0 -0
  150. /package/src/client/dist/spa/assets/{use-quasar-BBrzedjR.js → use-quasar-Ch82z8H5.js} +0 -0
  151. /package/src/client/dist/spa/assets/{vb-Cq5F87m3.js → vb-CZJr-DQz.js} +0 -0
  152. /package/src/client/dist/spa/assets/{wgsl-BAvW2lVr.js → wgsl-ivoXUo2e.js} +0 -0
@@ -1 +1 @@
1
- import{E as e,N as t,Q as n,S as r,j as i,k as a,l as o,o as s,y as c}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{O as l,j as u}from"./QIcon-B0-pH3Qs.js";import{f as d,m as f}from"./QBtn-p1aZtrJH.js";import{a as p,s as m}from"./private.use-form-D1RuEt2P.js";var h={},g=!1;function _(){g=!0}function v(){let e=null,t=c();function n(){e!==null&&(clearTimeout(e),e=null)}return i(n),a(n),{removeTimeout:n,registerTimeout(r,i){n(),f(t)===!1&&(e=setTimeout(()=>{e=null,r()},i))}}}function y(){let t,n=c();function r(){t=void 0}return i(r),a(r),{removeTick:r,registerTick(r){t=r,e(()=>{t===r&&(f(n)===!1&&t(),t=void 0)})}}}var b={transitionShow:{type:String,default:`fade`},transitionHide:{type:String,default:`fade`},transitionDuration:{type:[String,Number],default:300}};function x(e,t=()=>{},n=()=>{}){return{transitionProps:o(()=>{let r=`q-transition--${e.transitionShow||t()}`,i=`q-transition--${e.transitionHide||n()}`;return{appear:!0,enterFromClass:`${r}-enter-from`,enterActiveClass:`${r}-enter-active`,enterToClass:`${r}-enter-to`,leaveFromClass:`${i}-leave-from`,leaveActiveClass:`${i}-leave-active`,leaveToClass:`${i}-leave-to`}}),transitionStyle:o(()=>`--q-transition-duration: ${e.transitionDuration}ms`)}}var S=[],C=[],w=1,T=document.body;function E(e,t){let n=document.createElement(`div`);if(n.id=t===void 0?e:`q-portal--${t}--${w++}`,h.globalNodes!==void 0){let e=h.globalNodes.class;e!==void 0&&(n.className=e)}return T.appendChild(n),S.push(n),C.push(t),n}function D(e){let t=S.indexOf(e);S.splice(t,1),C.splice(t,1),e.remove()}var O=[];function k(e){return O.find(t=>t.contentEl!==null&&t.contentEl.contains(e))}function A(e,t){do{if(e.$options.name===`QMenu`){if(e.hide(t),e.$props.separateClosePopup===!0)return d(e)}else if(e.__qPortal===!0){let n=d(e);return n?.$options.name===`QPopupProxy`?(e.hide(t),n):e}e=d(e)}while(e!=null)}function j(e,t,n){for(;n!==0&&e!=null;){if(e.__qPortal===!0){if(n--,e.$options.name===`QMenu`){e=A(e,t);continue}e.hide(t)}e=d(e)}}var M=l({name:`QPortal`,setup(e,{slots:t}){return()=>t.default()}});function N(e){for(e=e.parent;e!=null;){if(e.type.name===`QGlobalDialog`)return!0;if(e.type.name===`QDialog`||e.type.name===`QMenu`)return!1;e=e.parent}return!1}function P(e,i,a,o){let c=n(!1),l=n(!1),d=null,f={},h=o===`dialog`&&N(e);function g(t){if(t===!0){m(f),l.value=!0;return}l.value=!1,c.value===!1&&(h===!1&&d===null&&(d=E(!1,o)),c.value=!0,O.push(e.proxy),p(f))}function _(t){if(l.value=!1,t!==!0)return;m(f),c.value=!1;let n=O.indexOf(e.proxy);n!==-1&&O.splice(n,1),d!==null&&(D(d),d=null)}return t(()=>{_(!0)}),e.proxy.__qPortal=!0,u(e.proxy,`contentEl`,()=>i.value),{showPortal:g,hidePortal:_,portalIsActive:c,portalIsAccessible:l,renderPortal:()=>h===!0?a():c.value===!0?[r(s,{to:d},r(M,a))]:void 0}}export{O as a,x as c,v as d,_ as f,k as i,b as l,g as m,A as n,E as o,h as p,j as r,D as s,P as t,y as u};
1
+ import{E as e,N as t,Q as n,S as r,j as i,k as a,l as o,o as s,y as c}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{O as l,j as u}from"./QIcon-B0-pH3Qs.js";import{h as d,p as f}from"./QBtn-CyzfM9-_.js";import{a as p,s as m}from"./private.use-form-D1RuEt2P.js";var h={},g=!1;function _(){g=!0}function v(){let e=null,t=c();function n(){e!==null&&(clearTimeout(e),e=null)}return i(n),a(n),{removeTimeout:n,registerTimeout(r,i){n(),d(t)===!1&&(e=setTimeout(()=>{e=null,r()},i))}}}function y(){let t,n=c();function r(){t=void 0}return i(r),a(r),{removeTick:r,registerTick(r){t=r,e(()=>{t===r&&(d(n)===!1&&t(),t=void 0)})}}}var b={transitionShow:{type:String,default:`fade`},transitionHide:{type:String,default:`fade`},transitionDuration:{type:[String,Number],default:300}};function x(e,t=()=>{},n=()=>{}){return{transitionProps:o(()=>{let r=`q-transition--${e.transitionShow||t()}`,i=`q-transition--${e.transitionHide||n()}`;return{appear:!0,enterFromClass:`${r}-enter-from`,enterActiveClass:`${r}-enter-active`,enterToClass:`${r}-enter-to`,leaveFromClass:`${i}-leave-from`,leaveActiveClass:`${i}-leave-active`,leaveToClass:`${i}-leave-to`}}),transitionStyle:o(()=>`--q-transition-duration: ${e.transitionDuration}ms`)}}var S=[],C=[],w=1,T=document.body;function E(e,t){let n=document.createElement(`div`);if(n.id=t===void 0?e:`q-portal--${t}--${w++}`,h.globalNodes!==void 0){let e=h.globalNodes.class;e!==void 0&&(n.className=e)}return T.appendChild(n),S.push(n),C.push(t),n}function D(e){let t=S.indexOf(e);S.splice(t,1),C.splice(t,1),e.remove()}var O=[];function k(e){return O.find(t=>t.contentEl!==null&&t.contentEl.contains(e))}function A(e,t){do{if(e.$options.name===`QMenu`){if(e.hide(t),e.$props.separateClosePopup===!0)return f(e)}else if(e.__qPortal===!0){let n=f(e);return n?.$options.name===`QPopupProxy`?(e.hide(t),n):e}e=f(e)}while(e!=null)}function j(e,t,n){for(;n!==0&&e!=null;){if(e.__qPortal===!0){if(n--,e.$options.name===`QMenu`){e=A(e,t);continue}e.hide(t)}e=f(e)}}var M=l({name:`QPortal`,setup(e,{slots:t}){return()=>t.default()}});function N(e){for(e=e.parent;e!=null;){if(e.type.name===`QGlobalDialog`)return!0;if(e.type.name===`QDialog`||e.type.name===`QMenu`)return!1;e=e.parent}return!1}function P(e,i,a,o){let c=n(!1),l=n(!1),d=null,f={},h=o===`dialog`&&N(e);function g(t){if(t===!0){m(f),l.value=!0;return}l.value=!1,c.value===!1&&(h===!1&&d===null&&(d=E(!1,o)),c.value=!0,O.push(e.proxy),p(f))}function _(t){if(l.value=!1,t!==!0)return;m(f),c.value=!1;let n=O.indexOf(e.proxy);n!==-1&&O.splice(n,1),d!==null&&(D(d),d=null)}return t(()=>{_(!0)}),e.proxy.__qPortal=!0,u(e.proxy,`contentEl`,()=>i.value),{showPortal:g,hidePortal:_,portalIsActive:c,portalIsAccessible:l,renderPortal:()=>h===!0?a():c.value===!0?[r(s,{to:d},r(M,a))]:void 0}}export{O as a,x as c,v as d,_ as f,k as i,b as l,g as m,A as n,E as o,h as p,j as r,D as s,P as t,y as u};
@@ -1 +1 @@
1
- import{m as e}from"./editor.api-D6ZaO4A_.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={comments:{blockComment:[`<!--`,`-->`]},brackets:[[`<`,`>`]],autoClosingPairs:[{open:`<`,close:`>`},{open:`'`,close:`'`},{open:`"`,close:`"`}],surroundingPairs:[{open:`<`,close:`>`},{open:`'`,close:`'`},{open:`"`,close:`"`}],onEnterRules:[{beforeText:RegExp(`<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`,`i`),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:s.languages.IndentAction.IndentOutdent}},{beforeText:RegExp(`<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,`i`),action:{indentAction:s.languages.IndentAction.Indent}}]},l={defaultToken:``,tokenPostfix:`.xml`,ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,``],{include:`@whitespace`},[/(<)(@qualifiedName)/,[{token:`delimiter`},{token:`tag`,next:`@tag`}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:`delimiter`},{token:`tag`},``,{token:`delimiter`}]],[/(<\?)(@qualifiedName)/,[{token:`delimiter`},{token:`metatag`,next:`@tag`}]],[/(<\!)(@qualifiedName)/,[{token:`delimiter`},{token:`metatag`,next:`@tag`}]],[/<\!\[CDATA\[/,{token:`delimiter.cdata`,next:`@cdata`}],[/&\w+;/,`string.escape`]],cdata:[[/[^\]]+/,``],[/\]\]>/,{token:`delimiter.cdata`,next:`@pop`}],[/\]/,``]],tag:[[/[ \t\r\n]+/,``],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,[`attribute.name`,``,`attribute.value`]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,[`attribute.name`,``,`attribute.value`]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,[`attribute.name`,``,`attribute.value`]],[/@qualifiedName/,`attribute.name`],[/\?>/,{token:`delimiter`,next:`@pop`}],[/(\/)(>)/,[{token:`tag`},{token:`delimiter`,next:`@pop`}]],[/>/,{token:`delimiter`,next:`@pop`}]],whitespace:[[/[ \t\r\n]+/,``],[/<!--/,{token:`comment`,next:`@comment`}]],comment:[[/[^<\-]+/,`comment.content`],[/-->/,{token:`comment`,next:`@pop`}],[/<!--/,`comment.content.invalid`],[/[<\-]/,`comment.content`]]}};export{c as conf,l as language};
1
+ import{m as e}from"./editor.api-C1-xuJKd.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={comments:{blockComment:[`<!--`,`-->`]},brackets:[[`<`,`>`]],autoClosingPairs:[{open:`<`,close:`>`},{open:`'`,close:`'`},{open:`"`,close:`"`}],surroundingPairs:[{open:`<`,close:`>`},{open:`'`,close:`'`},{open:`"`,close:`"`}],onEnterRules:[{beforeText:RegExp(`<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`,`i`),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:s.languages.IndentAction.IndentOutdent}},{beforeText:RegExp(`<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`,`i`),action:{indentAction:s.languages.IndentAction.Indent}}]},l={defaultToken:``,tokenPostfix:`.xml`,ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,``],{include:`@whitespace`},[/(<)(@qualifiedName)/,[{token:`delimiter`},{token:`tag`,next:`@tag`}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:`delimiter`},{token:`tag`},``,{token:`delimiter`}]],[/(<\?)(@qualifiedName)/,[{token:`delimiter`},{token:`metatag`,next:`@tag`}]],[/(<\!)(@qualifiedName)/,[{token:`delimiter`},{token:`metatag`,next:`@tag`}]],[/<\!\[CDATA\[/,{token:`delimiter.cdata`,next:`@cdata`}],[/&\w+;/,`string.escape`]],cdata:[[/[^\]]+/,``],[/\]\]>/,{token:`delimiter.cdata`,next:`@pop`}],[/\]/,``]],tag:[[/[ \t\r\n]+/,``],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,[`attribute.name`,``,`attribute.value`]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,[`attribute.name`,``,`attribute.value`]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,[`attribute.name`,``,`attribute.value`]],[/@qualifiedName/,`attribute.name`],[/\?>/,{token:`delimiter`,next:`@pop`}],[/(\/)(>)/,[{token:`tag`},{token:`delimiter`,next:`@pop`}]],[/>/,{token:`delimiter`,next:`@pop`}]],whitespace:[[/[ \t\r\n]+/,``],[/<!--/,{token:`comment`,next:`@comment`}]],comment:[[/[^<\-]+/,`comment.content`],[/-->/,{token:`comment`,next:`@pop`}],[/<!--/,`comment.content.invalid`],[/[<\-]/,`comment.content`]]}};export{c as conf,l as language};
@@ -1 +1 @@
1
- import{m as e}from"./editor.api-D6ZaO4A_.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={comments:{lineComment:`#`},brackets:[[`{`,`}`],[`[`,`]`],[`(`,`)`]],autoClosingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],surroundingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:s.languages.IndentAction.Indent}}]},l={tokenPostfix:`.yaml`,brackets:[{token:`delimiter.bracket`,open:`{`,close:`}`},{token:`delimiter.square`,open:`[`,close:`]`}],keywords:[`true`,`True`,`TRUE`,`false`,`False`,`FALSE`,`null`,`Null`,`Null`,`~`],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:`@whitespace`},{include:`@comment`},[/%[^ ]+.*$/,`meta.directive`],[/---/,`operators.directivesEnd`],[/\.{3}/,`operators.documentEnd`],[/[-?:](?= )/,`operators`],{include:`@anchor`},{include:`@tagHandle`},{include:`@flowCollections`},{include:`@blockStyle`},[/@numberInteger(?![ \t]*\S+)/,`number`],[/@numberFloat(?![ \t]*\S+)/,`number.float`],[/@numberOctal(?![ \t]*\S+)/,`number.octal`],[/@numberHex(?![ \t]*\S+)/,`number.hex`],[/@numberInfinity(?![ \t]*\S+)/,`number.infinity`],[/@numberNaN(?![ \t]*\S+)/,`number.nan`],[/@numberDate(?![ \t]*\S+)/,`number.date`],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,[`type`,`white`,`operators`,`white`]],{include:`@flowScalars`},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],object:[{include:`@whitespace`},{include:`@comment`},[/\}/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],[/:(?= )/,`operators`],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,`type`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\},]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],array:[{include:`@whitespace`},{include:`@comment`},[/\]/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\],]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],multiString:[[/^( +).+$/,`string`,`@multiStringContinued.$1`]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":`string`,"@default":{token:`@rematch`,next:`@popall`}}}]],whitespace:[[/[ \t\r\n]+/,`white`]],comment:[[/#.*$/,`comment`]],flowCollections:[[/\[/,`@brackets`,`@array`],[/\{/,`@brackets`,`@object`]],flowScalars:[[/"([^"\\]|\\.)*$/,`string.invalid`],[/'([^'\\]|\\.)*$/,`string.invalid`],[/'[^']*'/,`string`],[/"/,`string`,`@doubleQuotedString`]],doubleQuotedString:[[/[^\\"]+/,`string`],[/@escapes/,`string.escape`],[/\\./,`string.escape.invalid`],[/"/,`string`,`@pop`]],blockStyle:[[/[>|][0-9]*[+-]?$/,`operators`,`@multiString`]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,`number`],[/@numberFloat(?=[ \t]*[,\]\}])/,`number.float`],[/@numberOctal(?=[ \t]*[,\]\}])/,`number.octal`],[/@numberHex(?=[ \t]*[,\]\}])/,`number.hex`],[/@numberInfinity(?=[ \t]*[,\]\}])/,`number.infinity`],[/@numberNaN(?=[ \t]*[,\]\}])/,`number.nan`],[/@numberDate(?=[ \t]*[,\]\}])/,`number.date`]],tagHandle:[[/\![^ ]*/,`tag`]],anchor:[[/[&*][^ ]+/,`namespace`]]}};export{c as conf,l as language};
1
+ import{m as e}from"./editor.api-C1-xuJKd.js";var t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty,a=(e,a,o,s)=>{if(a&&typeof a==`object`||typeof a==`function`)for(let c of r(a))!i.call(e,c)&&c!==o&&t(e,c,{get:()=>a[c],enumerable:!(s=n(a,c))||s.enumerable});return e},o=(e,t,n)=>(a(e,t,`default`),n&&a(n,t,`default`)),s={};o(s,e);var c={comments:{lineComment:`#`},brackets:[[`{`,`}`],[`[`,`]`],[`(`,`)`]],autoClosingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],surroundingPairs:[{open:`{`,close:`}`},{open:`[`,close:`]`},{open:`(`,close:`)`},{open:`"`,close:`"`},{open:`'`,close:`'`}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:s.languages.IndentAction.Indent}}]},l={tokenPostfix:`.yaml`,brackets:[{token:`delimiter.bracket`,open:`{`,close:`}`},{token:`delimiter.square`,open:`[`,close:`]`}],keywords:[`true`,`True`,`TRUE`,`false`,`False`,`FALSE`,`null`,`Null`,`Null`,`~`],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:`@whitespace`},{include:`@comment`},[/%[^ ]+.*$/,`meta.directive`],[/---/,`operators.directivesEnd`],[/\.{3}/,`operators.documentEnd`],[/[-?:](?= )/,`operators`],{include:`@anchor`},{include:`@tagHandle`},{include:`@flowCollections`},{include:`@blockStyle`},[/@numberInteger(?![ \t]*\S+)/,`number`],[/@numberFloat(?![ \t]*\S+)/,`number.float`],[/@numberOctal(?![ \t]*\S+)/,`number.octal`],[/@numberHex(?![ \t]*\S+)/,`number.hex`],[/@numberInfinity(?![ \t]*\S+)/,`number.infinity`],[/@numberNaN(?![ \t]*\S+)/,`number.nan`],[/@numberDate(?![ \t]*\S+)/,`number.date`],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,[`type`,`white`,`operators`,`white`]],{include:`@flowScalars`},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],object:[{include:`@whitespace`},{include:`@comment`},[/\}/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],[/:(?= )/,`operators`],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,`type`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\},]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],array:[{include:`@whitespace`},{include:`@comment`},[/\]/,`@brackets`,`@pop`],[/,/,`delimiter.comma`],{include:`@flowCollections`},{include:`@flowScalars`},{include:`@tagHandle`},{include:`@anchor`},{include:`@flowNumber`},[/[^\],]+/,{cases:{"@keywords":`keyword`,"@default":`string`}}]],multiString:[[/^( +).+$/,`string`,`@multiStringContinued.$1`]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":`string`,"@default":{token:`@rematch`,next:`@popall`}}}]],whitespace:[[/[ \t\r\n]+/,`white`]],comment:[[/#.*$/,`comment`]],flowCollections:[[/\[/,`@brackets`,`@array`],[/\{/,`@brackets`,`@object`]],flowScalars:[[/"([^"\\]|\\.)*$/,`string.invalid`],[/'([^'\\]|\\.)*$/,`string.invalid`],[/'[^']*'/,`string`],[/"/,`string`,`@doubleQuotedString`]],doubleQuotedString:[[/[^\\"]+/,`string`],[/@escapes/,`string.escape`],[/\\./,`string.escape.invalid`],[/"/,`string`,`@pop`]],blockStyle:[[/[>|][0-9]*[+-]?$/,`operators`,`@multiString`]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,`number`],[/@numberFloat(?=[ \t]*[,\]\}])/,`number.float`],[/@numberOctal(?=[ \t]*[,\]\}])/,`number.octal`],[/@numberHex(?=[ \t]*[,\]\}])/,`number.hex`],[/@numberInfinity(?=[ \t]*[,\]\}])/,`number.infinity`],[/@numberNaN(?=[ \t]*[,\]\}])/,`number.nan`],[/@numberDate(?=[ \t]*[,\]\}])/,`number.date`]],tagHandle:[[/\![^ ]*/,`tag`]],anchor:[[/[&*][^ ]+/,`namespace`]]}};export{c as conf,l as language};
@@ -1,14 +1,15 @@
1
- <!DOCTYPE html><html><head><title>Kōbō</title><link rel=icon type=image/svg+xml href=/favicon.svg><meta charset=utf-8><meta name=description content="Kōbō — multi-workspace agent manager for Claude Code"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width,height=device-height"> <script type="module" crossorigin src="/assets/index-D997aY4Y.js"></script>
1
+ <!DOCTYPE html><html><head><title>Kōbō</title><link rel=icon type=image/svg+xml href=/favicon.svg><meta charset=utf-8><meta name=description content="Kōbō — multi-workspace agent manager for Claude Code"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width,height=device-height"> <script type="module" crossorigin src="/assets/index-9vZWx9Bu.js"></script>
2
2
  <link rel="modulepreload" crossorigin href="/assets/runtime-core.esm-bundler-C3IgBgY5.js">
3
3
  <link rel="modulepreload" crossorigin href="/assets/vue-i18n-CeG0hR0Z.js">
4
4
  <link rel="modulepreload" crossorigin href="/assets/QIcon-B0-pH3Qs.js">
5
5
  <link rel="modulepreload" crossorigin href="/assets/QSeparator-rkjCbX2M.js">
6
6
  <link rel="modulepreload" crossorigin href="/assets/use-id-CeduaJbU.js">
7
- <link rel="modulepreload" crossorigin href="/assets/QBtn-p1aZtrJH.js">
8
- <link rel="modulepreload" crossorigin href="/assets/scroll-Dh2g7BwR.js">
7
+ <link rel="modulepreload" crossorigin href="/assets/QBtn-CyzfM9-_.js">
9
8
  <link rel="modulepreload" crossorigin href="/assets/private.use-form-D1RuEt2P.js">
10
- <link rel="modulepreload" crossorigin href="/assets/use-portal-mhLq4Rqk.js">
11
- <link rel="modulepreload" crossorigin href="/assets/QDialog-D42GLa1i.js">
9
+ <link rel="modulepreload" crossorigin href="/assets/use-checkbox-DYiZQsbF.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/scroll-JVVkg2Ng.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/use-portal-DBe4lcC2.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/QDialog-DQeAxY3-.js">
12
13
  <link rel="modulepreload" crossorigin href="/assets/symbols-DCYodwb2.js">
13
14
  <link rel="stylesheet" crossorigin href="/assets/index-eX_lKHSg.css">
14
15
  </head><body><div id=q-app></div></body></html>
@@ -306,3 +306,194 @@ export function listWorkspaceImagesHandler(worktreePath: string): WorkspaceImage
306
306
  }
307
307
  })
308
308
  }
309
+
310
+ // ── Documents ────────────────────────────────────────────────────────────────
311
+
312
+ /** Directories (relative to the worktree root) scanned for AI-generated docs. */
313
+ export const DOCUMENT_DIRS = ['docs/plans', 'docs/superpowers', '.ai/thoughts'] as const
314
+
315
+ /** Depth cap to keep recursion bounded even on pathological symlink loops. */
316
+ const DOC_MAX_DEPTH = 8
317
+
318
+ /** Metadata for a markdown document surfaced by the documents tools. */
319
+ export interface DocumentDto {
320
+ path: string
321
+ name: string
322
+ modifiedAt: string
323
+ }
324
+
325
+ /** Content payload returned when reading a single document. */
326
+ export interface DocumentContentDto {
327
+ path: string
328
+ content: string
329
+ }
330
+
331
+ function walkMarkdownFiles(rootAbs: string, rootRel: string, out: DocumentDto[], depth = 0): void {
332
+ if (depth > DOC_MAX_DEPTH) return
333
+ let entries: string[]
334
+ try {
335
+ entries = fs.readdirSync(rootAbs)
336
+ } catch {
337
+ return
338
+ }
339
+ for (const entry of entries) {
340
+ if (entry.startsWith('.') && entry !== '.ai') continue
341
+ const absEntry = path.join(rootAbs, entry)
342
+ const relEntry = `${rootRel}/${entry}`
343
+ let stat: ReturnType<typeof fs.statSync>
344
+ try {
345
+ stat = fs.statSync(absEntry)
346
+ } catch {
347
+ continue
348
+ }
349
+ if (stat.isDirectory()) {
350
+ walkMarkdownFiles(absEntry, relEntry, out, depth + 1)
351
+ } else if (stat.isFile() && entry.endsWith('.md')) {
352
+ out.push({ path: relEntry, name: entry, modifiedAt: stat.mtime.toISOString() })
353
+ }
354
+ }
355
+ }
356
+
357
+ /**
358
+ * Recursively list every `.md` file under `docs/plans/`, `docs/superpowers/`,
359
+ * and `.ai/thoughts/` inside the given worktree. Sorted by modifiedAt desc.
360
+ */
361
+ export function listDocumentsHandler(worktreePath: string): DocumentDto[] {
362
+ const documents: DocumentDto[] = []
363
+ for (const dir of DOCUMENT_DIRS) {
364
+ const absDir = path.join(worktreePath, dir)
365
+ if (!fs.existsSync(absDir)) continue
366
+ walkMarkdownFiles(absDir, dir, documents)
367
+ }
368
+ documents.sort((a, b) => new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime())
369
+ return documents
370
+ }
371
+
372
+ /**
373
+ * Read a single document. The caller-supplied path must be relative to the
374
+ * worktree root and live under one of the allowed DOCUMENT_DIRS; `.md` only;
375
+ * traversal (`..`) is rejected.
376
+ */
377
+ export function readDocumentHandler(worktreePath: string, relPath: string): DocumentContentDto {
378
+ if (!relPath) throw new Error('path is required')
379
+ const normalized = path.normalize(relPath)
380
+ if (
381
+ normalized.includes('..') ||
382
+ !DOCUMENT_DIRS.some((dir) => normalized.startsWith(`${dir}/`) || normalized === dir)
383
+ ) {
384
+ throw new Error(`Invalid path: must be under ${DOCUMENT_DIRS.map((d) => `${d}/`).join(', ')}`)
385
+ }
386
+ if (!normalized.endsWith('.md')) {
387
+ throw new Error('Only .md files can be read')
388
+ }
389
+ const abs = path.join(worktreePath, normalized)
390
+ if (!fs.existsSync(abs)) {
391
+ throw new Error(`Document not found: ${normalized}`)
392
+ }
393
+ return { path: normalized, content: fs.readFileSync(abs, 'utf-8') }
394
+ }
395
+
396
+ /**
397
+ * Append a thought / decision / note to `.ai/thoughts/<YYYY-MM-DD>-<slug>.md`.
398
+ * Creates the directory if missing. Returns the path (worktree-relative) of
399
+ * the file actually written — useful for the agent to reference it in chat.
400
+ */
401
+ export function logThoughtHandler(
402
+ worktreePath: string,
403
+ data: { title: string; content: string; tag?: string },
404
+ ): { path: string } {
405
+ const title = data.title?.trim()
406
+ if (!title) throw new Error('title is required')
407
+ const content = data.content?.trim()
408
+ if (!content) throw new Error('content is required')
409
+
410
+ const thoughtsDir = path.join(worktreePath, '.ai', 'thoughts')
411
+ fs.mkdirSync(thoughtsDir, { recursive: true })
412
+
413
+ const date = new Date().toISOString().slice(0, 10)
414
+ const slug =
415
+ title
416
+ .toLowerCase()
417
+ .normalize('NFKD')
418
+ .replace(/[\u0300-\u036f]/g, '')
419
+ .replace(/[^a-z0-9]+/g, '-')
420
+ .replace(/^-+|-+$/g, '')
421
+ .slice(0, 60) || 'note'
422
+ const tagSuffix = data.tag ? `-${data.tag.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}` : ''
423
+ const filename = `${date}-${slug}${tagSuffix}.md`
424
+ const abs = path.join(thoughtsDir, filename)
425
+ const relPath = `.ai/thoughts/${filename}`
426
+
427
+ const header = `# ${title}\n\n_${new Date().toISOString()}_${data.tag ? ` · tag: \`${data.tag}\`` : ''}\n\n`
428
+ fs.writeFileSync(abs, header + content + (content.endsWith('\n') ? '' : '\n'), 'utf-8')
429
+
430
+ return { path: relPath }
431
+ }
432
+
433
+ // ── Session usage ────────────────────────────────────────────────────────────
434
+
435
+ /** Aggregated token / cost usage for a workspace. */
436
+ export interface SessionUsageDto {
437
+ workspaceTotals: { inputTokens: number; outputTokens: number; costUsd: number }
438
+ currentSession: {
439
+ sessionId: string | null
440
+ inputTokens: number
441
+ outputTokens: number
442
+ costUsd: number
443
+ }
444
+ }
445
+
446
+ interface UsagePayload {
447
+ kind?: string
448
+ inputTokens?: number
449
+ outputTokens?: number
450
+ costUsd?: number
451
+ }
452
+
453
+ /**
454
+ * Aggregate `usage` events from `ws_events` to report how many tokens and
455
+ * dollars the workspace has consumed — both in total and for the currently
456
+ * running agent_session (if any). Silently skips rows whose payload is not
457
+ * valid JSON or not a usage event.
458
+ */
459
+ export function getSessionUsageHandler(db: Database.Database, workspaceId: string): SessionUsageDto {
460
+ const runningSession = db
461
+ .prepare(
462
+ "SELECT id FROM agent_sessions WHERE workspace_id = ? AND status = 'running' ORDER BY started_at DESC LIMIT 1",
463
+ )
464
+ .get(workspaceId) as { id: string } | undefined
465
+ const currentSessionId = runningSession?.id ?? null
466
+
467
+ const rows = db
468
+ .prepare("SELECT payload, session_id FROM ws_events WHERE workspace_id = ? AND type = 'agent:event'")
469
+ .all(workspaceId) as Array<{ payload: string; session_id: string | null }>
470
+
471
+ const totals = { inputTokens: 0, outputTokens: 0, costUsd: 0 }
472
+ const current = { inputTokens: 0, outputTokens: 0, costUsd: 0 }
473
+
474
+ for (const row of rows) {
475
+ let parsed: UsagePayload
476
+ try {
477
+ parsed = JSON.parse(row.payload) as UsagePayload
478
+ } catch {
479
+ continue
480
+ }
481
+ if (parsed.kind !== 'usage') continue
482
+ const input = typeof parsed.inputTokens === 'number' ? parsed.inputTokens : 0
483
+ const output = typeof parsed.outputTokens === 'number' ? parsed.outputTokens : 0
484
+ const cost = typeof parsed.costUsd === 'number' ? parsed.costUsd : 0
485
+ totals.inputTokens += input
486
+ totals.outputTokens += output
487
+ totals.costUsd += cost
488
+ if (currentSessionId && row.session_id === currentSessionId) {
489
+ current.inputTokens += input
490
+ current.outputTokens += output
491
+ current.costUsd += cost
492
+ }
493
+ }
494
+
495
+ return {
496
+ workspaceTotals: totals,
497
+ currentSession: { sessionId: currentSessionId, ...current },
498
+ }
499
+ }
@@ -9,11 +9,15 @@ import {
9
9
  createTaskHandler,
10
10
  deleteTaskHandler,
11
11
  getDevServerStatusHandler,
12
+ getSessionUsageHandler,
12
13
  getSettingsHandler,
13
14
  getWorkspaceInfoHandler,
15
+ listDocumentsHandler,
14
16
  listTasksHandler,
15
17
  listWorkspaceImagesHandler,
18
+ logThoughtHandler,
16
19
  markTaskDoneHandler,
20
+ readDocumentHandler,
17
21
  updateTaskHandler,
18
22
  } from './kobo-tasks-handlers.js'
19
23
 
@@ -90,24 +94,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
90
94
  {
91
95
  name: 'list_tasks',
92
96
  description:
93
- 'List all tasks and acceptance criteria for the current workspace with their IDs and current status. Call this first to discover task IDs before calling mark_task_done / update_task / delete_task.',
94
- inputSchema: {
95
- type: 'object',
96
- properties: {},
97
- required: [],
98
- },
97
+ 'CALL FIRST on any non-trivial turn to know what the user wants done and what is already completed. Returns every task and acceptance criterion for the current workspace with its id and status. Re-call periodically (before marking something done, or after the user asks for a status) to stay in sync with user-added or external updates.',
98
+ inputSchema: { type: 'object', properties: {}, required: [] },
99
99
  },
100
100
  {
101
101
  name: 'mark_task_done',
102
102
  description:
103
- 'Mark a task or acceptance criterion as done. Use this when you have completed the work for a criterion and validated it.',
103
+ 'CALL AS SOON AS a task or acceptance criterion is finished AND verified (tests pass, feature works, diff committed). Do not wait for the end of the turn the user watches progress live and marking each item as it completes is the primary signal Kōbō uses to track you.',
104
104
  inputSchema: {
105
105
  type: 'object',
106
106
  properties: {
107
- task_id: {
108
- type: 'string',
109
- description: 'The ID of the task to mark as done (obtained from list_tasks)',
110
- },
107
+ task_id: { type: 'string', description: 'Task id from list_tasks.' },
111
108
  },
112
109
  required: ['task_id'],
113
110
  },
@@ -115,14 +112,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
115
112
  {
116
113
  name: 'create_task',
117
114
  description:
118
- 'Create a new task or acceptance criterion for the current workspace. Appended at the end of the list.',
115
+ 'CALL WHEN you discover follow-up work that was not in the original list and needs to stick around (e.g. "refactor this helper later", "add a test for edge case"). Appends at the end of the list. Do not use it for ephemeral internal notes — prefer log_thought for those.',
119
116
  inputSchema: {
120
117
  type: 'object',
121
118
  properties: {
122
- title: { type: 'string', description: 'Task title' },
119
+ title: { type: 'string', description: 'Short, imperative title (e.g. "Add retry to fetchUser").' },
123
120
  is_acceptance_criterion: {
124
121
  type: 'boolean',
125
- description: 'Whether this is an acceptance criterion (default: false)',
122
+ description: 'Mark as acceptance criterion rather than a task (default: false).',
126
123
  },
127
124
  },
128
125
  required: ['title'],
@@ -131,20 +128,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
131
128
  {
132
129
  name: 'update_task',
133
130
  description:
134
- 'Update an existing task — change title, status, or is_acceptance_criterion flag. At least one field is required.',
131
+ 'CALL WHEN you need to refine a task — rewording for clarity, flipping status to `in_progress` as you start it, or promoting a task to acceptance criterion. At least one mutable field is required.',
135
132
  inputSchema: {
136
133
  type: 'object',
137
134
  properties: {
138
- task_id: { type: 'string', description: 'The ID of the task to update' },
139
- title: { type: 'string', description: 'New title (optional)' },
135
+ task_id: { type: 'string', description: 'Task id from list_tasks.' },
136
+ title: { type: 'string', description: 'New title (optional).' },
140
137
  status: {
141
138
  type: 'string',
142
139
  enum: ['pending', 'in_progress', 'done'],
143
- description: 'New status (optional)',
140
+ description: 'New status (optional).',
144
141
  },
145
142
  is_acceptance_criterion: {
146
143
  type: 'boolean',
147
- description: 'Toggle acceptance criterion flag (optional)',
144
+ description: 'Toggle acceptance criterion flag (optional).',
148
145
  },
149
146
  },
150
147
  required: ['task_id'],
@@ -152,66 +149,75 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
152
149
  },
153
150
  {
154
151
  name: 'delete_task',
155
- description: 'Delete a task from the current workspace permanently.',
152
+ description:
153
+ 'CALL ONLY when a task was created in error or became truly irrelevant (scope change validated by user). Prefer marking done or in_progress over deleting.',
156
154
  inputSchema: {
157
155
  type: 'object',
158
156
  properties: {
159
- task_id: { type: 'string', description: 'The ID of the task to delete' },
157
+ task_id: { type: 'string', description: 'Task id from list_tasks.' },
160
158
  },
161
159
  required: ['task_id'],
162
160
  },
163
161
  },
164
162
  {
165
- name: 'get_settings',
163
+ name: 'get_workspace_info',
164
+ description:
165
+ 'CALL EARLY in a session to confirm project path, working/source branch, worktree path, model, and notion link. Cheap read — useful when the user refers to "this workspace" or when you need the worktree path to locate files.',
166
+ inputSchema: { type: 'object', properties: {}, required: [] },
167
+ },
168
+ {
169
+ name: 'get_git_info',
170
+ description:
171
+ 'CALL BEFORE creating a PR, committing in batches, or reporting progress to the user. Returns commit count ahead of source, files changed, insertions/deletions, and existing PR URL if any.',
172
+ inputSchema: { type: 'object', properties: {}, required: [] },
173
+ },
174
+ {
175
+ name: 'set_workspace_status',
166
176
  description:
167
- 'Read Kōbō settings (global + projects). Optionally filter to a specific project by path to get both global and that project override.',
177
+ 'CALL WHEN you believe the mission is done (`completed`), blocked beyond recovery (`error`), or explicitly idle awaiting user input (`idle`). Transitions are validated by the backend invalid ones are rejected.',
168
178
  inputSchema: {
169
179
  type: 'object',
170
180
  properties: {
171
- project_path: {
181
+ status: {
172
182
  type: 'string',
173
- description: 'Optional project path to resolve a specific project entry',
183
+ enum: ['idle', 'completed', 'error'],
184
+ description: 'Target status.',
174
185
  },
175
186
  },
176
- required: [],
187
+ required: ['status'],
177
188
  },
178
189
  },
179
190
  {
180
- name: 'get_dev_server_status',
191
+ name: 'get_notion_ticket',
181
192
  description:
182
- 'Get the live dev server status for the current workspace. Returns status (running/stopped/starting/error/unknown), URL, HTTP port, instance name, project name, and running container names.',
183
- inputSchema: {
184
- type: 'object',
185
- properties: {},
186
- required: [],
187
- },
193
+ 'CALL when the user references "the ticket", "the Notion page", or when you need the source-of-truth text for the mission. Returns the Notion URL + locally-extracted ticket content from .ai/thoughts/.',
194
+ inputSchema: { type: 'object', properties: {}, required: [] },
188
195
  },
189
196
  {
190
- name: 'get_workspace_info',
197
+ name: 'get_dev_server_status',
191
198
  description:
192
- 'Get all metadata about the current workspace (name, project path, branches, model, notion URL, worktree path, status).',
199
+ 'CALL BEFORE asking the user whether the app is running, or when your change is dev-server-sensitive. Returns running/stopped/starting/error + URL, port, container names.',
193
200
  inputSchema: { type: 'object', properties: {}, required: [] },
194
201
  },
195
202
  {
196
203
  name: 'start_dev_server',
197
- description: 'Start the dev server configured for the current workspace.',
204
+ description: 'CALL WHEN the user asks you to test the running app and the dev server is stopped.',
198
205
  inputSchema: { type: 'object', properties: {}, required: [] },
199
206
  },
200
207
  {
201
208
  name: 'stop_dev_server',
202
- description: 'Stop the dev server of the current workspace.',
209
+ description:
210
+ 'CALL WHEN the user explicitly asks to stop the dev server, or before destructive operations that require a clean boot.',
203
211
  inputSchema: { type: 'object', properties: {}, required: [] },
204
212
  },
205
213
  {
206
214
  name: 'get_dev_server_logs',
207
- description: 'Fetch the last N lines of the dev server logs for the current workspace.',
215
+ description:
216
+ 'CALL WHEN debugging a runtime issue the user describes as happening in the running app. Returns the last N lines of logs (default 200). Cheaper than asking the user to paste them.',
208
217
  inputSchema: {
209
218
  type: 'object',
210
219
  properties: {
211
- tail: {
212
- type: 'number',
213
- description: 'Number of lines to fetch from the end (default: 200)',
214
- },
220
+ tail: { type: 'number', description: 'Number of lines from the end (default: 200).' },
215
221
  },
216
222
  required: [],
217
223
  },
@@ -219,35 +225,90 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
219
225
  {
220
226
  name: 'list_workspace_images',
221
227
  description:
222
- 'List all images uploaded to the current workspace (from .ai/images/index.json). Returns uid, originalName, relativePath and createdAt for each image.',
228
+ 'CALL WHEN the user mentions "the screenshot", "the attached image", or when you need to reference a previously-uploaded image. Returns uid, originalName, relativePath, createdAt for every image in .ai/images/.',
223
229
  inputSchema: { type: 'object', properties: {}, required: [] },
224
230
  },
225
231
  {
226
- name: 'get_git_info',
232
+ name: 'get_settings',
227
233
  description:
228
- 'Get git stats for the current workspace (commit count, files changed, insertions, deletions, PR URL if any).',
234
+ 'CALL WHEN you need to confirm configured models, PR prompt templates, git conventions, or dev-server commands before acting on them. Pass project_path to merge global + project-specific entries.',
235
+ inputSchema: {
236
+ type: 'object',
237
+ properties: {
238
+ project_path: {
239
+ type: 'string',
240
+ description: 'Project path to resolve a specific project entry (optional).',
241
+ },
242
+ },
243
+ required: [],
244
+ },
245
+ },
246
+ // ── Knowledge / context tools ─────────────────────────────────────────────
247
+ {
248
+ name: 'list_documents',
249
+ description:
250
+ 'CALL EARLY on a new session to discover plans, specs, and thoughts previously written for this workspace. Recursively lists every .md under docs/plans/, docs/superpowers/, and .ai/thoughts/. Before writing a new plan, check if one already exists.',
229
251
  inputSchema: { type: 'object', properties: {}, required: [] },
230
252
  },
231
253
  {
232
- name: 'set_workspace_status',
254
+ name: 'read_document',
233
255
  description:
234
- 'Update the current workspace status. Valid values: idle, completed, error. Transitions are validated by the backend.',
256
+ 'CALL AFTER list_documents when a file title looks relevant to the current task. Returns the full markdown content. Scoped to docs/plans/, docs/superpowers/, .ai/thoughts/ — reject anything else.',
235
257
  inputSchema: {
236
258
  type: 'object',
237
259
  properties: {
238
- status: {
260
+ path: {
239
261
  type: 'string',
240
- enum: ['idle', 'completed', 'error'],
241
- description: 'New status (e.g. idle, completed)',
262
+ description:
263
+ 'Worktree-relative path from list_documents (e.g. "docs/superpowers/plans/2026-04-17-foo.md").',
242
264
  },
243
265
  },
244
- required: ['status'],
266
+ required: ['path'],
245
267
  },
246
268
  },
247
269
  {
248
- name: 'get_notion_ticket',
270
+ name: 'log_thought',
271
+ description:
272
+ 'CALL WHEN you make a decision worth remembering — architecture choice, trade-off taken, dead-end avoided, pattern discovered. Appends a dated markdown file to .ai/thoughts/. Keep entries short and focused; one decision per call. Use create_task for actionable follow-ups instead.',
273
+ inputSchema: {
274
+ type: 'object',
275
+ properties: {
276
+ title: { type: 'string', description: 'Short, descriptive title (becomes the filename slug and the # H1).' },
277
+ content: { type: 'string', description: 'Markdown body explaining the decision and its reasoning.' },
278
+ tag: {
279
+ type: 'string',
280
+ description: 'Optional short tag appended to filename (e.g. "arch", "bug", "perf").',
281
+ },
282
+ },
283
+ required: ['title', 'content'],
284
+ },
285
+ },
286
+ {
287
+ name: 'search_codebase',
288
+ description:
289
+ 'CALL WHEN you need to recall prior chat history across workspaces — past decisions, prior user requests, an agent message you remember but can’t locate. Full-text search over user messages + agent outputs persisted in Kōbō. Use the local Grep tool for searching source code; this tool searches CONVERSATIONS.',
290
+ inputSchema: {
291
+ type: 'object',
292
+ properties: {
293
+ query: { type: 'string', description: 'Search phrase. Plain text; no regex.' },
294
+ include_archived: {
295
+ type: 'boolean',
296
+ description: 'Include archived workspaces in the search (default: false).',
297
+ },
298
+ scope: {
299
+ type: 'string',
300
+ enum: ['workspace', 'all'],
301
+ description: 'Restrict to this workspace only (default) or search across every workspace.',
302
+ },
303
+ limit: { type: 'number', description: 'Max results to return (default 30, max 100).' },
304
+ },
305
+ required: ['query'],
306
+ },
307
+ },
308
+ {
309
+ name: 'get_session_usage',
249
310
  description:
250
- 'Get the Notion ticket info for the current workspace. Returns the Notion URL and the extracted ticket content (from .ai/thoughts/) if available.',
311
+ 'CALL when you need to self-regulate on long missions returns token/cost totals for the workspace lifetime and for the currently running agent_session. Useful before spawning heavy subagents or deep reasoning on already-expensive sessions.',
251
312
  inputSchema: { type: 'object', properties: {}, required: [] },
252
313
  },
253
314
  ],
@@ -379,6 +440,50 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
379
440
  return ok(result)
380
441
  }
381
442
 
443
+ if (name === 'list_documents') {
444
+ const info = getWorkspaceInfoHandler(db, workspaceId!)
445
+ return ok(listDocumentsHandler(info.worktreePath))
446
+ }
447
+
448
+ if (name === 'read_document') {
449
+ const docPath = a.path as string | undefined
450
+ if (!docPath) return fail('path parameter is required')
451
+ const info = getWorkspaceInfoHandler(db, workspaceId!)
452
+ return ok(readDocumentHandler(info.worktreePath, docPath))
453
+ }
454
+
455
+ if (name === 'log_thought') {
456
+ const title = a.title as string | undefined
457
+ const content = a.content as string | undefined
458
+ if (!title) return fail('title parameter is required')
459
+ if (!content) return fail('content parameter is required')
460
+ const info = getWorkspaceInfoHandler(db, workspaceId!)
461
+ return ok(
462
+ logThoughtHandler(info.worktreePath, {
463
+ title,
464
+ content,
465
+ tag: a.tag as string | undefined,
466
+ }),
467
+ )
468
+ }
469
+
470
+ if (name === 'get_session_usage') {
471
+ return ok(getSessionUsageHandler(db, workspaceId!))
472
+ }
473
+
474
+ if (name === 'search_codebase') {
475
+ const query = a.query as string | undefined
476
+ if (!query) return fail('query parameter is required')
477
+ const scope = (a.scope as string | undefined) ?? 'workspace'
478
+ const includeArchived = a.include_archived === true
479
+ const limit = Math.min(Math.max(1, (a.limit as number | undefined) ?? 30), 100)
480
+ const qs = new URLSearchParams({ q: query, limit: String(limit) })
481
+ if (includeArchived) qs.set('includeArchived', 'true')
482
+ const raw = (await backendRequest('GET', `/api/search?${qs.toString()}`)) as Array<Record<string, unknown>>
483
+ const results = scope === 'all' ? raw : raw.filter((r) => r.workspaceId === workspaceId)
484
+ return ok({ query, scope, total: results.length, results })
485
+ }
486
+
382
487
  return fail(`Unknown tool: ${name}`)
383
488
  } catch (err) {
384
489
  return fail(err instanceof Error ? err.message : String(err))