kontexta 4.5.2 → 4.6.1

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 (333) hide show
  1. package/bundle/standalone/CHANGELOG.md +59 -0
  2. package/bundle/standalone/apps/web/.next/BUILD_ID +1 -1
  3. package/bundle/standalone/apps/web/.next/app-build-manifest.json +89 -75
  4. package/bundle/standalone/apps/web/.next/app-path-routes-manifest.json +9 -7
  5. package/bundle/standalone/apps/web/.next/build-manifest.json +2 -2
  6. package/bundle/standalone/apps/web/.next/prerender-manifest.json +13 -13
  7. package/bundle/standalone/apps/web/.next/routes-manifest.json +8 -0
  8. package/bundle/standalone/apps/web/.next/server/app/_not-found/page.js +1 -1
  9. package/bundle/standalone/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  10. package/bundle/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/bundle/standalone/apps/web/.next/server/app/_not-found.html +1 -1
  12. package/bundle/standalone/apps/web/.next/server/app/_not-found.rsc +4 -4
  13. package/bundle/standalone/apps/web/.next/server/app/api/about/route.js.nft.json +1 -1
  14. package/bundle/standalone/apps/web/.next/server/app/api/about/route_client-reference-manifest.js +1 -1
  15. package/bundle/standalone/apps/web/.next/server/app/api/auth/login/route.js.nft.json +1 -1
  16. package/bundle/standalone/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
  17. package/bundle/standalone/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
  18. package/bundle/standalone/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  19. package/bundle/standalone/apps/web/.next/server/app/api/auth/setup/route.js.nft.json +1 -1
  20. package/bundle/standalone/apps/web/.next/server/app/api/auth/setup/route_client-reference-manifest.js +1 -1
  21. package/bundle/standalone/apps/web/.next/server/app/api/auth/status/route.js.nft.json +1 -1
  22. package/bundle/standalone/apps/web/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
  23. package/bundle/standalone/apps/web/.next/server/app/api/auth/token/route.js.nft.json +1 -1
  24. package/bundle/standalone/apps/web/.next/server/app/api/auth/token/route_client-reference-manifest.js +1 -1
  25. package/bundle/standalone/apps/web/.next/server/app/api/calendar/conflicts/route.js.nft.json +1 -1
  26. package/bundle/standalone/apps/web/.next/server/app/api/calendar/conflicts/route_client-reference-manifest.js +1 -1
  27. package/bundle/standalone/apps/web/.next/server/app/api/calendar/entities/[id]/route.js.nft.json +1 -1
  28. package/bundle/standalone/apps/web/.next/server/app/api/calendar/entities/[id]/route_client-reference-manifest.js +1 -1
  29. package/bundle/standalone/apps/web/.next/server/app/api/calendar/entities/route.js.nft.json +1 -1
  30. package/bundle/standalone/apps/web/.next/server/app/api/calendar/entities/route_client-reference-manifest.js +1 -1
  31. package/bundle/standalone/apps/web/.next/server/app/api/calendar/events/[id]/route.js.nft.json +1 -1
  32. package/bundle/standalone/apps/web/.next/server/app/api/calendar/events/[id]/route_client-reference-manifest.js +1 -1
  33. package/bundle/standalone/apps/web/.next/server/app/api/calendar/events/route.js.nft.json +1 -1
  34. package/bundle/standalone/apps/web/.next/server/app/api/calendar/events/route_client-reference-manifest.js +1 -1
  35. package/bundle/standalone/apps/web/.next/server/app/api/calendar/ics/route.js.nft.json +1 -1
  36. package/bundle/standalone/apps/web/.next/server/app/api/calendar/ics/route_client-reference-manifest.js +1 -1
  37. package/bundle/standalone/apps/web/.next/server/app/api/calendar/links/route.js.nft.json +1 -1
  38. package/bundle/standalone/apps/web/.next/server/app/api/calendar/links/route_client-reference-manifest.js +1 -1
  39. package/bundle/standalone/apps/web/.next/server/app/api/calendar/settings/route.js.nft.json +1 -1
  40. package/bundle/standalone/apps/web/.next/server/app/api/calendar/settings/route_client-reference-manifest.js +1 -1
  41. package/bundle/standalone/apps/web/.next/server/app/api/clip/route.js.nft.json +1 -1
  42. package/bundle/standalone/apps/web/.next/server/app/api/clip/route_client-reference-manifest.js +1 -1
  43. package/bundle/standalone/apps/web/.next/server/app/api/docker-config/route.js.nft.json +1 -1
  44. package/bundle/standalone/apps/web/.next/server/app/api/docker-config/route_client-reference-manifest.js +1 -1
  45. package/bundle/standalone/apps/web/.next/server/app/api/export/zip/route.js.nft.json +1 -1
  46. package/bundle/standalone/apps/web/.next/server/app/api/export/zip/route_client-reference-manifest.js +1 -1
  47. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/backlinks/route.js +1 -0
  48. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/backlinks/route.js.nft.json +1 -0
  49. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/backlinks/route_client-reference-manifest.js +1 -0
  50. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/download/route.js.nft.json +1 -1
  51. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/download/route_client-reference-manifest.js +1 -1
  52. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/export-html/route.js.nft.json +1 -1
  53. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/export-html/route_client-reference-manifest.js +1 -1
  54. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/export-pdf/route.js.nft.json +1 -1
  55. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/export-pdf/route_client-reference-manifest.js +1 -1
  56. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/history/restore/route.js.nft.json +1 -1
  57. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/history/restore/route_client-reference-manifest.js +1 -1
  58. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/history/route.js.nft.json +1 -1
  59. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/history/route_client-reference-manifest.js +1 -1
  60. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/move/route.js +1 -1
  61. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/move/route.js.nft.json +1 -1
  62. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/move/route_client-reference-manifest.js +1 -1
  63. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/route.js.nft.json +1 -1
  64. package/bundle/standalone/apps/web/.next/server/app/api/files/[id]/route_client-reference-manifest.js +1 -1
  65. package/bundle/standalone/apps/web/.next/server/app/api/files/route.js +1 -1
  66. package/bundle/standalone/apps/web/.next/server/app/api/files/route.js.nft.json +1 -1
  67. package/bundle/standalone/apps/web/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  68. package/bundle/standalone/apps/web/.next/server/app/api/files/upload/route.js +1 -1
  69. package/bundle/standalone/apps/web/.next/server/app/api/files/upload/route.js.nft.json +1 -1
  70. package/bundle/standalone/apps/web/.next/server/app/api/files/upload/route_client-reference-manifest.js +1 -1
  71. package/bundle/standalone/apps/web/.next/server/app/api/folders/route.js +1 -1
  72. package/bundle/standalone/apps/web/.next/server/app/api/folders/route.js.nft.json +1 -1
  73. package/bundle/standalone/apps/web/.next/server/app/api/folders/route_client-reference-manifest.js +1 -1
  74. package/bundle/standalone/apps/web/.next/server/app/api/git/remote/route.js.nft.json +1 -1
  75. package/bundle/standalone/apps/web/.next/server/app/api/git/remote/route_client-reference-manifest.js +1 -1
  76. package/bundle/standalone/apps/web/.next/server/app/api/git/sync-all/route.js.nft.json +1 -1
  77. package/bundle/standalone/apps/web/.next/server/app/api/git/sync-all/route_client-reference-manifest.js +1 -1
  78. package/bundle/standalone/apps/web/.next/server/app/api/hands/test-pattern/route.js.nft.json +1 -1
  79. package/bundle/standalone/apps/web/.next/server/app/api/hands/test-pattern/route_client-reference-manifest.js +1 -1
  80. package/bundle/standalone/apps/web/.next/server/app/api/hands/validate/route.js.nft.json +1 -1
  81. package/bundle/standalone/apps/web/.next/server/app/api/hands/validate/route_client-reference-manifest.js +1 -1
  82. package/bundle/standalone/apps/web/.next/server/app/api/health/route.js.nft.json +1 -1
  83. package/bundle/standalone/apps/web/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  84. package/bundle/standalone/apps/web/.next/server/app/api/install-snippets/route.js +9 -9
  85. package/bundle/standalone/apps/web/.next/server/app/api/install-snippets/route.js.nft.json +1 -1
  86. package/bundle/standalone/apps/web/.next/server/app/api/install-snippets/route_client-reference-manifest.js +1 -1
  87. package/bundle/standalone/apps/web/.next/server/app/api/mcp-tools/route.js +1 -1
  88. package/bundle/standalone/apps/web/.next/server/app/api/mcp-tools/route.js.nft.json +1 -1
  89. package/bundle/standalone/apps/web/.next/server/app/api/mcp-tools/route_client-reference-manifest.js +1 -1
  90. package/bundle/standalone/apps/web/.next/server/app/api/profile/route.js +1 -1
  91. package/bundle/standalone/apps/web/.next/server/app/api/profile/route.js.nft.json +1 -1
  92. package/bundle/standalone/apps/web/.next/server/app/api/profile/route_client-reference-manifest.js +1 -1
  93. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/hands-config/route.js.nft.json +1 -1
  94. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/hands-config/route_client-reference-manifest.js +1 -1
  95. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/journal-config/route.js.nft.json +1 -1
  96. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/journal-config/route_client-reference-manifest.js +1 -1
  97. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/journal-status/route.js.nft.json +1 -1
  98. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/journal-status/route_client-reference-manifest.js +1 -1
  99. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/onboard/route.js.nft.json +1 -1
  100. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/onboard/route_client-reference-manifest.js +1 -1
  101. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/refresh/route.js.nft.json +1 -1
  102. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/refresh/route_client-reference-manifest.js +1 -1
  103. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  104. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/route_client-reference-manifest.js +1 -1
  105. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/sync/route.js.nft.json +1 -1
  106. package/bundle/standalone/apps/web/.next/server/app/api/projects/[id]/sync/route_client-reference-manifest.js +1 -1
  107. package/bundle/standalone/apps/web/.next/server/app/api/projects/onboard/route.js.nft.json +1 -1
  108. package/bundle/standalone/apps/web/.next/server/app/api/projects/onboard/route_client-reference-manifest.js +1 -1
  109. package/bundle/standalone/apps/web/.next/server/app/api/projects/route.js.nft.json +1 -1
  110. package/bundle/standalone/apps/web/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  111. package/bundle/standalone/apps/web/.next/server/app/api/publish/html/route.js.nft.json +1 -1
  112. package/bundle/standalone/apps/web/.next/server/app/api/publish/html/route_client-reference-manifest.js +1 -1
  113. package/bundle/standalone/apps/web/.next/server/app/api/publish/route.js +1 -1
  114. package/bundle/standalone/apps/web/.next/server/app/api/publish/route.js.nft.json +1 -1
  115. package/bundle/standalone/apps/web/.next/server/app/api/publish/route_client-reference-manifest.js +1 -1
  116. package/bundle/standalone/apps/web/.next/server/app/api/refresh/route.js.nft.json +1 -1
  117. package/bundle/standalone/apps/web/.next/server/app/api/refresh/route_client-reference-manifest.js +1 -1
  118. package/bundle/standalone/apps/web/.next/server/app/api/reindex/route.js.nft.json +1 -1
  119. package/bundle/standalone/apps/web/.next/server/app/api/reindex/route_client-reference-manifest.js +1 -1
  120. package/bundle/standalone/apps/web/.next/server/app/api/reports/resources/[name]/route.js.nft.json +1 -1
  121. package/bundle/standalone/apps/web/.next/server/app/api/reports/resources/[name]/route_client-reference-manifest.js +1 -1
  122. package/bundle/standalone/apps/web/.next/server/app/api/search/route.js.nft.json +1 -1
  123. package/bundle/standalone/apps/web/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  124. package/bundle/standalone/apps/web/.next/server/app/api/tags/route.js +1 -1
  125. package/bundle/standalone/apps/web/.next/server/app/api/tags/route.js.nft.json +1 -1
  126. package/bundle/standalone/apps/web/.next/server/app/api/tags/route_client-reference-manifest.js +1 -1
  127. package/bundle/standalone/apps/web/.next/server/app/api/version-check/route.js.nft.json +1 -1
  128. package/bundle/standalone/apps/web/.next/server/app/api/version-check/route_client-reference-manifest.js +1 -1
  129. package/bundle/standalone/apps/web/.next/server/app/api/welcome/route.js +1 -0
  130. package/bundle/standalone/apps/web/.next/server/app/api/welcome/route.js.nft.json +1 -0
  131. package/bundle/standalone/apps/web/.next/server/app/api/welcome/route_client-reference-manifest.js +1 -0
  132. package/bundle/standalone/apps/web/.next/server/app/api/ws-config/route.js.nft.json +1 -1
  133. package/bundle/standalone/apps/web/.next/server/app/api/ws-config/route_client-reference-manifest.js +1 -1
  134. package/bundle/standalone/apps/web/.next/server/app/apple-icon.png/route.js.nft.json +1 -1
  135. package/bundle/standalone/apps/web/.next/server/app/calendar/page.js +2 -2
  136. package/bundle/standalone/apps/web/.next/server/app/calendar/page.js.nft.json +1 -1
  137. package/bundle/standalone/apps/web/.next/server/app/calendar/page_client-reference-manifest.js +1 -1
  138. package/bundle/standalone/apps/web/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  139. package/bundle/standalone/apps/web/.next/server/app/icon.png/route.js.nft.json +1 -1
  140. package/bundle/standalone/apps/web/.next/server/app/login/page.js +1 -1
  141. package/bundle/standalone/apps/web/.next/server/app/login/page.js.nft.json +1 -1
  142. package/bundle/standalone/apps/web/.next/server/app/login/page_client-reference-manifest.js +1 -1
  143. package/bundle/standalone/apps/web/.next/server/app/page.js +4 -4
  144. package/bundle/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
  145. package/bundle/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  146. package/bundle/standalone/apps/web/.next/server/app/preview/motifs/page.js +1 -1
  147. package/bundle/standalone/apps/web/.next/server/app/preview/motifs/page.js.nft.json +1 -1
  148. package/bundle/standalone/apps/web/.next/server/app/preview/motifs/page_client-reference-manifest.js +1 -1
  149. package/bundle/standalone/apps/web/.next/server/app/preview/motifs.html +2 -2
  150. package/bundle/standalone/apps/web/.next/server/app/preview/motifs.rsc +4 -4
  151. package/bundle/standalone/apps/web/.next/server/app-paths-manifest.json +9 -7
  152. package/bundle/standalone/apps/web/.next/server/chunks/{2144.js → 5156.js} +1 -1
  153. package/bundle/standalone/apps/web/.next/server/chunks/6680.js +1 -1
  154. package/bundle/standalone/apps/web/.next/server/pages/404.html +1 -1
  155. package/bundle/standalone/apps/web/.next/server/pages/500.html +1 -1
  156. package/bundle/standalone/apps/web/.next/server/pages/_app.js.nft.json +1 -1
  157. package/bundle/standalone/apps/web/.next/server/pages/_document.js.nft.json +1 -1
  158. package/bundle/standalone/apps/web/.next/server/pages/_error.js.nft.json +1 -1
  159. package/bundle/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  160. package/bundle/standalone/apps/web/.next/static/chunks/1480-3e4f4a95f11429a9.js +1 -0
  161. package/bundle/standalone/apps/web/.next/static/chunks/3466-1f8fcb266364a156.js +1 -0
  162. package/bundle/standalone/apps/web/.next/static/chunks/app/api/about/route-f5e054069fb68dd7.js +1 -0
  163. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/login/route-f5e054069fb68dd7.js +1 -0
  164. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/logout/route-f5e054069fb68dd7.js +1 -0
  165. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/setup/route-f5e054069fb68dd7.js +1 -0
  166. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/status/route-f5e054069fb68dd7.js +1 -0
  167. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/token/route-f5e054069fb68dd7.js +1 -0
  168. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/conflicts/route-f5e054069fb68dd7.js +1 -0
  169. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/entities/[id]/route-f5e054069fb68dd7.js +1 -0
  170. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/entities/route-f5e054069fb68dd7.js +1 -0
  171. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/events/[id]/route-f5e054069fb68dd7.js +1 -0
  172. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/events/route-f5e054069fb68dd7.js +1 -0
  173. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/ics/route-f5e054069fb68dd7.js +1 -0
  174. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/links/route-f5e054069fb68dd7.js +1 -0
  175. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/settings/route-f5e054069fb68dd7.js +1 -0
  176. package/bundle/standalone/apps/web/.next/static/chunks/app/api/clip/route-f5e054069fb68dd7.js +1 -0
  177. package/bundle/standalone/apps/web/.next/static/chunks/app/api/docker-config/route-f5e054069fb68dd7.js +1 -0
  178. package/bundle/standalone/apps/web/.next/static/chunks/app/api/export/zip/route-f5e054069fb68dd7.js +1 -0
  179. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/backlinks/route-f5e054069fb68dd7.js +1 -0
  180. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/download/route-f5e054069fb68dd7.js +1 -0
  181. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/export-html/route-f5e054069fb68dd7.js +1 -0
  182. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/export-pdf/route-f5e054069fb68dd7.js +1 -0
  183. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/history/restore/route-f5e054069fb68dd7.js +1 -0
  184. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/history/route-f5e054069fb68dd7.js +1 -0
  185. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/move/route-f5e054069fb68dd7.js +1 -0
  186. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/route-f5e054069fb68dd7.js +1 -0
  187. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/route-f5e054069fb68dd7.js +1 -0
  188. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/upload/route-f5e054069fb68dd7.js +1 -0
  189. package/bundle/standalone/apps/web/.next/static/chunks/app/api/folders/route-f5e054069fb68dd7.js +1 -0
  190. package/bundle/standalone/apps/web/.next/static/chunks/app/api/git/remote/route-f5e054069fb68dd7.js +1 -0
  191. package/bundle/standalone/apps/web/.next/static/chunks/app/api/git/sync-all/route-f5e054069fb68dd7.js +1 -0
  192. package/bundle/standalone/apps/web/.next/static/chunks/app/api/hands/test-pattern/route-f5e054069fb68dd7.js +1 -0
  193. package/bundle/standalone/apps/web/.next/static/chunks/app/api/hands/validate/route-f5e054069fb68dd7.js +1 -0
  194. package/bundle/standalone/apps/web/.next/static/chunks/app/api/health/route-f5e054069fb68dd7.js +1 -0
  195. package/bundle/standalone/apps/web/.next/static/chunks/app/api/install-snippets/route-f5e054069fb68dd7.js +1 -0
  196. package/bundle/standalone/apps/web/.next/static/chunks/app/api/mcp-tools/route-f5e054069fb68dd7.js +1 -0
  197. package/bundle/standalone/apps/web/.next/static/chunks/app/api/profile/route-f5e054069fb68dd7.js +1 -0
  198. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/hands-config/route-f5e054069fb68dd7.js +1 -0
  199. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/journal-config/route-f5e054069fb68dd7.js +1 -0
  200. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/journal-status/route-f5e054069fb68dd7.js +1 -0
  201. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/onboard/route-f5e054069fb68dd7.js +1 -0
  202. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/refresh/route-f5e054069fb68dd7.js +1 -0
  203. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/route-f5e054069fb68dd7.js +1 -0
  204. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/sync/route-f5e054069fb68dd7.js +1 -0
  205. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/onboard/route-f5e054069fb68dd7.js +1 -0
  206. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/route-f5e054069fb68dd7.js +1 -0
  207. package/bundle/standalone/apps/web/.next/static/chunks/app/api/publish/html/route-f5e054069fb68dd7.js +1 -0
  208. package/bundle/standalone/apps/web/.next/static/chunks/app/api/publish/route-f5e054069fb68dd7.js +1 -0
  209. package/bundle/standalone/apps/web/.next/static/chunks/app/api/refresh/route-f5e054069fb68dd7.js +1 -0
  210. package/bundle/standalone/apps/web/.next/static/chunks/app/api/reindex/route-f5e054069fb68dd7.js +1 -0
  211. package/bundle/standalone/apps/web/.next/static/chunks/app/api/reports/resources/[name]/route-f5e054069fb68dd7.js +1 -0
  212. package/bundle/standalone/apps/web/.next/static/chunks/app/api/search/route-f5e054069fb68dd7.js +1 -0
  213. package/bundle/standalone/apps/web/.next/static/chunks/app/api/tags/route-f5e054069fb68dd7.js +1 -0
  214. package/bundle/standalone/apps/web/.next/static/chunks/app/api/version-check/route-f5e054069fb68dd7.js +1 -0
  215. package/bundle/standalone/apps/web/.next/static/chunks/app/api/welcome/route-f5e054069fb68dd7.js +1 -0
  216. package/bundle/standalone/apps/web/.next/static/chunks/app/api/ws-config/route-f5e054069fb68dd7.js +1 -0
  217. package/bundle/standalone/apps/web/.next/static/chunks/app/calendar/page-f9542f9f518f9257.js +1 -0
  218. package/bundle/standalone/apps/web/.next/static/chunks/app/{layout-50d1052f9326c7ff.js → layout-3050c58d9809581a.js} +1 -1
  219. package/bundle/standalone/apps/web/.next/static/chunks/app/page-2f1ad9f037e15d25.js +1 -0
  220. package/bundle/standalone/apps/web/.next/static/chunks/app/preview/motifs/page-f5e054069fb68dd7.js +1 -0
  221. package/bundle/standalone/apps/web/.next/static/css/35a6a6dfd2c31933.css +3 -0
  222. package/bundle/standalone/apps/web/.next/static/m_hfSuYViCrjEik31It3-/_buildManifest.js +1 -0
  223. package/bundle/standalone/apps/web/package.json +5 -5
  224. package/bundle/standalone/apps/web/src/lib/mcp-tools.json +99 -13
  225. package/bundle/standalone/node_modules/kontexta-mcp/dist/agent-rules/rules-block.md +11 -0
  226. package/bundle/standalone/node_modules/kontexta-mcp/dist/index.js +207 -37
  227. package/bundle/standalone/node_modules/kontexta-mcp/dist/index.js.map +1 -1
  228. package/bundle/standalone/node_modules/kontexta-mcp/dist/migrations/008-content-class.sql +29 -0
  229. package/bundle/standalone/node_modules/kontexta-mcp/dist/migrations/009-content-class-windows-safe.sql +27 -0
  230. package/bundle/standalone/node_modules/kontexta-mcp/package.json +2 -2
  231. package/bundle/standalone/node_modules/kxta-core/dist/agent-rules/rules-block.md +11 -0
  232. package/bundle/standalone/node_modules/kxta-core/dist/clip/index.js +1 -1
  233. package/bundle/standalone/node_modules/kxta-core/dist/clip/index.js.map +1 -1
  234. package/bundle/standalone/node_modules/kxta-core/dist/content-class/index.d.ts +8 -0
  235. package/bundle/standalone/node_modules/kxta-core/dist/content-class/index.d.ts.map +1 -0
  236. package/bundle/standalone/node_modules/kxta-core/dist/content-class/index.js +31 -0
  237. package/bundle/standalone/node_modules/kxta-core/dist/content-class/index.js.map +1 -0
  238. package/bundle/standalone/node_modules/kxta-core/dist/db/migrations/008-content-class.sql +29 -0
  239. package/bundle/standalone/node_modules/kxta-core/dist/db/migrations/009-content-class-windows-safe.sql +27 -0
  240. package/bundle/standalone/node_modules/kxta-core/dist/files/index.d.ts +3 -1
  241. package/bundle/standalone/node_modules/kxta-core/dist/files/index.d.ts.map +1 -1
  242. package/bundle/standalone/node_modules/kxta-core/dist/files/index.js +60 -19
  243. package/bundle/standalone/node_modules/kxta-core/dist/files/index.js.map +1 -1
  244. package/bundle/standalone/node_modules/kxta-core/dist/files/layout.d.ts +11 -0
  245. package/bundle/standalone/node_modules/kxta-core/dist/files/layout.d.ts.map +1 -0
  246. package/bundle/standalone/node_modules/kxta-core/dist/files/layout.js +70 -0
  247. package/bundle/standalone/node_modules/kxta-core/dist/files/layout.js.map +1 -0
  248. package/bundle/standalone/node_modules/kxta-core/dist/index.d.ts +2 -1
  249. package/bundle/standalone/node_modules/kxta-core/dist/index.d.ts.map +1 -1
  250. package/bundle/standalone/node_modules/kxta-core/dist/index.js +2 -1
  251. package/bundle/standalone/node_modules/kxta-core/dist/index.js.map +1 -1
  252. package/bundle/standalone/node_modules/kxta-core/dist/metadata/index.d.ts +18 -0
  253. package/bundle/standalone/node_modules/kxta-core/dist/metadata/index.d.ts.map +1 -1
  254. package/bundle/standalone/node_modules/kxta-core/dist/metadata/index.js +82 -6
  255. package/bundle/standalone/node_modules/kxta-core/dist/metadata/index.js.map +1 -1
  256. package/bundle/standalone/node_modules/kxta-core/dist/profile/index.d.ts +8 -3
  257. package/bundle/standalone/node_modules/kxta-core/dist/profile/index.d.ts.map +1 -1
  258. package/bundle/standalone/node_modules/kxta-core/dist/profile/index.js +12 -1
  259. package/bundle/standalone/node_modules/kxta-core/dist/profile/index.js.map +1 -1
  260. package/bundle/standalone/node_modules/kxta-core/dist/reports/sanitize.d.ts.map +1 -1
  261. package/bundle/standalone/node_modules/kxta-core/dist/reports/sanitize.js +15 -5
  262. package/bundle/standalone/node_modules/kxta-core/dist/reports/sanitize.js.map +1 -1
  263. package/bundle/standalone/node_modules/kxta-core/dist/types.d.ts +5 -1
  264. package/bundle/standalone/node_modules/kxta-core/dist/types.d.ts.map +1 -1
  265. package/bundle/standalone/node_modules/kxta-core/dist/util/paths.d.ts.map +1 -1
  266. package/bundle/standalone/node_modules/kxta-core/dist/util/paths.js +3 -0
  267. package/bundle/standalone/node_modules/kxta-core/dist/util/paths.js.map +1 -1
  268. package/bundle/standalone/node_modules/kxta-core/package.json +2 -2
  269. package/bundle/standalone/node_modules/kxta-publish/dist/render/html-export.js +11 -3
  270. package/bundle/standalone/node_modules/kxta-publish/package.json +3 -3
  271. package/bundle/standalone/package.json +3 -2
  272. package/package.json +2 -2
  273. package/bundle/standalone/apps/web/.next/static/3-XgdPUkib9c8DXcXAxa1/_buildManifest.js +0 -1
  274. package/bundle/standalone/apps/web/.next/static/chunks/3068-293b252158eb9d15.js +0 -1
  275. package/bundle/standalone/apps/web/.next/static/chunks/9794-16144bed6de4bf26.js +0 -1
  276. package/bundle/standalone/apps/web/.next/static/chunks/app/api/about/route-c6ecffaac8622c61.js +0 -1
  277. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/login/route-c6ecffaac8622c61.js +0 -1
  278. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/logout/route-c6ecffaac8622c61.js +0 -1
  279. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/setup/route-c6ecffaac8622c61.js +0 -1
  280. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/status/route-c6ecffaac8622c61.js +0 -1
  281. package/bundle/standalone/apps/web/.next/static/chunks/app/api/auth/token/route-c6ecffaac8622c61.js +0 -1
  282. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/conflicts/route-c6ecffaac8622c61.js +0 -1
  283. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/entities/[id]/route-c6ecffaac8622c61.js +0 -1
  284. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/entities/route-c6ecffaac8622c61.js +0 -1
  285. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/events/[id]/route-c6ecffaac8622c61.js +0 -1
  286. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/events/route-c6ecffaac8622c61.js +0 -1
  287. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/ics/route-c6ecffaac8622c61.js +0 -1
  288. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/links/route-c6ecffaac8622c61.js +0 -1
  289. package/bundle/standalone/apps/web/.next/static/chunks/app/api/calendar/settings/route-c6ecffaac8622c61.js +0 -1
  290. package/bundle/standalone/apps/web/.next/static/chunks/app/api/clip/route-c6ecffaac8622c61.js +0 -1
  291. package/bundle/standalone/apps/web/.next/static/chunks/app/api/docker-config/route-c6ecffaac8622c61.js +0 -1
  292. package/bundle/standalone/apps/web/.next/static/chunks/app/api/export/zip/route-c6ecffaac8622c61.js +0 -1
  293. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/download/route-c6ecffaac8622c61.js +0 -1
  294. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/export-html/route-c6ecffaac8622c61.js +0 -1
  295. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/export-pdf/route-c6ecffaac8622c61.js +0 -1
  296. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/history/restore/route-c6ecffaac8622c61.js +0 -1
  297. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/history/route-c6ecffaac8622c61.js +0 -1
  298. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/move/route-c6ecffaac8622c61.js +0 -1
  299. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/[id]/route-c6ecffaac8622c61.js +0 -1
  300. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/route-c6ecffaac8622c61.js +0 -1
  301. package/bundle/standalone/apps/web/.next/static/chunks/app/api/files/upload/route-c6ecffaac8622c61.js +0 -1
  302. package/bundle/standalone/apps/web/.next/static/chunks/app/api/folders/route-c6ecffaac8622c61.js +0 -1
  303. package/bundle/standalone/apps/web/.next/static/chunks/app/api/git/remote/route-c6ecffaac8622c61.js +0 -1
  304. package/bundle/standalone/apps/web/.next/static/chunks/app/api/git/sync-all/route-c6ecffaac8622c61.js +0 -1
  305. package/bundle/standalone/apps/web/.next/static/chunks/app/api/hands/test-pattern/route-c6ecffaac8622c61.js +0 -1
  306. package/bundle/standalone/apps/web/.next/static/chunks/app/api/hands/validate/route-c6ecffaac8622c61.js +0 -1
  307. package/bundle/standalone/apps/web/.next/static/chunks/app/api/health/route-c6ecffaac8622c61.js +0 -1
  308. package/bundle/standalone/apps/web/.next/static/chunks/app/api/install-snippets/route-c6ecffaac8622c61.js +0 -1
  309. package/bundle/standalone/apps/web/.next/static/chunks/app/api/mcp-tools/route-c6ecffaac8622c61.js +0 -1
  310. package/bundle/standalone/apps/web/.next/static/chunks/app/api/profile/route-c6ecffaac8622c61.js +0 -1
  311. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/hands-config/route-c6ecffaac8622c61.js +0 -1
  312. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/journal-config/route-c6ecffaac8622c61.js +0 -1
  313. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/journal-status/route-c6ecffaac8622c61.js +0 -1
  314. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/onboard/route-c6ecffaac8622c61.js +0 -1
  315. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/refresh/route-c6ecffaac8622c61.js +0 -1
  316. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/route-c6ecffaac8622c61.js +0 -1
  317. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/[id]/sync/route-c6ecffaac8622c61.js +0 -1
  318. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/onboard/route-c6ecffaac8622c61.js +0 -1
  319. package/bundle/standalone/apps/web/.next/static/chunks/app/api/projects/route-c6ecffaac8622c61.js +0 -1
  320. package/bundle/standalone/apps/web/.next/static/chunks/app/api/publish/html/route-c6ecffaac8622c61.js +0 -1
  321. package/bundle/standalone/apps/web/.next/static/chunks/app/api/publish/route-c6ecffaac8622c61.js +0 -1
  322. package/bundle/standalone/apps/web/.next/static/chunks/app/api/refresh/route-c6ecffaac8622c61.js +0 -1
  323. package/bundle/standalone/apps/web/.next/static/chunks/app/api/reindex/route-c6ecffaac8622c61.js +0 -1
  324. package/bundle/standalone/apps/web/.next/static/chunks/app/api/reports/resources/[name]/route-c6ecffaac8622c61.js +0 -1
  325. package/bundle/standalone/apps/web/.next/static/chunks/app/api/search/route-c6ecffaac8622c61.js +0 -1
  326. package/bundle/standalone/apps/web/.next/static/chunks/app/api/tags/route-c6ecffaac8622c61.js +0 -1
  327. package/bundle/standalone/apps/web/.next/static/chunks/app/api/version-check/route-c6ecffaac8622c61.js +0 -1
  328. package/bundle/standalone/apps/web/.next/static/chunks/app/api/ws-config/route-c6ecffaac8622c61.js +0 -1
  329. package/bundle/standalone/apps/web/.next/static/chunks/app/calendar/page-c2b69b100c786dc4.js +0 -1
  330. package/bundle/standalone/apps/web/.next/static/chunks/app/page-c733552ab47df1c0.js +0 -1
  331. package/bundle/standalone/apps/web/.next/static/chunks/app/preview/motifs/page-c6ecffaac8622c61.js +0 -1
  332. package/bundle/standalone/apps/web/.next/static/css/3088e309ea0d151a.css +0 -3
  333. /package/bundle/standalone/apps/web/.next/static/{3-XgdPUkib9c8DXcXAxa1 → m_hfSuYViCrjEik31It3-}/_ssgManifest.js +0 -0
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { z } from "zod";
5
5
  import { createFile, readFile, updateFile, deleteFile, listFiles, addTags, removeTags, setFavorite, search, FtsQueryError, registerProject, discoverFiles, refreshIndex, listTags, listProjects, syncBackup, bundleSearch, estimateTokensFromBuffer, clipUrl, ClipError, getHistory, getDiff, getDatabase, findRelated, whatsNew, projectMap, getTagsForFiles, restoreVersion, parseOutline, findSection, replaceSection, listProjectFolders, createFolder, deleteFolder, moveFile, detectAgentContextFiles, assertPathInside, syncAgentRules, checkAgentRulesStatus, RULE_BLOCK_VERSION, gracefulShutdown, startDistillEngine, writeResource, listResources, deleteResource, } from "kxta-core";
