synabun 2.0.0-beta → 2.0.5

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 (374) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/NOTICE +5 -0
  3. package/README.md +1 -1
  4. package/THIRD-PARTY-LICENSES.md +51 -0
  5. package/benchmarks/fixtures/browser/README.md +1 -1
  6. package/benchmarks/fixtures/desktop/README.md +1 -1
  7. package/docs/whatsapp.md +2 -2
  8. package/hooks/claude-code/pre-compact.mjs +11 -0
  9. package/hooks/claude-code/prompt-submit.mjs +60 -184
  10. package/hooks/claude-code/session-start.mjs +73 -16
  11. package/hooks/claude-code/shared.mjs +20 -0
  12. package/hooks/claude-code/stop.mjs +5 -5
  13. package/launcher.mjs +40 -0
  14. package/lib/claude-hooks.js +61 -8
  15. package/lib/client-config-repair.js +4 -3
  16. package/lib/data-home-migration.js +4 -0
  17. package/lib/desktop-pwa.js +206 -0
  18. package/lib/external-tools.js +221 -0
  19. package/lib/first-launch-protection.js +5 -0
  20. package/lib/packaged-runtime.js +110 -0
  21. package/lib/start-launcher.js +1396 -0
  22. package/mcp-server/package-lock.json +2 -2
  23. package/mcp-server/package.json +1 -3
  24. package/neural-interface/i18n/en.json +1540 -154
  25. package/neural-interface/i18n/pt-BR.json +1538 -153
  26. package/neural-interface/lib/assistant-api.js +62 -6
  27. package/neural-interface/lib/assistant-brains/claude.js +4 -0
  28. package/neural-interface/lib/assistant-catalog.js +179 -27
  29. package/neural-interface/lib/assistant-config.js +31 -2
  30. package/neural-interface/lib/assistant-dispatch.js +62 -13
  31. package/neural-interface/lib/assistant-envelope.js +9 -6
  32. package/neural-interface/lib/assistant-hidden-models.js +393 -66
  33. package/neural-interface/lib/assistant-memory.js +24 -10
  34. package/neural-interface/lib/assistant-persona.js +6 -2
  35. package/neural-interface/lib/assistant-pricing.js +21 -6
  36. package/neural-interface/lib/assistant-runtime.js +181 -12
  37. package/neural-interface/lib/assistant-task-prompt.js +13 -3
  38. package/neural-interface/lib/assistant-test-density.js +142 -0
  39. package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
  40. package/neural-interface/lib/assistant-user-context.js +908 -0
  41. package/neural-interface/lib/assistant-user-knowledge.js +328 -0
  42. package/neural-interface/lib/assistant-user-learn.js +404 -0
  43. package/neural-interface/lib/assistant-user-threads.js +226 -0
  44. package/neural-interface/lib/assistant-user-writer.js +181 -0
  45. package/neural-interface/lib/augmented-path.js +23 -3
  46. package/neural-interface/lib/browser-background-tab.js +134 -0
  47. package/neural-interface/lib/browser-language.js +83 -0
  48. package/neural-interface/lib/browser-screenshot.js +678 -61
  49. package/neural-interface/lib/claude-agent-bridge.js +87 -153
  50. package/neural-interface/lib/claude-executable.js +241 -0
  51. package/neural-interface/lib/claude-model-catalog.js +82 -93
  52. package/neural-interface/lib/claude-temporary.js +46 -7
  53. package/neural-interface/lib/cloudflared/install.js +533 -0
  54. package/neural-interface/lib/cloudflared/manager.js +545 -0
  55. package/neural-interface/lib/cloudflared/pins.js +115 -0
  56. package/neural-interface/lib/cloudflared/pins.json +63 -0
  57. package/neural-interface/lib/cloudflared/tunnel.js +265 -0
  58. package/neural-interface/lib/editor-completion-endpoint.js +264 -0
  59. package/neural-interface/lib/editor-completion-opencode.js +436 -0
  60. package/neural-interface/lib/editor-completion.js +714 -0
  61. package/neural-interface/lib/editor-config.js +149 -0
  62. package/neural-interface/lib/editor-files-api.js +268 -0
  63. package/neural-interface/lib/editor-git-api.js +134 -0
  64. package/neural-interface/lib/file-explorer-api.js +1039 -0
  65. package/neural-interface/lib/file-explorer-system.js +355 -0
  66. package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
  67. package/neural-interface/lib/file-explorer-watch.js +239 -0
  68. package/neural-interface/lib/git-diff-api.js +103 -0
  69. package/neural-interface/lib/git-read.js +202 -0
  70. package/neural-interface/lib/greeting/api.js +89 -0
  71. package/neural-interface/lib/greeting/budgets.js +90 -0
  72. package/neural-interface/lib/greeting/engine.js +461 -0
  73. package/neural-interface/lib/greeting/facts.js +320 -0
  74. package/neural-interface/lib/greeting/sources.js +252 -0
  75. package/neural-interface/lib/greeting/writer.js +139 -0
  76. package/neural-interface/lib/http-guards.js +189 -13
  77. package/neural-interface/lib/machine-names.js +88 -0
  78. package/neural-interface/lib/memory-api.js +31 -7
  79. package/neural-interface/lib/native-binary-runtime.js +69 -337
  80. package/neural-interface/lib/native-loop-providers.js +40 -49
  81. package/neural-interface/lib/offline-page.js +54 -0
  82. package/neural-interface/lib/plugin-installer.js +4 -2
  83. package/neural-interface/lib/project-storage.js +5 -7
  84. package/neural-interface/lib/request-gate.js +589 -0
  85. package/neural-interface/lib/session-title-generator.js +5 -0
  86. package/neural-interface/lib/synabun-update-plan.js +19 -0
  87. package/neural-interface/lib/ui-blob-api.js +108 -0
  88. package/neural-interface/lib/ui-blob-store.js +68 -0
  89. package/neural-interface/package-lock.json +701 -253
  90. package/neural-interface/package.json +13 -2
  91. package/neural-interface/public/install-app.html +34 -0
  92. package/neural-interface/public/manifest.json +2 -0
  93. package/neural-interface/public/offline.html +402 -66
  94. package/neural-interface/public/onboarding.html +264 -147
  95. package/neural-interface/public/shared/agent-runtime-options.js +22 -6
  96. package/neural-interface/public/shared/assistant/asst-automation.js +286 -0
  97. package/neural-interface/public/shared/assistant/asst-brain-picker.js +312 -62
  98. package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
  99. package/neural-interface/public/shared/assistant/asst-mascot.js +26 -64
  100. package/neural-interface/public/shared/assistant/asst-menu.js +12 -8
  101. package/neural-interface/public/shared/assistant/asst-panel.js +150 -28
  102. package/neural-interface/public/shared/assistant/asst-rack-styles.js +0 -3
  103. package/neural-interface/public/shared/assistant/asst-render.js +219 -67
  104. package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
  105. package/neural-interface/public/shared/assistant/asst-route.js +23 -329
  106. package/neural-interface/public/shared/assistant/asst-routes-editor.js +495 -0
  107. package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
  108. package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
  109. package/neural-interface/public/shared/assistant/asst-state.js +185 -1
  110. package/neural-interface/public/shared/assistant/asst-styles.js +162 -6
  111. package/neural-interface/public/shared/blob-store.js +111 -22
  112. package/neural-interface/public/shared/cdx/cdx-panel.js +32 -11
  113. package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
  114. package/neural-interface/public/shared/cdx/cdx-styles.js +36 -0
  115. package/neural-interface/public/shared/cdx/cdx-tabs.js +87 -4
  116. package/neural-interface/public/shared/command-runner-model.js +30 -0
  117. package/neural-interface/public/shared/constants.js +6 -1
  118. package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
  119. package/neural-interface/public/shared/cp/cp-styles.js +15 -0
  120. package/neural-interface/public/shared/editor/editor-ai-logic.js +409 -0
  121. package/neural-interface/public/shared/editor/editor-ai.js +619 -0
  122. package/neural-interface/public/shared/editor/editor-commands.js +212 -0
  123. package/neural-interface/public/shared/editor/editor-compare.js +103 -0
  124. package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
  125. package/neural-interface/public/shared/editor/editor-doc.js +257 -0
  126. package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
  127. package/neural-interface/public/shared/editor/editor-git.js +95 -0
  128. package/neural-interface/public/shared/editor/editor-languages.js +156 -0
  129. package/neural-interface/public/shared/editor/editor-lint.js +229 -0
  130. package/neural-interface/public/shared/editor/editor-loader.js +51 -0
  131. package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
  132. package/neural-interface/public/shared/editor/editor-menu.js +233 -0
  133. package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
  134. package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
  135. package/neural-interface/public/shared/editor/editor-palette.js +125 -0
  136. package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
  137. package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
  138. package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
  139. package/neural-interface/public/shared/editor/editor-safety.js +500 -0
  140. package/neural-interface/public/shared/editor/editor-search.js +352 -0
  141. package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
  142. package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
  143. package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
  144. package/neural-interface/public/shared/editor/editor-theme.js +244 -0
  145. package/neural-interface/public/shared/editor/editor-view.js +332 -0
  146. package/neural-interface/public/shared/explorer/fx-api.js +216 -0
  147. package/neural-interface/public/shared/explorer/fx-links.js +204 -0
  148. package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
  149. package/neural-interface/public/shared/explorer/fx-main.js +865 -0
  150. package/neural-interface/public/shared/explorer/fx-names.js +87 -0
  151. package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
  152. package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
  153. package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
  154. package/neural-interface/public/shared/file-links.js +339 -0
  155. package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
  156. package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
  157. package/neural-interface/public/shared/html-shell.js +169 -170
  158. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -0
  159. package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
  160. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +99 -17
  161. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
  162. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +5 -0
  163. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +40 -0
  164. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
  165. package/neural-interface/public/shared/pwa-install-page.js +62 -0
  166. package/neural-interface/public/shared/pwa-install.css +12 -0
  167. package/neural-interface/public/shared/pwa-install.js +127 -0
  168. package/neural-interface/public/shared/settings/settings-code-editor-pane.js +412 -0
  169. package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
  170. package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
  171. package/neural-interface/public/shared/settings/settings-ia.js +18 -4
  172. package/neural-interface/public/shared/settings/settings-rules.js +160 -0
  173. package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
  174. package/neural-interface/public/shared/start-bridge.js +802 -0
  175. package/neural-interface/public/shared/styleguide/sg-colors.js +5 -2
  176. package/neural-interface/public/shared/styleguide/sg-import-export.js +4 -3
  177. package/neural-interface/public/shared/styleguide/sg-kit.js +4 -1
  178. package/neural-interface/public/shared/styleguide/sg-panel.js +9 -1
  179. package/neural-interface/public/shared/styleguide/sg-typography.js +1 -0
  180. package/neural-interface/public/shared/styles.css +2219 -1117
  181. package/neural-interface/public/shared/synabun-mascot.js +20 -56
  182. package/neural-interface/public/shared/term-ready.js +307 -0
  183. package/neural-interface/public/shared/tunnel-view.js +167 -0
  184. package/neural-interface/public/shared/ui-claude-panel.js +160 -19
  185. package/neural-interface/public/shared/ui-command-runner.js +251 -468
  186. package/neural-interface/public/shared/ui-detail.js +15 -0
  187. package/neural-interface/public/shared/ui-explorer.js +22 -0
  188. package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
  189. package/neural-interface/public/shared/ui-file-links.js +103 -0
  190. package/neural-interface/public/shared/ui-help.js +26 -0
  191. package/neural-interface/public/shared/ui-invite.js +126 -116
  192. package/neural-interface/public/shared/ui-judgments.js +1 -1
  193. package/neural-interface/public/shared/ui-keybinds.js +4 -0
  194. package/neural-interface/public/shared/ui-loading.js +367 -12
  195. package/neural-interface/public/shared/ui-menubar.js +21 -55
  196. package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
  197. package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
  198. package/neural-interface/public/shared/ui-navbar.js +45 -21
  199. package/neural-interface/public/shared/ui-settings.js +491 -547
  200. package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
  201. package/neural-interface/public/shared/ui-sync.js +16 -0
  202. package/neural-interface/public/shared/ui-terminal.js +153 -149
  203. package/neural-interface/public/shared/ui-tunnel.js +130 -0
  204. package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
  205. package/neural-interface/public/shared/ui-whiteboard.js +12 -0
  206. package/neural-interface/public/shared/ui-worker-end.js +245 -0
  207. package/neural-interface/public/shared/ui-workspaces.js +41 -27
  208. package/neural-interface/public/shared/worker-end.js +251 -0
  209. package/neural-interface/public/sw.js +3 -1
  210. package/neural-interface/public/variant/2d/main.js +2 -1
  211. package/neural-interface/public/variant/3d/main.js +2 -1
  212. package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
  213. package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
  214. package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
  215. package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
  216. package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
  217. package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
  218. package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
  219. package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
  220. package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
  221. package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
  222. package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
  223. package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
  224. package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
  225. package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
  226. package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
  227. package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
  228. package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
  229. package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
  230. package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
  231. package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
  232. package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
  233. package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
  234. package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
  235. package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
  236. package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
  237. package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
  238. package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
  239. package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
  240. package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
  241. package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
  242. package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
  243. package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
  244. package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
  245. package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
  246. package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
  247. package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
  248. package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
  249. package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
  250. package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
  251. package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
  252. package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
  253. package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
  254. package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
  255. package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
  256. package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
  257. package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
  258. package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
  259. package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
  260. package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
  261. package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
  262. package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
  263. package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
  264. package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
  265. package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
  266. package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
  267. package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
  268. package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
  269. package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
  270. package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
  271. package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
  272. package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
  273. package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
  274. package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
  275. package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
  276. package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
  277. package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
  278. package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
  279. package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
  280. package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
  281. package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
  282. package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
  283. package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
  284. package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
  285. package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
  286. package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
  287. package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
  288. package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
  289. package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
  290. package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
  291. package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
  292. package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
  293. package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
  294. package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
  295. package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
  296. package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
  297. package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
  298. package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
  299. package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
  300. package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
  301. package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
  302. package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
  303. package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
  304. package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
  305. package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
  306. package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
  307. package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
  308. package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
  309. package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
  310. package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
  311. package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
  312. package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
  313. package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
  314. package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
  315. package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
  316. package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
  317. package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
  318. package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
  319. package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
  320. package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
  321. package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
  322. package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
  323. package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
  324. package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
  325. package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
  326. package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
  327. package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
  328. package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
  329. package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
  330. package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
  331. package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
  332. package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
  333. package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
  334. package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
  335. package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
  336. package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
  337. package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
  338. package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
  339. package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
  340. package/neural-interface/scripts/build-vendor.mjs +78 -2
  341. package/neural-interface/scripts/rebuild-pty.js +15 -12
  342. package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
  343. package/neural-interface/server.js +1092 -1212
  344. package/neural-interface/templates/rulesets/README.md +1 -1
  345. package/package.json +13 -7
  346. package/postinstall.js +11 -81
  347. package/setup.js +111 -6
  348. package/benchmarks/browser-live-results.json +0 -79
  349. package/benchmarks/browser-v2-results.json +0 -258
  350. package/docs/browser-v2.md +0 -109
  351. package/docs/hook-integrity.md +0 -89
  352. package/docs/judgments.md +0 -182
  353. package/docs/style-guide.md +0 -246
  354. package/neural-interface/lib/codex-capabilities.d.ts +0 -41
  355. package/neural-interface/public/category-logos/claude.svg +0 -1
  356. package/neural-interface/public/category-logos/gemini-color.svg +0 -1
  357. package/neural-interface/public/category-logos/openai.svg +0 -1
  358. package/neural-interface/public/category-logos/synabun.png +0 -0
  359. package/neural-interface/public/claude.svg +0 -1
  360. package/neural-interface/public/gallery.svg +0 -6
  361. package/neural-interface/public/leonardoai.svg +0 -1
  362. package/neural-interface/public/mcp.svg +0 -4
  363. package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
  364. package/neural-interface/public/pin-svgrepo-com.svg +0 -25
  365. package/neural-interface/public/sessionicon.svg +0 -8
  366. package/neural-interface/public/toolbar-album.svg +0 -8
  367. package/neural-interface/public/toolbar-question.svg +0 -6
  368. package/neural-interface/public/toolbar-sessions.svg +0 -6
  369. package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
  370. package/scripts/audit-claude-hook-usage.mjs +0 -12
  371. package/scripts/audit-memory-duplicates.mjs +0 -44
  372. package/scripts/measure-claude-hook-overhead.mjs +0 -77
  373. package/scripts/recover-user-config.mjs +0 -254
  374. package/skills/synabun/synabunicon.png +0 -0
