synabun 2.0.10 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +17 -8
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +1 -1
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +228 -0
  24. package/lib/virustotal.mjs +192 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  37. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  38. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  39. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  40. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  42. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  44. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  45. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  46. package/mcp-server/dist/services/neural-interface.js +67 -11
  47. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  48. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  49. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  50. package/mcp-server/dist/services/server-instructions.js +20 -62
  51. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  52. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  53. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  54. package/mcp-server/dist/services/tool-catalog.js +339 -0
  55. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  56. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  60. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  61. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  62. package/mcp-server/dist/services/tool-list.js +334 -0
  63. package/mcp-server/dist/services/tool-list.js.map +1 -0
  64. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  66. package/mcp-server/dist/services/tool-runtime.js +400 -0
  67. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  68. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  70. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  71. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  72. package/mcp-server/dist/tools/agents.d.ts +10 -10
  73. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  74. package/mcp-server/dist/tools/agents.js +22 -13
  75. package/mcp-server/dist/tools/agents.js.map +1 -1
  76. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  77. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  78. package/mcp-server/dist/tools/image-tools.js +1 -1
  79. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  80. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  81. package/mcp-server/dist/tools/skill-tools.js +639 -0
  82. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  83. package/mcp-server/dist/tools/skill.d.ts +7 -0
  84. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  85. package/mcp-server/dist/tools/skill.js +11 -0
  86. package/mcp-server/dist/tools/skill.js.map +1 -0
  87. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  89. package/mcp-server/dist/tools/tool-groups.js +147 -0
  90. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  91. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  92. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  93. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  94. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  96. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  97. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  98. package/mcp-server/package-lock.json +2 -2
  99. package/mcp-server/package.json +1 -1
  100. package/mcp-server/src/http.ts +75 -12
  101. package/mcp-server/src/index.ts +147 -154
  102. package/mcp-server/src/preload.ts +12 -12
  103. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  104. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  105. package/mcp-server/src/services/neural-interface.ts +79 -20
  106. package/mcp-server/src/services/server-instructions.ts +27 -63
  107. package/mcp-server/src/services/tool-catalog.ts +403 -0
  108. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  109. package/mcp-server/src/services/tool-list.ts +410 -0
  110. package/mcp-server/src/services/tool-runtime.ts +457 -0
  111. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  112. package/mcp-server/src/tools/agents.ts +22 -14
  113. package/mcp-server/src/tools/image-tools.ts +1 -1
  114. package/mcp-server/src/tools/skill-tools.ts +589 -0
  115. package/mcp-server/src/tools/skill.ts +13 -0
  116. package/mcp-server/src/tools/tool-groups.ts +164 -0
  117. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  118. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  119. package/neural-interface/i18n/en.json +3463 -200
  120. package/neural-interface/i18n/pt-BR.json +3411 -151
  121. package/neural-interface/lib/assistant-api.js +127 -28
  122. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  123. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  124. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  125. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  126. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  127. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  128. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  129. package/neural-interface/lib/assistant-catalog.js +6 -7
  130. package/neural-interface/lib/assistant-dispatch.js +871 -156
  131. package/neural-interface/lib/assistant-envelope.js +3 -1
  132. package/neural-interface/lib/assistant-persona.js +80 -11
  133. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  134. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  135. package/neural-interface/lib/assistant-route-gate.js +6 -4
  136. package/neural-interface/lib/assistant-router.js +13 -8
  137. package/neural-interface/lib/assistant-runtime.js +56 -18
  138. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  139. package/neural-interface/lib/assistant-tools.js +51 -0
  140. package/neural-interface/lib/backup-service.js +620 -3
  141. package/neural-interface/lib/backup-zip-reader.js +10 -5
  142. package/neural-interface/lib/backup-zip-writer.js +257 -0
  143. package/neural-interface/lib/browser-execution.js +10 -2
  144. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  145. package/neural-interface/lib/browser-network-guard.js +284 -0
  146. package/neural-interface/lib/browser-policy-roots.js +64 -0
  147. package/neural-interface/lib/browser-tool-policy.js +186 -31
  148. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  149. package/neural-interface/lib/claude-hooks-status.js +103 -0
  150. package/neural-interface/lib/claude-temporary.js +5 -11
  151. package/neural-interface/lib/cli-install.js +18 -138
  152. package/neural-interface/lib/cli-resolver.js +4 -1
  153. package/neural-interface/lib/cli-update-planner.js +32 -31
  154. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  155. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  156. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  157. package/neural-interface/lib/confined-fs.js +30 -3
  158. package/neural-interface/lib/crash-notice-api.js +98 -0
  159. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  160. package/neural-interface/lib/exec-loop-command.js +4 -8
  161. package/neural-interface/lib/http-guards.js +8 -2
  162. package/neural-interface/lib/install-details.js +4 -1
  163. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  164. package/neural-interface/lib/mcp-client-config.js +0 -29
  165. package/neural-interface/lib/mcp-external-servers.js +323 -0
  166. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  167. package/neural-interface/lib/morelogin.js +30 -0
  168. package/neural-interface/lib/native-loop-providers.js +547 -47
  169. package/neural-interface/lib/native-loop-runtime.js +268 -41
  170. package/neural-interface/lib/opencode-config.js +557 -0
  171. package/neural-interface/lib/opencode-db-schema.js +17 -34
  172. package/neural-interface/lib/opencode-history.js +48 -218
  173. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  174. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  175. package/neural-interface/lib/opencode-protocol.js +249 -112
  176. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  177. package/neural-interface/lib/opencode-serve-records.js +39 -21
  178. package/neural-interface/lib/opencode-v2-client.js +48 -153
  179. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  180. package/neural-interface/lib/opencode-worker-data.js +27 -0
  181. package/neural-interface/lib/opencode2-client.js +73 -37
  182. package/neural-interface/lib/opencode2-proxy.js +23 -63
  183. package/neural-interface/lib/opencode2-translate.js +51 -2
  184. package/neural-interface/lib/owner-authority.js +249 -0
  185. package/neural-interface/lib/page-session.js +275 -0
  186. package/neural-interface/lib/peer-identity.js +763 -0
  187. package/neural-interface/lib/request-gate.js +17 -2
  188. package/neural-interface/lib/rulesets/api.js +1 -1
  189. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  190. package/neural-interface/lib/rulesets/installer.js +92 -27
  191. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  192. package/neural-interface/lib/session-cache-builder.js +17 -30
  193. package/neural-interface/lib/session-title-generator.js +58 -36
  194. package/neural-interface/lib/skills/api-v2.js +495 -0
  195. package/neural-interface/lib/skills/bundle.js +418 -0
  196. package/neural-interface/lib/skills/catalog.js +294 -0
  197. package/neural-interface/lib/skills/creation-api.js +191 -0
  198. package/neural-interface/lib/skills/creation-brief.js +247 -0
  199. package/neural-interface/lib/skills/creation-sources.js +267 -0
  200. package/neural-interface/lib/skills/creation.js +843 -0
  201. package/neural-interface/lib/skills/deploy.js +564 -0
  202. package/neural-interface/lib/skills/digest.js +119 -0
  203. package/neural-interface/lib/skills/discover.js +111 -0
  204. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  205. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  206. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  207. package/neural-interface/lib/skills/eval-trace.js +119 -0
  208. package/neural-interface/lib/skills/evals-api.js +212 -0
  209. package/neural-interface/lib/skills/evals.js +1573 -0
  210. package/neural-interface/lib/skills/export.js +200 -0
  211. package/neural-interface/lib/skills/frontmatter.js +64 -0
  212. package/neural-interface/lib/skills/health.js +75 -0
  213. package/neural-interface/lib/skills/json-edit.js +165 -0
  214. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  215. package/neural-interface/lib/skills/known-bridges.js +96 -0
  216. package/neural-interface/lib/skills/library.js +823 -0
  217. package/neural-interface/lib/skills/local-store.js +21 -0
  218. package/neural-interface/lib/skills/migration.js +433 -0
  219. package/neural-interface/lib/skills/operations.js +45 -0
  220. package/neural-interface/lib/skills/ownership.js +427 -0
  221. package/neural-interface/lib/skills/packages.js +88 -0
  222. package/neural-interface/lib/skills/reviews.js +212 -0
  223. package/neural-interface/lib/skills/revision.js +80 -0
  224. package/neural-interface/lib/skills/roots.js +262 -0
  225. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  226. package/neural-interface/lib/skills/scan-assets.js +252 -0
  227. package/neural-interface/lib/skills/scan-context.js +26 -0
  228. package/neural-interface/lib/skills/scan-shell.js +324 -0
  229. package/neural-interface/lib/skills/scan.js +364 -0
  230. package/neural-interface/lib/skills/scanners.js +54 -0
  231. package/neural-interface/lib/skills/service.js +522 -0
  232. package/neural-interface/lib/skills/telemetry.js +113 -0
  233. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  234. package/neural-interface/lib/tool-lists.js +124 -0
  235. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  236. package/neural-interface/lib/worker-confinement.js +1192 -0
  237. package/neural-interface/lib/worker-guard-setting.js +56 -0
  238. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  239. package/neural-interface/lib/worker-relay-ports.js +227 -0
  240. package/neural-interface/lib/worker-resume.js +229 -0
  241. package/neural-interface/lib/worker-sandbox.js +299 -0
  242. package/neural-interface/package-lock.json +18 -12
  243. package/neural-interface/package.json +2 -2
  244. package/neural-interface/public/index.html +3 -1
  245. package/neural-interface/public/index2d.html +19 -11
  246. package/neural-interface/public/onboarding.html +2 -2
  247. package/neural-interface/public/shared/account-menu.js +8 -5
  248. package/neural-interface/public/shared/agent-runtime-options.js +35 -19
  249. package/neural-interface/public/shared/api.js +1527 -1336
  250. package/neural-interface/public/shared/app-frame.js +192 -0
  251. package/neural-interface/public/shared/assistant/asst-agents-dock.js +33 -2
  252. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  253. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  254. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  255. package/neural-interface/public/shared/assistant/asst-brain-picker.js +49 -35
  256. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  257. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  258. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  259. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  260. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  261. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  262. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  263. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  264. package/neural-interface/public/shared/assistant/asst-panel.js +106 -11
  265. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  266. package/neural-interface/public/shared/assistant/asst-render.js +401 -77
  267. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  268. package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
  269. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  270. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  271. package/neural-interface/public/shared/assistant/asst-state.js +11 -15
  272. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  273. package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
  274. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
  275. package/neural-interface/public/shared/automation-context.js +5 -2
  276. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  277. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  278. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  279. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  280. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  281. package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
  282. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  283. package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
  284. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  285. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  286. package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
  287. package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
  288. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  289. package/neural-interface/public/shared/cli-install-view.js +4 -8
  290. package/neural-interface/public/shared/cli-status.js +2 -2
  291. package/neural-interface/public/shared/constants.js +2 -2
  292. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  293. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  294. package/neural-interface/public/shared/context-menu-model.js +366 -0
  295. package/neural-interface/public/shared/context-menu.js +372 -0
  296. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  297. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  298. package/neural-interface/public/shared/desktop-update-view.js +20 -0
  299. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  300. package/neural-interface/public/shared/html-shell.js +9 -11
  301. package/neural-interface/public/shared/i18n.js +2 -0
  302. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  303. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  304. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  305. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  306. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  307. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  308. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  309. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  310. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  311. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  312. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  313. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +47 -252
  314. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  315. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  316. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +6 -21
  317. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  318. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  319. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  320. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  321. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  322. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  323. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  324. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +0 -21
  325. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  326. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +14 -47
  327. package/neural-interface/public/shared/page-session.js +96 -0
  328. package/neural-interface/public/shared/registry.js +5 -3
  329. package/neural-interface/public/shared/retired-2d.js +3 -0
  330. package/neural-interface/public/shared/schedules-view.js +195 -61
  331. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  332. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  333. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  334. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  335. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  336. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  337. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  338. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  339. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  340. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  341. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  342. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  343. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  344. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  345. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  346. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  347. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  348. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  349. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  350. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  351. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  352. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  353. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  354. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  355. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  356. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  357. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  358. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  359. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  360. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  361. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  362. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  363. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  364. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  365. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  366. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  367. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  368. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  369. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  370. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  371. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  372. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  373. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  374. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  375. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  376. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  377. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  378. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  379. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  380. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  381. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  382. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  383. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  384. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  385. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  386. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  387. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  388. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  389. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  390. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  391. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  392. package/neural-interface/public/shared/styles.css +1525 -1779
  393. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  394. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  395. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  396. package/neural-interface/public/shared/tool-list-view.js +112 -0
  397. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  398. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  399. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  400. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  401. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  402. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  403. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  404. package/neural-interface/public/shared/tour/tour.css +144 -0
  405. package/neural-interface/public/shared/ui-automation-studio.js +91 -128
  406. package/neural-interface/public/shared/ui-claude-panel.js +77 -5
  407. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  408. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  409. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  410. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  411. package/neural-interface/public/shared/ui-detail.js +15 -3
  412. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  413. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  414. package/neural-interface/public/shared/ui-menubar.js +8 -18
  415. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  416. package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
  417. package/neural-interface/public/shared/ui-navbar.js +4 -24
  418. package/neural-interface/public/shared/ui-panels.js +96 -65
  419. package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
  420. package/neural-interface/public/shared/ui-sessions.js +5 -3
  421. package/neural-interface/public/shared/ui-settings.js +594 -462
  422. package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
  423. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  424. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  425. package/neural-interface/public/shared/ui-skills.js +33 -2830
  426. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  427. package/neural-interface/public/shared/ui-sync.js +15 -5
  428. package/neural-interface/public/shared/ui-terminal.js +124 -180
  429. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  430. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  431. package/neural-interface/public/shared/ui-trash.js +6 -4
  432. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  433. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  434. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  435. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  436. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  437. package/neural-interface/public/shared/ui-update.js +38 -2
  438. package/neural-interface/public/shared/ui-whiteboard.js +311 -58
  439. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  440. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  441. package/neural-interface/public/shared/view-variant.js +66 -0
  442. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  443. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  444. package/neural-interface/public/shared/worker-end.js +10 -3
  445. package/neural-interface/public/shared/worker-restart.js +12 -0
  446. package/neural-interface/public/sidepanel-session.html +1 -1
  447. package/neural-interface/public/variant/2d/background.js +2 -1
  448. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  449. package/neural-interface/public/variant/2d/graph.js +42 -20
  450. package/neural-interface/public/variant/2d/hulls.js +14 -9
  451. package/neural-interface/public/variant/2d/lasso.js +11 -7
  452. package/neural-interface/public/variant/2d/main.js +32 -1
  453. package/neural-interface/public/variant/2d/minimap.js +11 -13
  454. package/neural-interface/public/variant/3d/main.js +69 -6
  455. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  456. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  457. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  458. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  459. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  460. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  461. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  462. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  463. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  464. package/neural-interface/scripts/build-vendor.mjs +32 -0
  465. package/neural-interface/server.js +3934 -2454
  466. package/neural-interface/templates/rulesets/core.md +1 -1
  467. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  468. package/neural-interface/templates/rulesets/manifest.json +8 -8
  469. package/npm-shrinkwrap.json +18 -12
  470. package/package.json +16 -3
  471. package/postinstall.js +9 -4
  472. package/preuninstall.js +1 -1
  473. package/setup.js +97 -322
  474. package/skills/skill-forge/SKILL.md +177 -0
  475. package/skills/skill-forge/evals/evals.json +54 -0
  476. package/skills/skill-forge/references/package.md +105 -0
  477. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  478. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  479. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  480. package/skills/synabun/SKILL.md +10 -7
  481. package/skills/synabun/modules/audit.md +1 -5
  482. package/skills/synabun/modules/changelog.md +1 -5
  483. package/skills/synabun/modules/idea.md +1 -1
  484. package/skills/synabun/modules/memorize.md +6 -9
  485. package/skills/synabun/modules/schedule.md +4 -4
  486. package/mcp-server/dist/services/profiles.d.ts +0 -120
  487. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  488. package/mcp-server/dist/services/profiles.js +0 -387
  489. package/mcp-server/dist/services/profiles.js.map +0 -1
  490. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  491. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  492. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  493. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  494. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  495. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  496. package/mcp-server/dist/tools/leonardo.js +0 -21
  497. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  498. package/mcp-server/dist/tools/profile.d.ts +0 -17
  499. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  500. package/mcp-server/dist/tools/profile.js +0 -114
  501. package/mcp-server/dist/tools/profile.js.map +0 -1
  502. package/mcp-server/src/services/profiles.ts +0 -428
  503. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  504. package/mcp-server/src/tools/leonardo.ts +0 -30
  505. package/mcp-server/src/tools/profile.ts +0 -122
  506. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  507. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  508. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  509. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  510. package/skills/leonardo/SKILL.md +0 -168
  511. package/skills/leonardo/icon.svg +0 -1
  512. package/skills/leonardo/modules/browser-guide.md +0 -378
  513. package/skills/leonardo/modules/image-prompter.md +0 -283
  514. package/skills/leonardo/modules/model-advisor.md +0 -100
  515. package/skills/leonardo/modules/prompt-library.md +0 -110
  516. package/skills/leonardo/modules/style-guide.md +0 -186
  517. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -0,0 +1,475 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: the Library's rows
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Every skill, agent and command a runtime can load, once each, with where it lives, who made it, its health and
6
+ // which runtimes have it. Built from what the Skills Service answers (lib/skills/api-v2.js): the catalog of every
7
+ // runtime folder (GET /catalog), the managed library (GET /skills), the service status (GET /status) and the open
8
+ // reviews (GET /reviews?open=true).
9
+ //
10
+ // One row per thing, not per path a runtime reads it through:
11
+ // - the same folder seen through two roots (a Claude account whose folder is a link to ~/.claude) is one row;
12
+ // - a plugin cached in several versions is one row (its copies counted);
13
+ // - an old prompt / command bridge (~/.codex/prompts, ~/.config/opencode/command) is not a row of its own: it is
14
+ // how that runtime reaches the skill it points to, shown in that skill's runtime column;
15
+ // - a skill SynaBun ships is one row with its installed copies (a folder of its name in a runtime's own folder);
16
+ // - a library skill is the row of the folder it was adopted from or installed into, else a row of its own (a
17
+ // draft not installed anywhere yet).
18
+ // Health is a list of codes with a severity (err, warn, info, off, ok), never a colour alone: the view writes the
19
+ // words (i18n `skillsStudio.health.*`). No DOM and no imports.
20
+
21
+ export const RUNTIMES = Object.freeze(['claude', 'codex', 'opencode']);
22
+ export const SORTS = Object.freeze(['attention', 'name', 'updated', 'kind', 'source']);
23
+ export const KINDS = Object.freeze(['skill', 'agent', 'command']);
24
+ export const SOURCES = Object.freeze(['mine', 'project', 'synabun', 'plugin', 'account', 'system', 'shared', 'library']);
25
+ export const HEALTH_FILTERS = Object.freeze(['attention', 'invalid', 'warnings', 'update', 'duplicates', 'drafts', 'readonly', 'ok']);
26
+ // 'archived' lists the skills deleted from the library (their versions kept), from GET /v2/skills/archived, not rows.
27
+ export const VIEWS = Object.freeze(['all', 'attention', 'mine', 'synabun', 'plugins', 'account', 'drafts', 'archived']);
28
+
29
+ import { revealHidden } from '../ss-unicode.js';
30
+
31
+ const SEVERITY_RANK = { err: 0, warn: 1, info: 2, off: 3, ok: 4 };
32
+ // What a row weighs when the Library is sorted by "needs attention".
33
+ const ATTENTION = { 'not-text': 1000, invalid: 1000, update: 400, shadowed: 300, twice: 300, 'same-description': 200, bridge: 150, warnings: 100, review: 80, draft: 50 };
34
+
35
+ const str = (v) => (typeof v === 'string' ? v : '');
36
+ const lower = (v) => str(v).toLowerCase();
37
+ const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
38
+
39
+ /** Where an entry comes from, as the Library groups it. */
40
+ export function sourceOf(entry) {
41
+ if (!entry) return 'library';
42
+ if (entry.sourceKind === 'bundled' || entry.family === 'synabun') return 'synabun';
43
+ if (entry.scope === 'plugin') return 'plugin';
44
+ if (entry.scope === 'synced') return 'account';
45
+ if (entry.scope === 'managed' || entry.scope === 'system' || entry.scope === 'admin') return 'system';
46
+ if (entry.scope === 'shared') return 'shared';
47
+ if (entry.scope === 'project') return 'project';
48
+ return 'mine';
49
+ }
50
+
51
+ /** The kind a row is shown as: a Codex prompt and a legacy command file are commands. */
52
+ const rowKind = (entry) => (entry?.kind === 'prompt' ? 'command' : (KINDS.includes(entry?.kind) ? entry.kind : 'skill'));
53
+
54
+ /** An old bridge (a SynaBun pointer file), and the runtime it serves: { runtime, skill } or null. */
55
+ function bridgeOf(entry) {
56
+ const b = entry?.bridge;
57
+ if (!b || typeof b !== 'object' || typeof b.skill !== 'string' || !b.skill) return null;
58
+ const runtime = RUNTIMES.includes(b.runtime) ? b.runtime : (entry.family === 'codex' ? 'codex' : entry.family === 'opencode' ? 'opencode' : null);
59
+ return runtime ? { runtime, skill: b.skill } : null;
60
+ }
61
+
62
+ const problemsOf = (entry) => ({ errors: num(entry?.problems?.errors), warnings: num(entry?.problems?.warnings) });
63
+
64
+ function newRow(key, entry) {
65
+ return {
66
+ key,
67
+ kind: rowKind(entry),
68
+ name: str(entry?.name) || str(entry?.folderName),
69
+ folderName: str(entry?.folderName) || str(entry?.name),
70
+ description: str(entry?.description),
71
+ invocationName: str(entry?.invocationName) || str(entry?.name),
72
+ source: sourceOf(entry),
73
+ scope: entry?.scope || 'library',
74
+ pluginId: entry?.pluginId || null,
75
+ projectLabel: entry?.projectLabel || null,
76
+ projectPath: entry?.projectPath || null,
77
+ path: entry?.path || null,
78
+ primary: entry || null,
79
+ entries: entry ? [entry] : [],
80
+ bridges: [],
81
+ copies: entry ? 1 : 0,
82
+ accounts: entry?.accountId ? [entry.accountId] : [],
83
+ skill: null,
84
+ review: null,
85
+ readOnly: entry ? !!entry.readOnly : false,
86
+ binary: !!entry?.binary,
87
+ problems: problemsOf(entry),
88
+ collisions: Array.isArray(entry?.collisions) ? entry.collisions.slice() : [],
89
+ update: null,
90
+ updatedAt: entry?.updatedAt || entry?.ownership?.installedAt || null,
91
+ sameDescriptionAs: [],
92
+ runtimes: {},
93
+ health: [],
94
+ attention: 0,
95
+ };
96
+ }
97
+
98
+ function absorb(row, entry) {
99
+ if (!entry || row.entries.includes(entry)) return;
100
+ row.entries.push(entry);
101
+ if (entry.accountId && !row.accounts.includes(entry.accountId)) row.accounts.push(entry.accountId);
102
+ for (const c of entry.collisions || []) if (!row.collisions.some((x) => x.runtime === c.runtime && x.outcome === c.outcome)) row.collisions.push(c);
103
+ if (entry.updatedAt && (!row.updatedAt || entry.updatedAt > row.updatedAt)) row.updatedAt = entry.updatedAt;
104
+ }
105
+
106
+ const isOwnerFolder = (entry) => entry?.kind === 'skill' && !entry.readOnly && (entry.scope === 'user' || entry.scope === 'shared');
107
+
108
+ /**
109
+ * The rows of the Library.
110
+ * @param {object} o
111
+ * @param {object[]} [o.entries] catalog items (GET /catalog `items`, every page)
112
+ * @param {object[]} [o.skills] library skill summaries (GET /skills)
113
+ * @param {object} [o.status] GET /status (updatesAvailable)
114
+ * @param {object[]} [o.reviews] open reviews (GET /reviews?open=true)
115
+ * @param {object} [o.upstream] bundled skill name → { revisionId, headDigest } of what this app version ships
116
+ * @param {Record<string, string>} [o.kept] skill name → the upstream revision the owner said "Keep mine" to: no update
117
+ * is shown for it (no health, no banner, no older-version marks) until SynaBun ships another version
118
+ * @returns {object[]}
119
+ */
120
+ export function buildRows({ entries = [], skills = [], status = null, reviews = [], upstream = {}, kept = {} } = {}) {
121
+ const rows = [];
122
+ const byPath = new Map();
123
+ const byPlugin = new Map();
124
+ const bridges = [];
125
+ const list = (Array.isArray(entries) ? entries : []).filter((e) => e && typeof e === 'object' && typeof e.id === 'string');
126
+
127
+ for (const entry of list) {
128
+ const bridge = bridgeOf(entry);
129
+ if (bridge) { bridges.push({ ...bridge, entry }); continue; }
130
+ if (entry.scope === 'plugin') {
131
+ const key = `plugin:${entry.pluginId || ''}:${rowKind(entry)}:${lower(entry.name)}`;
132
+ const row = byPlugin.get(key);
133
+ if (row) { absorb(row, entry); if (!row.entries.some((x) => x !== entry && x.path === entry.path)) row.copies = new Set(row.entries.map((x) => x.path)).size; continue; }
134
+ const created = newRow(key, entry);
135
+ byPlugin.set(key, created);
136
+ rows.push(created);
137
+ continue;
138
+ }
139
+ const key = `path:${entry.path || entry.id}`;
140
+ const row = byPath.get(key);
141
+ if (row) {
142
+ absorb(row, entry);
143
+ // The runtimes of one folder are those of every root it is seen through.
144
+ continue;
145
+ }
146
+ const created = newRow(key, entry);
147
+ byPath.set(key, created);
148
+ rows.push(created);
149
+ }
150
+
151
+ // A skill SynaBun ships takes its installed copies (a folder of its name in a runtime's own folder) into its row.
152
+ const bundled = rows.filter((r) => r.source === 'synabun' && r.kind === 'skill');
153
+ for (const b of bundled) {
154
+ for (const r of rows.slice()) {
155
+ if (r === b || r.source === 'synabun' || r.kind !== 'skill' || !r.primary || !isOwnerFolder(r.primary)) continue;
156
+ if (lower(r.folderName) !== lower(b.folderName)) continue;
157
+ for (const e of r.entries) absorb(b, e);
158
+ b.installedCopies = [...(b.installedCopies || []), ...r.entries];
159
+ rows.splice(rows.indexOf(r), 1);
160
+ }
161
+ }
162
+
163
+ // Bridges: the runtime they serve, in the row of the skill they point to.
164
+ for (const br of bridges) {
165
+ const target = bundled.find((r) => lower(r.folderName) === lower(br.skill))
166
+ || rows.find((r) => r.kind === 'skill' && lower(r.folderName) === lower(br.skill));
167
+ if (target) { target.bridges.push({ runtime: br.runtime, entry: br.entry }); continue; }
168
+ // A bridge to a skill nobody has any more: listed as what it is.
169
+ const row = newRow(`path:${br.entry.path || br.entry.id}`, br.entry);
170
+ row.orphanBridge = br.runtime;
171
+ rows.push(row);
172
+ }
173
+
174
+ // Library skills: the row of the folder they came from or went to, else a row of their own.
175
+ const taken = new Set();
176
+ const skillList = (Array.isArray(skills) ? skills : []).filter((s) => s && typeof s.skillId === 'string');
177
+ const rank = (s) => (s.origin?.kind === 'adopted' ? 0 : s.origin?.kind === 'bundled' ? 1 : 2);
178
+ // The folders a library skill was installed into are that skill: one row, with every runtime it reaches.
179
+ const absorbInstalled = (row, s) => {
180
+ const dests = new Set((s.installations || []).map((i) => i.destination).filter(Boolean));
181
+ if (!dests.size) return;
182
+ for (const r of rows.slice()) {
183
+ if (r === row || taken.has(r) || r.source === 'synabun' || r.kind !== 'skill' || !r.entries.length) continue;
184
+ if (!r.entries.every((e) => dests.has(e.path))) continue;
185
+ for (const e of r.entries) absorb(row, e);
186
+ rows.splice(rows.indexOf(r), 1);
187
+ }
188
+ };
189
+ for (const s of skillList.slice().sort((a, b) => rank(a) - rank(b))) {
190
+ let row = null;
191
+ if (s.origin?.kind === 'bundled') row = bundled.find((r) => lower(r.folderName) === lower(s.origin.name));
192
+ if (!row && s.origin?.kind === 'adopted') row = rows.find((r) => r.entries.some((e) => e.id === s.origin.artifactId || (s.origin.path && e.path === s.origin.path)));
193
+ if (!row) {
194
+ const dests = (s.installations || []).map((i) => i.destination).filter(Boolean);
195
+ row = rows.find((r) => r.kind === 'skill' && r.entries.some((e) => dests.includes(e.path)));
196
+ }
197
+ if (row && !taken.has(row)) { row.skill = s; taken.add(row); absorbInstalled(row, s); continue; }
198
+ const own = newRow(`lib:${s.skillId}`, null);
199
+ own.kind = 'skill';
200
+ own.name = str(s.name);
201
+ own.folderName = str(s.name);
202
+ own.invocationName = str(s.name);
203
+ own.description = str(s.description);
204
+ own.source = 'library';
205
+ own.scope = 'library';
206
+ own.skill = s;
207
+ own.updatedAt = s.updatedAt || null;
208
+ rows.push(own);
209
+ }
210
+
211
+ // Updates: an installation of a bundled skill from an older revision than this app ships, or an installed copy
212
+ // whose SKILL.md is not the shipped one (a folder from before SynaBun kept a ledger cannot say more).
213
+ const updates = Array.isArray(status?.updatesAvailable) ? status.updatesAvailable : [];
214
+ for (const row of rows) {
215
+ if (row.source !== 'synabun' || row.kind !== 'skill') continue;
216
+ const up = upstream?.[lower(row.folderName)] || null;
217
+ const deploy = updates.filter((u) => row.skill && u.skillId === row.skill.skillId);
218
+ const legacy = (row.installedCopies || []).filter((e) => {
219
+ if (!up?.headDigest || typeof e.revision !== 'string') return false;
220
+ const installedByLibrary = (row.skill?.installations || []).some((i) => i.destination === e.path);
221
+ return !installedByLibrary && e.revision !== up.headDigest;
222
+ });
223
+ if (deploy.length || legacy.length) {
224
+ const upstreamRevisionId = up?.revisionId || deploy[0]?.to || row.skill?.pointers?.upstream || null;
225
+ // "Keep mine" for exactly this version of SynaBun's: the copy is the owner's as it is, and says nothing more.
226
+ if (upstreamRevisionId && kept && typeof kept === 'object' && kept[row.folderName] === upstreamRevisionId) { row.keptUpdate = upstreamRevisionId; continue; }
227
+ row.update = {
228
+ upstreamRevisionId,
229
+ installations: deploy.map((u) => u.installationId),
230
+ legacyCopies: legacy.map((e) => ({ path: e.path, runtimes: e.runtimes || [], artifactId: e.id })),
231
+ kind: deploy.length ? 'deploy' : 'legacy',
232
+ };
233
+ }
234
+ }
235
+
236
+ // Open reviews, by skill.
237
+ const open = (Array.isArray(reviews) ? reviews : []).filter((r) => r && ['pending', 'changes_requested', 'approved', 'partial', 'publish_failed'].includes(r.state));
238
+ for (const row of rows) {
239
+ if (!row.skill) continue;
240
+ row.review = open.find((r) => r.skillId === row.skill.skillId) || null;
241
+ }
242
+
243
+ markSameDescriptions(rows);
244
+ for (const row of rows) finishRow(row);
245
+ return rows;
246
+ }
247
+
248
+ /** Two rows a runtime lists with the same description: the model cannot tell them apart. */
249
+ function markSameDescriptions(rows) {
250
+ const groups = new Map();
251
+ for (const row of rows) {
252
+ const d = lower(row.description).replace(/\s+/g, ' ').trim();
253
+ if (d.length < 24 || row.source === 'synabun') continue;
254
+ if (!groups.has(d)) groups.set(d, []);
255
+ groups.get(d).push(row);
256
+ }
257
+ for (const list of groups.values()) {
258
+ if (list.length < 2) continue;
259
+ for (const row of list) row.sameDescriptionAs = list.filter((x) => x !== row).map((x) => x.name);
260
+ }
261
+ }
262
+
263
+ /** What each runtime has of a row: installed, outdated, an old bridge only, present (a folder it reads), or none. */
264
+ function runtimeStates(row) {
265
+ const out = Object.fromEntries(RUNTIMES.map((r) => [r, { state: 'none', paths: [] }]));
266
+ const installs = row.skill?.installations || [];
267
+ // An update the owner kept their copy over ("Keep mine") marks no installation as an older version.
268
+ const older = (i) => !!i?.updateAvailable && !row.keptUpdate;
269
+ for (const e of row.entries) {
270
+ if (row.source === 'synabun' && e === row.primary) continue; // the bundled source itself is read by no runtime
271
+ for (const r of e.runtimes || []) {
272
+ if (!out[r]) continue;
273
+ const byLibrary = installs.find((i) => i.destination === e.path);
274
+ const bySynabun = !!e.ownership && e.ownership.managedBy === 'synabun';
275
+ const next = older(byLibrary) ? 'outdated' : (byLibrary || bySynabun ? 'installed' : 'present');
276
+ if (rankState(next) > rankState(out[r].state)) out[r].state = next;
277
+ if (!out[r].paths.includes(e.path)) out[r].paths.push(e.path);
278
+ }
279
+ }
280
+ for (const i of installs) {
281
+ if (!out[i.runtime]) continue;
282
+ const next = older(i) ? 'outdated' : 'installed';
283
+ if (rankState(next) > rankState(out[i.runtime].state)) out[i.runtime].state = next;
284
+ if (i.destination && !out[i.runtime].paths.includes(i.destination)) out[i.runtime].paths.push(i.destination);
285
+ }
286
+ for (const copy of row.update?.legacyCopies || []) {
287
+ for (const r of copy.runtimes) if (out[r] && out[r].state !== 'none') out[r].state = 'outdated';
288
+ }
289
+ for (const b of row.bridges) {
290
+ if (out[b.runtime] && out[b.runtime].state === 'none') { out[b.runtime].state = 'bridge'; out[b.runtime].paths.push(b.entry.path); }
291
+ }
292
+ return out;
293
+ }
294
+ const STATE_RANK = { none: 0, bridge: 1, present: 2, installed: 3, outdated: 4 };
295
+ const rankState = (s) => STATE_RANK[s] ?? 0;
296
+
297
+ /** The health capsule of a row: codes with a severity, worst first. */
298
+ export function healthOf(row) {
299
+ const out = [];
300
+ const add = (code, sev, extra = {}) => out.push({ code, sev, ...extra });
301
+ if (row.binary) add('not-text', 'err');
302
+ else if (row.problems.errors) add('invalid', 'err', { count: row.problems.errors });
303
+ if (row.update) add('update', 'info');
304
+ const shadowed = row.collisions.find((c) => c.outcome === 'shadowed');
305
+ const twice = row.collisions.find((c) => c.outcome === 'both-listed');
306
+ if (shadowed) add('shadowed', 'warn', { runtime: shadowed.runtime, why: str(shadowed.why) });
307
+ else if (twice) add('twice', 'warn', { runtime: twice.runtime, why: str(twice.why) });
308
+ if (row.sameDescriptionAs.length) add('same-description', 'warn', { names: row.sameDescriptionAs.slice(0, 3) });
309
+ const bridgeOnly = row.bridges.filter((b) => row.runtimes[b.runtime]?.state === 'bridge');
310
+ if (bridgeOnly.length || row.orphanBridge) add('bridge', 'warn', { runtimes: bridgeOnly.map((b) => b.runtime).concat(row.orphanBridge ? [row.orphanBridge] : []) });
311
+ if (row.problems.warnings && !row.binary) add('warnings', 'warn', { count: row.problems.warnings });
312
+ if (row.review) add('review', 'info', { state: row.review.state });
313
+ const s = row.skill;
314
+ if (s && (s.pointers?.draft || !s.revisions) && !(s.installations || []).length) add('draft', 'info');
315
+ if (row.readOnly) add('readonly', 'off');
316
+ if (!out.some((h) => h.sev === 'err' || h.sev === 'warn' || h.sev === 'info')) add('ok', 'ok');
317
+ return out.sort((a, b) => SEVERITY_RANK[a.sev] - SEVERITY_RANK[b.sev]);
318
+ }
319
+
320
+ function finishRow(row) {
321
+ // What the page shows: hidden and direction-changing characters escaped (U+202E), so a name reads as it is.
322
+ row.label = revealHidden(row.name || row.folderName || '');
323
+ row.shownDescription = revealHidden(row.description || '');
324
+ row.runtimes = runtimeStates(row);
325
+ row.health = healthOf(row);
326
+ row.attention = row.health.reduce((n, h) => n + (ATTENTION[h.code] || 0) * (h.code === 'invalid' || h.code === 'warnings' ? Math.max(1, Math.min(9, num(h.count))) : 1), 0);
327
+ row.needsAttention = row.attention > 0;
328
+ }
329
+
330
+ // ── Filtering, sorting, counting ──
331
+
332
+ /** Whether a row matches a health filter. */
333
+ export function healthMatches(row, health) {
334
+ if (!health || health === 'any') return true;
335
+ const codes = new Set(row.health.map((h) => h.code));
336
+ switch (health) {
337
+ case 'attention': return row.needsAttention;
338
+ case 'invalid': return codes.has('invalid') || codes.has('not-text');
339
+ case 'warnings': return codes.has('warnings');
340
+ case 'update': return codes.has('update');
341
+ case 'duplicates': return codes.has('shadowed') || codes.has('twice') || codes.has('same-description');
342
+ case 'drafts': return codes.has('draft');
343
+ case 'readonly': return codes.has('readonly');
344
+ case 'ok': return codes.has('ok');
345
+ default: return true;
346
+ }
347
+ }
348
+
349
+ /** Whether a row belongs to a saved view of the rail (or a project: `project:<path>`). */
350
+ export function viewMatches(row, view) {
351
+ if (!view || view === 'all') return true;
352
+ if (view.startsWith('project:')) return row.projectPath === view.slice(8);
353
+ switch (view) {
354
+ case 'attention': return row.needsAttention;
355
+ case 'mine': return row.source === 'mine' || row.source === 'project' || row.source === 'library';
356
+ case 'synabun': return row.source === 'synabun';
357
+ case 'plugins': return row.source === 'plugin';
358
+ case 'account': return row.source === 'account';
359
+ case 'drafts': return row.health.some((h) => h.code === 'draft');
360
+ case 'archived': return false; // not rows: the view draws the archived list
361
+ default: return true;
362
+ }
363
+ }
364
+
365
+ /** The text a search looks in: name, description, invocation, plugin, project. */
366
+ const haystack = (row) => `${row.name} ${row.description} ${row.invocationName} ${row.pluginId || ''} ${row.projectLabel || ''}`.toLowerCase();
367
+
368
+ /**
369
+ * @param {object[]} rows
370
+ * @param {{ view?: string, kind?: string, source?: string, runtime?: string, health?: string, q?: string }} f
371
+ */
372
+ export function filterRows(rows, f = {}) {
373
+ const q = lower(f.q).trim();
374
+ const words = q ? q.split(/\s+/) : [];
375
+ return rows.filter((row) => viewMatches(row, f.view)
376
+ && (!f.kind || f.kind === 'all' || row.kind === f.kind)
377
+ && (!f.source || f.source === 'all' || row.source === f.source)
378
+ && (!f.runtime || f.runtime === 'all' || ['installed', 'outdated', 'present', 'bridge'].includes(row.runtimes[f.runtime]?.state))
379
+ && healthMatches(row, f.health)
380
+ && (!words.length || words.every((w) => haystack(row).includes(w))));
381
+ }
382
+
383
+ const byName = (a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) || a.key.localeCompare(b.key);
384
+ const SOURCE_ORDER = Object.fromEntries(SOURCES.map((s, i) => [s, i]));
385
+ const KIND_ORDER = { skill: 0, agent: 1, command: 2 };
386
+
387
+ /** Rows in a sort order; ties by name. */
388
+ export function sortRows(rows, sort = 'attention') {
389
+ const list = rows.slice();
390
+ switch (sort) {
391
+ case 'name': return list.sort(byName);
392
+ case 'updated': return list.sort((a, b) => (a.updatedAt && b.updatedAt ? (a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0) : a.updatedAt ? -1 : b.updatedAt ? 1 : 0) || byName(a, b));
393
+ case 'kind': return list.sort((a, b) => (KIND_ORDER[a.kind] - KIND_ORDER[b.kind]) || byName(a, b));
394
+ case 'source': return list.sort((a, b) => (SOURCE_ORDER[a.source] - SOURCE_ORDER[b.source]) || byName(a, b));
395
+ default: return list.sort((a, b) => (b.attention - a.attention) || byName(a, b));
396
+ }
397
+ }
398
+
399
+ /** How many rows each saved view, kind, source, runtime and health filter would show (for the rail and the menus). */
400
+ export function countRows(rows) {
401
+ const count = (fn) => rows.reduce((n, r) => n + (fn(r) ? 1 : 0), 0);
402
+ return {
403
+ total: rows.length,
404
+ views: Object.fromEntries(VIEWS.map((v) => [v, count((r) => viewMatches(r, v))])),
405
+ kinds: Object.fromEntries(KINDS.map((k) => [k, count((r) => r.kind === k)])),
406
+ sources: Object.fromEntries(SOURCES.map((s) => [s, count((r) => r.source === s)])),
407
+ runtimes: Object.fromEntries(RUNTIMES.map((rt) => [rt, count((r) => ['installed', 'outdated', 'present', 'bridge'].includes(r.runtimes[rt]?.state))])),
408
+ health: Object.fromEntries(HEALTH_FILTERS.map((h) => [h, count((r) => healthMatches(r, h))])),
409
+ };
410
+ }
411
+
412
+ /** The registered projects, from the catalog's roots, with how many rows each has. */
413
+ export function projectsOf(roots = [], rows = []) {
414
+ const seen = new Map();
415
+ for (const root of Array.isArray(roots) ? roots : []) {
416
+ if (root?.scope !== 'project' || typeof root.path !== 'string') continue;
417
+ const path = typeof root.projectPath === 'string' && root.projectPath ? root.projectPath : null;
418
+ const key = path || root.projectLabel;
419
+ if (!key || seen.has(key)) continue;
420
+ seen.set(key, { path: path || '', label: str(root.projectLabel) || (path ? path.split(/[\\/]/).filter(Boolean).pop() : '') });
421
+ }
422
+ // A root row from an older server carries no projectPath: the rows' own say which project they are in.
423
+ for (const row of rows) {
424
+ if (!row.projectPath) continue;
425
+ const known = [...seen.values()].find((p) => p.path === row.projectPath || (!p.path && p.label === row.projectLabel));
426
+ if (known) { if (!known.path) known.path = row.projectPath; continue; }
427
+ seen.set(row.projectPath, { path: row.projectPath, label: row.projectLabel || row.projectPath.split(/[\\/]/).filter(Boolean).pop() });
428
+ }
429
+ return [...seen.values()].filter((p) => p.path).map((p) => ({ ...p, count: rows.filter((r) => r.projectPath === p.path).length }))
430
+ .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
431
+ }
432
+
433
+ /** The worst severity of a row's health (what its capsule is coloured by, next to its word). */
434
+ export const worstHealth = (row) => row.health[0] || { code: 'ok', sev: 'ok' };
435
+
436
+ /** Rows that hold a row key, in a list (bulk selection kept across redraws). */
437
+ export function keepSelection(selected, rows) {
438
+ const keys = new Set(rows.map((r) => r.key));
439
+ return new Set([...selected].filter((k) => keys.has(k)));
440
+ }
441
+
442
+ /** Start a catalog search without a saved view silently excluding matches. Explicit filters can then narrow it. */
443
+ export function searchLibrary(lib, query) {
444
+ const starting = !String(lib.q || '').trim() && String(query).trim();
445
+ if (starting) Object.assign(lib, { view: 'all', kind: 'all', source: 'all', runtime: 'all', health: 'any' });
446
+ lib.q = query;
447
+ lib.focus = null;
448
+ return lib;
449
+ }
450
+
451
+ /** Load every catalog page before exposing searchable rows. A catalog reset restarts once, never blends generations. */
452
+ export async function readWholeCatalog(fetchPage) {
453
+ let reset = false;
454
+ for (;;) {
455
+ const out = { items: [], roots: [], diagnostics: [], truncated: false };
456
+ const seen = new Set();
457
+ let cursor = null;
458
+ for (;;) {
459
+ const res = await fetchPage({ cursor, limit: 500 });
460
+ if (res.cursorReset) {
461
+ if (reset) throw Object.assign(new Error('catalog_incomplete'), { code: 'catalog_incomplete' });
462
+ reset = true;
463
+ break;
464
+ }
465
+ out.items.push(...(res.items || []));
466
+ out.roots = res.roots || out.roots;
467
+ out.diagnostics = res.diagnostics || out.diagnostics;
468
+ out.truncated ||= !!res.truncated;
469
+ if (!res.nextCursor) return out;
470
+ if (seen.has(res.nextCursor)) throw Object.assign(new Error('catalog_incomplete'), { code: 'catalog_incomplete' });
471
+ seen.add(res.nextCursor);
472
+ cursor = res.nextCursor;
473
+ }
474
+ }
475
+ }