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,690 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: the window and its places
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The Studio's shell, in Automation Studio's manner (one view machine, one render switch): a window with a rail of
6
+ // three places (Library · Review · Create) and, from a place, one workspace for the skill, agent or command opened
7
+ // (Edit · Test · Versions · Publish). Built on the Skills Service (lib/skills/api-v2.js): the catalog of every
8
+ // runtime folder, the managed library, its drafts, revisions, install plans and the Review queue.
9
+ //
10
+ // The window is non-modal unless the owner dims the background (then the page under it waits, and the keyboard
11
+ // stays in the window); it can be docked beside the side panels so a conversation stays usable next to it; it
12
+ // opens where it was left, at the size it had. Keys reach it only while the focus is in it: Escape closes the
13
+ // topmost thing that is open (a question, a menu), leaves a text field, and only then closes the window, which
14
+ // asks first when something is unsaved. Every answer that arrives after the window closed (or another workspace
15
+ // opened) is dropped. Strings are `skillsStudio.*` (i18n/en.json, pt-BR.json).
16
+
17
+ import { studioErrorText } from './sx-errors.js';
18
+ import { t, tp, getLocale } from '../../i18n.js';
19
+ import { storage } from '../../storage.js';
20
+ import { emit } from '../../state.js';
21
+ import { isGuest, hasPermission, showGuestToast } from '../../ui-sync.js';
22
+ import { KEYS } from '../../constants.js';
23
+ import { createUnloadGuard } from '../../editor/editor-safety.js';
24
+ import { askInPanel, dialogOpen, closeDialog } from '../ss-dialog.js';
25
+ import { closeDropdowns } from '../../cc-dropdown.js';
26
+ import * as api from '../../api.js';
27
+ import { readWholeCatalog, buildRows, countRows, projectsOf, keepSelection } from './sx-library.js';
28
+ import { applyFixState, readDismissed, addDismissed, removeDismissed, DISMISSED_KEY } from './sx-fixes.js';
29
+ import { initialRect, parseOpen } from './sx-geometry.js';
30
+ import { frameRect } from '../../app-frame.js';
31
+ import { updateCards, reviewCount } from './sx-review.js';
32
+ import { newWorkspace, dirtyTabs, saveTab, dropDraft, backups, keptDrafts } from './sx-docs.js';
33
+ import { html, setHTML, ICONS, idempotencyKey, inTextField, focusableIn, trapIndex } from './sx-dom.js';
34
+ import { renderLibraryPlace, focusLibrary, leaveLibraryPlace } from './sx-library-view.js';
35
+ import { renderReviewPlace, leaveReviewPlace, forgetReviewOutcomes } from './sx-review-view.js';
36
+ import { renderCreatePlace, startImport, leaveCreatePlace, openCreation } from './sx-create-view.js';
37
+ import { importFile } from './sx-import.js';
38
+ import { renderWorkspace, closeWorkspaceMenus, workspaceShortcut, disposeWorkspace, disposeStudio, refreshWorkspaceChrome } from './sx-workspace-view.js';
39
+ import { refreshPublishBusy } from './sx-publish-view.js';
40
+ import { leaveTestTab } from './sx-test-view.js';
41
+ import { renderOperationsPlace, leaveOperationsPlace } from './sx-operations-view.js';
42
+ import { onInstallBusy } from './sx-install-flow.js';
43
+
44
+ const PANEL_ID = 'skills-studio-panel';
45
+ const DOCK_KEY = 'skills-studio-docked';
46
+ const DIM_KEY = 'skills-studio-dim';
47
+ const RAIL_KEY = 'skills-studio-rail-collapsed';
48
+ const KEPT_UPDATES_KEY = 'skills-studio-kept-updates';
49
+ const LIB_KEY = 'skills-studio-library-view';
50
+ const MAC = /Mac|iPhone|iPad/.test(globalThis.navigator?.platform || '');
51
+
52
+ // ── State ──
53
+
54
+ const S = {
55
+ panel: null, backdrop: null, gen: 0, life: null, unload: null, opener: null,
56
+ caps: null, capsFailed: false,
57
+ data: null, loading: null, loadError: null,
58
+ rows: [], counts: null, projects: [], updates: [],
59
+ place: 'library', ws: null, workspaces: new Map(),
60
+ lib: { view: 'all', kind: 'all', source: 'all', runtime: 'all', health: 'any', q: '', sort: 'attention', selected: new Set(), focus: null },
61
+ docked: false, dim: false, changeTimer: 0,
62
+ };
63
+ let _cache = null; // what was last read (a later opening paints from it, then reads again behind it)
64
+ let _capsPromise = null;
65
+
66
+ const guest = () => isGuest();
67
+
68
+ function readJson(key, fallback) {
69
+ try { const v = JSON.parse(storage.getItem(key) || 'null'); return v ?? fallback; } catch { return fallback; }
70
+ }
71
+
72
+ // ── Capabilities and data ──
73
+
74
+ /** What the service can do (GET /v2/capabilities), once per page; null while unknown. */
75
+ export function capabilities() {
76
+ if (S.caps) return Promise.resolve(S.caps);
77
+ if (!_capsPromise) {
78
+ _capsPromise = api.fetchSkillsCapabilities()
79
+ .then((res) => { S.caps = new Set(Array.isArray(res?.capabilities) ? res.capabilities.filter((c) => typeof c === 'string') : []); S.capsFailed = false; return S.caps; })
80
+ .catch((err) => { S.capsFailed = true; _capsPromise = null; throw err; });
81
+ }
82
+ return _capsPromise;
83
+ }
84
+
85
+ async function readCatalog() {
86
+ return readWholeCatalog(api.fetchSkillsCatalog);
87
+ }
88
+
89
+ /** What this app version ships of each bundled skill: its upstream revision and the digest of its SKILL.md. */
90
+ async function readUpstream(skills) {
91
+ const out = {};
92
+ for (const s of skills.filter((x) => x.origin?.kind === 'bundled' && x.pointers?.upstream)) {
93
+ try {
94
+ const rev = (await api.fetchSkillsRevision(s.pointers.upstream)).revision;
95
+ const head = (rev?.manifest?.files || []).find((f) => f.path === 'SKILL.md');
96
+ out[String(s.origin.name || s.name).toLowerCase()] = { revisionId: rev.revisionId, headDigest: head?.digest || null };
97
+ } catch { /* an update cannot be told for it: no card */ }
98
+ }
99
+ return out;
100
+ }
101
+
102
+ /** Read everything the Library shows (the catalog, the library, the status, the queue). */
103
+ export function loadData() {
104
+ if (S.loading) return S.loading;
105
+ const run = (async () => {
106
+ await capabilities();
107
+ const [catalog, skills, status, reviews, config, notice, archived] = await Promise.all([
108
+ readCatalog(),
109
+ api.fetchSkillsList().then((r) => r.skills || []),
110
+ api.fetchSkillsStatus().catch(() => null),
111
+ api.fetchSkillsReviews({}).then((r) => r.reviews || []).catch(() => []),
112
+ // The skills settings (the shared folder on or off): what a review's install goes to is decided with them.
113
+ api.fetchSkillsConfig().then((r) => r.config || {}).catch(() => ({ sharedAgentsRoot: false })),
114
+ // The start-up move of the old bridges, said once (a server from before it has no notice: nothing is shown).
115
+ S.caps?.has('auto-migration') ? api.fetchSkillsMigrationNotice().then((r) => r.notice || null).catch(() => null) : Promise.resolve(null),
116
+ // The skills deleted from the library (a server from before Restore lists none).
117
+ S.caps?.has('library-restore') ? api.fetchSkillsArchived().then((r) => (Array.isArray(r.archived) ? r.archived : [])).catch(() => []) : Promise.resolve([]),
118
+ ]);
119
+ const upstream = await readUpstream(skills);
120
+ _cache = { catalog, skills, status, reviews, upstream, config, notice, archived, at: Date.now() };
121
+ return _cache;
122
+ })();
123
+ S.loading = run;
124
+ // Settle the loading state before callers render the fulfilled/rejected request.
125
+ // A chained finally runs after an awaiting caller's catch and could leave the Library stuck on Loading.
126
+ run.then(() => { S.loadError = null; if (S.loading === run) S.loading = null; }, (err) => { S.loadError = err; if (S.loading === run) S.loading = null; });
127
+ return run;
128
+ }
129
+
130
+ function deriveRows() {
131
+ const d = _cache;
132
+ if (!d) { S.rows = []; S.counts = countRows([]); S.projects = []; S.updates = []; return; }
133
+ S.data = d;
134
+ // "Keep mine" is read here once for every place: the rows (health, attention, runtimes), the workspace banner, the
135
+ // Review cards and the rail's count all leave a kept update out.
136
+ const kept = readJson(KEPT_UPDATES_KEY, {});
137
+ S.rows = buildRows({ entries: d.catalog.items, skills: d.skills, status: d.status, reviews: d.reviews.filter((r) => ['pending', 'changes_requested', 'approved', 'partial', 'publish_failed'].includes(r.state)), upstream: d.upstream, kept });
138
+ // The problems only the fix system finds (a second library record, an older installation), and the ones the owner
139
+ // dismissed taken out (sx-fixes.js), before anything counts them.
140
+ applyFixState(S.rows, readDismissed(storage.getItem(DISMISSED_KEY)));
141
+ S.counts = countRows(S.rows);
142
+ S.counts.views.archived = Array.isArray(d.archived) ? d.archived.length : 0;
143
+ S.projects = projectsOf(d.catalog.roots, S.rows);
144
+ S.updates = updateCards(S.rows, kept);
145
+ S.lib.selected = keepSelection(S.lib.selected, S.rows);
146
+ // An open workspace follows its row (a new library state, installations, a review).
147
+ for (const ws of S.workspaces.values()) {
148
+ const row = S.rows.find((r) => r.key === ws.key) || (ws.skill ? S.rows.find((r) => r.skill?.skillId === ws.skill.skillId) : null);
149
+ // The Library's summary is what was just read: its installations and pointers win over what the workspace read
150
+ // earlier (the earlier reading keeps only what the summary lacks, its log).
151
+ if (row) { ws.row = row; if (row.skill) ws.skill = { ...(ws.skill?.skillId === row.skill.skillId ? ws.skill : {}), ...row.skill }; }
152
+ }
153
+ }
154
+
155
+ /** Launch prefetch: the Library read once after the page painted, so the first opening paints at once. */
156
+ export function prefetchStudio() {
157
+ if (_cache || (guest() && !hasPermission('skills'))) return Promise.resolve();
158
+ return loadData().catch(() => {});
159
+ }
160
+
161
+ // ── Opening and closing ──
162
+
163
+ export const isStudioOpen = () => !!S.panel;
164
+
165
+ /**
166
+ * Open the Studio (or bring it forward), going where `detail` says (sx-geometry.js parseOpen).
167
+ * @param {object} [detail]
168
+ */
169
+ export async function openStudio(detail = {}) {
170
+ if (guest() && !hasPermission('skills')) { showGuestToast(t('skillsStudio.guestDisabled')); return; }
171
+ const want = parseOpen(detail);
172
+ if (S.panel) {
173
+ S.panel.focus({ preventScroll: true });
174
+ await navigateTo(want);
175
+ return;
176
+ }
177
+ S.gen++;
178
+ S.opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
179
+ S.docked = storage.getItem(DOCK_KEY) === 'true';
180
+ S.dim = storage.getItem(DIM_KEY) === 'true' && !S.docked;
181
+ S.lib = { ...S.lib, ...readJson(LIB_KEY, {}), selected: new Set(), focus: null, installNotice: null, fixNotice: null };
182
+ S.backdrop = document.createElement('div');
183
+ S.backdrop.className = `studio-backdrop sx-backdrop${S.dim ? '' : ' backdrop-hidden'}`;
184
+ document.body.appendChild(S.backdrop);
185
+ const panel = document.createElement('section');
186
+ panel.id = PANEL_ID;
187
+ panel.className = `skills-studio-panel sx glass resizable${S.docked ? ' sx-docked' : ''}`;
188
+ panel.tabIndex = -1;
189
+ panel.setAttribute('role', 'dialog');
190
+ panel.setAttribute('aria-labelledby', 'sx-title');
191
+ panel.setAttribute('aria-modal', S.dim ? 'true' : 'false');
192
+ if (storage.getItem(RAIL_KEY) === 'true') panel.classList.add('sx-rail-collapsed');
193
+ setHTML(panel, shellHTML());
194
+ document.body.appendChild(panel);
195
+ S.panel = panel;
196
+ // Docked, it holds the work area as the code editor does: the map's top-right toolbar steps aside.
197
+ document.body.classList.toggle('sx-studio-docked', S.docked);
198
+ placeWindow();
199
+ wireShell();
200
+ S.unload = createUnloadGuard({ target: window, isDirty: () => anyDirty(), beforeLeave: () => backups().flush({ leaving: true }) });
201
+ S.unload.install();
202
+ const gen = S.gen;
203
+ if (_cache) deriveRows();
204
+ S.place = want.place === 'library' && !want.skillId && !want.artifactId && !want.name ? 'library' : want.place;
205
+ render();
206
+ requestAnimationFrame(() => { S.backdrop?.classList.add('open'); S.panel?.classList.add('open'); });
207
+ panel.focus({ preventScroll: true });
208
+ try {
209
+ await loadData();
210
+ if (gen !== S.gen || !S.panel) return;
211
+ deriveRows();
212
+ render({ keepFocus: true });
213
+ await navigateTo(want, { first: true });
214
+ } catch (err) {
215
+ if (gen !== S.gen || !S.panel) return;
216
+ render({ keepFocus: true });
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Import a bundle file the menu's "Import…" picked before the Studio was loaded (the picker opens in the click that
222
+ * asked for it; the Studio is loaded while the file is chosen): open on Create, read what the server can do, import.
223
+ */
224
+ export async function openStudioWithImport(file) {
225
+ await openStudio({ place: 'create' });
226
+ if (!S.panel || !file) return;
227
+ await importFile(ctx(), file, capabilities);
228
+ }
229
+
230
+ function placeWindow() {
231
+ const panel = S.panel;
232
+ if (S.docked) { panel.style.left = ''; panel.style.top = ''; panel.style.width = ''; panel.style.height = ''; return; }
233
+ // Where the shared panel system saved it the last time it was moved or resized (ui-panels.js savePanelLayout).
234
+ let saved = null;
235
+ try { saved = JSON.parse(storage.getItem(`${KEYS.PANEL_PREFIX}${PANEL_ID}`) || 'null'); } catch { saved = null; }
236
+ const rect = initialRect({ vw: window.innerWidth, vh: window.innerHeight, saved, frame: frameRect() });
237
+ Object.assign(panel.style, { left: `${rect.left}px`, top: `${rect.top}px`, width: `${rect.width}px`, height: `${rect.height}px` });
238
+ }
239
+
240
+ function anyDirty() {
241
+ for (const ws of S.workspaces.values()) if (dirtyTabs(ws).length) return true;
242
+ return false;
243
+ }
244
+
245
+ /** Close the window. Unsaved documents are saved, kept for later (they come back), or discarded: the owner says. */
246
+ export async function requestCloseStudio() {
247
+ if (!S.panel || dialogOpen()) return;
248
+ const dirty = [];
249
+ for (const ws of S.workspaces.values()) for (const tab of dirtyTabs(ws)) dirty.push({ ws, tab });
250
+ if (dirty.length) {
251
+ const names = dirty.slice(0, 6).map(({ ws, tab }) => `• ${ws.label} › ${tab.label}`);
252
+ if (dirty.length > 6) names.push(`• ${t('skillsStudio.dialog.andMore', { count: dirty.length - 6 })}`);
253
+ const answer = await ask({
254
+ title: t('skillsStudio.dialog.unsavedTitle'),
255
+ lines: [tp('skillsStudio.dialog.unsavedFiles', dirty.length), ...names, t('skillsStudio.dialog.keepForLaterHelp')],
256
+ buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'discard', label: t('skillsStudio.dialog.discard'), kind: 'danger' }, { id: 'keep', label: t('skillsStudio.dialog.keepForLater') }, { id: 'save', label: t('skillsStudio.dialog.saveAll'), kind: 'primary' }],
257
+ });
258
+ if (answer === 'save') {
259
+ for (const { ws, tab } of dirty) {
260
+ try {
261
+ const res = await saveTab(ws, tab, api, { key: idempotencyKey });
262
+ if (res.status === 'conflict' || res.status === 'gone') { toast(t('skillsStudio.toast.saveConflict', { file: tab.label }), 'error'); return; }
263
+ } catch (err) { toast(t('skillsStudio.toast.saveFailed', { file: tab.label, error: studioErrorText(t, err) }), 'error'); return; }
264
+ }
265
+ } else if (answer === 'discard') {
266
+ for (const { ws, tab } of dirty) dropDraft(ws, tab);
267
+ } else if (answer === 'keep') {
268
+ backups().flush();
269
+ } else return;
270
+ }
271
+ closeStudio();
272
+ }
273
+
274
+ /** Close now (the owner already said what happens to unsaved text). */
275
+ export function closeStudio() {
276
+ if (!S.panel) return;
277
+ S.gen++; // every answer still on its way belongs to this opening: dropped
278
+ clearTimeout(S.changeTimer);
279
+ closeDialog();
280
+ closeWorkspaceMenus(null);
281
+ // The workspaces and the window's editor go now (the editor's view and listeners would outlive the panel).
282
+ disposeStudio(S.workspaces.values());
283
+ // The place drawn last ends its listeners, timers and follow-ups too (a creation's clock would tick on a removed panel).
284
+ leaveLibraryPlace(); leaveReviewPlace(); leaveCreatePlace(); leaveTestTab(); leaveOperationsPlace();
285
+ forgetReviewOutcomes();
286
+ S.life?.abort();
287
+ S.life = null;
288
+ S.unload?.remove();
289
+ S.unload = null;
290
+ S.backdrop?.remove();
291
+ S.backdrop = null;
292
+ S.panel.remove();
293
+ S.panel = null;
294
+ document.body.classList.remove('sx-studio-docked');
295
+ S.ws = null;
296
+ S.workspaces.clear();
297
+ S.place = 'library';
298
+ storage.setItem(LIB_KEY, JSON.stringify({ view: S.lib.view, kind: S.lib.kind, source: S.lib.source, runtime: S.lib.runtime, health: S.lib.health, sort: S.lib.sort }));
299
+ const back = S.opener;
300
+ S.opener = null;
301
+ if (back?.isConnected) back.focus({ preventScroll: true });
302
+ }
303
+
304
+ // ── The shell ──
305
+
306
+ function shellHTML() {
307
+ const handles = ['t', 'r', 'b', 'l', 'tl', 'tr', 'bl', 'br'].map((d) => html`<div class="resize-handle resize-handle-${d}" data-resize="${d}"></div>`);
308
+ return html`${handles}
309
+ <header class="sx-head${S.docked ? '' : ' drag-handle'}" data-drag="${PANEL_ID}">
310
+ <span class="sx-head-mark" aria-hidden="true">${ICONS.mark}</span>
311
+ <h2 class="sx-title" id="sx-title">${t('skillsStudio.title')}</h2>
312
+ <span class="sx-head-note" id="sx-head-note" role="status"></span>
313
+ <span class="sx-spacer"></span>
314
+ <button type="button" class="sx-icon-btn" data-act="toggle-rail" aria-pressed="false" aria-label="${t('skillsStudio.shell.collapseRail')}" title="${t('skillsStudio.shell.collapseRail')}">${ICONS.library}</button>
315
+ <button type="button" class="sx-icon-btn" data-act="dock" aria-pressed="${S.docked ? 'true' : 'false'}" aria-label="${t('skillsStudio.shell.dock')}" title="${t('skillsStudio.shell.dockHelp')}">${S.docked ? ICONS.undock : ICONS.dock}</button>
316
+ <button type="button" class="sx-icon-btn" data-act="dim" aria-pressed="${S.dim ? 'true' : 'false'}" aria-label="${t('skillsStudio.shell.dim')}" title="${t('skillsStudio.shell.dimHelp')}"${S.docked ? ' disabled' : ''}>${ICONS.dim}</button>
317
+ <button type="button" class="sx-icon-btn sx-close" data-act="close" aria-label="${t('skillsStudio.shell.close')}" title="${t('skillsStudio.shell.close')}">${ICONS.close}</button>
318
+ </header>
319
+ <div class="sx-body">
320
+ <nav class="sx-rail" id="sx-rail" aria-label="${t('skillsStudio.title')}"></nav>
321
+ <main class="sx-main" id="sx-main" aria-labelledby="sx-title"></main>
322
+ </div>
323
+ <p class="sx-sr" id="sx-live" role="status" aria-live="polite"></p>`;
324
+ }
325
+
326
+ function wireShell() {
327
+ S.life?.abort();
328
+ S.life = new AbortController();
329
+ const signal = S.life.signal;
330
+ S.panel.addEventListener('click', (e) => {
331
+ const el = e.target.closest?.('[data-act]');
332
+ if (!el || !S.panel.contains(el) || el.disabled) return;
333
+ const act = el.dataset.act;
334
+ if (act === 'close') requestCloseStudio();
335
+ else if (act === 'dock') toggleDock();
336
+ else if (act === 'dim') toggleDim();
337
+ else if (act === 'toggle-rail') toggleRail();
338
+ else if (act === 'place') goPlace(el.dataset.place);
339
+ else if (act === 'view') { S.lib.view = el.dataset.view; S.lib.focus = null; goPlace('library'); }
340
+ else if (act === 'runtime-filter') { S.lib.runtime = el.dataset.runtime; S.lib.view = 'all'; goPlace('library'); }
341
+ else if (act === 'runtime-fix') openSkillFolders();
342
+ }, { signal });
343
+ // Keys only while the focus is in the Studio: the window is not modal, and the page beside it keeps its own.
344
+ document.addEventListener('keydown', onKeydown, { signal });
345
+ window.addEventListener('resize', () => { if (S.docked) return; placeWithinViewport(); }, { signal });
346
+ }
347
+
348
+ function placeWithinViewport() {
349
+ if (!S.panel) return;
350
+ const r = initialRect({ vw: window.innerWidth, vh: window.innerHeight, frame: frameRect(), saved: { left: S.panel.style.left, top: S.panel.style.top, width: S.panel.style.width, height: S.panel.style.height } });
351
+ Object.assign(S.panel.style, { left: `${r.left}px`, top: `${r.top}px`, width: `${r.width}px`, height: `${r.height}px` });
352
+ }
353
+
354
+ /**
355
+ * Dimmed, the window is modal (aria-modal): Tab and Shift+Tab go round inside it, from the last control to the first
356
+ * and back, and bring the keyboard back in from the page under it. True when the key was taken.
357
+ */
358
+ function trapTab(e, inside) {
359
+ if (!S.dim || e.key !== 'Tab' || e.altKey || e.ctrlKey || e.metaKey) return false;
360
+ const items = focusableIn(S.panel);
361
+ const active = document.activeElement;
362
+ let pos = null;
363
+ if (inside) {
364
+ const at = items.indexOf(active);
365
+ pos = at >= 0 ? at : items.filter((el) => el.compareDocumentPosition(active) & Node.DOCUMENT_POSITION_FOLLOWING).length - 0.5;
366
+ }
367
+ const to = trapIndex(items.length, pos, { shift: e.shiftKey });
368
+ if (to === -1) return false;
369
+ e.preventDefault();
370
+ (to === -2 ? S.panel : items[to]).focus({ preventScroll: false });
371
+ return true;
372
+ }
373
+
374
+ function onKeydown(e) {
375
+ if (!S.panel || e.defaultPrevented || dialogOpen()) return;
376
+ const inside = S.panel.contains(e.target) || S.panel.contains(document.activeElement);
377
+ if (trapTab(e, inside)) return;
378
+ if (!inside) return;
379
+ const mod = MAC ? e.metaKey : e.ctrlKey;
380
+ if (e.key === 'Escape') {
381
+ // The topmost thing that is open, and nothing under it.
382
+ if (closeDropdowns(S.panel, { focus: true })) { e.preventDefault(); return; }
383
+ if (closeWorkspaceMenus(S.panel)) { e.preventDefault(); return; }
384
+ if (inTextField(e.target) && (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA')) {
385
+ if (e.target.type === 'search' && e.target.value) { e.target.value = ''; e.target.dispatchEvent(new Event('input', { bubbles: true })); }
386
+ else e.target.blur();
387
+ e.preventDefault();
388
+ return;
389
+ }
390
+ e.preventDefault();
391
+ requestCloseStudio();
392
+ return;
393
+ }
394
+ if (mod && !e.shiftKey && !e.altKey && e.key.toLowerCase() === 'p') { e.preventDefault(); goPlace('library'); focusLibrary(S.panel); return; }
395
+ if (S.place === 'workspace' && S.ws && workspaceShortcut(e, ctx())) { e.preventDefault(); return; }
396
+ }
397
+
398
+ function toggleDock() {
399
+ S.docked = !S.docked;
400
+ storage.setItem(DOCK_KEY, S.docked ? 'true' : 'false');
401
+ if (S.docked && S.dim) toggleDim(false);
402
+ S.panel.classList.toggle('sx-docked', S.docked);
403
+ document.body.classList.toggle('sx-studio-docked', S.docked);
404
+ const head = S.panel.querySelector('.sx-head');
405
+ head?.classList.toggle('drag-handle', !S.docked);
406
+ const btn = S.panel.querySelector('[data-act="dock"]');
407
+ if (btn) { btn.setAttribute('aria-pressed', S.docked ? 'true' : 'false'); setHTML(btn, S.docked ? ICONS.undock : ICONS.dock); }
408
+ const dim = S.panel.querySelector('[data-act="dim"]');
409
+ if (dim) dim.disabled = S.docked;
410
+ placeWindow();
411
+ announce(t(S.docked ? 'skillsStudio.shell.docked' : 'skillsStudio.shell.undocked'));
412
+ }
413
+
414
+ function toggleDim(force) {
415
+ S.dim = typeof force === 'boolean' ? force : !S.dim;
416
+ storage.setItem(DIM_KEY, S.dim ? 'true' : 'false');
417
+ S.backdrop?.classList.toggle('backdrop-hidden', !S.dim);
418
+ S.panel.setAttribute('aria-modal', S.dim ? 'true' : 'false');
419
+ const btn = S.panel.querySelector('[data-act="dim"]');
420
+ if (btn) btn.setAttribute('aria-pressed', S.dim ? 'true' : 'false');
421
+ announce(t(S.dim ? 'skillsStudio.shell.dimmed' : 'skillsStudio.shell.notDimmed'));
422
+ }
423
+
424
+ function toggleRail() {
425
+ const on = !S.panel.classList.contains('sx-rail-collapsed');
426
+ S.panel.classList.toggle('sx-rail-collapsed', on);
427
+ storage.setItem(RAIL_KEY, on ? 'true' : 'false');
428
+ S.panel.querySelector('[data-act="toggle-rail"]')?.setAttribute('aria-pressed', on ? 'true' : 'false');
429
+ }
430
+
431
+ // ── Navigation ──
432
+
433
+ function goPlace(place) {
434
+ if (!['library', 'review', 'create', 'discover', 'health'].includes(place)) return;
435
+ S.place = place;
436
+ render();
437
+ focusPlace();
438
+ }
439
+
440
+ /**
441
+ * Open a Library row in its workspace (the one it had, with its documents, when it was open before).
442
+ * `revisionId`, `reviewId` and `preset` open Publish for something named (a version, an approved submission, an
443
+ * update SynaBun ships, `preset(pub, targets)` ticking what it replaces): Publish starts over for it before it
444
+ * is drawn, its ticks and any plan of before dropped.
445
+ */
446
+ export async function openRow(key, { tab = null, file = null, line = null, revisionId = null, reviewId = null, preset = null } = {}) {
447
+ const row = S.rows.find((r) => r.key === key);
448
+ if (!row) return false;
449
+ let ws = S.workspaces.get(row.key);
450
+ if (!ws) { ws = newWorkspace(row, { guest: guest(), caps: S.caps || new Set() }); S.workspaces.set(row.key, ws); }
451
+ if (tab) ws.tab = tab;
452
+ if (revisionId || reviewId || preset) {
453
+ ws.pub = null;
454
+ ws.publishRevisionId = revisionId || null;
455
+ ws.publishReviewId = reviewId || null;
456
+ ws.pubPresetApply = typeof preset === 'function' ? preset : null;
457
+ }
458
+ if (file || line) ws.pendingReveal = { file, line };
459
+ S.ws = ws;
460
+ S.place = 'workspace';
461
+ render();
462
+ return true;
463
+ }
464
+
465
+ async function navigateTo(want, { first = false } = {}) {
466
+ if (!S.panel) return;
467
+ if (want.skillId || want.artifactId || want.name) {
468
+ const row = S.rows.find((r) => (want.skillId && r.skill?.skillId === want.skillId) || (want.artifactId && r.entries.some((e) => e.id === want.artifactId))
469
+ || (want.name && (r.folderName === want.name || r.name === want.name) && r.kind === 'skill'));
470
+ if (row) { await openRow(row.key, { tab: want.tab, file: want.file, line: want.line }); return; }
471
+ }
472
+ if (want.place === 'review') { S.place = 'review'; S.reviewFocus = want.reviewId; render(); focusPlace(); return; }
473
+ if (want.place === 'create') {
474
+ S.place = 'create';
475
+ // A link to an AI drafting opens it in Create's box (its progress, or what it made).
476
+ if (want.creationId) openCreation(want.creationId);
477
+ render();
478
+ if (want.create === 'import') startImport(ctx());
479
+ else focusPlace();
480
+ return;
481
+ }
482
+ if (!first || S.place === 'library') { S.place = 'library'; render(); }
483
+ }
484
+
485
+ function focusPlace() {
486
+ requestAnimationFrame(() => {
487
+ const target = S.panel?.querySelector('#sx-main [data-autofocus]') || S.panel?.querySelector('#sx-main h3, #sx-main h2');
488
+ if (target) { if (!target.hasAttribute('tabindex') && !/^(INPUT|TEXTAREA|BUTTON)$/.test(target.tagName)) target.tabIndex = -1; target.focus({ preventScroll: true }); }
489
+ });
490
+ }
491
+
492
+ // ── Rendering ──
493
+
494
+ function railHTML() {
495
+ const c = S.counts || countRows([]);
496
+ const waiting = reviewCount((S.data?.reviews || []), S.updates);
497
+ const place = S.place === 'workspace' ? 'library' : S.place;
498
+ const item = (id, icon, label, count, extra = '') => html`<li><button type="button" class="sx-rail-item" data-act="place" data-place="${id}"${place === id ? html` aria-current="page"` : ''} title="${label}">
499
+ <span class="sx-rail-icon">${icon}</span><span class="sx-rail-label">${label}</span>${count !== null ? html`<span class="sx-rail-count">${count}${extra ? html`<span class="sx-sr"> ${extra}</span>` : ''}</span>` : ''}</button></li>`;
500
+ const views = ['attention', 'mine', 'synabun', 'plugins', 'account', 'drafts', ...(S.caps?.has('library-restore') ? ['archived'] : [])].map((v) => html`<li><button type="button" class="sx-rail-view" data-act="view" data-view="${v}"${S.place === 'library' && S.lib.view === v ? html` aria-current="true"` : ''}>
501
+ <span class="sx-rail-label">${t(`skillsStudio.rail.views.${v}`)}</span><span class="sx-rail-count">${c.views[v] ?? 0}</span></button></li>`);
502
+ const projects = S.projects.map((p) => html`<li><button type="button" class="sx-rail-view" data-act="view" data-view="${`project:${p.path}`}"${S.place === 'library' && S.lib.view === `project:${p.path}` ? html` aria-current="true"` : ''} title="${p.path}">
503
+ <span class="sx-rail-label">${p.label}</span><span class="sx-rail-count">${p.count}</span></button></li>`);
504
+ return html`
505
+ <ul class="sx-rail-list">
506
+ ${item('library', ICONS.library, t('skillsStudio.rail.library'), c.total, '')}
507
+ ${item('review', ICONS.review, t('skillsStudio.rail.review'), waiting || null, t('skillsStudio.rail.waiting'))}
508
+ ${item('create', ICONS.create, t('skillsStudio.rail.create'), null)}
509
+ ${item('discover', ICONS.search, t('skillsStudio.operations.discover'), null)}
510
+ ${item('health', ICONS.history, t('skillsStudio.operations.health'), null)}
511
+ </ul>
512
+ <div class="sx-rail-groups">
513
+ <p class="sx-rail-group-label" id="sx-rail-views">${t('skillsStudio.rail.viewsTitle')}</p>
514
+ <ul class="sx-rail-list" aria-labelledby="sx-rail-views">${views}</ul>
515
+ ${S.projects.length ? html`<p class="sx-rail-group-label" id="sx-rail-projects">${t('skillsStudio.rail.projectsTitle')}</p><ul class="sx-rail-list" aria-labelledby="sx-rail-projects">${projects}</ul>` : ''}
516
+ </div>
517
+ ${runtimeFootHTML()}`;
518
+ }
519
+
520
+ /**
521
+ * Settings → Skills → Skill folders, where the old bridge files are moved to skill folders (Move). The settings
522
+ * module is loaded when asked for (never at the Studio's load: it is the page's, and heavy).
523
+ */
524
+ async function openSkillFolders() {
525
+ try {
526
+ const m = await import('../../ui-settings.js');
527
+ await m.openSettingsModal({ tab: 'tool-access', expand: ['stg-sec-skills'], scrollTo: 'stg-skill-folders' });
528
+ } catch { toast(t('skillsStudio.rail.runtimeFixFailed'), 'error'); }
529
+ }
530
+
531
+ /**
532
+ * The runtimes at the foot of the rail: ready, reached through old bridge files only (the old format: the row opens
533
+ * Settings → Skills → Skill folders, where Move gives each skill a folder of its own), empty, or not set up.
534
+ */
535
+ function runtimeFootHTML() {
536
+ const roots = S.data?.catalog?.roots || [];
537
+ const state = (runtime) => {
538
+ const family = runtime;
539
+ const own = roots.filter((r) => r.family === family && r.kind === 'skills' && r.scope === 'user' && r.sourceKind === 'native');
540
+ const bridged = S.rows.some((r) => r.bridges.some((b) => b.runtime === runtime));
541
+ if (bridged) return 'bridge';
542
+ if (own.some((r) => r.status === 'ok')) return 'ready';
543
+ return own.length ? 'empty' : 'unknown';
544
+ };
545
+ const rows = ['claude', 'codex', 'opencode', 'antigravity'].map((rt) => {
546
+ const st = state(rt);
547
+ const name = t(`skillsStudio.runtime.${rt}`);
548
+ const label = st === 'bridge' ? t('skillsStudio.rail.runtimeFix', { runtime: name }) : `${name}: ${t(`skillsStudio.rail.runtimeState.${st}`)}`;
549
+ return html`<button type="button" class="sx-rt-row${st === 'bridge' ? ' sx-rt-row--fix' : ''}" data-act="${st === 'bridge' ? 'runtime-fix' : 'runtime-filter'}" data-runtime="${rt}" aria-label="${label}" title="${label}">
550
+ <span class="sx-rt-dot sx-rt-${st}" aria-hidden="true"></span><span class="sx-rt-name">${name}</span><span class="sx-rt-state">${t(`skillsStudio.rail.runtimeState.${st}`)}</span></button>`;
551
+ });
552
+ return html`<div class="sx-rail-foot" role="group" aria-label="${t('skillsStudio.rail.runtimesTitle')}">${rows}</div>`;
553
+ }
554
+
555
+ function headNote() {
556
+ const note = S.panel?.querySelector('#sx-head-note');
557
+ if (!note) return;
558
+ if (S.capsFailed) setHTML(note, html`<span class="sx-note-warn">${t('skillsStudio.shell.olderServer')}</span>`);
559
+ else if (S.loading && !_cache) setHTML(note, t('skillsStudio.shell.loading'));
560
+ else setHTML(note, '');
561
+ }
562
+
563
+ /** Draw the current place (keeping where the keyboard was when asked). */
564
+ export function render({ keepFocus = false } = {}) {
565
+ if (!S.panel) return;
566
+ const active = keepFocus ? document.activeElement : null;
567
+ const activeId = active && S.panel.contains(active) ? active.id || null : null;
568
+ setHTML(S.panel.querySelector('#sx-rail'), railHTML());
569
+ headNote();
570
+ const main = S.panel.querySelector('#sx-main');
571
+ const c = ctx();
572
+ main.dataset.place = S.place;
573
+ if (S.capsFailed && !_cache) { renderOlderServer(main); return; }
574
+ // Every place listens on the one main area: the one drawn before ends its listeners first (a Library listener left
575
+ // on would also answer inside a workspace: a dropdown opened and closed by the same click).
576
+ leaveLibraryPlace(); leaveReviewPlace(); leaveCreatePlace(); leaveTestTab(); leaveOperationsPlace();
577
+ if (S.place === 'library') renderLibraryPlace(main, c);
578
+ else if (S.place === 'review') renderReviewPlace(main, c);
579
+ else if (S.place === 'create') renderCreatePlace(main, c);
580
+ else if (S.place === 'discover' || S.place === 'health') renderOperationsPlace(main, c, S.place);
581
+ else if (S.place === 'workspace' && S.ws) renderWorkspace(main, c, S.ws);
582
+ else { S.place = 'library'; renderLibraryPlace(main, c); }
583
+ if (activeId) { const again = S.panel.querySelector(`#${CSS.escape(activeId)}`); if (again) again.focus({ preventScroll: true }); }
584
+ }
585
+
586
+ function renderOlderServer(main) {
587
+ setHTML(main, html`<div class="sx-empty" role="alert">
588
+ <h3 class="sx-empty-title" tabindex="-1" data-autofocus>${t('skillsStudio.shell.olderServerTitle')}</h3>
589
+ <p>${t('skillsStudio.shell.olderServerHelp')}</p>
590
+ <button type="button" class="sx-btn" id="sx-retry-caps">${t('skillsStudio.shell.retry')}</button>
591
+ </div>`);
592
+ main.querySelector('#sx-retry-caps')?.addEventListener('click', async () => {
593
+ try { await capabilities(); await loadData(); deriveRows(); } catch { /* still older */ }
594
+ render();
595
+ });
596
+ }
597
+
598
+ // ── Changes made elsewhere ──
599
+
600
+ // An install taken up or let go on any surface (sx-install-flow.js): the surfaces on screen draw it as on its way,
601
+ // or not, so no second one starts from a button that still looks free.
602
+ onInstallBusy(({ skillId }) => {
603
+ if (!S.panel) return;
604
+ if (S.place === 'review') { render({ keepFocus: true }); return; }
605
+ if (S.place === 'workspace' && S.ws && S.ws.skill?.skillId === skillId) {
606
+ const c = ctx();
607
+ refreshWorkspaceChrome(c, S.ws);
608
+ if (S.ws.tab === 'publish') refreshPublishBusy(c, S.ws);
609
+ }
610
+ });
611
+
612
+ /** A skill changed somewhere (another window, the Assistant, an install, a review): read again, a moment later. */
613
+ export function onSkillsChanged(event = {}) {
614
+ if (!S.panel) { _cache = null; return; }
615
+ clearTimeout(S.changeTimer);
616
+ const gen = S.gen;
617
+ S.changeTimer = setTimeout(async () => {
618
+ try {
619
+ await loadData();
620
+ if (gen !== S.gen || !S.panel) return;
621
+ deriveRows();
622
+ // A workspace that shows something being edited is not redrawn under the owner: its rail and its banners are.
623
+ if (S.place === 'workspace' && S.ws) {
624
+ setHTML(S.panel.querySelector('#sx-rail'), railHTML());
625
+ S.ws.onExternalChange?.(event);
626
+ } else render({ keepFocus: true });
627
+ } catch { /* what was read stays */ }
628
+ }, 300);
629
+ }
630
+
631
+ // ── Messages ──
632
+
633
+ export function toast(message, kind = 'info') {
634
+ const el = document.createElement('div');
635
+ el.className = `ss-toast ss-toast--${kind === 'error' ? 'error' : 'info'}`;
636
+ el.setAttribute('role', kind === 'error' ? 'alert' : 'status');
637
+ el.textContent = String(message ?? '');
638
+ document.body.appendChild(el);
639
+ requestAnimationFrame(() => el.classList.add('show'));
640
+ setTimeout(() => { el.classList.remove('show'); setTimeout(() => el.remove(), 300); }, kind === 'error' ? 6000 : 2800);
641
+ }
642
+
643
+ export function announce(message) {
644
+ const live = S.panel?.querySelector('#sx-live');
645
+ if (!live) return;
646
+ live.textContent = '';
647
+ requestAnimationFrame(() => { live.textContent = String(message ?? ''); });
648
+ }
649
+
650
+ const ask = (o) => askInPanel({ host: S.panel, ...o });
651
+
652
+ /** What the views are given: the state they read, and what they may do. */
653
+ function ctx() {
654
+ return {
655
+ t, tp, locale: getLocale(), mac: MAC, api, storage, emit,
656
+ guest: guest(),
657
+ caps: S.caps || new Set(),
658
+ // Whether the server has said what it can do: until then nothing is said to be missing from it.
659
+ capsKnown: !!S.caps,
660
+ panel: S.panel,
661
+ get data() { return S.data; },
662
+ get rows() { return S.rows; },
663
+ get counts() { return S.counts; },
664
+ get projects() { return S.projects; },
665
+ get updates() { return S.updates; },
666
+ get loading() { return !!S.loading && !_cache; },
667
+ get loadError() { return S.loadError; },
668
+ get reviewFocus() { return S.reviewFocus || null; },
669
+ clearReviewFocus() { S.reviewFocus = null; },
670
+ lib: S.lib,
671
+ gen: () => S.gen,
672
+ alive: (gen) => gen === S.gen && !!S.panel,
673
+ render, goPlace, openRow, toast, announce, ask,
674
+ // Review, with one submission brought forward (a finished AI draft's "Open in Review").
675
+ openReview: (reviewId) => { S.reviewFocus = reviewId || null; goPlace('review'); },
676
+ reload: async () => { const gen = S.gen; try { await loadData(); } catch (err) { if (gen === S.gen) toast(t('skillsStudio.toast.loadFailed', { error: studioErrorText(t, err) }), 'error'); } if (gen !== S.gen || !S.panel) return; deriveRows(); render({ keepFocus: true }); },
677
+ keepUpdate: (name, revisionId) => { const kept = readJson(KEPT_UPDATES_KEY, {}); kept[name] = revisionId; storage.setItem(KEPT_UPDATES_KEY, JSON.stringify(kept)); deriveRows(); },
678
+ saveLibraryView: () => storage.setItem(LIB_KEY, JSON.stringify({ view: S.lib.view, kind: S.lib.kind, source: S.lib.source, runtime: S.lib.runtime, health: S.lib.health, sort: S.lib.sort })),
679
+ keptDrafts: () => keptDrafts(),
680
+ // Problems the owner has seen and leaves as they are (sx-fixes.js fingerprints): kept, and said again when they change.
681
+ dismissed: () => readDismissed(storage.getItem(DISMISSED_KEY)),
682
+ dismissIssues: (fingerprints) => { storage.setItem(DISMISSED_KEY, addDismissed(storage.getItem(DISMISSED_KEY), fingerprints)); deriveRows(); render({ keepFocus: true }); },
683
+ restoreIssues: (fingerprints) => { storage.setItem(DISMISSED_KEY, removeDismissed(storage.getItem(DISMISSED_KEY), fingerprints)); deriveRows(); render({ keepFocus: true }); },
684
+ openSkillFolders,
685
+ workspaces: S.workspaces,
686
+ closeWorkspace: (ws) => { disposeWorkspace(ws); S.workspaces.delete(ws.key); if (S.ws === ws) { S.ws = null; S.place = 'library'; render(); } },
687
+ key: idempotencyKey,
688
+ capabilities,
689
+ };
690
+ }