6
6
  import RE2Class from "./re2-compat.js";
7
- import { isAbsolute, join, resolve, sep, dirname } from "node:path";
7
+ import { isAbsolute, join, relative, resolve, sep, dirname } from "node:path";
8
8
  import { statSync, lstatSync, openSync, readSync, closeSync, readFileSync, existsSync, realpathSync } from "node:fs";
9
9
  import { createHash } from "node:crypto";
10
10
  import { fileURLToPath } from "node:url";
@@ -18,7 +18,7 @@ import { registerCommitUpgradesTool } from "./journal-commit-upgrades-tool.js";
18
18
  import { registerHousekeepTool } from "./journal-housekeep-tool.js";
19
19
  import { registerCalendarTools } from "./calendar-tools.js";
20
20
  import { handleGetProfile } from "./profile-tool.js";
21
- import { getDataDir } from "kxta-core";
21
+ import { getDataDir, profileRelPath, getEmptySections, listEvents, findConflicts, getEntity } from "kxta-core";
22
22
  const dataDir = getDataDir();
23
23
  const PROJECT_TOKEN_WARN_THRESHOLD = Number(process.env.KONTEXTA_PROJECT_TOKEN_WARN ?? 100_000);
24
24
  function getAgentRulesWarning(projectId) {
@@ -141,10 +141,108 @@ catch (e) {
141
141
  if (!pkgVersionFound) {
142
142
  console.warn("Could not locate kontexta-mcp package.json by walking up from module dir; defaulting version to 0.0.0");
143
143
  }
144
- const server = new McpServer({
145
- name: "kontexta",
146
- version: pkgVersion,
147
- });
144
+ function resolveKindFolder({ destination, folder, kind }) {
145
+ if (destination !== "knowledge")
146
+ return { folder };
147
+ if (!kind)
148
+ return { error: "kind is required for destination='knowledge': must be 'dictionary' or 'note'" };
149
+ const norm = folder?.replace(/^\/+|\/+$/g, "") ?? "";
150
+ // Non-KB buckets (html/mermaid) also skip rewrite — their layout forbids nesting under knowledge/.
151
+ const CLASS_PREFIXES = ["knowledge/dictionary", "knowledge/notes", "knowledge/urlclips", "journal", "html", "mermaid"];
152
+ const alreadyClassScoped = CLASS_PREFIXES.some((p) => norm === p || norm.startsWith(p + "/"));
153
+ if (alreadyClassScoped) {
154
+ const impliedDict = norm.startsWith("knowledge/dictionary") || norm.startsWith("knowledge/urlclips");
155
+ const impliedNote = norm.startsWith("knowledge/notes");
156
+ const impliedClass = impliedDict ? "dictionary" : impliedNote ? "note" : null;
157
+ if (impliedClass && impliedClass !== kind) {
158
+ return { folder, warning: `kind='${kind}' but folder targets '${impliedClass}' tree — path wins; content_class will be '${impliedClass}'.` };
159
+ }
160
+ return { folder };
161
+ }
162
+ const classRoot = kind === "dictionary" ? "knowledge/dictionary" : "knowledge/notes";
163
+ return { folder: norm ? `${classRoot}/${norm}` : classRoot };
164
+ }
165
+ // Format as UTC — server/user timezone divergence would silently mislead otherwise.
166
+ function fmtWhen(iso) {
167
+ const d = new Date(iso);
168
+ if (isNaN(d.getTime()))
169
+ return iso;
170
+ const y = d.getUTCFullYear();
171
+ const mo = String(d.getUTCMonth() + 1).padStart(2, "0");
172
+ const dd = String(d.getUTCDate()).padStart(2, "0");
173
+ const hh = String(d.getUTCHours()).padStart(2, "0");
174
+ const mm = String(d.getUTCMinutes()).padStart(2, "0");
175
+ return `${y}-${mo}-${dd} ${hh}:${mm}Z`;
176
+ }
177
+ function buildCalendarSection(now) {
178
+ try {
179
+ const from = now.toISOString();
180
+ const to = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString();
181
+ const events = listEvents({ from, to, limit: 3 });
182
+ const conflicts = findConflicts({ from, to }).conflicts;
183
+ const entityName = (id) => getEntity(id)?.name ?? `#${id}`;
184
+ const lines = [];
185
+ lines.push("📅 UPCOMING (next 7 days, times in UTC)");
186
+ if (events.length === 0) {
187
+ lines.push(" (no events)");
188
+ }
189
+ else {
190
+ for (const e of events) {
191
+ // strip trailing Z from the range endpoint since the header already labels the block UTC
192
+ const from = fmtWhen(e.starts_at).replace(/Z$/, "");
193
+ const to = fmtWhen(e.ends_at).replace(/Z$/, "").slice(11);
194
+ lines.push(` ${from}–${to} ${e.title} (${entityName(e.entity_id)})`);
195
+ }
196
+ }
197
+ if (conflicts.length > 0) {
198
+ lines.push("");
199
+ lines.push(`⚠️ CONFLICTS (${conflicts.length})`);
200
+ for (const c of conflicts.slice(0, 5)) {
201
+ lines.push(` ${c.kind}: "${c.event_a.title}" (${entityName(c.event_a.entity_id)}) vs "${c.event_b.title}" (${entityName(c.event_b.entity_id)}) — ${c.reason}`);
202
+ }
203
+ }
204
+ return lines.join("\n");
205
+ }
206
+ catch (e) {
207
+ return `📅 (calendar unavailable: ${e.message})`;
208
+ }
209
+ }
210
+ function profileFreshnessNote(profilePath, empty) {
211
+ const parts = [];
212
+ if (empty.length > 0)
213
+ parts.push(`${empty.length} section(s) still empty: ${empty.join(", ")}`);
214
+ try {
215
+ const mtime = statSync(profilePath).mtime;
216
+ const days = Math.floor((Date.now() - mtime.getTime()) / (24 * 60 * 60 * 1000));
217
+ if (days >= 30)
218
+ parts.push(`profile hasn't been updated in ${days} days`);
219
+ }
220
+ catch { }
221
+ if (parts.length === 0)
222
+ return "";
223
+ return `\n\n💡 Reminder: keep your profile current so I can act on it. ${parts.join("; ")}. Edit at knowledge/profile.md or via the dashboard's profile pane.`;
224
+ }
225
+ // Build the MCP session-instructions blob: welcome, profile, upcoming events, conflicts.
226
+ function loadProfileInstructions() {
227
+ const now = new Date();
228
+ const today = now.toISOString().slice(0, 10);
229
+ const path = join(dataDir, profileRelPath());
230
+ const profileExists = existsSync(path);
231
+ const content = profileExists ? readFileSync(path, "utf8").trim() : "";
232
+ const empty = profileExists ? getEmptySections(content) : [];
233
+ const header = `👋 Kontexta welcome — ${today}\n\n` +
234
+ `This is the user's session context. Read and honor it for the rest of this session — especially "Session coding style" (comment style, git etiquette, review-before-push) and "Team members and roles" (who's who when writing updates or referencing people).`;
235
+ const profileBlock = profileExists && content
236
+ ? `📋 PROFILE (knowledge/profile.md)\n${content}`
237
+ : `📋 PROFILE\n (not set up yet — nudge the user to fill it in at knowledge/profile.md or the dashboard's profile pane)`;
238
+ const calendarBlock = buildCalendarSection(now);
239
+ const freshness = profileExists ? profileFreshnessNote(path, empty) : "";
240
+ return [header, "", profileBlock, "", calendarBlock, freshness].filter(Boolean).join("\n");
241
+ }
242
+ const server = new McpServer({ name: "kontexta", version: pkgVersion }, { instructions: loadProfileInstructions() });
243
+ server.tool("refresh_session_context", "Re-read the session context (profile, upcoming events within 7d, conflicts, freshness nudge) as it stands NOW. Call this when the user just edited their profile or added/moved calendar events and you want the current picture instead of the snapshot taken at session start. Read-only; no side effects. Returns the same block Kontexta sent as MCP instructions at session start.", {}, async () => ({
244
+ content: [{ type: "text", text: loadProfileInstructions() ?? "(no session context available)" }],
245
+ }));
148
246
  const handsRegistry = new HandsRegistry(server);
149
247
  // Initialize journal capture for auto-wrapping all tool calls
150
248
  const baseJournalDir = join(dataDir, "knowledge", "journal");
@@ -259,7 +357,7 @@ server.tool("journal_append", "Append a timestamped text entry to today's daily
259
357
  };
260
358
  }
261
359
  });