@@ -7,6 +7,7 @@
7
7
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
8
8
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
9
9
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
10
+ <link rel="manifest" href="/manifest.json">
10
11
  <style>
11
12
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
12
13
 
@@ -539,7 +540,7 @@
539
540
  .mcp-other-section.open .mcp-other-chevron { transform: rotate(180deg); }
540
541
  .mcp-other-body { padding: 0 14px 12px; }
541
542
 
542
- /* ── Rules (Step 5 option, Step 6 list): one row per connected tool ── */
543
+ /* ── Rules (Step 5 option, Step 6 installer) ── */
543
544
  .rules-option {
544
545
  display: flex;
545
546
  align-items: center;
@@ -550,12 +551,37 @@
550
551
  cursor: pointer;
551
552
  user-select: none;
552
553
  }
554
+ /* A step whose height follows what the installer reports: on a short window it scrolls (its parts keep their
555
+ height instead of being squeezed) rather than being cut off. */
556
+ .step[data-step="6"] { max-height: calc(90vh - 58px); }
557
+ .step[data-step="6"] > * { flex-shrink: 0; }
558
+ .step[data-step="6"] .subtitle { margin-bottom: 16px; }
559
+ /* The step's one primary action: what it does, then the button. */
560
+ .rules-install-all {
561
+ display: flex;
562
+ align-items: center;
563
+ gap: 14px;
564
+ padding: 12px 14px;
565
+ margin-bottom: 12px;
566
+ border: 1px solid var(--accent-border);
567
+ border-radius: var(--r-card);
568
+ background: var(--accent-bg);
569
+ }
570
+ .rules-install-all .ria-text { flex: 1 1 0; min-width: 0; font-size: 12px; color: var(--t-primary); line-height: 1.5; }
571
+ .rules-install-all .action-btn { flex-shrink: 0; padding: 9px 18px; font-size: 12px; white-space: nowrap; }
572
+ /* What the installer answered, and where the rules live. */
573
+ .rules-result { margin-bottom: 12px; font-size: 12px; color: var(--t-primary); line-height: 1.5; overflow-wrap: anywhere; }
574
+ .rules-result.ok .rr-message::before { content: '\2713'; margin-right: 8px; color: var(--green); }
575
+ .rules-result.fail .rr-message { color: var(--red); }
576
+ .rules-result .rr-where { margin-top: 4px; font-size: 11px; color: var(--t-muted); }
553
577
  .rules-list {
554
578
  display: flex;
555
579
  flex-direction: column;
556
580
  gap: 8px;
557
581
  margin-bottom: 12px;
558
582
  }
