synabun 2.0.10 → 2.2.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 (656) hide show
  1. package/CHANGELOG.md +207 -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 +13 -13
  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 +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  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 +302 -0
  24. package/lib/virustotal.mjs +203 -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/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,369 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: the Review place
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What waits for the owner (sx-review.js): submissions of the Review queue (a new skill or a change from the SynaBun
6
+ // Assistant or another agent: what it does, who made it, its checks, its scan findings, what it can do that the
7
+ // approved revision could not, the name it installs as, every file it changes with its lines) and the updates
8
+ // SynaBun ships for skills that are installed. Approving installs: "Approve & install" approves and installs to the
9
+ // targets the submission named (sx-install-flow.js: the service's plan, one question only when the plan asks one,
10
+ // then exactly that plan), and the card then says where it went and what a running session needs; "Approve only"
11
+ // leaves it approved with Install on its card. Ask for changes (the Assistant reads why) or reject. An update is
12
+ // read file by file before it is applied; "Keep mine" leaves the copy as it is until SynaBun ships another version.
13
+
14
+ import { scanFindingText } from './sx-scan.js';
15
+ import { studioErrorText } from './sx-errors.js';
16
+ import { html, setHTML, ICONS, relativeTime, fullTime, plural } from './sx-dom.js';
17
+ import { reviewCardModel, sortReviews, updateTargetKeys } from './sx-review.js';
18
+ import { serviceDiff, invertDiff, lineDiff } from './sx-diff.js';
19
+ import { diffHTML, textDiffModel } from './sx-diff-view.js';
20
+ import { targetWords, keyWords, reviewTargets, installFlow, flowWords, installBusy, acquireInstall, refreshSkillWorkspaces } from './sx-install-flow.js';
21
+
22
+ let _life = null;
23
+ const FILTERS = ['waiting', 'all', 'decided'];
24
+ /** Scan codes whose words carry the number of findings. */
25
+ const SCAN_COUNTED = ['blocked', 'incomplete', 'review', 'findings'];
26
+ /** What an install from a card said, by review id, for as long as the page lives: the card shows it after the reload. */
27
+ const _said = new Map();
28
+
29
+ function authorWords(t, a) {
30
+ if (a.kind === 'owner') return t('skillsStudio.review.author.owner');
31
+ const who = a.kind === 'assistant' ? t('skillsStudio.review.author.assistant') : t('skillsStudio.review.author.agent');
32
+ const bits = [a.provider, a.model].filter(Boolean).join(' · ');
33
+ return bits ? `${who} (${bits})` : who;
34
+ }
35
+
36
+ /**
37
+ * Where a card's install goes, said whole: what the submission named, what of that is not available here and why,
38
+ * where it then goes (never a silent fallback), or that nothing of it is available (then Publish is the way).
39
+ */
40
+ function destinationHTML(ctx, card, dest) {
41
+ const { t } = ctx;
42
+ if (!dest) return '';
43
+ const lines = [];
44
+ if (card.targets.length) lines.push(html`<li>${t('skillsStudio.review.forTargets', { targets: card.targets.map((tg) => targetWords(ctx, tg)).join(', ') })}</li>`);
45
+ for (const m of dest.missing) lines.push(html`<li class="sx-warn-text">${t('skillsStudio.review.targetUnavailable', { target: m.words, reason: m.why })}</li>`);
46
+ if (!dest.targets.length) lines.push(html`<li class="sx-warn-text">${t('skillsStudio.review.noDestination')}</li>`);
47
+ else if (dest.missing.length || !card.targets.length) {
48
+ const why = dest.fallback === 'installed' ? t('skillsStudio.review.fallbackInstalled') : dest.fallback === 'claude' ? t('skillsStudio.review.fallbackDefault') : dest.fallback === 'assistants' ? t('skillsStudio.review.fallbackAssistants') : '';
49
+ lines.push(html`<li>${t('skillsStudio.review.installsFor', { targets: dest.words.join(', ') })}${why ? html` <span class="sx-dim">${why}</span>` : ''}</li>`);
50
+ }
51
+ // One tile per runtime, as Publish shows them: where this card's install goes (Codex and OpenCode are first-class).
52
+ if (dest.targets.length) {
53
+ const picked = (rt) => [...dest.keys].some((k) => String(k).startsWith(`${rt}:`));
54
+ lines.push(html`<li class="sx-dest-tiles" role="group" aria-label="${t('skillsStudio.review.destTilesLabel')}">${['claude', 'codex', 'opencode', 'antigravity'].map((rt) => html`<span class="sx-dest-tile${picked(rt) ? ' is-on' : ''}" title="${picked(rt) ? t('skillsStudio.review.destTileOn', { runtime: t(`skillsStudio.runtime.${rt}`) }) : t('skillsStudio.review.destTileOff', { runtime: t(`skillsStudio.runtime.${rt}`) })}">${picked(rt) ? ICONS.ok : ''}<span>${t(`skillsStudio.runtime.${rt}`)}</span></span>`)}</li>`);
55
+ }
56
+ return html`${lines}`;
57
+ }
58
+
59
+ function cardHTML(ctx, card, focused) {
60
+ const { t, locale } = ctx;
61
+ const installs = card.actions.includes('approve_install') || card.actions.includes('install');
62
+ const review = installs ? (ctx.data?.reviews || []).find((r) => r.reviewId === card.id) : null;
63
+ const row = review ? ctx.rows.find((r) => r.skill?.skillId === review.skillId) || null : null;
64
+ const dest = review ? reviewTargets(ctx, { review, skill: row?.skill || null, row }) : null;
65
+ const canInstall = !!dest && dest.targets.length > 0;
66
+ // An install of this skill on its way from any surface: its buttons are off here too, however the card is drawn again.
67
+ const busy = installs && installBusy(card.skillId);
68
+ const authority = card.authority.map((a) => html`<li>${a.added.length ? t('skillsStudio.review.authorityAdds', { what: t(`skillsStudio.review.authority.${a.kind}`), items: a.added.slice(0, 6).join(', ') }) : ''}${a.removed.length ? t('skillsStudio.review.authorityRemoves', { what: t(`skillsStudio.review.authority.${a.kind}`), items: a.removed.slice(0, 6).join(', ') }) : ''}</li>`);
69
+ return html`<article class="sx-card sx-card--${card.state}${focused ? ' is-focused' : ''}" id="sx-review-${card.id}" aria-labelledby="sx-review-${card.id}-h" tabindex="-1">
70
+ <header class="sx-card-top">
71
+ <h4 class="sx-card-kind" id="sx-review-${card.id}-h"><span class="sx-card-mark" aria-hidden="true">${card.kind === 'new' ? ICONS.spark : ICONS.edit}</span>${t(`skillsStudio.review.kind.${card.kind}`)} · <span class="sx-mono">/${card.installAs || card.name}</span></h4>
72
+ <span class="sx-state-chip sx-state-chip--review-${card.state}">${t(`skillsStudio.review.state.${card.state}`)}</span>
73
+ <span class="sx-spacer"></span>
74
+ <span class="sx-card-from">${authorWords(t, card.author)}${card.createdAt ? html` · <time datetime="${card.createdAt}" title="${fullTime(card.createdAt, locale)}">${relativeTime(card.createdAt, locale)}</time>` : ''}</span>
75
+ </header>
76
+ ${card.notes ? html`<p class="sx-card-notes">${card.notes}</p>` : ''}
77
+ ${card.assumptions.length ? html`<ul class="sx-plain-list sx-card-assumptions">${card.assumptions.map((a) => html`<li>${t('skillsStudio.review.assumed', { text: a })}</li>`)}</ul>` : ''}
78
+ <ul class="sx-facts sx-card-facts">
79
+ ${card.installAs ? html`<li>${t('skillsStudio.review.installsAs')} <span class="sx-mono">/${card.installAs}</span></li>` : ''}
80
+ ${dest ? destinationHTML(ctx, card, dest) : card.targets.length ? html`<li>${t('skillsStudio.review.forTargets', { targets: card.targets.map((tg) => targetWords(ctx, tg)).join(', ') })}</li>` : ''}
81
+ <li class="${card.lint.errors ? 'sx-err-text' : ''}">${card.lint.errors ? ctx.tp('skillsStudio.review.lintErrors', card.lint.errors) : t('skillsStudio.review.lintOk')}${card.lint.warnings ? ` · ${ctx.tp('skillsStudio.review.lintWarnings', card.lint.warnings)}` : ''}</li>
82
+ <li class="sx-scan-${card.scan.sev}">${SCAN_COUNTED.includes(card.scan.code) ? plural(t, `skillsStudio.review.scan.${card.scan.code}`, card.scan.found) : t(`skillsStudio.review.scan.${card.scan.code}`)}</li>
83
+ </ul>
84
+ ${card.scan.findings.length ? html`<details class="sx-details"><summary>${ctx.tp('skillsStudio.review.findings', card.scan.findings.length)}</summary><ul class="sx-findings">${card.scan.findings.map((f) => html`<li class="sx-finding sx-finding--${f.severity}"><span class="sx-finding-sev">${t(`skillsStudio.review.severity.${f.severity === 'info' ? 'low' : f.severity}`)}</span> ${scanFindingText(t, f)}${f.file ? html` <span class="sx-mono sx-dim">${f.file}${f.line ? `:${f.line}` : ''}</span>` : ''}</li>`)}</ul></details>` : ''}
85
+ ${authority.length ? html`<div class="sx-authority"><p class="sx-help">${t('skillsStudio.review.authorityTitle')}</p><ul class="sx-plain-list">${authority}</ul></div>` : ''}
86
+ <details class="sx-details" data-review-diff="${card.id}"><summary>${card.files.length ? ctx.tp('skillsStudio.review.filesChanged', card.files.length) : t('skillsStudio.review.showFiles')}</summary><div class="sx-diff-box" data-diff-box="${card.id}"><p class="sx-help">${t('skillsStudio.review.loadingDiff')}</p></div></details>
87
+ ${card.comments.length ? html`<ul class="sx-plain-list sx-card-comments">${card.comments.map((c) => html`<li><span class="sx-dim">${c.by === 'owner' ? t('skillsStudio.review.author.owner') : t('skillsStudio.review.author.assistant')}:</span> ${c.body}</li>`)}</ul>` : ''}
88
+ ${card.reason ? html`<p class="sx-help">${t('skillsStudio.review.yourNote', { text: card.reason })}</p>` : ''}
89
+ ${saidHTML(ctx, card)}
90
+ ${card.publication.length && !_said.has(card.id) ? html`<ul class="sx-plain-list">${card.publication.map((p) => html`<li class="${p.status === 'committed' ? '' : 'sx-warn-text'}">${keyWords(ctx, p.targetKey)}: ${t(`skillsStudio.publish.result.${['committed', 'skipped', 'needs-confirmation', 'failed'].includes(p.status) ? p.status : 'failed'}`)}${p.error ? ` · ${p.error}` : ''}</li>`)}</ul>` : ''}
91
+ ${card.actions.length ? html`<div class="sx-card-actions" data-card-actions="${card.id}" data-busy="${busy ? '1' : '0'}">
92
+ ${card.actions.includes('approve_install') && canInstall ? html`<button type="button" class="sx-btn sx-btn--primary" data-review="approve_install" data-id="${card.id}"${busy ? ' disabled' : ''}>${ICONS.publish}<span>${busy ? t('skillsStudio.review.installing') : t('skillsStudio.review.approveInstall')}</span></button>` : ''}
93
+ ${card.actions.includes('install') && canInstall ? html`<button type="button" class="sx-btn sx-btn--primary" data-review="install" data-id="${card.id}"${busy ? ' disabled' : ''}>${ICONS.publish}<span>${busy ? t('skillsStudio.review.installing') : t('skillsStudio.review.installNow')}</span></button>` : ''}
94
+ ${card.actions.includes('approve') ? html`<button type="button" class="sx-btn${canInstall ? '' : ' sx-btn--primary'}" data-review="approve" data-id="${card.id}"${busy ? ' disabled' : ''}>${canInstall ? t('skillsStudio.review.approveOnly') : t('skillsStudio.review.approveOnlyPrimary')}</button>` : ''}
95
+ ${card.actions.includes('request_changes') ? html`<button type="button" class="sx-btn" data-review="request_changes" data-id="${card.id}"${busy ? ' disabled' : ''}>${t('skillsStudio.review.requestChanges')}</button>` : ''}
96
+ ${card.actions.includes('reject') ? html`<button type="button" class="sx-btn sx-btn--ghost sx-btn--danger" data-review="reject" data-id="${card.id}"${busy ? ' disabled' : ''}>${t('skillsStudio.review.reject')}</button>` : ''}
97
+ ${card.actions.includes('install') ? html`<button type="button" class="sx-btn${canInstall ? ' sx-btn--ghost' : ' sx-btn--primary'}" data-review="publish" data-id="${card.id}"${busy ? ' disabled' : ''}>${t('skillsStudio.review.openPublish')}</button>` : ''}
98
+ <button type="button" class="sx-btn sx-btn--ghost" data-review="open" data-id="${card.id}">${t('skillsStudio.review.open')}</button>
99
+ </div>` : ''}
100
+ </article>`;
101
+ }
102
+
103
+ /** What the last install from this card said: where it went and what a running session needs, or why it did not go. */
104
+ function saidHTML(ctx, card) {
105
+ const said = _said.get(card.id);
106
+ if (!said) return '';
107
+ return html`<div class="sx-outcome sx-outcome--${said.kind}" role="status" data-card-said="${card.id}">
108
+ <p class="sx-outcome-line">${said.kind === 'info' ? ICONS.ok : ICONS.warn}<strong>${said.line}</strong></p>
109
+ ${said.lines.length ? html`<ul class="sx-plain-list sx-outcome-notes">${said.lines.map((x) => html`<li>${x}</li>`)}</ul>` : ''}
110
+ ${said.kind !== 'info' ? html`<p class="sx-help">${ctx.t('skillsStudio.review.notInstalledHelp')}</p>` : ''}
111
+ </div>`;
112
+ }
113
+
114
+ function updateCardHTML(ctx, u) {
115
+ const { t } = ctx;
116
+ const where = [...new Set([...u.legacyCopies.map((c) => c.path), ...(u.installations.length ? [plural(t, 'skillsStudio.review.libraryInstalls', u.installations.length)] : [])])];
117
+ const hid = `sx-update-${String(u.name).replace(/[^A-Za-z0-9_-]/g, '_')}-h`;
118
+ return html`<article class="sx-card sx-card--update" aria-labelledby="${hid}">
119
+ <header class="sx-card-top">
120
+ <h4 class="sx-card-kind" id="${hid}"><span class="sx-card-mark" aria-hidden="true">${ICONS.update}</span>${t('skillsStudio.review.kind.update')} · <span class="sx-mono">/${u.label}</span></h4>
121
+ <span class="sx-spacer"></span><span class="sx-card-from">${t('skillsStudio.review.fromSynabun')}</span>
122
+ </header>
123
+ <p class="sx-card-notes">${t('skillsStudio.review.updateBody', { name: u.label })}</p>
124
+ <ul class="sx-facts">${where.map((w) => html`<li class="sx-mono">${w}</li>`)}${u.runtimes.length ? html`<li>${t('skillsStudio.review.updateRuntimes', { runtimes: u.runtimes.map((r) => t(`skillsStudio.runtime.${r}`)).join(', ') })}</li>` : ''}</ul>
125
+ <details class="sx-details" data-update-diff="${u.key}"><summary>${t('skillsStudio.review.reviewChanges')}</summary><div class="sx-diff-box" data-update-box="${u.key}"><p class="sx-help">${t('skillsStudio.review.loadingDiff')}</p></div></details>
126
+ ${ctx.guest ? '' : html`<div class="sx-card-actions">
127
+ <button type="button" class="sx-btn sx-btn--primary" data-update="apply" data-key="${u.key}">${t('skillsStudio.review.update')}</button>
128
+ <button type="button" class="sx-btn sx-btn--ghost" data-update="keep" data-key="${u.key}">${t('skillsStudio.review.keepMine')}</button>
129
+ </div>`}
130
+ </article>`;
131
+ }
132
+
133
+ /** Draw the Review place. */
134
+ /** Review is left for another place: its listeners on the shared main area end. */
135
+ export function leaveReviewPlace() { _life?.abort(); _life = null; }
136
+
137
+ /** The window closed: what the cards said of their installs goes with it. */
138
+ export function forgetReviewOutcomes() { _said.clear(); }
139
+
140
+ export function renderReviewPlace(host, ctx) {
141
+ const { t } = ctx;
142
+ _life?.abort();
143
+ _life = new AbortController();
144
+ const signal = _life.signal;
145
+ const filter = ctx.lib.reviewFilter || 'waiting';
146
+ const { open, decided } = sortReviews(ctx.data?.reviews || []);
147
+ // A submission installed from its card stays in the waiting list, saying where it went, until the window closes.
148
+ const justInstalled = decided.filter((r) => _said.has(r.reviewId));
149
+ const cards = (filter === 'decided' ? decided : filter === 'all' ? [...open, ...decided] : [...open, ...justInstalled]).map((r) => reviewCardModel(r, { guest: ctx.guest }));
150
+ const updates = filter === 'decided' ? [] : ctx.updates;
151
+ const focus = ctx.reviewFocus;
152
+ setHTML(host, html`<section class="sx-place sx-place--review" aria-labelledby="sx-review-title">
153
+ <div class="sx-place-head">
154
+ <h3 class="sx-place-title" id="sx-review-title" tabindex="-1" data-autofocus>${t('skillsStudio.review.title')}</h3>
155
+ <p class="sx-place-sub">${t('skillsStudio.review.subtitle')}</p>
156
+ <span class="sx-spacer"></span>
157
+ <div class="sx-seg" role="group" aria-label="${t('skillsStudio.review.filterLabel')}">${FILTERS.map((f) => html`<button type="button" data-review-filter="${f}" aria-pressed="${filter === f ? 'true' : 'false'}">${t(`skillsStudio.review.filter.${f}`)}</button>`)}</div>
158
+ </div>
159
+ <div class="sx-place-scroll">
160
+ <div class="sx-cards">
161
+ ${updates.length ? html`<h4 class="sx-section-title">${ctx.tp('skillsStudio.review.updatesTitle', updates.length)}</h4>${updates.map((u) => updateCardHTML(ctx, u))}` : ''}
162
+ ${cards.length ? html`<h4 class="sx-section-title">${filter === 'decided' ? t('skillsStudio.review.decidedTitle') : ctx.tp('skillsStudio.review.waitingTitle', cards.length)}</h4>${cards.map((c) => cardHTML(ctx, c, c.id === focus))}` : ''}
163
+ ${!cards.length && !updates.length ? html`<div class="sx-empty"><span class="sx-placeholder-icon" aria-hidden="true">${ICONS.review}</span><h4 class="sx-empty-title">${t('skillsStudio.review.emptyTitle')}</h4><p>${t('skillsStudio.review.emptyBody')}</p></div>` : ''}
164
+ </div>
165
+ </div>
166
+ </section>`);
167
+ if (focus) { ctx.clearReviewFocus(); requestAnimationFrame(() => { const el = host.querySelector(`#sx-review-${CSS.escape(focus)}`); el?.scrollIntoView({ block: 'nearest' }); el?.focus(); }); }
168
+ host.addEventListener('click', (e) => onClick(e, host, ctx), { signal });
169
+ host.addEventListener('toggle', (e) => {
170
+ const d = e.target;
171
+ if (!d.open) return;
172
+ if (d.dataset?.reviewDiff) loadReviewDiff(host, ctx, d.dataset.reviewDiff);
173
+ if (d.dataset?.updateDiff) loadUpdateDiff(host, ctx, d.dataset.updateDiff);
174
+ }, { signal, capture: true });
175
+ }
176
+
177
+ async function loadReviewDiff(host, ctx, id) {
178
+ const { t } = ctx;
179
+ const box = host.querySelector(`[data-diff-box="${CSS.escape(id)}"]`);
180
+ const review = (ctx.data?.reviews || []).find((r) => r.reviewId === id);
181
+ if (!box || !review || box.dataset.loaded) return;
182
+ box.dataset.loaded = '1';
183
+ const card = reviewCardModel(review);
184
+ const gen = ctx.gen();
185
+ try {
186
+ if (card.fromRevisionId && card.revisionId) {
187
+ const res = await ctx.api.fetchSkillsDiff(card.fromRevisionId, card.revisionId);
188
+ if (!ctx.alive(gen) || !box.isConnected) return;
189
+ setHTML(box, diffHTML(t, serviceDiff(res.diff)));
190
+ } else if (card.revisionId) {
191
+ // A new skill: every file is new, each shown as added.
192
+ const rev = (await ctx.api.fetchSkillsRevision(card.revisionId)).revision;
193
+ const files = [];
194
+ for (const f of (rev?.manifest?.files || []).slice(0, 20)) {
195
+ if (f.encoding !== 'utf-8') { files.push({ path: f.path, status: 'added', hunks: [], added: 0, removed: 0, note: 'binary' }); continue; }
196
+ const got = await ctx.api.fetchSkillsRevisionFile(card.revisionId, f.path);
197
+ const d = lineDiff('', got.content ?? '');
198
+ files.push({ ...(textDiffModel(f.path, d).files[0] || { path: f.path, hunks: [], added: 0, removed: 0 }), status: 'added' });
199
+ }
200
+ if (!ctx.alive(gen) || !box.isConnected) return;
201
+ setHTML(box, diffHTML(t, { files }, { open: 2 }));
202
+ }
203
+ } catch (err) { if (box.isConnected) setHTML(box, html`<p class="sx-note sx-note--warn" role="alert">${t('skillsStudio.review.diffFailed', { error: studioErrorText(ctx.t, err) })}</p>`); }
204
+ }
205
+
206
+ /** The changes an update makes, from the installed copy's side (what updating it would change). */
207
+ async function updateDiffModel(ctx, row) {
208
+ const upstream = row.update?.upstreamRevisionId;
209
+ if (!upstream) return { model: null, partial: false };
210
+ const deploy = (row.skill?.installations || []).find((i) => row.update.installations.includes(i.installationId));
211
+ if (deploy) return { model: serviceDiff((await ctx.api.fetchSkillsDiff(deploy.sourceRevisionId, upstream)).diff), partial: false };
212
+ const copy = row.update.legacyCopies[0];
213
+ if (!copy) return { model: null, partial: false };
214
+ if (ctx.caps.has('compare-folder')) return { model: serviceDiff(invertDiff((await ctx.api.fetchSkillsCompare(upstream, copy.artifactId)).diff)), partial: false };
215
+ // An older server: SKILL.md alone, the copy's text against SynaBun's.
216
+ const entry = (await ctx.api.fetchSkillsCatalogEntry(copy.artifactId)).entry;
217
+ const shipped = await ctx.api.fetchSkillsRevisionFile(upstream, 'SKILL.md');
218
+ return { model: textDiffModel('SKILL.md', lineDiff(entry.text || '', shipped.content || '')), partial: true };
219
+ }
220
+
221
+ async function loadUpdateDiff(host, ctx, key) {
222
+ const { t } = ctx;
223
+ const box = host.querySelector(`[data-update-box="${CSS.escape(key)}"]`);
224
+ const row = ctx.rows.find((r) => r.key === key);
225
+ if (!box || !row || box.dataset.loaded) return;
226
+ box.dataset.loaded = '1';
227
+ const gen = ctx.gen();
228
+ try {
229
+ const { model, partial } = await updateDiffModel(ctx, row);
230
+ if (!ctx.alive(gen) || !box.isConnected) return;
231
+ setHTML(box, html`${partial ? html`<p class="sx-note" role="note">${t('skillsStudio.review.partialDiff')}</p>` : ''}${diffHTML(t, model)}`);
232
+ } catch (err) { if (box.isConnected) setHTML(box, html`<p class="sx-note sx-note--warn" role="alert">${t('skillsStudio.review.diffFailed', { error: studioErrorText(ctx.t, err) })}</p>`); }
233
+ }
234
+
235
+ /**
236
+ * An update of a shipped skill, from its workspace banner: read it (`review`), or go install SynaBun's version
237
+ * where the old copy is (`apply`: Publish, with those targets picked; the plan shows what it replaces, backed up).
238
+ */
239
+ export async function reviewUpdateFlow(ctx, row, mode) {
240
+ const { t } = ctx;
241
+ if (mode === 'review') {
242
+ const box = document.createElement('div');
243
+ box.className = 'sx-diff-box';
244
+ box.textContent = t('skillsStudio.review.loadingDiff');
245
+ const shown = ctx.ask({ title: t('skillsStudio.review.updateDiffTitle', { name: row.folderName }), extra: box, wide: true, buttons: [{ id: 'close', label: t('skillsStudio.dialog.close'), kind: 'primary' }], cancelId: 'close' });
246
+ try {
247
+ const { model, partial } = await updateDiffModel(ctx, row);
248
+ if (box.isConnected) setHTML(box, html`${partial ? html`<p class="sx-note" role="note">${t('skillsStudio.review.partialDiff')}</p>` : ''}${diffHTML(t, model)}`);
249
+ } catch (err) { if (box.isConnected) box.textContent = t('skillsStudio.review.diffFailed', { error: studioErrorText(ctx.t, err) }); }
250
+ await shown;
251
+ return;
252
+ }
253
+ // What the update replaces is ticked: the folder each old copy is in, the target of each older installation (never
254
+ // a second copy for a runtime that reads the first one). Handed to openRow, which starts Publish over with it before
255
+ // Publish is drawn (set afterwards, it raced the reading Publish had already begun: "Cannot set properties of null").
256
+ const update = row.update;
257
+ const installations = row.skill?.installations || [];
258
+ const preset = (pub, targets) => { pub.checked = new Set(updateTargetKeys(update, installations, targets)); };
259
+ await ctx.openRow(row.key, { tab: 'publish', revisionId: update?.upstreamRevisionId || null, preset });
260
+ }
261
+
262
+ async function askReason(ctx, { title, prompt, required }) {
263
+ const { t } = ctx;
264
+ const wrap = document.createElement('div');
265
+ wrap.className = 'sx-dlg-form';
266
+ const label = document.createElement('label');
267
+ label.className = 'sx-field-label';
268
+ label.htmlFor = 'sx-reason';
269
+ label.textContent = prompt;
270
+ const area = document.createElement('textarea');
271
+ area.id = 'sx-reason';
272
+ area.className = 'sx-input sx-textarea';
273
+ area.rows = 4;
274
+ area.maxLength = 4000;
275
+ const err = document.createElement('p');
276
+ err.className = 'ss-dlg-error';
277
+ err.setAttribute('role', 'alert');
278
+ wrap.append(label, area, err);
279
+ setTimeout(() => area.focus(), 0);
280
+ const answer = await ctx.ask({
281
+ title, extra: wrap,
282
+ buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'ok', label: t('skillsStudio.review.send'), kind: 'primary' }],
283
+ focus: 'cancel',
284
+ accept: (id) => { if (id === 'ok' && required && !area.value.trim()) { err.textContent = t('skillsStudio.review.reasonRequired'); return false; } return true; },
285
+ });
286
+ return answer === 'ok' ? area.value.trim() : null;
287
+ }
288
+
289
+ async function onClick(e, host, ctx) {
290
+ const { t } = ctx;
291
+ const filterBtn = e.target.closest?.('[data-review-filter]');
292
+ if (filterBtn) { ctx.lib.reviewFilter = filterBtn.dataset.reviewFilter; ctx.render(); host.querySelector(`[data-review-filter="${filterBtn.dataset.reviewFilter}"]`)?.focus(); return; }
293
+ const upd = e.target.closest?.('[data-update]');
294
+ if (upd) {
295
+ const row = ctx.rows.find((r) => r.key === upd.dataset.key);
296
+ if (!row) return;
297
+ if (upd.dataset.update === 'keep') { ctx.keepUpdate(row.folderName, row.update?.upstreamRevisionId); ctx.toast(t('skillsStudio.toast.keptMine', { name: row.folderName })); ctx.render(); }
298
+ else reviewUpdateFlow(ctx, row, 'apply');
299
+ return;
300
+ }
301
+ const btn = e.target.closest?.('[data-review]');
302
+ if (!btn || btn.disabled) return;
303
+ const review = (ctx.data?.reviews || []).find((r) => r.reviewId === btn.dataset.id);
304
+ if (!review) return;
305
+ const card = reviewCardModel(review);
306
+ const action = btn.dataset.review;
307
+ const row = ctx.rows.find((r) => r.skill?.skillId === review.skillId);
308
+ if (action === 'open') { if (row) ctx.openRow(row.key, { tab: 'versions' }); return; }
309
+ if (action === 'publish') { if (row) ctx.openRow(row.key, { tab: 'publish', revisionId: card.revisionId, reviewId: card.id }); return; }
310
+ if (action === 'install' || action === 'approve_install') { await installFromCard(ctx, host, card, review, btn, { approve: action === 'approve_install' }); return; }
311
+ if (installBusy(review.skillId) && action !== 'open') { ctx.toast(t('skillsStudio.publish.busyElsewhere'), 'error'); return; }
312
+ let reason = '';
313
+ if (action !== 'approve') {
314
+ reason = await askReason(ctx, {
315
+ title: t(action === 'reject' ? 'skillsStudio.review.rejectTitle' : 'skillsStudio.review.changesTitle', { name: card.name }),
316
+ prompt: t(action === 'reject' ? 'skillsStudio.review.rejectPrompt' : 'skillsStudio.review.changesPrompt'),
317
+ required: action === 'request_changes',
318
+ });
319
+ if (reason === null) return;
320
+ }
321
+ const gen = ctx.gen();
322
+ btn.disabled = true;
323
+ try {
324
+ await ctx.api.decideSkillsReview(card.id, action, reason);
325
+ if (!ctx.alive(gen)) return;
326
+ ctx.toast(t(`skillsStudio.review.decided.${action}`, { name: card.name }));
327
+ await ctx.reload();
328
+ } catch (err) {
329
+ if (!ctx.alive(gen)) return;
330
+ btn.disabled = false;
331
+ ctx.toast(t('skillsStudio.review.decideFailed', { error: studioErrorText(ctx.t, err) }), 'error');
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Approve (when asked) and install to the targets the submission named, in one go; then the card says where it went
337
+ * and what a running session needs, or why it did not go. Nothing is asked unless the plan asks it.
338
+ */
339
+ async function installFromCard(ctx, host, card, review, btn, { approve }) {
340
+ const { t } = ctx;
341
+ const row = ctx.rows.find((r) => r.skill?.skillId === review.skillId) || null;
342
+ // Where it goes is decided before anything is approved: nothing of what it named available here means no install
343
+ // (and no approval from this button), never an install elsewhere.
344
+ const dest = reviewTargets(ctx, { review, skill: row?.skill || null, row });
345
+ if (!dest.targets.length) { ctx.toast(t('skillsStudio.review.noDestination'), 'error'); return; }
346
+ // One install of a skill at a time, from whichever surface (the lock covers the approval too, and is let go
347
+ // however this ends); while it is held every card and banner of the skill shows it as on its way.
348
+ const release = acquireInstall(review.skillId);
349
+ if (!release) { ctx.toast(t('skillsStudio.publish.busyElsewhere'), 'error'); return; }
350
+ const gen = ctx.gen();
351
+ let out = null;
352
+ try {
353
+ if (approve) {
354
+ try { await ctx.api.decideSkillsReview(card.id, 'approve', ''); } catch (err) {
355
+ if (ctx.alive(gen)) ctx.toast(t('skillsStudio.review.decideFailed', { error: studioErrorText(ctx.t, err) }), 'error');
356
+ return;
357
+ }
358
+ if (!ctx.alive(gen)) return;
359
+ }
360
+ out = await installFlow(ctx, { revisionId: card.revisionId, targets: dest.targets, reviewId: card.id });
361
+ } finally { release(); }
362
+ if (!ctx.alive(gen) || out.status === 'gone') return;
363
+ const words = flowWords(ctx, out);
364
+ if (words) { _said.set(card.id, words); ctx.toast(words.line, words.kind); ctx.announce(words.line); }
365
+ else if (approve) ctx.toast(t('skillsStudio.review.decided.approve', { name: card.name }));
366
+ // The skill's open workspace reads it again: its header pill, banners and Publish say what the Library row says.
367
+ if (out.status === 'done') await refreshSkillWorkspaces(ctx, review.skillId, { resetPublish: true });
368
+ await ctx.reload();
369
+ }
@@ -0,0 +1,148 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: the Review place, as data
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Review holds what waits for the owner (owner decision 2026-10-22: nothing the Assistant makes installs by itself):
6
+ // - submissions of the Review queue (lib/skills/reviews.js): a new skill or a change, from the SynaBun Assistant
7
+ // or another agent, with the evidence the service computed for exactly its bytes (the checks per target, the
8
+ // scan, what it can do that the approved revision could not, the files it changes, the name it installs as);
9
+ // - updates from SynaBun: a skill this app version ships in a newer version than the copy that is installed
10
+ // (an installation of an older revision, or a folder of its name whose SKILL.md is not the shipped one).
11
+ // Cards are codes and data; the view words them (`skillsStudio.review.*`). An update the owner chose to keep as it
12
+ // is ("Keep mine") stays out of the list until SynaBun ships yet another version. No DOM and no imports.
13
+
14
+ import { revealHidden } from '../ss-unicode.js';
15
+
16
+ export const OPEN_STATES = Object.freeze(['pending', 'changes_requested', 'approved', 'partial', 'publish_failed']);
17
+ export const DECIDED_STATES = Object.freeze(['published', 'rejected', 'withdrawn']);
18
+ const AUTHORITY_KINDS = ['scripts', 'binaries', 'dynamicContext', 'hooks', 'toolGrants', 'domains', 'sensitive', 'persistentWrites', 'installers'];
19
+
20
+ const list = (v) => (Array.isArray(v) ? v : []);
21
+ const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
22
+
23
+ /** What a revision can do that the approved one could not: one entry per kind, with what was added and removed. */
24
+ export function authorityRows(delta) {
25
+ const out = [];
26
+ for (const [kind, change] of Object.entries(delta?.byKind || {})) {
27
+ if (!AUTHORITY_KINDS.includes(kind)) continue;
28
+ const added = list(change?.added).map(String);
29
+ const removed = list(change?.removed).map(String);
30
+ if (added.length || removed.length) out.push({ kind, added, removed });
31
+ }
32
+ return out;
33
+ }
34
+
35
+ /** The scan of a submission, as a code and a severity. */
36
+ export function scanState(scan) {
37
+ if (!scan) return { code: 'not-scanned', sev: 'warn', found: 0 };
38
+ const found = Object.values(scan.counts || {}).reduce((n, c) => n + num(c), 0);
39
+ if (scan.status === 'error') return { code: 'error', sev: 'err', found };
40
+ if (scan.gate === 'blocked') return { code: 'blocked', sev: 'err', found };
41
+ if (scan.status === 'no_findings') return { code: 'clean', sev: 'ok', found: 0 };
42
+ if (scan.status === 'incomplete') return { code: 'incomplete', sev: 'warn', found };
43
+ return { code: scan.gate === 'review' ? 'review' : 'findings', sev: scan.gate === 'review' ? 'warn' : 'info', found };
44
+ }
45
+
46
+ /**
47
+ * What the owner can do with a submission now. Approving installs: a pending submission's first action is
48
+ * `approve_install` (approve, then install to the targets it named, in one go); `approve` alone stays. One approved
49
+ * but not installed (or installed in part, or whose install failed) offers `install`.
50
+ */
51
+ export function reviewActions(review, { guest = false } = {}) {
52
+ if (guest) return [];
53
+ if (review?.state === 'pending') return ['approve_install', 'approve', 'request_changes', 'reject'];
54
+ if (review?.state === 'changes_requested') return ['reject'];
55
+ if (['approved', 'partial', 'publish_failed'].includes(review?.state)) return ['install'];
56
+ return [];
57
+ }
58
+
59
+ /**
60
+ * One submission as a card.
61
+ * @returns {object}
62
+ */
63
+ export function reviewCardModel(review, { guest = false } = {}) {
64
+ const ev = review?.evidence || {};
65
+ const lintErrors = Object.values(ev.lint?.profiles || {}).reduce((n, p) => n + num(p?.errors), 0);
66
+ const lintWarnings = Object.values(ev.lint?.profiles || {}).reduce((n, p) => n + num(p?.warnings), 0);
67
+ const author = review?.author || {};
68
+ return {
69
+ id: review?.reviewId,
70
+ skillId: review?.skillId || null,
71
+ name: revealHidden(String(review?.skillName || review?.skillId || '')),
72
+ kind: review?.baseApprovedRevisionId ? 'change' : 'new',
73
+ state: review?.state || 'pending',
74
+ author: { kind: author.kind === 'owner' ? 'owner' : author.kind === 'assistant' ? 'assistant' : 'agent', provider: author.provider || null, model: author.model || null, runId: author.runId || null },
75
+ createdAt: review?.createdAt || null,
76
+ decidedAt: review?.decidedAt || null,
77
+ notes: String(review?.releaseNotes || ''),
78
+ assumptions: list(review?.assumptions).map(String).slice(0, 20),
79
+ targets: list(review?.targets).map((t) => ({ runtime: t?.runtime, scope: t?.scope || 'user', projectPath: t?.projectPath || null, accountId: t?.accountId || null })),
80
+ installAs: typeof ev.installName === 'string' && ev.installName ? revealHidden(ev.installName) : null,
81
+ lint: { errors: lintErrors, warnings: lintWarnings, resourceProblems: num(ev.lint?.resourceProblems) },
82
+ scan: { ...scanState(ev.scan), findings: list(ev.scan?.findings).slice(0, 10).map((f) => ({ ...f, severity: ['critical', 'high', 'medium', 'low', 'info'].includes(f?.severity) ? f.severity : 'low', message: String(f?.message ?? ''), file: f?.file || null, line: f?.line || null, ruleId: f?.ruleId || null })) },
83
+ authority: authorityRows(ev.authority),
84
+ files: list(ev.diff?.files).map((f) => ({ path: String(f.path ?? ''), status: f.status })),
85
+ fromRevisionId: ev.diff?.from?.revisionId || review?.baseApprovedRevisionId || null,
86
+ revisionId: review?.submittedRevisionId || null,
87
+ reason: review?.decisionReason || '',
88
+ comments: list(review?.comments).slice(-5).map((c) => ({ by: c?.by?.kind === 'owner' ? 'owner' : (c?.by?.kind || 'agent'), body: String(c?.body ?? ''), at: c?.at || null })),
89
+ publication: list(review?.publication?.targetResults).slice(-12).map((r) => ({ targetKey: r.targetKey, status: r.status, error: r.error || null })),
90
+ actions: reviewActions(review, { guest }),
91
+ };
92
+ }
93
+
94
+ /** Open submissions first (pending, then the rest), newest first in each; decided ones apart. */
95
+ export function sortReviews(reviews) {
96
+ const rank = { pending: 0, changes_requested: 1, approved: 2, partial: 3, publish_failed: 4 };
97
+ const open = list(reviews).filter((r) => OPEN_STATES.includes(r?.state))
98
+ .sort((a, b) => (rank[a.state] - rank[b.state]) || String(b.createdAt || '').localeCompare(String(a.createdAt || '')));
99
+ const decided = list(reviews).filter((r) => DECIDED_STATES.includes(r?.state))
100
+ .sort((a, b) => String(b.updatedAt || b.decidedAt || '').localeCompare(String(a.updatedAt || a.decidedAt || '')));
101
+ return { open, decided };
102
+ }
103
+
104
+ /**
105
+ * The updates SynaBun ships, from the Library rows (sx-library.js marks a bundled row's `update`), less the ones the
106
+ * owner keeps as they are for that upstream revision.
107
+ * @param {object[]} rows
108
+ * @param {Record<string, string>} [kept] skill name → the upstream revision id the owner said "Keep mine" to
109
+ */
110
+ export function updateCards(rows, kept = {}) {
111
+ return list(rows).filter((r) => r?.update && r.source === 'synabun' && r.kind === 'skill')
112
+ .filter((r) => !(r.update.upstreamRevisionId && kept?.[r.folderName] === r.update.upstreamRevisionId))
113
+ .map((r) => ({
114
+ key: r.key,
115
+ name: r.folderName,
116
+ label: r.label || revealHidden(r.folderName || ''),
117
+ skillId: r.skill?.skillId || null,
118
+ upstreamRevisionId: r.update.upstreamRevisionId,
119
+ kind: r.update.kind,
120
+ installations: r.update.installations,
121
+ legacyCopies: r.update.legacyCopies,
122
+ runtimes: [...new Set([...(r.update.legacyCopies || []).flatMap((c) => c.runtimes || []), ...((r.skill?.installations || []).filter((i) => r.update.installations.includes(i.installationId)).map((i) => i.runtime))])],
123
+ }));
124
+ }
125
+
126
+ /**
127
+ * The install targets an update of a shipped skill replaces: the folder each old copy is in (the target whose root
128
+ * holds it), and the target of each older installation. Never every runtime that reads a copy: OpenCode reads
129
+ * Claude Code's folder too, and a second copy in its own folder would be listed twice.
130
+ * @param {object} update a row's `update` (sx-library.js)
131
+ * @param {object[]} installations the library skill's installations (GET /skills)
132
+ * @param {{ key: string, path: string|null, runtime: string, scope: string, accountId: string, projectPath: string|null }[]} targets sx-subject.js installTargets()
133
+ * @returns {string[]} target keys
134
+ */
135
+ export function updateTargetKeys(update, installations, targets) {
136
+ const trim = (p) => String(p || '').replace(/[\\/]+$/, '');
137
+ const parent = (p) => trim(p).replace(/[\\/][^\\/]*$/, '');
138
+ const folders = new Set(list(update?.legacyCopies).map((c) => parent(c?.path)).filter(Boolean));
139
+ const ids = new Set(list(update?.installations));
140
+ const keyOf = (i) => (i?.scope === 'project' ? `${i.runtime}:project:${i.projectPath}` : `${i?.runtime}:user:${i?.accountId || 'default'}`);
141
+ const installed = new Set(list(installations).filter((i) => ids.has(i?.installationId)).map(keyOf));
142
+ return list(targets).filter((tg) => !tg.off && ((tg.path && folders.has(trim(tg.path))) || installed.has(tg.key))).map((tg) => tg.key);
143
+ }
144
+
145
+ /** How many things wait in Review: open submissions that need the owner, and updates. */
146
+ export function reviewCount(reviews, updates = []) {
147
+ return list(reviews).filter((r) => r?.state === 'pending' || ['approved', 'partial', 'publish_failed'].includes(r?.state)).length + list(updates).length;
148
+ }
@@ -0,0 +1,35 @@
1
+ // Scan evidence belongs to the exact immutable bytes. Missing/stale/partial evidence never becomes a clean badge.
2
+ export const SCAN_SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
3
+ export function scanStatus(scan, digest) {
4
+ const counts = Object.fromEntries(SCAN_SEVERITIES.map((s) => [s, Number.isSafeInteger(scan?.counts?.[s]) && scan.counts[s] >= 0 ? scan.counts[s] : (scan?.findings || []).filter((f) => f.severity === s).length]));
5
+ const count = Object.values(counts).reduce((a, b) => a + b, 0);
6
+ let code = 'notScanned';
7
+ if (scan && scan.digest === digest) {
8
+ if (scan.status === 'error') code = 'error';
9
+ else if (scan.coverage?.complete === false || ['incomplete', 'skipped'].includes(scan.status)) code = 'incomplete';
10
+ else if (scan.coverage?.complete === true && ['no_findings', 'findings'].includes(scan.status)) code = count || scan.status === 'findings' ? 'findings' : 'clean';
11
+ }
12
+ return { code, counts, count, passed: ['clean', 'findings'].includes(code) && scan?.gate === 'allowed' && !['critical', 'high', 'medium'].some((s) => counts[s]) };
13
+ }
14
+ export function scanText(t, scan, digest) {
15
+ const status = scanStatus(scan, digest);
16
+ const counts = SCAN_SEVERITIES.filter((s) => status.counts[s]).map((s) => t('skillsStudio.scan.severityCount', { severity: t(`skillsStudio.scan.severity.${s}`), count: status.counts[s] })).join(', ');
17
+ return t('skillsStudio.scan.summary', { status: t(`skillsStudio.scan.status.${status.code}`), count: status.count, detail: counts ? ` · ${counts}` : '' });
18
+ }
19
+
20
+ export function scanReasonText(t, reason) {
21
+ const keys = { 'asset-invalid-structure': 'assetInvalid', 'asset-script-content': 'assetScript', 'asset-too-large': 'assetTooLarge', 'asset-decompression-budget': 'assetBudget', 'shell-analysis-limit': 'shellLimit' };
22
+ return keys[reason] ? t(`skillsStudio.scan.reasons.${keys[reason]}`) : reason;
23
+ }
24
+ export function scanFindingText(t, finding) {
25
+ if (finding.reason) return scanReasonText(t, finding.reason);
26
+ if (finding.ruleId === 'S-09-asset-command') return t('skillsStudio.scan.assetCommand');
27
+ if (finding.ruleId === 'S-08-download-run') {
28
+ const { download, run } = finding.locations || {};
29
+ if (!download?.file || !download?.line || !run?.file || !run?.line) return finding.message;
30
+ return t('skillsStudio.scan.downloadRun', { download: `${download.file}:${download.line}`, run: `${run.file}:${run.line}` });
31
+ }
32
+ if (finding.ruleId === 'S-09-asset-exec') return t('skillsStudio.scan.assetExecution');
33
+ if (finding.ruleId === 'S-12-hidden-chars' && finding.total) return t('skillsStudio.scan.hiddenTotal', { count: finding.total, listed: finding.samples?.length || 0 }) + (finding.samples?.length ? ` · ${finding.samples.map((s) => `${s.file}:${s.line}:${s.column} ${s.label}`).join('; ')}` : finding.label ? `: ${finding.label}` : '');
34
+ return finding.message;
35
+ }