262
- server.tool("create_file", "Create a new markdown, mermaid, or HTML file in the knowledge base or project. This operation writes a new file to disk and adds it to the local SQLite FTS5 index. Destination can be 'knowledge' (global KB), 'project' (reference file inside a project repo), or 'kontexta' (internal Kontexta schema file). If destination is 'project' or 'kontexta', project_id is strictly required. No external auth required. Rate limits do not apply (local operation). Returns the created file metadata including its new ID, path, and estimated tokens. If the destination directory does not exist, it will be created automatically. Use this tool to instantiate new contextual documents or notes. To modify an existing file, use 'update_file' instead. Parameters: 'destination' dictates required fields; if 'project' or 'kontexta', 'project_id' must be a valid integer. 'tags' and 'folder' are optional. Pass format='mmd' to create a Mermaid diagram file (.mmd) or format='html' for HTML reports; defaults to 'md'.", {
360
+ server.tool("create_file", "Create a new markdown, mermaid, or HTML file in the knowledge base or project. This operation writes a new file to disk and adds it to the local SQLite FTS5 index. Destination can be 'knowledge' (global KB), 'project' (reference file inside a project repo), or 'kontexta' (internal Kontexta schema file). If destination is 'project' or 'kontexta', project_id is strictly required. If destination is 'knowledge', 'kind' is strictly required — pick 'dictionary' (authoritative source-of-truth) or 'note' (informational snapshot); see the kind param for the rubric. No external auth required. Rate limits do not apply (local operation). Returns the created file metadata including its new ID, path, and estimated tokens. If the destination directory does not exist, it will be created automatically. Use this tool to instantiate new contextual documents or notes. To modify an existing file, use 'update_file' instead. Parameters: 'destination' dictates required fields; if 'project' or 'kontexta', 'project_id' must be a valid integer; if 'knowledge', 'kind' must be 'dictionary' or 'note'. 'tags' and 'folder' are optional. Pass format='mmd' to create a Mermaid diagram file (.mmd) or format='html' for HTML reports; defaults to 'md'.", {
263
361
  title: z.string().describe("Title of the file"),
264
362
  content: z.string().describe("Content of the file"),
265
363
  destination: z.enum(["knowledge", "project", "kontexta"]).describe("Destination type"),
@@ -267,19 +365,28 @@ server.tool("create_file", "Create a new markdown, mermaid, or HTML file in the
267
365
  folder: z.string().optional().describe("Optional folder path"),
268
366
  tags: z.array(z.string()).optional().describe("Optional array of tags"),
269
367
  format: z.enum(["md", "mmd", "html"]).optional().describe("File extension to write. Defaults to 'md'. Use 'html' for HTML reports."),
270
- }, async ({ title, content, destination, project_id, folder, tags, format }) => {
368
+ kind: z.enum(["dictionary", "note"]).optional()
369
+ .describe("REQUIRED for destination='knowledge'. 'dictionary' = source of truth (mappings, glossaries, runbooks, PR templates). 'note' = snapshot (meeting notes, sprint reviews, PR findings, post-mortems). Test: if this file disagreed with the code, who wins? File wins → dictionary; file loses → note. Ignored for destination='project' or 'kontexta'."),
370
+ }, async ({ title, content, destination, project_id, folder, tags, format, kind }) => {
371
+ const resolved = resolveKindFolder({ destination, folder, kind });
372
+ if ("error" in resolved) {
373
+ return { isError: true, content: [{ type: "text", text: JSON.stringify({ error: resolved.error }, null, 2) }] };
374
+ }
271
375
  const result = await createFile({
272
376
  title,
273
377
  content,
274
378
  destination,
275
379
  projectId: project_id,
276
- folder,
380
+ folder: resolved.folder,
277
381
  tags,
278
382
  dataDir,
279
383
  format,
280
384
  });
385
+ const payload = annotateTokens(result);
386
+ if (resolved.warning)
387
+ payload.warning = resolved.warning;
281
388
  return {
282
- content: [{ type: "text", text: JSON.stringify(annotateTokens(result), null, 2) }],
389
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
283
390
  };
284
391
  });
285
392
  server.tool("add_report_resource", "Write an image or other binary resource into the shared reports/resources/ folder. Returns { filename, size, src, url } — embed the report's <img>/<a> tags with `src` exactly as given (e.g. `<img src=\"resources/chart.png\">`); it is the only form that resolves correctly both in the dashboard viewer and in PDF/PNG export. Do not use `url` inside report HTML — it only works in the dashboard. Bytes are passed base64-encoded.", {
@@ -554,13 +661,15 @@ server.tool("grep_in_file", "Match a JS regex against one file's lines and retur
554
661
  };
555
662
  }
556
663
  });
557
- server.tool("regex_search", "Match a JS regex against the body of every file in scope (project, KB, or all) and return per-file hits with line numbers. Slower than FTS `search` because it reads each file's content; use only when FTS misses substrings, URLs, or code identifiers. Read-only; no side effects, auth, or rate limits. Capped at 500 files / 10 hits per file by default; the response reports `files_truncated` and per-file truncation so the agent can re-scope. `project_id: null` = KB only; omit = everywhere. Invalid regex throws.", {
664
+ server.tool("regex_search", "Match a JS regex against the body of every file in scope (project, KB, or all) and return per-file hits with line numbers. Slower than FTS `search` because it reads each file's content; use only when FTS misses substrings, URLs, or code identifiers. Read-only; no side effects, auth, or rate limits. Capped at 500 files / 10 hits per file by default; the response reports `files_truncated` and per-file truncation so the agent can re-scope. `project_id: null` = KB only; omit = everywhere; `kind` narrows to one content class. Invalid regex throws.", {
558
665
  pattern: z.string().describe("JavaScript RegExp source"),
559
666
  project_id: z.number().nullable().optional().describe("Scope to one project, null for KB-only, omit for everything"),
560
667
  case_insensitive: z.boolean().optional(),
561
668
  max_files: z.number().int().positive().max(2000).optional().describe("Cap on files scanned (default 500)"),
562
669
  max_matches_per_file: z.number().int().positive().max(100).optional().describe("Per-file hit cap (default 10)"),
563
- }, async ({ pattern, project_id, case_insensitive, max_files, max_matches_per_file }) => {
670
+ kind: z.enum(["dictionary", "note", "journal", "project"]).optional()
671
+ .describe("Filter by content class before scanning."),
672
+ }, async ({ pattern, project_id, case_insensitive, max_files, max_matches_per_file, kind }) => {
564
673
  try {
565
674
  let re;
566
675
  try {
@@ -572,15 +681,20 @@ server.tool("regex_search", "Match a JS regex against the body of every file in
572
681
  const fileCap = max_files ?? 500;
573
682
  const perFileCap = max_matches_per_file ?? 10;
574
683
  const db = getDatabase();
575
- let where = "";
684
+ const whereParts = [];
576
685
  const params = [];
577
686
  if (project_id === null) {
578
- where = "WHERE project_id IS NULL";
687
+ whereParts.push("project_id IS NULL");
579
688
  }
580
689
  else if (typeof project_id === "number") {
581
- where = "WHERE project_id = ?";
690
+ whereParts.push("project_id = ?");
582
691
  params.push(project_id);
583
692
  }
693
+ if (kind) {
694
+ whereParts.push("content_class = ?");
695
+ params.push(kind);
696
+ }
697
+ const where = whereParts.length ? `WHERE ${whereParts.join(" AND ")}` : "";
584
698
  const rows = db
585
699
  .prepare(`SELECT id, path, title FROM files ${where} LIMIT ?`)
586
700
  .all(...params, fileCap);
@@ -648,7 +762,7 @@ server.tool("delete_file", "DESTRUCTIVE. Permanently delete one file by ID. KB f
648
762
  content: [{ type: "text", text: JSON.stringify({ success: true }, null, 2) }],
649
763
  };
650
764
  });
651
- server.tool("list_files", "List file metadata with optional filters (project_id, tag, favorite, folder, untagged) and pagination. Read-only; no side effects, auth, or rate limits. Each row is annotated with tags, est_tokens, and size_bytes; the response includes `total_est_tokens` so you can budget before reading bodies. `project_id: null` returns ONLY Knowledge Base files; omit the field to span everything. Use to browse known structure; for keyword/content lookup use `search`; for a denser whole-vault dump use `project_map`.", {
765
+ server.tool("list_files", "List file metadata with optional filters (project_id, tag, favorite, folder, untagged, kind) and pagination. Read-only; no side effects, auth, or rate limits. Each row is annotated with tags, est_tokens, size_bytes, and content_class; the response includes `total_est_tokens` so you can budget before reading bodies. `project_id: null` returns ONLY Knowledge Base files; omit the field to span everything; `kind` narrows to one content class. Use to browse known structure; for keyword/content lookup use `search`; for a denser whole-vault dump use `project_map`.", {
652
766
  project_id: z.number().nullable().optional().describe("Filter by project ID. Pass null to list ONLY Knowledge Base files (project_id IS NULL)."),
653
767
  tag: z.string().optional().describe("Filter by tag name"),
654
768
  favorite: z.boolean().optional().describe("Filter by favorite status"),
@@ -656,7 +770,9 @@ server.tool("list_files", "List file metadata with optional filters (project_id,
656
770
  untagged: z.boolean().optional().describe("If true, return only files that have no tags. Useful for bulk-tagging workflows."),
657
771
  limit: z.number().optional().describe("Maximum number of results"),
658
772
  offset: z.number().optional().describe("Offset for pagination"),
659
- }, async ({ project_id, tag, favorite, folder, untagged, limit, offset }) => {
773
+ kind: z.enum(["dictionary", "note", "journal", "project"]).optional()
774
+ .describe("Filter by content class. dictionary = authoritative KB, note = informational KB, journal = time-log, project = project file."),
775
+ }, async ({ project_id, tag, favorite, folder, untagged, limit, offset, kind }) => {
660
776
  const filters = {};
661
777
  if (project_id !== undefined)
662
778
  filters.project_id = project_id;
@@ -672,6 +788,8 @@ server.tool("list_files", "List file metadata with optional filters (project_id,
672
788
  filters.limit = limit;
673
789
  if (offset !== undefined)
674
790
  filters.offset = offset;
791
+ if (kind !== undefined)
792
+ filters.content_class = kind;
675
793
  const result = listFiles({ dataDir, filters });
676
794
  const annotated = attachTags(result.map(annotateTokens));
677
795
  const total_est_tokens = annotated.reduce((s, f) => s + (f.est_tokens ?? 0), 0);
@@ -679,12 +797,14 @@ server.tool("list_files", "List file metadata with optional filters (project_id,
679
797
  content: [{ type: "text", text: JSON.stringify({ files: annotated, total_est_tokens }, null, 2) }],
680
798
  };
681
799
  });
682
- server.tool("search", "Full-text (SQLite FTS5) keyword search across files. Returns ranked matches with inline match_excerpt and title_highlight (no follow-up `read_file` needed for snippets) plus tags, est_tokens, size_bytes, and aggregate `total_est_tokens`. Read-only; no side effects, auth, or rate limits. FTS is tokenised: it WILL miss URLs, hyphenated terms, and partial substrings — fall back to `regex_search` for those. `project_id: null` searches only the KB; omit the field to span everything; `tags[]` requires ALL listed tags to match. For prompt-ready bundled bodies use `bundle_search`.", {
800
+ server.tool("search", "Full-text (SQLite FTS5) keyword search across files. Returns ranked matches with inline match_excerpt and title_highlight (no follow-up `read_file` needed for snippets) plus tags, est_tokens, size_bytes, content_class, and aggregate `total_est_tokens`. Read-only; no side effects, auth, or rate limits. Ordering: dictionary hits sort above everything else for the same query (dictionary-wins on conflict), then BM25 rank. FTS is tokenised: it WILL miss URLs, hyphenated terms, and partial substrings — fall back to `regex_search` for those. `project_id: null` searches only the KB; omit the field to span everything; `tags[]` requires ALL listed tags to match; `kind` narrows to one content class. For prompt-ready bundled bodies use `bundle_search`.", {
683
801
  query: z.string().describe("Search query"),
684
802
  project_id: z.number().nullable().optional().describe("Filter by project ID. Pass null to search ONLY Knowledge Base files."),
685
803
  tags: z.array(z.string()).optional().describe("Filter by tags (all must match)"),
686
804
  favorite: z.boolean().optional().describe("Filter by favorite status"),
687
- }, async ({ query, project_id, tags, favorite }) => {
805
+ kind: z.enum(["dictionary", "note", "journal", "project"]).optional()
806
+ .describe("Filter by content class. dictionary = authoritative KB (system IDs, mappings, glossaries), note = informational KB, journal = time-log, project = project file. Omit to see all classes with dictionary-first ordering."),
807
+ }, async ({ query, project_id, tags, favorite, kind }) => {
688
808
  const filters = { query };
689
809
  if (project_id !== undefined)
690
810
  filters.project_id = project_id;
@@ -692,6 +812,8 @@ server.tool("search", "Full-text (SQLite FTS5) keyword search across files. Retu
692
812
  filters.tags = tags;
693
813
  if (favorite !== undefined)
694
814
  filters.favorite = favorite;
815
+ if (kind !== undefined)
816
+ filters.content_class = kind;
695
817
  let result;
696
818
  try {
697
819
  result = search(filters);
@@ -708,7 +830,7 @@ server.tool("search", "Full-text (SQLite FTS5) keyword search across files. Retu
708
830
  content: [{ type: "text", text: JSON.stringify({ matches: annotated, total_est_tokens }, null, 2) }],
709
831
  };
710
832
  });
711
- server.tool("bundle_search", "Run an FTS search and concatenate matched bodies into a single prompt-ready bundle (XML `<document>` blocks or markdown headers + fences) capped at `max_tokens`. Files are added in rank order until the next would exceed the budget; the rest go to `skipped[]`. Read-only; no side effects, auth, or rate limits. Use instead of `search` + N×`read_file` when you need several related files as one context blob. Defaults: format=xml, max_tokens=50000. `project_id: null` = KB only; `tags[]` requires ALL to match.", {
833
+ server.tool("bundle_search", "Run an FTS search and concatenate matched bodies into a single prompt-ready bundle (XML `<document>` blocks or markdown headers + fences) capped at `max_tokens`. Files are added in rank order (dictionary-wins first, then BM25) until the next would exceed the budget; the rest go to `skipped[]`. Read-only; no side effects, auth, or rate limits. Use instead of `search` + N×`read_file` when you need several related files as one context blob. Defaults: format=xml, max_tokens=50000. `project_id: null` = KB only; `tags[]` requires ALL to match; `kind` narrows to one content class.", {
712
834
  query: z.string().describe("Full-text search query"),
713
835
  project_id: z.number().nullable().optional().describe("Filter by project ID. Pass null to bundle ONLY Knowledge Base files."),
714
836
  tags: z.array(z.string()).optional().describe("Filter by tags (all must match)"),
@@ -717,7 +839,9 @@ server.tool("bundle_search", "Run an FTS search and concatenate matched bodies i
717
839
  .describe("Bundle format. xml = Anthropic-recommended <document> tags; markdown = ## headers + fenced blocks"),
718
840
  max_tokens: z.number().int().positive().default(50000)
719
841
  .describe("Token budget. Files added in rank order until the next would exceed; remainder go to skipped[]"),
720
- }, async ({ query, project_id, tags, favorite, format, max_tokens }) => {
842
+ kind: z.enum(["dictionary", "note", "journal", "project"]).optional()
843
+ .describe("Filter by content class. dictionary = authoritative KB, note = informational KB, journal = time-log, project = project file."),
844
+ }, async ({ query, project_id, tags, favorite, format, max_tokens, kind }) => {
721
845
  const filters = { query };
722
846
  if (project_id !== undefined)
723
847
  filters.project_id = project_id;
@@ -725,6 +849,8 @@ server.tool("bundle_search", "Run an FTS search and concatenate matched bodies i
725
849
  filters.tags = tags;
726
850
  if (favorite !== undefined)
727
851
  filters.favorite = favorite;
852
+ if (kind !== undefined)
853
+ filters.content_class = kind;
728
854
  let result;
729
855
  try {
730
856
  result = await bundleSearch(filters, { format, max_tokens });
@@ -1149,7 +1275,7 @@ server.tool("commit_backup", "SIDE-EFFECTFUL — TOUCHES THE NETWORK. Sync the p
1149
1275
  };
1150
1276
  }
1151
1277
  });
1152
- server.tool("clip_url", "SIDE-EFFECTFUL — fetches an EXTERNAL URL and writes a NEW KB file. Downloads the page, extracts the main article via Readability, converts to markdown, and saves it as a new clipping. NOT idempotent / no de-dup — re-clipping the same URL creates a second file. AUTH: anonymous by default; pass `headers` (e.g. `{Cookie: 'session=...'}` or `{Authorization: 'Bearer ...'}`) to clip behind logins. Kontexta does not rate-limit but the upstream may throttle. On auth-required pages returns isError with `code: AUTH_REQUIRED`, optional `login_url`, and a hint to retry with `headers`. Returns `{file_id, path, title, source}`. Use to ingest external docs into the KB.", {
1278
+ server.tool("clip_url", "SIDE-EFFECTFUL — fetches an EXTERNAL URL and writes a NEW KB file. Downloads the page, extracts the main article via Readability, converts to markdown, and saves it under `knowledge/urlclips/`. Auto-classified as content_class='dictionary' (clipped external references are treated as authoritative reference material). NOT idempotent / no de-dup — re-clipping the same URL creates a second file. AUTH: anonymous by default; pass `headers` (e.g. `{Cookie: 'session=...'}` or `{Authorization: 'Bearer ...'}`) to clip behind logins. Kontexta does not rate-limit but the upstream may throttle. On auth-required pages returns isError with `code: AUTH_REQUIRED`, optional `login_url`, and a hint to retry with `headers`. Returns `{file_id, path, title, source}`. Use to ingest external docs into the KB.", {
1153
1279
  url: z.string().url().describe("The URL to clip"),
1154
1280
  title: z.string().optional().describe("Optional title override (defaults to the page's <title>)"),
1155
1281
  headers: z
@@ -1380,7 +1506,8 @@ server.tool("list_folders", "List folder paths under a project root (or the Know
1380
1506
  }, async ({ project_id }) => {
1381
1507
  try {
1382
1508
  const base = resolveFolderBase(project_id);
1383
- const folders = listProjectFolders(base);
1509
+ // Normalize to POSIX so the wire shape matches web /api/folders.
1510
+ const folders = listProjectFolders(base).map((f) => f.replace(/\\/g, "/"));
1384
1511
  return {
1385
1512
  content: [{ type: "text", text: JSON.stringify({ folders, base_path: base }, null, 2) }],
1386
1513
  };
@@ -1399,7 +1526,7 @@ server.tool("create_folder", "Create a folder under a project root or the KB. Id
1399
1526
  try {
1400
1527
  validateFolderName(name);
1401
1528
  const base = resolveFolderBase(project_id);
1402
- const path = createFolder(base, name);
1529
+ const path = createFolder(base, name, { dataDir });
1403
1530
  return {
1404
1531
  content: [{ type: "text", text: JSON.stringify({ path, base_path: base }, null, 2) }],
1405
1532
  };
@@ -1430,6 +1557,15 @@ server.tool("delete_folder", "DESTRUCTIVE — recursively delete a folder under
1430
1557
  ],
1431
1558
  };
1432
1559
  }
1560
+ // Refuse deleting a bare bucket name — would wipe the whole bucket and orphan DB rows.
1561
+ const KB_BUCKETS_TOP = new Set(["journal", "knowledge", "mermaid", "html"]);
1562
+ const segments = name.split(/[/\\]/).filter(Boolean);
1563
+ if (segments.length === 1 && KB_BUCKETS_TOP.has(segments[0])) {
1564
+ return {
1565
+ isError: true,
1566
+ content: [{ type: "text", text: JSON.stringify({ error: `Cannot delete the '${segments[0]}' bucket — part of the fixed KB layout.` }, null, 2) }],
1567
+ };
1568
+ }
1433
1569
  const base = resolveFolderBase(null);
1434
1570
  deleteFolder(base, name);
1435
1571
  return {
@@ -1443,19 +1579,35 @@ server.tool("delete_folder", "DESTRUCTIVE — recursively delete a folder under
1443
1579
  };
1444
1580
  }
1445
1581
  });
1446
- server.tool("move_file", "Move/rename a file. Destination 'new_path' must be absolute and resolve INSIDE the file's owning project or global knowledge directory. Cross-project moves are rejected. Operates locally with no auth or limits. Parameters: 'file_id' is a valid file ID. 'new_path' is an absolute path.", {
1582
+ server.tool("move_file", "Move/rename a file. Destination 'new_path' must be absolute and resolve INSIDE the file's owning project or global knowledge directory. Cross-project moves are rejected. Alternative: pass `kind='dictionary'|'note'` (with no `new_path`) to move a KB file into the mirrored path in the other class tree — subfolder path is preserved. Operates locally with no auth or limits.", {
1447
1583
  file_id: z.number().describe("File ID"),
1448
- new_path: z.string().describe("Absolute destination path"),
1449
- }, async ({ file_id, new_path }) => {
1584
+ new_path: z.string().optional().describe("Absolute destination path"),
1585
+ kind: z.enum(["dictionary", "note"]).optional()
1586
+ .describe("Move the file to the mirrored path in the other class tree. Subfolder path is preserved: knowledge/dictionary/slt/ids.md ↔ knowledge/notes/slt/ids.md. Ignored if `new_path` is also provided."),
1587
+ }, async ({ file_id, new_path, kind }) => {
1450
1588
  try {
1589
+ const file = readFile(file_id);
1590
+ if (kind && !new_path) {
1591
+ if (file.storage_type !== "local") {
1592
+ throw new Error("move_file with kind is only supported for KB files (storage_type='local')");
1593
+ }
1594
+ const kbRoot = join(dataDir, "knowledge");
1595
+ const currentRel = relative(kbRoot, file.path);
1596
+ const parts = currentRel.split(sep);
1597
+ const classIdx = parts.findIndex((p) => p === "dictionary" || p === "notes" || p === "urlclips");
1598
+ if (classIdx === -1) {
1599
+ throw new Error("move_file with kind requires the source to live under knowledge/{dictionary,notes,urlclips}");
1600
+ }
1601
+ parts[classIdx] = kind === "dictionary" ? "dictionary" : "notes";
1602
+ new_path = join(kbRoot, ...parts);
1603
+ }
1451
1604
  if (typeof new_path !== "string" || new_path.length === 0) {
1452
- throw new Error("new_path is required");
1605
+ throw new Error("new_path or kind is required");
1453
1606
  }
1454
1607
  if (new_path.includes("\0"))
1455
1608
  throw new Error("new_path contains null byte");
1456
1609
  if (!isAbsolute(new_path))
1457
1610
  throw new Error("new_path must be absolute");
1458
- const file = readFile(file_id);
1459
1611
  let base;
1460
1612
  if (file.storage_type === "reference" && file.project_id) {
1461
1613
  const project = getDatabase()
@@ -1508,7 +1660,7 @@ server.tool("move_file", "Move/rename a file. Destination 'new_path' must be abs
1508
1660
  if (srcResolved !== baseResolved && !srcResolved.startsWith(baseResolved + sep)) {
1509
1661
  throw new Error(`source path ${file.path} is no longer inside ${base}; refusing to move`);
1510
1662
  }
1511
- const updated = moveFile(file_id, new_path);
1663
+ const updated = moveFile(file_id, new_path, dataDir);
1512
1664
  return {
1513
1665
  content: [{ type: "text", text: JSON.stringify(annotateTokens(updated), null, 2) }],
1514
1666
  };
@@ -1520,11 +1672,19 @@ server.tool("move_file", "Move/rename a file. Destination 'new_path' must be abs
1520
1672
  };
1521
1673
  }
1522
1674
  });
1523
- server.tool("find_related", "Find other files sharing tags with the given file, ranked by `shared_tag_count` descending. Read-only; no side effects, auth, or rate limits. Returns annotated file rows with `shared_tag_count` and `shared_tags`. Empty result means the file has no tags or no other file shares them — try `search`/`regex_search` for content-based discovery, or `suggest_tags` to bootstrap labels first. Default limit 10.", {
1675
+ server.tool("find_related", "Find other files sharing tags with the given file, ranked by `shared_tag_count` descending. Read-only; no side effects, auth, or rate limits. Returns annotated file rows with `shared_tag_count` and `shared_tags`. Empty result means the file has no tags or no other file shares them — try `search`/`regex_search` for content-based discovery, or `suggest_tags` to bootstrap labels first. `kind` narrows to one content class. Default limit 10.", {
1524
1676
  file_id: z.number().describe("ID of the file to find relations for"),
1525
1677
  limit: z.number().optional().describe("Maximum number of related files to return (default 10)"),
1526
- }, async ({ file_id, limit }) => {
1527
- const related = findRelated(file_id, limit ?? 10);
1678
+ kind: z.enum(["dictionary", "note", "journal", "project"]).optional()
1679
+ .describe("Filter related results to a single content class."),
1680
+ }, async ({ file_id, limit, kind }) => {
1681
+ // Post-filter by kind since findRelated has no class filter today; over-fetch
1682
+ // when a filter is set so the final result honors `limit` after filtering.
1683
+ const overfetch = kind ? Math.max((limit ?? 10) * 4, 40) : (limit ?? 10);
1684
+ let related = findRelated(file_id, overfetch);
1685
+ if (kind) {
1686
+ related = related.filter((r) => r.content_class === kind).slice(0, limit ?? 10);
1687
+ }
1528
1688
  const annotated = related.map((r) => ({ ...annotateTokens(r), shared_tag_count: r.shared_tag_count, shared_tags: r.shared_tags }));
1529
1689
  return {
1530
1690
  content: [
@@ -1535,7 +1695,7 @@ server.tool("find_related", "Find other files sharing tags with the given file,
1535
1695
  ],
1536
1696
  };
1537
1697
  });
1538
- server.tool("create_files", "Batch variant of `create_file` — create up to 200 markdown or mermaid files in one call. Each item follows the same rules (project_id required if destination is `project` or `kontexta`). SIDE EFFECTS: writes new files to disk and inserts FTS5 rows; missing folders are mkdir'd. Per-item failures are isolated to `errors[]` and the rest of the batch still commits — partial success is the norm, always inspect `error_count`. No external auth or rate limits. Returns `{created_count, error_count, created, errors}`. Use for bulk ingestion; for >200 items, page yourself. Pass format='mmd' on an item to create a Mermaid diagram file (.mmd); defaults to 'md'.", {
1698
+ server.tool("create_files", "Batch variant of `create_file` — create up to 200 markdown, mermaid, or HTML files in one call. Each item follows the same rules: `project_id` required if `destination` is `project`/`kontexta`; `kind` ('dictionary' or 'note') required if `destination` is `knowledge`. SIDE EFFECTS: writes new files to disk and inserts FTS5 rows; missing folders are mkdir'd. Per-item failures are isolated to `errors[]` and the rest of the batch still commits — partial success is the norm, always inspect `error_count`. No external auth or rate limits. Returns `{created_count, error_count, created, errors}`. Use for bulk ingestion; for >200 items, page yourself. Pass format='mmd' on an item to create a Mermaid diagram file (.mmd) or 'html' for HTML reports; defaults to 'md'.", {
1539
1699
  files: z
1540
1700
  .array(z.object({
1541
1701
  title: z.string(),
@@ -1544,7 +1704,9 @@ server.tool("create_files", "Batch variant of `create_file` — create up to 200
1544
1704
  project_id: z.number().optional(),
1545
1705
  folder: z.string().optional(),
1546
1706
  tags: z.array(z.string()).optional(),
1547
- format: z.enum(["md", "mmd"]).optional(),
1707
+ format: z.enum(["md", "mmd", "html"]).optional(),
1708
+ kind: z.enum(["dictionary", "note"]).optional()
1709
+ .describe("REQUIRED per-item for destination='knowledge'. See create_file for the dictionary/note rubric."),
1548
1710
  }))
1549
1711
  .min(1)
1550
1712
  .max(200)
@@ -1555,17 +1717,25 @@ server.tool("create_files", "Batch variant of `create_file` — create up to 200
1555
1717
  for (let i = 0; i < files.length; i++) {
1556
1718
  const f = files[i];
1557
1719
  try {
1720
+ const resolved = resolveKindFolder({ destination: f.destination, folder: f.folder, kind: f.kind });
1721
+ if ("error" in resolved) {
1722
+ errors.push({ index: i, title: f.title, error: resolved.error });
1723
+ continue;
1724
+ }
1558
1725
  const result = await createFile({
1559
1726
  title: f.title,
1560
1727
  content: f.content,
1561
1728
  destination: f.destination,
1562
1729
  projectId: f.project_id,
1563
- folder: f.folder,
1730
+ folder: resolved.folder,
1564
1731
  tags: f.tags,
1565
1732
  dataDir,
1566
1733
  format: f.format,
1567
1734
  });
1568
- created.push(annotateTokens(result));
1735
+ const payload = annotateTokens(result);
1736
+ if (resolved.warning)
1737
+ payload.warning = resolved.warning;
1738
+ created.push(payload);
1569
1739
  }
1570
1740
  catch (e) {
1571
1741
  errors.push({ index: i, title: f.title, error: e?.message ?? String(e) });