583
+ .rules-list:empty { display: none; }
584
+ .rules-list .mcp-toggle-row.has-note { align-items: flex-start; }
559
585
  .rules-list .mtr-state {
560
586
  margin-left: 6px;
561
587
  font-size: 10px;
@@ -563,8 +589,9 @@
563
589
  color: var(--t-muted);
564
590
  }
565
591
  .rules-list .mtr-state.ok { color: var(--green); }
592
+ .rules-list .mtr-note { margin-top: 4px; font-size: 11px; color: var(--t-secondary); line-height: 1.45; overflow-wrap: anywhere; }
566
593
  .rules-actions { display: flex; gap: 6px; flex-shrink: 0; }
567
- .rules-actions button {
594
+ .rules-actions button, .rules-note button {
568
595
  padding: 5px 10px;
569
596
  border-radius: 8px;
570
597
  border: 1px solid var(--b-light);
@@ -576,11 +603,16 @@
576
603
  white-space: nowrap;
577
604
  transition: all var(--ease);
578
605
  }
579
- .rules-actions button:hover { background: var(--s-medium); color: var(--t-bright); border-color: var(--b-hover); }
580
- .rules-actions button:disabled { opacity: 0.4; cursor: default; }
606
+ .rules-actions button:hover, .rules-note button:hover { background: var(--s-medium); color: var(--t-bright); border-color: var(--b-hover); }
607
+ .rules-actions button.primary { border-color: var(--accent-border); background: var(--s-active); color: var(--t-bright); font-weight: 600; }
608
+ .rules-actions button:disabled, .rules-note button:disabled { opacity: 0.4; cursor: default; }
581
609
  .rules-note { font-size: 11px; color: var(--t-muted); margin-bottom: 12px; line-height: 1.5; }
610
+ .rules-note button { margin-left: 8px; }
611
+ /* Under the installer: the text SynaBun cannot install, to paste by hand. */
612
+ .rules-manual-label { margin: 4px 0 8px; font-size: 10px; color: var(--t-muted); text-transform: uppercase; letter-spacing: 0.5px; }
582
613
  .rules-extra { margin-top: 8px; }
583
614
  #rulesViewCode, #rulesCursorCode, #rulesCoexistCode { max-height: 160px; }
615
+ #rulesViewCode { margin-bottom: 12px; }
584
616
 
585
617
  /* ── Dependency List ── */
586
618
  .dep-list {
@@ -1188,14 +1220,14 @@
1188
1220
  <span class="mascot-toast" id="mascotToast"></span>
1189
1221
  </div>
1190
1222
  <h2>AI Tool Integration</h2>
1191
- <p class="subtitle">Toggle on the AI coding tools you use. SynaBun registers itself with each one automatically.</p>
1223
+ <p class="subtitle">Toggle on the AI coding tools you use. SynaBun registers itself with each one and installs its rules automatically.</p>
1192
1224
  <div class="mcp-toggle-list" id="mcpToggleList"></div>
1193
1225
  <label class="rules-option" for="rulesInstallOpt">
1194
1226
  <span class="key-backup-checkbox-wrap">
1195
1227
  <input type="checkbox" id="rulesInstallOpt" checked>
1196
1228
  <span class="key-backup-checkmark"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></span>
1197
1229
  </span>
1198
- <span>Install SynaBun's rules for this tool</span>
1230
+ <span>Install SynaBun's rules with each tool I connect</span>
1199
1231
  </label>
1200
1232
  <div class="mcp-other-section" id="mcpOtherSection">
1201
1233
  <div class="mcp-other-header" id="mcpOtherHeader">
@@ -1213,27 +1245,31 @@
1213
1245
  </div>
1214
1246
  </div>
1215
1247
 
1216
- <!-- Step 6: SynaBun rules -->
1248
+ <!-- Step 6: Install SynaBun rules. The installer leads; the copy boxes at the bottom are only for what it cannot write. -->
1217
1249
  <div class="step right" data-step="6">
1218
- <div style="position:relative;display:flex;justify-content:center;align-items:center;">
1219
- <div class="step-eyes"></div>
1220
- <span class="mascot-toast" id="rulesToast"></span>
1250
+ <div class="step-eyes"></div>
1251
+ <h2>Install SynaBun Rules</h2>
1252
+ <p class="subtitle">SynaBun installs its rules where each connected tool loads them for every project, and keeps them up to date. There is nothing to paste.</p>
1253
+ <div class="rules-note" id="rulesNote" role="status" style="display:none;"></div>
1254
+ <div class="rules-install-all" id="rulesInstallAll" style="display:none;">
1255
+ <div class="ria-text" id="rulesInstallAllHelp"></div>
1256
+ <button type="button" class="action-btn" id="rulesInstallAllBtn" aria-describedby="rulesInstallAllHelp"></button>
1221
1257
  </div>
1222
- <h2>SynaBun Rules</h2>
1223
- <p class="subtitle">SynaBun puts its rules where each tool loads them for every project, and keeps them up to date. There is nothing to paste.</p>
1224
- <div class="rules-note" id="rulesNote" style="display:none;"></div>
1225
- <div class="rules-list" id="rulesList"></div>
1226
- <div class="code-copy-wrap" id="rulesViewWrap" style="display:none;margin-bottom:12px;">
1227
- <div class="instructions-preview" id="rulesViewCode"></div>
1228
- <span class="code-copy-label">copy</span>
1258
+ <div class="rules-result" id="rulesResult" role="status" style="display:none;">
1259
+ <div class="rr-message" id="rulesResultMessage"></div>
1260
+ <div class="rr-where" id="rulesResultWhere"></div>
1229
1261
  </div>
1262
+ <div class="rules-list" id="rulesList"></div>
1263
+ <div class="instructions-preview" id="rulesViewCode" role="region" tabindex="0" style="display:none;"></div>
1264
+ <div class="rules-note" id="rulesIdle" style="display:none;"></div>
1265
+ <div class="rules-manual-label">Manual setup</div>
1230
1266
  <div class="mcp-other-section" id="rulesCursorSection">
1231
1267
  <div class="mcp-other-header" id="rulesCursorHeader">
1232
- <span>Cursor (copy and paste)</span>
1268
+ <span>Cursor (its rules are pasted by hand)</span>
1233
1269
  <svg class="mcp-other-chevron" viewBox="0 0 24 24" width="14" height="14"><polyline points="6 9 12 15 18 9" fill="none" stroke="currentColor" stroke-width="2"/></svg>
1234
1270
  </div>
1235
1271
  <div class="mcp-other-body" id="rulesCursorBody" style="display:none;">
1236
- <div style="font-size:12px;color:var(--t-secondary);margin-bottom:6px;">Cursor keeps User Rules inside the app. Paste this into Cursor Settings &rarr; Rules:</div>
1272
+ <div id="rulesCursorHelp" style="font-size:12px;color:var(--t-secondary);margin-bottom:6px;">Cursor keeps its User Rules inside the app, so SynaBun cannot install them. Copy the rules and paste them into Cursor Settings &rarr; Rules.</div>
1237
1273
  <div class="code-copy-wrap" id="rulesCursorWrap">
1238
1274
  <div class="instructions-preview" id="rulesCursorCode"></div>
1239
1275
  <span class="code-copy-label">copy</span>
@@ -1246,7 +1282,7 @@
1246
1282
  <svg class="mcp-other-chevron" viewBox="0 0 24 24" width="14" height="14"><polyline points="6 9 12 15 18 9" fill="none" stroke="currentColor" stroke-width="2"/></svg>
1247
1283
  </div>
1248
1284
  <div class="mcp-other-body" id="rulesCoexistBody" style="display:none;">
1249
- <div style="font-size:12px;color:var(--t-secondary);margin-bottom:6px;">Copy this, name the other tool in it, and paste it into your instructions file. SynaBun never installs it.</div>
1285
+ <div id="rulesCoexistHelp" style="font-size:12px;color:var(--t-secondary);margin-bottom:6px;">Copy this, name the other tool in it, and paste it into your instructions file. SynaBun never installs it.</div>
1250
1286
  <div class="code-copy-wrap" id="rulesCoexistWrap">
1251
1287
  <div class="instructions-preview" id="rulesCoexistCode"></div>
1252
1288
  <span class="code-copy-label">copy</span>
@@ -1261,7 +1297,7 @@
1261
1297
  <h2 style="text-align:center;margin-bottom:12px;">Setup Complete!</h2>
1262
1298
  <ul class="checklist" id="checklist"></ul>
1263
1299
  <div id="restartNotice" style="margin:16px 0 0;padding:12px 14px;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.10);border-radius:var(--r-card);font-size:12px;color:var(--t-primary);line-height:1.5;">
1264
- <span style="color:var(--t-bright);font-weight:600;">Restart required</span> — Relaunch <span id="restartToolName" style="color:var(--t-bright);font-weight:500;"></span> for the MCP changes to be picked up.
1300
+ <span style="color:var(--t-bright);font-weight:600;">Restart required</span> — Relaunch <span id="restartToolName" style="color:var(--t-bright);font-weight:500;"></span> for the MCP changes and SynaBun's rules to be picked up.
1265
1301
  </div>
1266
1302
  <div style="text-align:center;margin-top:16px;">
1267
1303
  <button class="action-btn" id="launchBtn" style="font-size:15px;padding:12px 32px;">Launch Neural Interface</button>
@@ -1371,12 +1407,27 @@
1371
1407
 
1372
1408
  <script type="module">
1373
1409
 
1410
+ // The rules step is the installer of Settings → AI connections: the same client calls and the same reading
1411
+ // of the status (see STEP 6). Both modules are import-free, so the wizard still loads nothing of the app.
1412
+ import { fetchRulesStatus, fetchRulesTextWithFallback, installRules, installAllRules } from './shared/api.js';
1413
+ import {
1414
+ describeRulesHost, describeRulesInstall, describeRulesInstallAll, describeRulesInPlace, rulesInstallPlan, catalogueTranslator,
1415
+ } from './shared/settings/settings-rules.js';
1416
+ // Install as an app: the wizard has no step for it. The browser's own dialog opens once, from a click that
1417
+ // moves the wizard on anyway (the welcome choices, Next), and the wizard never reads the answer. Also import-free.
1418
+ import { initPwaInstall, isStandalone, offerPwaInstallOnce } from './shared/pwa-install.js';
1419
+
1420
+ // Chrome and Edge hand the page their install event when it can be installed; Safari and Firefox have none.
1421
+ if (!isStandalone()) initPwaInstall();
1422
+
1374
1423
  // ═══════════════════════════════════════════
1375
1424
  // STATE
1376
1425
  // ═══════════════════════════════════════════
1377
1426
 
1378
1427
  const TOTAL_STEPS = 8;
1379
1428
  let currentStep = 0;
1429
+ // Step 6: a connected tool still has no rules. Leaving the step then is a skip, and the Next button says so.
1430
+ let rulesPending = false;
1380
1431
 
1381
1432
  const wizardState = {
1382
1433
  collectionName: 'claude_memory',
@@ -1386,6 +1437,8 @@ const wizardState = {
1386
1437
  enabledTools: [], // tool ids toggled on via Step 5 install toggles
1387
1438
  dataHome: '',
1388
1439
  packageRoot: '',
1440
+ mcpCommand: '',
1441
+ mcpArgs: [],
1389
1442
  buildReady: false,
1390
1443
  mcpWritten: false,
1391
1444
  projects: [],
@@ -1479,7 +1532,8 @@ function updateNav() {
1479
1532
  $prevBtn.disabled = currentStep === 0;
1480
1533
 
1481
1534
  const labels = ['Begin', 'Next', 'Next', 'Next', 'Next', 'Next', 'Next', 'Finish'];
1482
- $nextBtn.textContent = (labels[currentStep] || 'Next') + ' \u2192';
1535
+ const label = currentStep === 6 && rulesPending ? 'Skip' : (labels[currentStep] || 'Next');
1536
+ $nextBtn.textContent = label + ' \u2192';
1483
1537
 
1484
1538
  if (currentStep === TOTAL_STEPS - 1) {
1485
1539
  $nextBtn.style.display = 'none';
@@ -1491,6 +1545,7 @@ function updateNav() {
1491
1545
  $prevBtn.addEventListener('click', () => goToStep(currentStep - 1));
1492
1546
  $nextBtn.addEventListener('click', () => {
1493
1547
  if (validateStep(currentStep)) {
1548
+ offerPwaInstallOnce(); // first, while the click still counts as the user's: see pwa-install.js
1494
1549
  goToStep(currentStep + 1);
1495
1550
  }
1496
1551
  });
@@ -1774,11 +1829,14 @@ function renderHappyBun() {}
1774
1829
 
1775
1830
  // Fresh Install → proceed to step 1
1776
1831
  document.getElementById('choiceFreshInstall').addEventListener('click', () => {
1832
+ offerPwaInstallOnce();
1777
1833
  goToStep(1);
1778
1834
  });
1779
1835
 
1780
- // Restore → show restore panel
1836
+ // Restore → show restore panel. A restore never passes Next, so this is its click for the browser's
1837
+ // install dialog; the file picker opens on a later click of its own.
1781
1838
  document.getElementById('choiceRestore').addEventListener('click', () => {
1839
+ offerPwaInstallOnce();
1782
1840
  $choices.style.display = 'none';
1783
1841
  $restorePanel.style.display = 'block';
1784
1842
  requestAnimationFrame(() => {
@@ -2541,7 +2599,11 @@ function getGenericMcpJson() {
2541
2599
  const mcpPath = pkgRoot + '/mcp-server/run.mjs';
2542
2600
  const envPath = dataHome + '/.env';
2543
2601
  const env = { DOTENV_PATH: envPath, SYNABUN_DATA_HOME: dataHome, MEMORY_DATA_DIR: dataHome + '/mcp-data' };
2544
- return JSON.stringify({ mcpServers: { SynaBun: { command: 'node', args: [mcpPath], env } } }, null, 2);
2602
+ // A packaged application is started through its own executable; the server says which.
2603
+ const packaged = wizardState.mcpCommand && wizardState.mcpCommand !== 'node';
2604
+ const command = packaged ? wizardState.mcpCommand : 'node';
2605
+ const args = packaged ? wizardState.mcpArgs : [mcpPath];
2606
+ return JSON.stringify({ mcpServers: { SynaBun: { command, args, env } } }, null, 2);
2545
2607
  }
2546
2608
 
2547
2609
  // "Other providers" collapsible — shows the generic MCP JSON for paste-based setups.
@@ -2576,156 +2638,207 @@ document.getElementById('mcpCodeWrap').addEventListener('click', () => {
2576
2638
  });
2577
2639
 
2578
2640
  // ═══════════════════════════════════════════
2579
- // STEP 6: SynaBun rules
2641
+ // STEP 6: Install SynaBun rules
2580
2642
  // ═══════════════════════════════════════════
2581
- // One row per connected tool with where its rules are and what state they are
2582
- // in (GET /api/setup/rules), plus copy boxes for Cursor and the coexistence
2583
- // snippet. Nothing here has to be clicked to continue.
2643
+ // The installer, as Settings → AI connections → SynaBun rules shows it: one
2644
+ // button that installs the rules for every connected tool, then one row per
2645
+ // tool with where its rules live, what state they are in (GET /api/setup/rules)
2646
+ // and the action that state calls for, and under them what the installer
2647
+ // answered. The calls are the shared client's (shared/api.js). What a state is
2648
+ // called, which action it gets and every sentence about it come from
2649
+ // shared/settings/settings-rules.js and the Settings strings (i18n/en.json), so
2650
+ // the two surfaces cannot drift apart. Nothing is pasted in this step: the two
2651
+ // boxes under "Manual setup" hold what SynaBun cannot install (Cursor's User
2652
+ // Rules, the coexistence snippet). Nothing here has to be clicked to continue.
2584
2653
 
2585
2654
  const RESTART_HINT = 'Restart SynaBun to finish this update.';
2586
- const RULES_STATE_TEXT = {
2587
- installed: 'Installed', newer: 'Installed by a newer SynaBun', outdated: 'Update available', modified: 'Edited', conflict: 'Conflict',
2588
- error: 'Error', shadowed: 'Shadowed', 'not-installed': 'Not installed',
2589
- };
2590
- const RULES_PRIMARY = { 'not-installed': 'Install', outdated: 'Update', modified: 'Replace' };
2655
+ const RULES_SETTINGS_PATH = 'Settings → AI connections → SynaBun rules';
2591
2656
  let rulesStatus = null; // null: the route is missing or failed
2657
+ let rulesTx = null; // the Settings strings; null until they are loaded
2658
+ let rulesReport = null; // what the last install answered: { message, note, ok }
2659
+ let rulesBusy = false; // an install is on its way: every button of the step waits
2660
+ let rulesViewHost = ''; // the tool whose rules text is open under the list
2592
2661
  const rulesTextCache = {};
2593
2662
 
2594
- async function loadRulesStatus() {
2663
+ // The wizard's names for the tools (the ones Step 5 shows), for every sentence that names one.
2664
+ const RULES_TOOL_NAMES = Object.fromEntries(TOOLS.map(t => [t.id, t.name]));
2665
+ const ruleToolNames = (hosts) => hosts.map(host => RULES_TOOL_NAMES[host] || host).join(', ');
2666
+
2667
+ // The words of this step are the Settings ones. One request; a failed one is tried again on the next visit.
2668
+ async function loadRulesStrings() {
2669
+ if (rulesTx) return rulesTx;
2595
2670
  try {
2596
- const res = await fetch('/api/setup/rules');
2597
- if (!res.ok) return null;
2598
- const data = await res.json();
2599
- return data && data.ok ? data : null;
2600
- } catch { return null; }
2671
+ const res = await fetch('/i18n/en.json');
2672
+ if (res.ok) rulesTx = catalogueTranslator(await res.json());
2673
+ } catch { /* the step says so and offers to try again */ }
2674
+ return rulesTx;
2601
2675
  }
2602
2676
 
2603
2677
  // The rendered text for a host, from the rules route or the older read-only endpoint.
2604
2678
  async function loadRulesText(host) {
2605
- if (rulesTextCache[host]) return rulesTextCache[host];
2606
- let text = '';
2607
- try {
2608
- const res = await fetch(`/api/setup/rules/${host}/text`);
2609
- if (res.ok) text = (await res.json()).text || '';
2610
- } catch { /* try the older endpoint */ }
2611
- if (!text) {
2612
- const format = host === 'opencode' ? 'generic' : host;
2613
- const res = await fetch(`/api/claude-code/ruleset?format=${format}`);
2614
- const data = res.ok ? await res.json() : {};
2615
- text = data.ruleset || '';
2616
- }
2617
- if (!text) throw new Error('Rules text not available');
2618
- rulesTextCache[host] = text;
2619
- return text;
2620
- }
2621
-
2622
- function showRulesToast(msg, duration = 2600) {
2623
- const toast = document.getElementById('rulesToast');
2624
- if (!toast) return;
2625
- toast.textContent = msg;
2626
- toast.classList.add('show');
2627
- clearTimeout(toast._timer);
2628
- toast._timer = setTimeout(() => toast.classList.remove('show'), duration);
2679
+ if (!rulesTextCache[host]) rulesTextCache[host] = await fetchRulesTextWithFallback(host);
2680
+ return rulesTextCache[host];
2629
2681
  }
2630
2682
 
2631
2683
  function enabledRuleTools() {
2632
2684
  return wizardState.enabledTools.map(id => TOOLS.find(t => t.id === id)).filter(Boolean);
2633
2685
  }
2634
2686
 
2635
- function renderRulesList() {
2636
- const $list = document.getElementById('rulesList');
2637
- const $note = document.getElementById('rulesNote');
2638
- const tools = enabledRuleTools();
2687
+ function renderRulesStep() {
2688
+ const el = (id) => document.getElementById(id);
2689
+ const tx = rulesTx;
2690
+ const plan = rulesInstallPlan(rulesStatus);
2691
+ const ready = !!tx && !!rulesStatus;
2692
+
2693
+ // What stands in the way, if anything: the step's own text, the installer, or no connected tool.
2694
+ const note = !tx ? 'This step could not load its text. Check that SynaBun is running, then try again.'
2695
+ : !rulesStatus ? `The rules installer did not answer. ${RESTART_HINT} The rules can also be installed later in ${RULES_SETTINGS_PATH}.`
2696
+ : !plan.connected.length ? 'No tool is connected yet. Connect one in the previous step and its rules are installed with it.'
2697
+ : '';
2698
+ const $note = el('rulesNote');
2699
+ $note.textContent = note;
2700
+ if (note && !ready) {
2701
+ const retry = document.createElement('button');
2702
+ retry.type = 'button';
2703
+ retry.textContent = 'Try again';
2704
+ retry.disabled = rulesBusy;
2705
+ retry.addEventListener('click', () => onRulesetStep());
2706
+ $note.appendChild(retry);
2707
+ }
2708
+ $note.style.display = note ? '' : 'none';
2709
+
2710
+ // The primary action, while a connected tool still has rules to install or update.
2711
+ rulesPending = ready && plan.pending.length > 0;
2712
+ el('rulesInstallAll').style.display = rulesPending ? '' : 'none';
2713
+ if (tx) {
2714
+ el('rulesInstallAllHelp').textContent = tx('settings.redesign.control.set002.help');
2715
+ el('rulesInstallAllBtn').textContent = tx('settings.redesign.control.set002.label');
2716
+ el('rulesCursorHelp').textContent = tx('settings.redesign.setup.cursorHelp');
2717
+ el('rulesCoexistHelp').textContent = tx('settings.redesign.setup.coexistHelp');
2718
+ }
2719
+ el('rulesInstallAllBtn').disabled = rulesBusy;
2720
+
2721
+ // The result: what the last install answered, or, with nothing left to install, the rules that are in place
2722
+ // (the toggles of Step 5 installed them) and the file each tool reads.
2723
+ const report = rulesReport || (ready && !rulesPending ? describeRulesInPlace(rulesStatus, { tx, labels: RULES_TOOL_NAMES }) : null);
2724
+ const $result = el('rulesResult');
2725
+ $result.style.display = report ? '' : 'none';
2726
+ if (report) {
2727
+ $result.className = 'rules-result ' + (report.ok === false ? 'fail' : 'ok');
2728
+ el('rulesResultMessage').textContent = report.message;
2729
+ el('rulesResultWhere').textContent = report.note || '';
2730
+ el('rulesResultWhere').style.display = report.note ? '' : 'none';
2731
+ }
2732
+
2733
+ // One row per tool that has rules or is connected. Without a status only the tools connected in Step 5 are known.
2734
+ const $list = el('rulesList');
2639
2735
  $list.innerHTML = '';
2640
-
2641
- const notes = [];
2642
- if (!rulesStatus) notes.push(`${RESTART_HINT} Until then the rules can be viewed and copied here.`);
2643
- if (!tools.length) notes.push('No tool was connected in the previous step. Connect one there, or copy the rules below.');
2644
- $note.textContent = notes.join(' ');
2645
- $note.style.display = notes.length ? '' : 'none';
2646
-
2647
- for (const tool of tools) {
2648
- const info = rulesStatus && rulesStatus.hosts ? rulesStatus.hosts[tool.id] : null;
2649
- const state = info ? info.state : '';
2650
- const good = state === 'installed' || state === 'newer';
2651
- // A file without SynaBun's markers is the user's own: the server refuses to replace it, so no button.
2652
- const primary = info && info.detected !== false && info.replaceable !== false ? (RULES_PRIMARY[state] || '') : '';
2653
- const detail = !info ? RESTART_HINT
2654
- : info.detected === false ? `${tool.name} was not found on this machine`
2655
- : ((['conflict', 'error', 'shadowed'].includes(state) || info.replaceable === false) && (info.error || info.detail)) || info.path || '';
2656
-
2736
+ const idle = [];
2737
+ for (const tool of (tx ? (rulesStatus ? TOOLS : enabledRuleTools()) : [])) {
2738
+ const info = rulesStatus?.hosts?.[tool.id];
2739
+ const view = describeRulesHost(info, { label: tool.name, version: rulesStatus?.version, tx });
2740
+ // Rules are for a tool that talks to SynaBun. One that is not connected and has none is only named, under
2741
+ // the list: connecting it in Step 5 installs them.
2742
+ if (view.known && view.state === 'not-installed' && !plan.connected.includes(tool.id)) { idle.push(tool.name); continue; }
2657
2743
  const row = document.createElement('div');
2658
- row.className = 'mcp-toggle-row' + (good ? ' connected' : '');
2744
+ row.className = 'mcp-toggle-row' + (view.good ? ' connected' : '') + (view.note ? ' has-note' : '');
2659
2745
  row.dataset.host = tool.id;
2660
2746
  row.innerHTML = `
2661
2747
  <div class="mtr-body">
2662
- <div class="mtr-name">${tool.name}<span class="mtr-state${good ? ' ok' : ''}" data-role="state"></span></div>
2748
+ <div class="mtr-name">${tool.name}<span class="mtr-state${view.good ? ' ok' : ''}" data-role="state"></span></div>
2663
2749
  <div class="mtr-path" data-role="path"></div>
2750
+ <div class="mtr-note" data-role="note" style="display:none"></div>
2664
2751
  </div>
2665
2752
  <div class="rules-actions">
2666
- ${primary ? `<button type="button" data-role="primary">${primary}</button>` : ''}
2667
- <button type="button" data-role="view">View</button>
2668
- <button type="button" data-role="copy">Copy</button>
2753
+ ${view.action ? '<button type="button" class="primary" data-role="primary"></button>' : ''}
2754
+ ${view.known ? '<button type="button" data-role="view"></button>' : ''}
2669
2755
  </div>
2670
2756
  `;
2671
- row.querySelector('[data-role="state"]').textContent = info ? (RULES_STATE_TEXT[state] || state) : 'Unavailable';
2757
+ row.querySelector('[data-role="state"]').textContent = view.badge;
2672
2758
  const $path = row.querySelector('[data-role="path"]');
2673
- $path.textContent = detail;
2674
- $path.title = detail;
2675
- row.querySelector('[data-role="primary"]')?.addEventListener('click', (e) => installToolRules(tool, state, e.currentTarget));
2676
- row.querySelector('[data-role="view"]').addEventListener('click', () => viewRules(tool.id, tool.name));
2677
- row.querySelector('[data-role="copy"]').addEventListener('click', () => copyRules(tool.id, tool.name));
2759
+ $path.textContent = view.pathText;
2760
+ $path.title = view.path;
2761
+ const $rowNote = row.querySelector('[data-role="note"]');
2762
+ $rowNote.textContent = view.note;
2763
+ $rowNote.style.display = view.note ? '' : 'none';
2764
+ const $primary = row.querySelector('[data-role="primary"]');
2765
+ if ($primary) {
2766
+ $primary.textContent = view.actionLabel;
2767
+ $primary.disabled = rulesBusy;
2768
+ $primary.addEventListener('click', () => installToolRules(tool));
2769
+ }
2770
+ const $view = row.querySelector('[data-role="view"]');
2771
+ if ($view) {
2772
+ $view.textContent = tx(rulesViewHost === tool.id ? 'settings.redesign.setup.hide' : 'settings.redesign.setup.view');
2773
+ $view.setAttribute('aria-expanded', String(rulesViewHost === tool.id));
2774
+ $view.setAttribute('aria-controls', 'rulesViewCode');
2775
+ $view.addEventListener('click', () => viewRules(tool));
2776
+ }
2678
2777
  $list.appendChild(row);
2679
2778
  }
2779
+ el('rulesViewCode').style.display = rulesViewHost && $list.querySelector(`[data-host="${rulesViewHost}"] [data-role="view"]`) ? '' : 'none';
2780
+ // The other tools, in one line. With no tool connected at all, the note at the top already says it.
2781
+ const $idle = el('rulesIdle');
2782
+ $idle.textContent = idle.length && plan.connected.length ? `${tx('settings.redesign.setup.notConnected')}: ${idle.join(', ')}. Connecting a tool in the previous step installs its rules.` : '';
2783
+ $idle.style.display = $idle.textContent ? '' : 'none';
2784
+
2785
+ updateNav();
2680
2786
  requestAnimationFrame(() => resizeViewport(currentStep));
2681
2787
  }
2682
2788
 
2683
- async function installToolRules(tool, state, btn) {
2684
- const force = state === 'modified';
2685
- if (force && !confirm(`Replace your edited ${tool.name} rules with SynaBun's current version?\n\nYour edited copy is backed up first.`)) return;
2686
- btn.disabled = true;
2789
+ // Run one install, keep what it answered for the result line, then read the status again.
2790
+ async function runRulesInstall(work) {
2791
+ if (rulesBusy) return;
2792
+ rulesBusy = true;
2793
+ renderRulesStep();
2687
2794
  try {
2688
- const res = await fetch(`/api/setup/rules/${tool.id}`, {
2689
- method: 'POST',
2690
- headers: { 'Content-Type': 'application/json' },
2691
- body: JSON.stringify({ force }),
2692
- });
2693
- const data = await res.json().catch(() => ({}));
2694
- if (!res.ok || data.ok === false) showRulesToast(data.error || (res.status === 404 ? RESTART_HINT : `${tool.name} rules were not installed`), 5000);
2695
- else showRulesToast(`${tool.name} rules installed`);
2795
+ rulesReport = await work();
2696
2796
  } catch (err) {
2697
- showRulesToast(`Failed: ${err.message}`, 5000);
2698
- }
2699
- rulesStatus = await loadRulesStatus();
2700
- renderRulesList();
2797
+ // The server's own sentence (an edited copy, damaged markers, another SynaBun at work). A route that is not
2798
+ // there is the server that was not restarted.
2799
+ const message = err?.message === 'HTTP 404' ? RESTART_HINT : (err?.message || rulesTx('settings.redesign.setup.rulesActionFailed'));
2800
+ rulesReport = { message, note: '', ok: false };
2801
+ }
2802
+ rulesStatus = await fetchRulesStatus();
2803
+ rulesBusy = false;
2804
+ renderRulesStep();
2701
2805
  }
2702
2806
 
2703
- async function viewRules(host, name) {
2704
- const $wrap = document.getElementById('rulesViewWrap');
2807
+ // "Install for all": every connected tool, by the installer's own rule. Same call as the button in Settings.
2808
+ function installAllToolRules() {
2809
+ runRulesInstall(async () => {
2810
+ const report = describeRulesInstallAll(await installAllRules(), { tx: rulesTx, labels: RULES_TOOL_NAMES });
2811
+ return { message: report.message, note: report.note, ok: report.failed.length === 0 };
2812
+ });
2813
+ }
2814
+
2815
+ // One tool's Install, Update or Replace. Replace is behind the confirm that says the edited copy is backed up.
2816
+ function installToolRules(tool) {
2817
+ const view = describeRulesHost(rulesStatus?.hosts?.[tool.id], { label: tool.name, version: rulesStatus?.version, tx: rulesTx });
2818
+ if (!view.action) return;
2819
+ if (view.force && !confirm(rulesTx('settings.redesign.setup.replaceYourEditedRulesWithSynabun', { label: tool.name }))) return;
2820
+ delete rulesTextCache[tool.id];
2821
+ runRulesInstall(async () => {
2822
+ const result = await installRules(tool.id, { force: view.force });
2823
+ return { message: describeRulesInstall(result, view.state, { label: tool.name, tx: rulesTx }), note: '', ok: true };
2824
+ });
2825
+ }
2826
+
2827
+ // View shows the exact text a tool receives, under the list; a second press hides it.
2828
+ async function viewRules(tool) {
2705
2829
  const $code = document.getElementById('rulesViewCode');
2706
- if ($wrap.style.display !== 'none' && $wrap.dataset.host === host) {
2707
- $wrap.style.display = 'none';
2830
+ if (rulesViewHost === tool.id) {
2831
+ rulesViewHost = '';
2708
2832
  } else {
2709
- try { $code.textContent = await loadRulesText(host); }
2710
- catch { $code.textContent = `The ${name} rules could not be loaded. ${RESTART_HINT}`; }
2711
- $wrap.dataset.host = host;
2712
- $wrap.style.display = '';
2833
+ try { $code.textContent = await loadRulesText(tool.id); }
2834
+ catch { $code.textContent = rulesTx('settings.redesign.setup.couldNotLoadTheRules', { label: tool.name, RESTART_HINT }); }
2835
+ $code.setAttribute('aria-label', rulesTx('settings.redesign.setup.previewRules', { name: tool.name }));
2836
+ rulesViewHost = tool.id;
2713
2837
  }
2714
- requestAnimationFrame(() => resizeViewport(currentStep));
2838
+ renderRulesStep();
2715
2839
  }
2716
2840
 
2717
- async function copyRules(host, name) {
2718
- let text;
2719
- try { text = await loadRulesText(host); }
2720
- catch { showRulesToast(`Could not load the ${name} rules. ${RESTART_HINT}`, 5000); return; }
2721
- try {
2722
- await navigator.clipboard.writeText(text);
2723
- showRulesToast(`${name} rules copied`);
2724
- } catch {
2725
- // A refused clipboard is the browser's doing; restarting SynaBun would not help.
2726
- showRulesToast('Could not copy: the browser refused access to the clipboard.', 5000);
2727
- }
2728
- }
2841
+ document.getElementById('rulesInstallAllBtn').addEventListener('click', () => installAllToolRules());
2729
2842
 
2730
2843
  // A click-to-copy code box (the pattern of the MCP JSON box in Step 5). The
2731
2844
  // label says "copied!" only once the clipboard took the text; a browser that
@@ -2747,7 +2860,7 @@ function wireRulesCopyBox(wrapId, codeId) {
2747
2860
  });
2748
2861
  }
2749
2862
 
2750
- // A collapsible copy-only section: loads its text the first time it opens.
2863
+ // A collapsible copy-only section, for text SynaBun cannot install: loads it the first time it opens.
2751
2864
  function wireRulesExtra(sectionId, headerId, bodyId, wrapId, codeId, host, name) {
2752
2865
  const $section = document.getElementById(sectionId);
2753
2866
  const $body = document.getElementById(bodyId);
@@ -2768,15 +2881,10 @@ function wireRulesExtra(sectionId, headerId, bodyId, wrapId, codeId, host, name)
2768
2881
  wireRulesExtra('rulesCursorSection', 'rulesCursorHeader', 'rulesCursorBody', 'rulesCursorWrap', 'rulesCursorCode', 'cursor', 'Cursor rules');
2769
2882
  wireRulesExtra('rulesCoexistSection', 'rulesCoexistHeader', 'rulesCoexistBody', 'rulesCoexistWrap', 'rulesCoexistCode', 'coexistence', 'coexistence snippet');
2770
2883
 
2771
- document.getElementById('rulesViewWrap').addEventListener('click', () => {
2772
- const $wrap = document.getElementById('rulesViewWrap');
2773
- const tool = TOOLS.find(t => t.id === $wrap.dataset.host);
2774
- if (tool) copyRules(tool.id, tool.name);
2775
- });
2776
-
2777
2884
  async function onRulesetStep() {
2778
- rulesStatus = await loadRulesStatus();
2779
- renderRulesList();
2885
+ rulesReport = null;
2886
+ [rulesStatus] = await Promise.all([fetchRulesStatus(), loadRulesStrings()]);
2887
+ renderRulesStep();
2780
2888
  if (currentStep === 6) resizeViewport(6);
2781
2889
  }
2782
2890
 
@@ -2805,18 +2913,23 @@ async function onCompleteStep() {
2805
2913
  } catch { /* non-critical — user can enable via Settings later */ }
2806
2914
  }
2807
2915
 
2808
- // Rules count as installed when every connected tool has its copy in place.
2809
- rulesStatus = await loadRulesStatus();
2810
- const ruleTools = enabledRuleTools();
2811
- const rulesInstalled = ruleTools.length > 0 && !!rulesStatus
2812
- && ruleTools.every(t => ['installed', 'newer', 'shadowed'].includes(rulesStatus.hosts?.[t.id]?.state));
2916
+ // Rules count as installed when every connected tool has its copy in place. When they are not, the item says
2917
+ // for which tools and where the installer is from now on.
2918
+ rulesStatus = await fetchRulesStatus();
2919
+ const rulesPlan = rulesInstallPlan(rulesStatus);
2920
+ const rulesMissing = rulesPlan.connected.filter(host => !rulesPlan.inPlace.includes(host));
2921
+ const rulesInstalled = rulesPlan.connected.length > 0 && rulesMissing.length === 0;
2922
+ const rulesLabel = rulesInstalled ? 'Rules installed'
2923
+ : !rulesStatus ? `Rules not installed: the installer did not answer (${RULES_SETTINGS_PATH})`
2924
+ : !rulesPlan.connected.length ? 'Rules not installed: no tool is connected'
2925
+ : `Rules not installed for ${ruleToolNames(rulesMissing)} (${RULES_SETTINGS_PATH})`;
2813
2926
 
2814
2927
  const items = [
2815
2928
  { label: 'Memory name set', done: !!wizardState.collectionName },
2816
2929
  { label: 'Dependencies installed & MCP built', done: wizardState.buildReady },
2817
2930
  { label: 'Projects registered', done: (wizardState.projects || []).length > 0 },
2818
2931
  { label: 'AI tool integration enabled', done: wizardState.enabledTools.length > 0 || wizardState.mcpWritten },
2819
- { label: 'Rules installed', done: rulesInstalled },
2932
+ { label: rulesLabel, done: rulesInstalled },
2820
2933
  ];
2821
2934
 
2822
2935
  const $list = document.getElementById('checklist');
@@ -2853,6 +2966,10 @@ async function init() {
2853
2966
  if (data.packageRoot) {
2854
2967
  wizardState.packageRoot = data.packageRoot;
2855
2968
  }
2969
+ if (data.mcpCommand && Array.isArray(data.mcpArgs)) {
2970
+ wizardState.mcpCommand = data.mcpCommand;
2971
+ wizardState.mcpArgs = data.mcpArgs;
2972
+ }
2856
2973
  } catch {}
2857
2974
 
2858
2975
  // Enable the entrance animation only after every top-level initializer has