synabun 2.0.9 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (679) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +68 -4
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +2 -2
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/launcher.mjs +9 -3
  10. package/lib/claude-hooks.js +172 -18
  11. package/lib/client-config-repair.js +17 -8
  12. package/lib/desktop-app.js +7 -1
  13. package/lib/external-tools.js +1 -1
  14. package/lib/first-launch-protection.js +57 -12
  15. package/lib/mcp-dist.js +176 -0
  16. package/lib/release-npm-ci.mjs +138 -0
  17. package/lib/release-publish.mjs +91 -0
  18. package/lib/release-scan.mjs +50 -0
  19. package/lib/server-crash-record.js +123 -0
  20. package/lib/server-exit.js +318 -0
  21. package/lib/server-supervisor.js +508 -0
  22. package/lib/start-launcher.js +43 -5
  23. package/lib/tools-cli.js +45 -0
  24. package/lib/update-snapshot.js +228 -0
  25. package/lib/virustotal.mjs +192 -0
  26. package/lib/windows-crash-event.js +269 -0
  27. package/mcp-server/dist/http.d.ts +20 -1
  28. package/mcp-server/dist/http.d.ts.map +1 -1
  29. package/mcp-server/dist/http.js +59 -12
  30. package/mcp-server/dist/http.js.map +1 -1
  31. package/mcp-server/dist/index.d.ts +33 -27
  32. package/mcp-server/dist/index.d.ts.map +1 -1
  33. package/mcp-server/dist/index.js +128 -143
  34. package/mcp-server/dist/index.js.map +1 -1
  35. package/mcp-server/dist/preload.js +1 -1
  36. package/mcp-server/dist/preload.js.map +1 -1
  37. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  38. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  39. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  40. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  41. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  42. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  43. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  44. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  45. package/mcp-server/dist/services/neural-interface.d.ts +16 -10
  46. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  47. package/mcp-server/dist/services/neural-interface.js +67 -11
  48. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  49. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  50. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  51. package/mcp-server/dist/services/server-instructions.js +20 -62
  52. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  53. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  54. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  55. package/mcp-server/dist/services/tool-catalog.js +339 -0
  56. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  57. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  58. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  59. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  60. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  61. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  62. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  63. package/mcp-server/dist/services/tool-list.js +334 -0
  64. package/mcp-server/dist/services/tool-list.js.map +1 -0
  65. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  66. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  67. package/mcp-server/dist/services/tool-runtime.js +400 -0
  68. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  69. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  70. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  71. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  72. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  73. package/mcp-server/dist/tools/agents.d.ts +10 -10
  74. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  75. package/mcp-server/dist/tools/agents.js +22 -13
  76. package/mcp-server/dist/tools/agents.js.map +1 -1
  77. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  78. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  79. package/mcp-server/dist/tools/image-tools.js +1 -1
  80. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  81. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  82. package/mcp-server/dist/tools/skill-tools.js +639 -0
  83. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  84. package/mcp-server/dist/tools/skill.d.ts +7 -0
  85. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  86. package/mcp-server/dist/tools/skill.js +11 -0
  87. package/mcp-server/dist/tools/skill.js.map +1 -0
  88. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  89. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  90. package/mcp-server/dist/tools/tool-groups.js +147 -0
  91. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  92. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  93. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  94. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  95. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  96. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  97. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  98. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  99. package/mcp-server/package-lock.json +2 -2
  100. package/mcp-server/package.json +1 -1
  101. package/mcp-server/src/http.ts +75 -12
  102. package/mcp-server/src/index.ts +147 -154
  103. package/mcp-server/src/preload.ts +12 -12
  104. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  105. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  106. package/mcp-server/src/services/neural-interface.ts +79 -20
  107. package/mcp-server/src/services/server-instructions.ts +27 -63
  108. package/mcp-server/src/services/tool-catalog.ts +403 -0
  109. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  110. package/mcp-server/src/services/tool-list.ts +410 -0
  111. package/mcp-server/src/services/tool-runtime.ts +457 -0
  112. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  113. package/mcp-server/src/tools/agents.ts +22 -14
  114. package/mcp-server/src/tools/image-tools.ts +1 -1
  115. package/mcp-server/src/tools/skill-tools.ts +589 -0
  116. package/mcp-server/src/tools/skill.ts +13 -0
  117. package/mcp-server/src/tools/tool-groups.ts +164 -0
  118. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  119. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  120. package/neural-interface/i18n/en.json +3680 -206
  121. package/neural-interface/i18n/pt-BR.json +3628 -157
  122. package/neural-interface/lib/account-usage.js +5 -3
  123. package/neural-interface/lib/app-controls-api.js +161 -0
  124. package/neural-interface/lib/assistant-api.js +148 -35
  125. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  126. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  127. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  128. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  129. package/neural-interface/lib/assistant-brains/opencode.js +122 -44
  130. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +59 -0
  131. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +97 -0
  132. package/neural-interface/lib/assistant-catalog.js +23 -9
  133. package/neural-interface/lib/assistant-dispatch.js +874 -156
  134. package/neural-interface/lib/assistant-envelope.js +22 -2
  135. package/neural-interface/lib/assistant-persona.js +80 -11
  136. package/neural-interface/lib/assistant-plan-permissions.js +10 -9
  137. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  138. package/neural-interface/lib/assistant-route-gate.js +6 -4
  139. package/neural-interface/lib/assistant-router.js +13 -8
  140. package/neural-interface/lib/assistant-runtime.js +183 -26
  141. package/neural-interface/lib/assistant-task-prompt.js +78 -14
  142. package/neural-interface/lib/assistant-tools.js +51 -0
  143. package/neural-interface/lib/backup-service.js +620 -3
  144. package/neural-interface/lib/backup-zip-reader.js +10 -5
  145. package/neural-interface/lib/backup-zip-writer.js +257 -0
  146. package/neural-interface/lib/browser-execution.js +10 -2
  147. package/neural-interface/lib/browser-guard-cdp.js +83 -0
  148. package/neural-interface/lib/browser-network-guard.js +284 -0
  149. package/neural-interface/lib/browser-policy-roots.js +64 -0
  150. package/neural-interface/lib/browser-tool-policy.js +186 -31
  151. package/neural-interface/lib/claude-agent-bridge.js +4 -2
  152. package/neural-interface/lib/claude-hooks-status.js +103 -0
  153. package/neural-interface/lib/claude-model-catalog.js +64 -14
  154. package/neural-interface/lib/claude-temporary.js +5 -11
  155. package/neural-interface/lib/cli-install.js +18 -138
  156. package/neural-interface/lib/cli-resolver.js +4 -1
  157. package/neural-interface/lib/cli-update-planner.js +32 -31
  158. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  159. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  160. package/neural-interface/lib/codex-settings-schema.js +109 -47
  161. package/neural-interface/lib/codex-worker-app-server.js +172 -0
  162. package/neural-interface/lib/confined-fs.js +30 -3
  163. package/neural-interface/lib/crash-notice-api.js +98 -0
  164. package/neural-interface/lib/editor-completion-opencode.js +164 -67
  165. package/neural-interface/lib/exec-loop-command.js +4 -1
  166. package/neural-interface/lib/http-guards.js +8 -2
  167. package/neural-interface/lib/install-details.js +4 -1
  168. package/neural-interface/lib/launch-cache.js +293 -0
  169. package/neural-interface/lib/login-start.js +524 -0
  170. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  171. package/neural-interface/lib/mcp-client-config.js +0 -29
  172. package/neural-interface/lib/mcp-external-servers.js +323 -0
  173. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  174. package/neural-interface/lib/morelogin.js +30 -0
  175. package/neural-interface/lib/native-loop-providers.js +565 -50
  176. package/neural-interface/lib/native-loop-runtime.js +287 -41
  177. package/neural-interface/lib/opencode-config.js +557 -0
  178. package/neural-interface/lib/opencode-db-schema.js +58 -0
  179. package/neural-interface/lib/opencode-history.js +49 -204
  180. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  181. package/neural-interface/lib/opencode-native-boundary.js +44 -0
  182. package/neural-interface/lib/opencode-protocol.js +310 -0
  183. package/neural-interface/lib/opencode-provider-view.js +81 -0
  184. package/neural-interface/lib/opencode-runtime-config.js +32 -15
  185. package/neural-interface/lib/opencode-serve-records.js +624 -0
  186. package/neural-interface/lib/opencode-v2-client.js +53 -131
  187. package/neural-interface/lib/opencode-v2-ws-requests.js +35 -195
  188. package/neural-interface/lib/opencode-worker-activity.js +229 -0
  189. package/neural-interface/lib/opencode-worker-data.js +27 -0
  190. package/neural-interface/lib/opencode2-client.js +796 -0
  191. package/neural-interface/lib/opencode2-proxy.js +76 -0
  192. package/neural-interface/lib/opencode2-translate.js +1057 -0
  193. package/neural-interface/lib/owner-authority.js +249 -0
  194. package/neural-interface/lib/page-session.js +275 -0
  195. package/neural-interface/lib/peer-identity.js +763 -0
  196. package/neural-interface/lib/process-identity.js +361 -0
  197. package/neural-interface/lib/request-gate.js +17 -2
  198. package/neural-interface/lib/rulesets/api.js +1 -1
  199. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  200. package/neural-interface/lib/rulesets/installer.js +92 -27
  201. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  202. package/neural-interface/lib/session-cache-builder.js +18 -10
  203. package/neural-interface/lib/session-title-generator.js +58 -18
  204. package/neural-interface/lib/skills/api-v2.js +495 -0
  205. package/neural-interface/lib/skills/bundle.js +418 -0
  206. package/neural-interface/lib/skills/catalog.js +294 -0
  207. package/neural-interface/lib/skills/creation-api.js +191 -0
  208. package/neural-interface/lib/skills/creation-brief.js +247 -0
  209. package/neural-interface/lib/skills/creation-sources.js +267 -0
  210. package/neural-interface/lib/skills/creation.js +843 -0
  211. package/neural-interface/lib/skills/deploy.js +564 -0
  212. package/neural-interface/lib/skills/digest.js +119 -0
  213. package/neural-interface/lib/skills/discover.js +111 -0
  214. package/neural-interface/lib/skills/draft-confinement.js +214 -0
  215. package/neural-interface/lib/skills/eval-confinement.js +397 -0
  216. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  217. package/neural-interface/lib/skills/eval-trace.js +119 -0
  218. package/neural-interface/lib/skills/evals-api.js +212 -0
  219. package/neural-interface/lib/skills/evals.js +1573 -0
  220. package/neural-interface/lib/skills/export.js +200 -0
  221. package/neural-interface/lib/skills/frontmatter.js +64 -0
  222. package/neural-interface/lib/skills/health.js +75 -0
  223. package/neural-interface/lib/skills/json-edit.js +165 -0
  224. package/neural-interface/lib/skills/known-bridge-digests.json +387 -0
  225. package/neural-interface/lib/skills/known-bridges.js +96 -0
  226. package/neural-interface/lib/skills/library.js +823 -0
  227. package/neural-interface/lib/skills/local-store.js +21 -0
  228. package/neural-interface/lib/skills/migration.js +433 -0
  229. package/neural-interface/lib/skills/operations.js +45 -0
  230. package/neural-interface/lib/skills/ownership.js +427 -0
  231. package/neural-interface/lib/skills/packages.js +88 -0
  232. package/neural-interface/lib/skills/reviews.js +212 -0
  233. package/neural-interface/lib/skills/revision.js +80 -0
  234. package/neural-interface/lib/skills/roots.js +262 -0
  235. package/neural-interface/lib/skills/runtime-checks.js +355 -0
  236. package/neural-interface/lib/skills/scan-assets.js +252 -0
  237. package/neural-interface/lib/skills/scan-context.js +26 -0
  238. package/neural-interface/lib/skills/scan-shell.js +324 -0
  239. package/neural-interface/lib/skills/scan.js +364 -0
  240. package/neural-interface/lib/skills/scanners.js +54 -0
  241. package/neural-interface/lib/skills/service.js +522 -0
  242. package/neural-interface/lib/skills/telemetry.js +113 -0
  243. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  244. package/neural-interface/lib/style-guide/api.js +4 -3
  245. package/neural-interface/lib/tool-lists.js +124 -0
  246. package/neural-interface/lib/untrusted-file-headers.js +27 -0
  247. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  248. package/neural-interface/lib/worker-confinement.js +1192 -0
  249. package/neural-interface/lib/worker-guard-setting.js +56 -0
  250. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  251. package/neural-interface/lib/worker-relay-ports.js +227 -0
  252. package/neural-interface/lib/worker-resume.js +229 -0
  253. package/neural-interface/lib/worker-sandbox.js +299 -0
  254. package/neural-interface/package-lock.json +1354 -50
  255. package/neural-interface/package.json +6 -2
  256. package/neural-interface/public/index.html +5 -3
  257. package/neural-interface/public/index2d.html +19 -11
  258. package/neural-interface/public/onboarding.html +2 -2
  259. package/neural-interface/public/shared/account-menu.js +8 -5
  260. package/neural-interface/public/shared/agent-runtime-options.js +76 -16
  261. package/neural-interface/public/shared/api.js +1527 -1336
  262. package/neural-interface/public/shared/app-frame.js +192 -0
  263. package/neural-interface/public/shared/assistant/asst-agents-dock.js +205 -19
  264. package/neural-interface/public/shared/assistant/asst-api.js +11 -3
  265. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  266. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  267. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  268. package/neural-interface/public/shared/assistant/asst-brain-picker.js +241 -112
  269. package/neural-interface/public/shared/assistant/asst-composer.js +1 -1
  270. package/neural-interface/public/shared/assistant/asst-control.js +157 -26
  271. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  272. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  273. package/neural-interface/public/shared/assistant/asst-hosts.js +49 -2
  274. package/neural-interface/public/shared/assistant/asst-limit-dock.js +40 -0
  275. package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -0
  276. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  277. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  278. package/neural-interface/public/shared/assistant/asst-menu.js +84 -12
  279. package/neural-interface/public/shared/assistant/asst-panel.js +303 -44
  280. package/neural-interface/public/shared/assistant/asst-rack-styles.js +93 -58
  281. package/neural-interface/public/shared/assistant/asst-redact.js +186 -0
  282. package/neural-interface/public/shared/assistant/asst-render.js +770 -100
  283. package/neural-interface/public/shared/assistant/asst-route.js +83 -12
  284. package/neural-interface/public/shared/assistant/asst-run-stage.js +231 -0
  285. package/neural-interface/public/shared/assistant/asst-sanitize.js +193 -3
  286. package/neural-interface/public/shared/assistant/asst-scene-styles.js +273 -0
  287. package/neural-interface/public/shared/assistant/asst-scenes.js +530 -0
  288. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +3 -15
  289. package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
  290. package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
  291. package/neural-interface/public/shared/assistant/asst-state.js +25 -15
  292. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  293. package/neural-interface/public/shared/assistant/asst-styles.js +102 -21
  294. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +165 -13
  295. package/neural-interface/public/shared/automation-context.js +5 -2
  296. package/neural-interface/public/shared/cc-dropdown.js +550 -0
  297. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  298. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  299. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  300. package/neural-interface/public/shared/cdx/cdx-output.js +8 -7
  301. package/neural-interface/public/shared/cdx/cdx-panel.js +58 -150
  302. package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
  303. package/neural-interface/public/shared/cdx/cdx-render.js +62 -32
  304. package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
  305. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  306. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  307. package/neural-interface/public/shared/cdx/cdx-tabs.js +201 -74
  308. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  309. package/neural-interface/public/shared/cli-install-view.js +4 -8
  310. package/neural-interface/public/shared/cli-status.js +2 -2
  311. package/neural-interface/public/shared/constants.js +2 -2
  312. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  313. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  314. package/neural-interface/public/shared/context-menu-model.js +366 -0
  315. package/neural-interface/public/shared/context-menu.js +372 -0
  316. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  317. package/neural-interface/public/shared/cp/cp-markdown.js +4 -57
  318. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  319. package/neural-interface/public/shared/data-cache.js +223 -0
  320. package/neural-interface/public/shared/desktop-bridge.js +11 -0
  321. package/neural-interface/public/shared/desktop-titlebar-view.js +78 -1
  322. package/neural-interface/public/shared/desktop-titlebar.js +214 -28
  323. package/neural-interface/public/shared/desktop-update-view.js +35 -5
  324. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  325. package/neural-interface/public/shared/file-links.js +73 -8
  326. package/neural-interface/public/shared/html-shell.js +78 -86
  327. package/neural-interface/public/shared/i18n.js +2 -0
  328. package/neural-interface/public/shared/launch-prefetch.js +59 -0
  329. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  330. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  331. package/neural-interface/public/shared/ocp-v2/ocp-v2-caps.js +3 -2
  332. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  333. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +9 -2
  334. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  335. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  336. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  337. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  338. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  339. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  340. package/neural-interface/public/shared/ocp-v2/ocp-v2-modelpicker.js +54 -17
  341. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  342. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +140 -254
  343. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  344. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +97 -203
  345. package/neural-interface/public/shared/ocp-v2/ocp-v2-providers.js +11 -9
  346. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +15 -4
  347. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -22
  348. package/neural-interface/public/shared/ocp-v2/ocp-v2-send-logic.js +401 -0
  349. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +20 -13
  350. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  351. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +53 -82
  352. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  353. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  354. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  355. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  356. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +48 -40
  357. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  358. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +205 -62
  359. package/neural-interface/public/shared/opencode-providers.js +39 -0
  360. package/neural-interface/public/shared/page-session.js +96 -0
  361. package/neural-interface/public/shared/registry.js +5 -3
  362. package/neural-interface/public/shared/retired-2d.js +3 -0
  363. package/neural-interface/public/shared/schedules-view.js +452 -0
  364. package/neural-interface/public/shared/settings/settings-app-pane.js +335 -0
  365. package/neural-interface/public/shared/settings/settings-app.js +243 -0
  366. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  367. package/neural-interface/public/shared/settings/settings-data.js +90 -0
  368. package/neural-interface/public/shared/settings/settings-ia.js +23 -6
  369. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  370. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  371. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  372. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  373. package/neural-interface/public/shared/sidepanel-session-status.js +49 -0
  374. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  375. package/neural-interface/public/shared/skills/ss-creation.js +219 -0
  376. package/neural-interface/public/shared/skills/ss-dialog.js +130 -0
  377. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  378. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  379. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  380. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  381. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  382. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  383. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  384. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  385. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  386. package/neural-interface/public/shared/skills/ss-lint.js +141 -0
  387. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  388. package/neural-interface/public/shared/skills/ss-profiles.js +475 -0
  389. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  390. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  391. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  392. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  393. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  394. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  395. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  396. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +236 -0
  397. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  398. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  399. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  400. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  401. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  402. package/neural-interface/public/shared/skills/studio/sx-dom.js +229 -0
  403. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  404. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  405. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  406. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  407. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  408. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  409. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +279 -0
  410. package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
  411. package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
  412. package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
  413. package/neural-interface/public/shared/skills/studio/sx-main.js +681 -0
  414. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  415. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  416. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  417. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  418. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  419. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  420. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  421. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  422. package/neural-interface/public/shared/skills/studio/sx-subject.js +248 -0
  423. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  424. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  425. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  426. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +425 -0
  427. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  428. package/neural-interface/public/shared/styles.css +2065 -2150
  429. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  430. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  431. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  432. package/neural-interface/public/shared/tool-list-view.js +112 -0
  433. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  434. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  435. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  436. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  437. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  438. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  439. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  440. package/neural-interface/public/shared/tour/tour.css +144 -0
  441. package/neural-interface/public/shared/ui-assistant.js +27 -5
  442. package/neural-interface/public/shared/ui-automation-studio.js +155 -152
  443. package/neural-interface/public/shared/ui-bookmarks.js +2 -1
  444. package/neural-interface/public/shared/ui-claude-panel.js +153 -13
  445. package/neural-interface/public/shared/ui-codex-panel.js +1 -0
  446. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  447. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  448. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  449. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  450. package/neural-interface/public/shared/ui-detail.js +21 -9
  451. package/neural-interface/public/shared/ui-file-links.js +1 -0
  452. package/neural-interface/public/shared/ui-image-gallery.js +56 -52
  453. package/neural-interface/public/shared/ui-invite.js +18 -2
  454. package/neural-interface/public/shared/ui-keybinds.js +26 -1
  455. package/neural-interface/public/shared/ui-link.js +9 -9
  456. package/neural-interface/public/shared/ui-menubar.js +229 -40
  457. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  458. package/neural-interface/public/shared/ui-native-loop-router.js +9 -1
  459. package/neural-interface/public/shared/ui-navbar.js +4 -24
  460. package/neural-interface/public/shared/ui-opencode-panel-v2.js +1 -0
  461. package/neural-interface/public/shared/ui-panels.js +96 -65
  462. package/neural-interface/public/shared/ui-schedules-studio.js +2267 -953
  463. package/neural-interface/public/shared/ui-sessions.js +20 -7
  464. package/neural-interface/public/shared/ui-settings.js +679 -496
  465. package/neural-interface/public/shared/ui-sidebar.js +4 -4
  466. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +13 -0
  467. package/neural-interface/public/shared/ui-sidepanel-sessions.js +56 -5
  468. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  469. package/neural-interface/public/shared/ui-sidepanel-tray.js +84 -8
  470. package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
  471. package/neural-interface/public/shared/ui-skills.js +42 -2781
  472. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  473. package/neural-interface/public/shared/ui-sync.js +22 -5
  474. package/neural-interface/public/shared/ui-terminal.js +159 -199
  475. package/neural-interface/public/shared/ui-tool-updates.js +3 -1
  476. package/neural-interface/public/shared/ui-tooltip.js +10 -5
  477. package/neural-interface/public/shared/ui-trash.js +14 -5
  478. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  479. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  480. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -61
  481. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  482. package/neural-interface/public/shared/ui-tutorial.js +285 -499
  483. package/neural-interface/public/shared/ui-update.js +121 -16
  484. package/neural-interface/public/shared/ui-whiteboard.js +313 -59
  485. package/neural-interface/public/shared/ui-worker-end.js +4 -0
  486. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  487. package/neural-interface/public/shared/view-variant.js +66 -0
  488. package/neural-interface/public/shared/whatsapp/wa-view.js +1 -1
  489. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  490. package/neural-interface/public/shared/worker-end.js +10 -3
  491. package/neural-interface/public/shared/worker-restart.js +12 -0
  492. package/neural-interface/public/sidepanel-session.html +1 -1
  493. package/neural-interface/public/variant/2d/background.js +2 -1
  494. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  495. package/neural-interface/public/variant/2d/graph.js +42 -20
  496. package/neural-interface/public/variant/2d/hulls.js +14 -9
  497. package/neural-interface/public/variant/2d/lasso.js +11 -7
  498. package/neural-interface/public/variant/2d/main.js +36 -2
  499. package/neural-interface/public/variant/2d/minimap.js +11 -13
  500. package/neural-interface/public/variant/3d/main.js +72 -6
  501. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  502. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  503. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  504. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  505. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  506. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  507. package/neural-interface/public/vendor/highlight/LICENSE +29 -0
  508. package/neural-interface/public/vendor/highlight/github-dark-dimmed-11.12.0.min.css +9 -0
  509. package/neural-interface/public/vendor/highlight/highlight-11.12.0.min.js +1262 -0
  510. package/neural-interface/public/vendor/highlight/manifest.json +5 -0
  511. package/neural-interface/public/vendor/html2canvas/LICENSE +22 -0
  512. package/neural-interface/public/vendor/html2canvas/html2canvas-1.4.1.min.js +20 -0
  513. package/neural-interface/public/vendor/html2canvas/manifest.json +4 -0
  514. package/neural-interface/public/vendor/marked/LICENSE.md +44 -0
  515. package/neural-interface/public/vendor/marked/manifest.json +4 -0
  516. package/neural-interface/public/vendor/marked/marked-14.1.4.esm.js +2502 -0
  517. package/neural-interface/public/vendor/mermaid/LICENSE +21 -0
  518. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/abnfDiagram-PAR5IXJI.mjs +1 -0
  519. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architecture-7GRP2DOG-M7G3SWCO.mjs +1 -0
  520. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/architectureDiagram-2PNWDENW.mjs +36 -0
  521. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/blockDiagram-PUWGUBOA.mjs +129 -0
  522. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/c4Diagram-3NLNHZP5.mjs +38 -0
  523. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-2NMSUPJH.mjs +1 -0
  524. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-32MCUW2P.mjs +206 -0
  525. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3NPNU5GP.mjs +1 -0
  526. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-3SMGFRGC.mjs +1 -0
  527. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4CAJECRU.mjs +14 -0
  528. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-4OADUZ5Q.mjs +10 -0
  529. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IMINLNL.mjs +1 -0
  530. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5IQCK5RY.mjs +3 -0
  531. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-5MVFYWFU.mjs +1 -0
  532. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AQ6EADP3.mjs +1 -0
  533. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-AZZRMDJM.mjs +15 -0
  534. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-CLS4B6BI.mjs +168 -0
  535. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-DZP67EKU.mjs +70 -0
  536. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-EICP7K5X.mjs +1 -0
  537. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-GDY2UAB4.mjs +1 -0
  538. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-H7VHZCWX.mjs +1 -0
  539. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-IJBDOHL6.mjs +1 -0
  540. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JFFVMJIA.mjs +1 -0
  541. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JQRUD6KW.mjs +1 -0
  542. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-JZGDI5JK.mjs +1 -0
  543. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-KD6SPIPN.mjs +2 -0
  544. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NF366YI6.mjs +231 -0
  545. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-NUDPKBEF.mjs +32 -0
  546. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-OJINMHTS.mjs +1 -0
  547. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Q6NGPIAH.mjs +2 -0
  548. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-QJSWEUOL.mjs +132 -0
  549. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-R7JDMUKV.mjs +1 -0
  550. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RCXAM2GK.mjs +1 -0
  551. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-RT4MAWR3.mjs +1 -0
  552. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SFMINX4W.mjs +1 -0
  553. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-SLDQ463B.mjs +88 -0
  554. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-STOV2HOB.mjs +1 -0
  555. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-T5OCTHI4.mjs +1 -0
  556. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-TCEHX7R3.mjs +1 -0
  557. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VQC2EELJ.mjs +1 -0
  558. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VRZKL3EH.mjs +1 -0
  559. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VTHDQ7BV.mjs +1 -0
  560. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-VY2OSTJR.mjs +2 -0
  561. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-WUBWJARI.mjs +1 -0
  562. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YBMQ5JGJ.mjs +2 -0
  563. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-YDMLL4PJ.mjs +1 -0
  564. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-Z6V44ARX.mjs +62 -0
  565. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZJJ7VQJP.mjs +339 -0
  566. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZNLK7HRR.mjs +1 -0
  567. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZQN7UHIL.mjs +1 -0
  568. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZS6VBONO.mjs +174 -0
  569. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/chunk-ZUNWM646.mjs +1 -0
  570. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-FGAMPOII.mjs +1 -0
  571. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/classDiagram-v2-AI5RJEK4.mjs +1 -0
  572. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cose-bilkent-Q72C2OJ3.mjs +1 -0
  573. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefin-OW5HDTMX-6OCTWOGG.mjs +1 -0
  574. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/cynefinDiagram-2OOWUY4M.mjs +62 -0
  575. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/dagre-MPVFI544.mjs +4 -0
  576. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-AE4NWXFW.mjs +24 -0
  577. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-BUJID5UG.mjs +41 -0
  578. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-C44CZ5NI.mjs +24 -0
  579. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-EYOLEXXS.mjs +30 -0
  580. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/diagram-P7B6PWOJ.mjs +3 -0
  581. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ebnfDiagram-FTPZ3PV6.mjs +1 -0
  582. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/erDiagram-H3ERR26V.mjs +99 -0
  583. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/eventmodeling-NTZA5JFV-WKSWQHFS.mjs +1 -0
  584. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/flowDiagram-YHGXBVSY.mjs +1 -0
  585. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ganttDiagram-RWFTB7ED.mjs +292 -0
  586. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraph-4MIJSDKK-JH2HKYUO.mjs +1 -0
  587. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/gitGraphDiagram-MAR6GKOP.mjs +106 -0
  588. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/info-A6RAGUB7-3TX3OFWW.mjs +1 -0
  589. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/infoDiagram-LV547AFH.mjs +2 -0
  590. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/ishikawaDiagram-PUJQGNDS.mjs +70 -0
  591. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/journeyDiagram-EW5QJT4W.mjs +139 -0
  592. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/kanban-definition-JSQERAZB.mjs +89 -0
  593. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/katex-CZ4GXH2S.mjs +257 -0
  594. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/mindmap-definition-VBJCLQLM.mjs +96 -0
  595. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/packet-AYTQ26CC-MKII6P3Y.mjs +1 -0
  596. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pegDiagram-7WILOQTJ.mjs +1 -0
  597. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pie-WAS4IAKB-6B4OPMJ5.mjs +1 -0
  598. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/pieDiagram-EFA7CBSB.mjs +39 -0
  599. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/quadrantDiagram-WARVYA7P.mjs +7 -0
  600. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/radar-RG4KPBEZ-UPTR3XUL.mjs +1 -0
  601. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-74A4TZTK-MEFUPFMS.mjs +1 -0
  602. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-abnf-HS5TGJTU-R5YG2S5D.mjs +1 -0
  603. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-ebnf-LZEXJU2U-G5QEX2L6.mjs +1 -0
  604. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroad-peg-WCYAUIDC-H5I2ZRY2.mjs +1 -0
  605. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/railroadDiagram-5NIQVNLQ.mjs +1 -0
  606. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/requirementDiagram-SXREGOKZ.mjs +84 -0
  607. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sankeyDiagram-VV74M2VP.mjs +40 -0
  608. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sequenceDiagram-52ZFIFDL.mjs +162 -0
  609. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/sizeCapture-FGA5AGYP.mjs +1 -0
  610. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-3QAKPIVI.mjs +1 -0
  611. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/stateDiagram-v2-HN2P4RDZ.mjs +1 -0
  612. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanes-QLVP42QD.mjs +1 -0
  613. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/swimlanesDiagram-JKPDBFCN.mjs +8 -0
  614. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/timeline-definition-GS45XAXW.mjs +120 -0
  615. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treeView-Q6P3EWNA-ZRJGANNQ.mjs +1 -0
  616. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/treemap-WGGIJYW6-K64VN5AS.mjs +1 -0
  617. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/vennDiagram-QIQU2ZRJ.mjs +34 -0
  618. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardley-WFR3VGLG-D67OWCCH.mjs +1 -0
  619. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/wardleyDiagram-U6YNM66N.mjs +78 -0
  620. package/neural-interface/public/vendor/mermaid/chunks/mermaid.esm.min/xychartDiagram-XJAZEDNM.mjs +7 -0
  621. package/neural-interface/public/vendor/mermaid/manifest.json +4 -0
  622. package/neural-interface/public/vendor/mermaid/mermaid-11.17.2.esm.min.mjs +14 -0
  623. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  624. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  625. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  626. package/neural-interface/scripts/build-vendor.mjs +116 -3
  627. package/neural-interface/server.js +4834 -2541
  628. package/neural-interface/templates/rulesets/core.md +1 -1
  629. package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
  630. package/neural-interface/templates/rulesets/manifest.json +8 -8
  631. package/npm-shrinkwrap.json +18 -12
  632. package/package.json +16 -3
  633. package/postinstall.js +9 -4
  634. package/preuninstall.js +1 -1
  635. package/setup.js +97 -322
  636. package/skills/skill-forge/SKILL.md +177 -0
  637. package/skills/skill-forge/evals/evals.json +54 -0
  638. package/skills/skill-forge/references/package.md +105 -0
  639. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  640. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  641. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  642. package/skills/synabun/SKILL.md +10 -7
  643. package/skills/synabun/modules/audit.md +1 -5
  644. package/skills/synabun/modules/changelog.md +1 -5
  645. package/skills/synabun/modules/idea.md +1 -1
  646. package/skills/synabun/modules/memorize.md +6 -9
  647. package/skills/synabun/modules/schedule.md +5 -5
  648. package/mcp-server/dist/services/profiles.d.ts +0 -120
  649. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  650. package/mcp-server/dist/services/profiles.js +0 -387
  651. package/mcp-server/dist/services/profiles.js.map +0 -1
  652. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  653. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  654. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  655. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  656. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  657. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  658. package/mcp-server/dist/tools/leonardo.js +0 -21
  659. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  660. package/mcp-server/dist/tools/profile.d.ts +0 -17
  661. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  662. package/mcp-server/dist/tools/profile.js +0 -114
  663. package/mcp-server/dist/tools/profile.js.map +0 -1
  664. package/mcp-server/src/services/profiles.ts +0 -428
  665. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  666. package/mcp-server/src/tools/leonardo.ts +0 -30
  667. package/mcp-server/src/tools/profile.ts +0 -122
  668. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  669. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  670. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  671. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  672. package/skills/leonardo/SKILL.md +0 -168
  673. package/skills/leonardo/icon.svg +0 -1
  674. package/skills/leonardo/modules/browser-guide.md +0 -378
  675. package/skills/leonardo/modules/image-prompter.md +0 -283
  676. package/skills/leonardo/modules/model-advisor.md +0 -100
  677. package/skills/leonardo/modules/prompt-library.md +0 -110
  678. package/skills/leonardo/modules/style-guide.md +0 -186
  679. package/skills/leonardo/modules/video-prompter.md +0 -317
@@ -0,0 +1,308 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: the inspector beside the document
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Form | YAML over the same document, never a copy of it (P0's lossless frontmatter: ss-frontmatter.js parses,
6
+ // ss-form.js says what each field shows, a change of a field patches that one key of the text). Keys the form has
7
+ // no field for are listed under Advanced, "kept as written", with the lines they are on. The description helper
8
+ // counts each runtime's budget and says whether the description tells what the skill does and when to use it.
9
+ // Problems come from the same checks as the server (ss-lint.js) and, for a skill, each runtime's profile
10
+ // (ss-profiles.js); a problem jumps to its line. Snippets go in at the caret (body) or as keys (frontmatter).
11
+ // While another file of the skill is showing, the form only shows: it changes SKILL.md, and Save saves the file
12
+ // on screen. Every string is a `skillsStudio.*` key; values from the document reach the markup as text only.
13
+
14
+ import { dropdownHTML } from '../../cc-dropdown.js';
15
+ import { formView, fieldPatchValue, KNOWN_TOOLS, KNOWN_MODELS } from '../ss-form.js';
16
+ import { lintArtifact } from '../ss-lint.js';
17
+ import { lintForProfile } from '../ss-profiles.js';
18
+ import { revealHidden } from '../ss-unicode.js';
19
+ import { readStoredPresets } from '../ss-presets.js';
20
+ import { html, raw, setHTML, ICONS } from './sx-dom.js';
21
+ import { descriptionHelp, keptKeys, frontmatterText } from './sx-editor-model.js';
22
+ import { snippetsFor, applySnippet, snippetMatches, SNIPPET_GROUPS } from './sx-snippets.js';
23
+
24
+ const OTHER_PROFILES = ['portable-six-field', 'codex-native', 'opencode-native'];
25
+ let _life = null;
26
+
27
+ /** The checks of a head document: the Studio's (gutter and list) and, for a skill, each other runtime's. */
28
+ export function headProblems({ type, parsed, folderName, knownNames = [], files = [] }) {
29
+ const studio = lintArtifact({ type, parsed, folderName, knownNames });
30
+ const others = type === 'skill' ? OTHER_PROFILES.map((id) => {
31
+ try {
32
+ const lint = lintForProfile(id, { parsed, folderName, files });
33
+ return { id, problems: lint.all.filter((d) => d.severity === 'error' || d.severity === 'warning') };
34
+ } catch { return { id, problems: [] }; }
35
+ }).filter((p) => p.problems.length) : [];
36
+ return { studio, others };
37
+ }
38
+
39
+ const fieldLabel = (t, type, key) => t(`skillsStudio.form.field.${key}`);
40
+
41
+ export function fieldHTML(t, f, { locked, type, folderName }) {
42
+ const id = `sx-f-${f.key}`;
43
+ const dis = locked || !f.writable;
44
+ const note = f.note ? html`<p class="sx-field-note">${noteWords(t, f.note)}</p>` : '';
45
+ const label = html`<label class="sx-field-label" for="${id}" id="${id}-label">${fieldLabel(t, type, f.key)}</label>`;
46
+ switch (f.control) {
47
+ case 'textarea': return html`<div class="sx-field">${label}<textarea class="sx-input sx-textarea" id="${id}" data-fm="${f.key}" rows="4"${dis ? ' disabled' : ''}>${f.value}</textarea>${note}</div>`;
48
+ case 'number': return html`<div class="sx-field">${label}<input class="sx-input" type="number" min="0" step="1" id="${id}" data-fm="${f.key}" value="${f.value}" placeholder="${t('skillsStudio.form.unlimited')}"${dis ? ' disabled' : ''}>${note}</div>`;
49
+ case 'checkbox':
50
+ if (f.key === 'disable-model-invocation') {
51
+ const name = folderName ? `/${folderName}` : '/name';
52
+ return html`<fieldset class="sx-field sx-choices"${dis ? ' disabled' : ''}><legend class="sx-field-label">${t('skillsStudio.form.invocation')}</legend>
53
+ <label class="sx-choice"><input type="radio" name="sx-inv" data-fm-radio="disable-model-invocation" value="false"${f.value ? '' : ' checked'}> ${t('skillsStudio.form.invocationAuto', { name })}</label>
54
+ <label class="sx-choice"><input type="radio" name="sx-inv" data-fm-radio="disable-model-invocation" value="true"${f.value ? ' checked' : ''}> ${t('skillsStudio.form.invocationManual', { name })}</label>${note}</fieldset>`;
55
+ }
56
+ return html`<div class="sx-field sx-field--check"><label class="sx-choice"><input type="checkbox" id="${id}" data-fm="${f.key}"${f.value ? ' checked' : ''}${dis ? ' disabled' : ''}> ${fieldLabel(t, type, f.key)}</label>${note}</div>`;
57
+ case 'model': {
58
+ const entries = [{ value: '', label: t('skillsStudio.form.modelInherit'), selected: !f.value }, ...KNOWN_MODELS.map((m) => ({ value: m, label: m, selected: f.value === m }))];
59
+ if (f.value && !KNOWN_MODELS.includes(f.value)) entries.push({ value: f.value, label: f.value, selected: true });
60
+ return html`<div class="sx-field"><span class="sx-field-label" id="${id}-label">${fieldLabel(t, type, f.key)}</span>${raw(dropdownHTML(id, entries, { labelledBy: `${id}-label`, disabled: dis, inputAttrs: `data-fm="${f.key}"` }))}${note}</div>`;
61
+ }
62
+ case 'tools':
63
+ if (Array.isArray(f.value)) {
64
+ const entries = [{ value: '', label: t('skillsStudio.form.addTool'), selected: true, disabled: true }, ...KNOWN_TOOLS.filter((x) => !f.value.includes(x)).map((x) => ({ value: x, label: x }))];
65
+ return html`<div class="sx-field"><span class="sx-field-label" id="${id}-label">${t('skillsStudio.form.field.allowed-tools')} <span class="sx-count">${f.value.length}</span></span>
66
+ <p class="sx-help">${t('skillsStudio.form.toolsHelp')}</p>
67
+ <div class="sx-chips" role="list">${f.value.map((tool) => html`<span class="sx-chip" role="listitem">${tool}${dis ? '' : html`<button type="button" class="sx-chip-x" data-tool-remove="${tool}" aria-label="${t('skillsStudio.form.removeTool', { tool })}">${ICONS.close}</button>`}</span>`)}</div>
68
+ ${dis ? '' : html`<div class="sx-tools-add">${raw(dropdownHTML('sx-f-tool-add', entries, { label: t('skillsStudio.form.addTool') }))}
69
+ <label class="sx-sr" for="sx-f-tool-pattern">${t('skillsStudio.form.toolPattern')}</label>
70
+ <input class="sx-input sx-input--sm" id="sx-f-tool-pattern" placeholder="${t('skillsStudio.form.toolPatternPlaceholder')}" autocomplete="off" spellcheck="false">
71
+ <button type="button" class="sx-btn" data-tool-add-pattern="1">${t('skillsStudio.form.add')}</button></div>`}${note}</div>`;
72
+ }
73
+ return html`<div class="sx-field">${label}<input class="sx-input" id="${id}" data-fm="${f.key}" value="${f.value}"${dis ? ' disabled' : ''}>${note}</div>`;
74
+ default:
75
+ return html`<div class="sx-field">${label}<input class="sx-input" id="${id}" data-fm="${f.key}" value="${f.value}" autocomplete="off" spellcheck="false"${dis ? ' disabled' : ''}>${f.key === 'name' ? html`<p class="sx-field-note" id="sx-name-check"></p>` : ''}${note}</div>`;
76
+ }
77
+ }
78
+
79
+ /** The form's notes come from ss-form.js in English: each is said in the page's language. */
80
+ function noteWords(t, note) {
81
+ if (/^Not text/.test(note)) return t('skillsStudio.form.note.notText');
82
+ if (/^Not a list of tool names/.test(note)) return t('skillsStudio.form.note.notTools');
83
+ if (/^Not true or false/.test(note)) return t('skillsStudio.form.note.notBoolean');
84
+ if (/^Not a whole number/.test(note)) return t('skillsStudio.form.note.notNumber');
85
+ if (/^An alias/.test(note)) return t('skillsStudio.form.note.alias');
86
+ return note;
87
+ }
88
+
89
+ function reasonWords(t, view) {
90
+ if (!view.reason) return '';
91
+ return /errors/.test(view.reason) ? t('skillsStudio.form.reason.errors') : t('skillsStudio.form.reason.unsupported');
92
+ }
93
+
94
+ export function helperHTML(t, data) {
95
+ const help = descriptionHelp(data);
96
+ return html`<div class="sx-helper" aria-live="polite">
97
+ <ul class="sx-budgets">${help.budgets.map((b) => html`<li class="sx-budget${b.over ? ' is-over' : ''}">
98
+ <span class="sx-budget-name">${t(`skillsStudio.form.budget.${b.runtime}`)}</span>
99
+ <span class="sx-meter" aria-hidden="true"><i style="width:${Math.round(b.ratio * 100)}%"></i></span>
100
+ <span class="sx-budget-count">${t('skillsStudio.form.budgetCount', { used: b.used, limit: b.limit })}</span></li>`)}</ul>
101
+ <ul class="sx-checks">
102
+ <li class="${help.what ? 'is-ok' : 'is-warn'}">${help.what ? ICONS.ok : ICONS.warn}<span>${t(help.what ? 'skillsStudio.form.saysWhat' : 'skillsStudio.form.missingWhat')}</span></li>
103
+ <li class="${help.when ? 'is-ok' : 'is-warn'}">${help.when ? ICONS.ok : ICONS.warn}<span>${t(help.when ? 'skillsStudio.form.saysWhen' : 'skillsStudio.form.missingWhen')}</span></li>
104
+ </ul>
105
+ <p class="sx-help">${t(`skillsStudio.form.advice.${help.advice}`)}</p>
106
+ </div>`;
107
+ }
108
+
109
+ export function nameCheck(t, value, folderName) {
110
+ if (!value) return { ok: false, text: t('skillsStudio.form.nameMissing') };
111
+ if (folderName && value !== folderName) return { ok: false, text: t('skillsStudio.form.nameFolder', { folder: folderName }) };
112
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value)) return { ok: false, text: t('skillsStudio.form.nameFormat') };
113
+ return { ok: true, text: folderName ? t('skillsStudio.form.nameMatches') : t('skillsStudio.form.nameOk') };
114
+ }
115
+
116
+ export function problemsHTML(t, problems) {
117
+ const shown = problems.studio.all.filter((d) => d.severity !== 'info');
118
+ const item = (d, profile = null) => html`<li><button type="button" class="sx-problem sx-problem--${d.severity}" data-line="${Number(d.line) || ''}">
119
+ ${d.severity === 'error' ? ICONS.err : ICONS.warn}<span class="sx-problem-text">${d.line ? html`<span class="sx-problem-line">${t('skillsStudio.edit.line', { line: d.line })}</span> ` : ''}${d.message}</span>${profile ? html`<span class="sx-problem-tag">${t(`skillsStudio.form.budget.${profile}`)}</span>` : ''}</button></li>`;
120
+ const others = problems.others.flatMap((p) => p.problems.slice(0, 6).map((d) => item(d, p.id === 'portable-six-field' ? 'portable' : p.id.replace('-native', ''))));
121
+ return html`<section class="sx-insp-section" aria-labelledby="sx-problems-h">
122
+ <h4 class="sx-insp-h" id="sx-problems-h">${t('skillsStudio.edit.problems')} <span class="sx-count">${shown.length}</span></h4>
123
+ ${shown.length ? html`<ul class="sx-problems">${shown.map((d) => item(d))}</ul>` : html`<p class="sx-help sx-ok-text">${ICONS.ok} ${t('skillsStudio.edit.noProblems')}</p>`}
124
+ ${others.length ? html`<details class="sx-details"><summary>${t('skillsStudio.edit.otherRuntimes', { count: others.length })}</summary><ul class="sx-problems">${others}</ul></details>` : ''}
125
+ </section>`;
126
+ }
127
+
128
+ export function snippetsHTML(t, list, query) {
129
+ const words = (s) => (s.custom ? `${s.name} ${s.description}` : `${t(`skillsStudio.snippets.${s.id}.name`)} ${t(`skillsStudio.snippets.${s.id}.desc`)}`);
130
+ const shown = list.filter((s) => snippetMatches(s, query, words));
131
+ const groups = SNIPPET_GROUPS.map((g) => ({ g, items: shown.filter((s) => s.group === g) })).filter((x) => x.items.length);
132
+ return html`<section class="sx-insp-section" aria-labelledby="sx-snip-h">
133
+ <h4 class="sx-insp-h" id="sx-snip-h">${t('skillsStudio.snippets.title')}</h4>
134
+ <label class="sx-sr" for="sx-snip-search">${t('skillsStudio.snippets.search')}</label>
135
+ <input type="search" class="sx-input sx-input--sm" id="sx-snip-search" placeholder="${t('skillsStudio.snippets.search')}" value="${query}" autocomplete="off">
136
+ <div id="sx-snip-list">${groups.map(({ g, items }) => html`<p class="sx-snip-group">${t(`skillsStudio.snippets.group.${g}`)}</p>
137
+ <ul class="sx-snippets">${items.map((s) => html`<li class="sx-snippet" draggable="true" data-snippet="${s.id}">
138
+ <span class="sx-snippet-text"><span class="sx-snippet-name">${s.custom ? s.name : t(`skillsStudio.snippets.${s.id}.name`)}</span>
139
+ <span class="sx-snippet-where">${s.kind === 'frontmatter' ? t('skillsStudio.snippets.whereKeys', { keys: Object.keys(s.patch || {}).join(', ') }) : (s.custom ? (s.description || t('skillsStudio.snippets.whereCaret')) : t(`skillsStudio.snippets.${s.id}.desc`))}</span></span>
140
+ <button type="button" class="sx-btn sx-btn--sm" data-insert="${s.id}" aria-label="${t('skillsStudio.snippets.insertNamed', { name: s.custom ? s.name : t(`skillsStudio.snippets.${s.id}.name`) })}">${t('skillsStudio.snippets.insert')}</button></li>`)}</ul>`)}
141
+ ${shown.length ? '' : html`<p class="sx-help">${t('skillsStudio.snippets.none')}</p>`}</div>
142
+ </section>`;
143
+ }
144
+
145
+ /**
146
+ * Draw the inspector for a workspace.
147
+ * @param {HTMLElement} host
148
+ * @param {object} o
149
+ * @param {object} o.ctx @param {object} o.ws
150
+ * @param {object|null} o.fm ss-frontmatter.js (null while it loads)
151
+ * @param {object|null} o.head the head document (SKILL.md, or the agent's / command's file)
152
+ * @param {boolean} o.headShowing the head document is the one on screen
153
+ * @param {(next: string, o?: object) => void} o.replaceHead change the head document's text (one undo step)
154
+ * @param {(line: number) => void} o.reveal show a line of the head document
155
+ * @param {() => void} o.openHead show the head document
156
+ * @param {() => number|null} o.caret where the caret is in the showing document
157
+ * @param {(snippet) => void} o.insertSnippet
158
+ */
159
+ export function renderInspector(host, o) {
160
+ const { ctx, ws, fm, head } = o;
161
+ const { t } = ctx;
162
+ _life?.abort();
163
+ _life = new AbortController();
164
+ const signal = _life.signal;
165
+ const mode = ws.inspectorMode || 'form';
166
+ const type = ws.kind;
167
+ const folderName = ws.kind === 'skill' ? (ws.row.folderName || ws.label) : '';
168
+ let body;
169
+ if (!head) body = html`<p class="sx-help">${t('skillsStudio.edit.noHead')}</p>`;
170
+ else if (head.binary) body = html`<p class="sx-note sx-note--warn" role="note">${t('skillsStudio.form.binaryHead')}</p>`;
171
+ else if (!fm) body = html`<p class="sx-help" role="status">${t('skillsStudio.form.loading')}</p>`;
172
+ else {
173
+ let parsed = null;
174
+ try { parsed = fm.parse(head.content); } catch { parsed = null; }
175
+ if (!parsed) body = html`<p class="sx-note" role="note">${t('skillsStudio.form.cannotRead')}</p>`;
176
+ else {
177
+ const view = formView(type, parsed);
178
+ const locked = !ws.subject.editable || head.readOnly || !view.editable || !o.headShowing;
179
+ const kept = keptKeys(type, parsed, head.content);
180
+ const problems = headProblems({ type, parsed, folderName, knownNames: o.knownNames || [], files: (ws.files || []).filter((f) => f.path).map((f) => ({ path: f.path, size: f.size || 0 })) });
181
+ ws.lastProblems = problems;
182
+ const notShowing = !o.headShowing && ws.subject.editable;
183
+ const formPart = mode === 'yaml'
184
+ ? html`<div class="sx-yaml"><pre class="sx-pre" tabindex="0" aria-label="${t('skillsStudio.form.yamlLabel')}">${frontmatterText(head.content) || t('skillsStudio.form.noFrontmatter')}</pre>
185
+ <button type="button" class="sx-btn" data-insp="yaml-edit">${t('skillsStudio.form.editInDocument')}</button>
186
+ <p class="sx-help">${t('skillsStudio.form.yamlHelp')}</p></div>`
187
+ : html`${view.reason && ws.subject.editable ? html`<p class="sx-note" role="note">${reasonWords(t, view)}</p>` : ''}
188
+ ${notShowing ? html`<p class="sx-note" role="note">${t('skillsStudio.form.notShowing', { file: head.label })} <button type="button" class="sx-link" data-insp="open-head">${t('skillsStudio.form.openIt')}</button></p>` : ''}
189
+ <div class="sx-form" role="group" aria-label="${t('skillsStudio.form.label')}">${view.fields.map((f) => fieldHTML(t, f, { locked, type, folderName }))}</div>
190
+ ${type === 'skill' ? helperHTML(t, parsed.data || {}) : ''}`;
191
+ const keptPart = kept.length ? html`<details class="sx-details sx-advanced"${ws.advancedOpen ? ' open' : ''} data-insp-details="advanced">
192
+ <summary>${ctx.tp('skillsStudio.form.advanced', kept.length)}</summary>
193
+ <p class="sx-help">${t('skillsStudio.form.advancedHelp')}</p>
194
+ <ul class="sx-kept">${kept.map((k) => html`<li><button type="button" class="sx-link" data-line="${k.line}">${k.key}</button><pre class="sx-pre sx-pre--sm">${k.text}</pre></li>`)}</ul>
195
+ </details>` : html`<p class="sx-help">${t('skillsStudio.form.advancedNone')}</p>`;
196
+ const custom = readStoredPresets(ctx.storage.getItem('neural-skills-custom-presets')).presets;
197
+ body = html`
198
+ <div class="sx-seg" role="group" aria-label="${t('skillsStudio.form.viewLabel')}">
199
+ <button type="button" data-insp="mode-form" aria-pressed="${mode === 'form' ? 'true' : 'false'}">${t('skillsStudio.form.form')}</button>
200
+ <button type="button" data-insp="mode-yaml" aria-pressed="${mode === 'yaml' ? 'true' : 'false'}">${t('skillsStudio.form.yaml')}</button>
201
+ </div>
202
+ ${formPart}
203
+ ${keptPart}
204
+ ${problemsHTML(t, problems)}
205
+ ${ws.subject.editable ? snippetsHTML(t, snippetsFor(type, custom), ws.snippetQuery || '') : ''}`;
206
+ }
207
+ }
208
+ setHTML(host, html`<div class="sx-inspector">${body}</div>`);
209
+ if (!head || !fm || head.binary) return;
210
+ // (its dropdowns are served by the Edit panel's delegated listeners, which hold this pane: wiring them here too
211
+ // would open and close them with the same click)
212
+ const nameInput = host.querySelector('#sx-f-name');
213
+ const showName = () => {
214
+ const out = host.querySelector('#sx-name-check');
215
+ if (!out || !nameInput) return;
216
+ const check = nameCheck(t, nameInput.value.trim(), folderName && ws.kind === 'skill' ? folderName : '');
217
+ out.textContent = `${check.ok ? '✓' : '✕'} ${check.text}`;
218
+ out.className = `sx-field-note ${check.ok ? 'sx-ok-text' : 'sx-warn-text'}`;
219
+ };
220
+ showName();
221
+ const apply = (key, input) => {
222
+ const parsed = fm.parse(head.content);
223
+ const view = formView(type, parsed);
224
+ const field = view.fields.find((f) => f.key === key);
225
+ if (!view.editable || !field?.writable || head.readOnly || !o.headShowing) return;
226
+ const savedKeys = new Set((fm.parse(head.saved.raw).keys || []).map((k) => k.key));
227
+ const next = fieldPatchValue({ field, input, current: parsed.data?.[key], savedHasKey: savedKeys.has(key) });
228
+ const el = host.querySelector(`#sx-f-${CSS.escape(key)}`);
229
+ if (next.invalid) { if (el) { el.setAttribute('aria-invalid', 'true'); el.title = t('skillsStudio.form.invalidNumber'); } return; }
230
+ if (el) { el.removeAttribute('aria-invalid'); el.removeAttribute('title'); }
231
+ const result = fm.patch(head.content, { [key]: next.value });
232
+ if (!result.ok) { ctx.toast(t('skillsStudio.form.patchFailed', { field: fieldLabel(t, type, key) }), 'error'); return; }
233
+ if (result.changed) o.replaceHead(result.text);
234
+ };
235
+ host.addEventListener('input', (e) => {
236
+ const el = e.target;
237
+ if (el.dataset?.fm && el.type !== 'checkbox' && el.type !== 'hidden') { apply(el.dataset.fm, el.value); if (el.dataset.fm === 'name') showName(); }
238
+ if (el.id === 'sx-snip-search') { ws.snippetQuery = el.value; redrawSnippets(host, ctx, ws, type); }
239
+ }, { signal });
240
+ host.addEventListener('change', (e) => {
241
+ const el = e.target;
242
+ if (el.type === 'checkbox' && el.dataset?.fm) apply(el.dataset.fm, el.checked);
243
+ else if (el.type === 'hidden' && el.dataset?.fm) apply(el.dataset.fm, el.value);
244
+ else if (el.dataset?.fmRadio) apply(el.dataset.fmRadio, el.value === 'true');
245
+ else if (el.id === 'sx-f-tool-add' && el.value) {
246
+ const parsed = fm.parse(head.content);
247
+ const tools = Array.isArray(parsed.data?.['allowed-tools']) ? parsed.data['allowed-tools'] : [];
248
+ apply('allowed-tools', [...tools, el.value]);
249
+ }
250
+ }, { signal });
251
+ host.addEventListener('toggle', (e) => { if (e.target.dataset?.inspDetails === 'advanced') ws.advancedOpen = e.target.open; }, { signal, capture: true });
252
+ host.addEventListener('click', (e) => {
253
+ const btn = e.target.closest?.('button');
254
+ if (!btn || !host.contains(btn)) return;
255
+ if (btn.dataset.insp === 'mode-form' || btn.dataset.insp === 'mode-yaml') { ws.inspectorMode = btn.dataset.insp === 'mode-yaml' ? 'yaml' : 'form'; renderInspector(host, o); host.querySelector(`[data-insp="${btn.dataset.insp}"]`)?.focus(); return; }
256
+ if (btn.dataset.insp === 'open-head') { o.openHead(); return; }
257
+ if (btn.dataset.insp === 'yaml-edit') { o.openHead(); o.reveal(2); return; }
258
+ if (btn.dataset.line) { const line = Number(btn.dataset.line); if (line) { o.openHead(); o.reveal(line); } return; }
259
+ if (btn.dataset.toolRemove) {
260
+ const parsed = fm.parse(head.content);
261
+ const tools = Array.isArray(parsed.data?.['allowed-tools']) ? parsed.data['allowed-tools'] : [];
262
+ apply('allowed-tools', tools.filter((x) => x !== btn.dataset.toolRemove));
263
+ return;
264
+ }
265
+ if (btn.dataset.toolAddPattern) {
266
+ const input = host.querySelector('#sx-f-tool-pattern');
267
+ const value = input?.value.trim();
268
+ if (!value) return;
269
+ const parsed = fm.parse(head.content);
270
+ const tools = Array.isArray(parsed.data?.['allowed-tools']) ? parsed.data['allowed-tools'] : [];
271
+ if (!tools.includes(value)) apply('allowed-tools', [...tools, value]);
272
+ return;
273
+ }
274
+ if (btn.dataset.insert) {
275
+ const custom = readStoredPresets(ctx.storage.getItem('neural-skills-custom-presets')).presets;
276
+ const snippet = snippetsFor(type, custom).find((s) => s.id === btn.dataset.insert);
277
+ if (snippet) o.insertSnippet(snippet);
278
+ }
279
+ }, { signal });
280
+ host.addEventListener('dragstart', (e) => {
281
+ const item = e.target.closest?.('[data-snippet]');
282
+ if (!item) return;
283
+ const custom = readStoredPresets(ctx.storage.getItem('neural-skills-custom-presets')).presets;
284
+ const snippet = snippetsFor(type, custom).find((s) => s.id === item.dataset.snippet);
285
+ if (!snippet || snippet.kind !== 'body') { e.preventDefault(); return; }
286
+ e.dataTransfer.setData('text/plain', snippet.text);
287
+ e.dataTransfer.effectAllowed = 'copy';
288
+ }, { signal });
289
+ }
290
+
291
+ function redrawSnippets(host, ctx, ws, type) {
292
+ const custom = readStoredPresets(ctx.storage.getItem('neural-skills-custom-presets')).presets;
293
+ const list = snippetsFor(type, custom);
294
+ const box = host.querySelector('#sx-snip-list');
295
+ if (!box) return;
296
+ const tmp = document.createElement('div');
297
+ setHTML(tmp, snippetsHTML(ctx.t, list, ws.snippetQuery || ''));
298
+ const fresh = tmp.querySelector('#sx-snip-list');
299
+ if (fresh) box.replaceWith(fresh);
300
+ }
301
+
302
+ /** Insert a snippet into a text (the view hands the result to the editor). */
303
+ export function snippetResult(text, snippet, { caret, fm }) {
304
+ return applySnippet(text, snippet, { caret, fm });
305
+ }
306
+
307
+ /** A hidden-character-safe label of a document (a name may hold characters nobody can see). */
308
+ export const shownName = (name) => revealHidden(String(name ?? ''));
@@ -0,0 +1,279 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: install in one go
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The one flow behind every Install of the Studio (the Publish tab's button, Review's "Approve & install", the
6
+ // workspace banner): the service plans the install (POST /v2/installations/plan), the owner is asked one question
7
+ // only when the plan asks one (a folder SynaBun did not install or that changed since, the scan's findings: what
8
+ // lib/skills/deploy.js needs confirmed), and the install sends exactly that plan (its digest, the decisions per
9
+ // target). A plan that changed under it is planned again, once, and asked again. The outcome comes back as data
10
+ // (sx-install.js installOutcome) with its words. Every protection of the service is kept: nothing is written
11
+ // without a plan, nothing replaced without its digest, nothing blocked is sent.
12
+
13
+ import { scanFindingText } from './sx-scan.js';
14
+ import { studioErrorText } from './sx-errors.js';
15
+ import { html, setHTML } from './sx-dom.js';
16
+ import { BLOCK_CODES, planModel, resultModel } from './sx-plan.js';
17
+ import { installTargets, wholeTarget } from './sx-subject.js';
18
+ import { confirmations, installOutcome, parseTargetKey, reviewDestinations, assistantsOf, installedRuntimesOf, alsoInstallTargets, alsoInstallRevision } from './sx-install.js';
19
+
20
+ // ── One install at a time per skill ──
21
+ // Every surface that installs (the Publish tab's button, Review's card, the workspace banner) takes this lock for
22
+ // the skill before it approves or plans, and lets it go when the flow ends, however it ends. While it is held every
23
+ // surface shows the install as on its way and refuses a second one (a click, or Enter on a button it disables).
24
+ const _busy = new Map(); // skillId → since
25
+ const _watchers = new Set(); // told when a lock is taken or let go
26
+
27
+ /** Whether an install of this skill is on its way, from any surface. */
28
+ export const installBusy = (skillId) => !!skillId && _busy.has(skillId);
29
+
30
+ /**
31
+ * Take the lock for a skill: what lets it go, or null when it is held already (the caller refuses).
32
+ * @param {string} skillId
33
+ * @returns {(() => void)|null}
34
+ */
35
+ export function acquireInstall(skillId) {
36
+ if (!skillId || _busy.has(skillId)) return null;
37
+ _busy.set(skillId, Date.now());
38
+ tell(skillId, true);
39
+ let done = false;
40
+ return () => { if (done) return; done = true; _busy.delete(skillId); tell(skillId, false); };
41
+ }
42
+
43
+ /** Be told when a lock is taken or let go (the surfaces draw the install as on its way, or not). */
44
+ export function onInstallBusy(fn) { _watchers.add(fn); return () => _watchers.delete(fn); }
45
+ function tell(skillId, busy) { for (const fn of _watchers) { try { fn({ skillId, busy }); } catch { /* a watcher's own trouble */ } } }
46
+
47
+ /** The words of a target: "Claude Code · all projects", "Codex · project Repo", "Claude Code · account work". */
48
+ export function targetWords(ctx, tg) {
49
+ const { t } = ctx;
50
+ const runtime = t(`skillsStudio.runtime.${['claude', 'codex', 'opencode', 'agents'].includes(tg?.runtime) ? tg.runtime : 'claude'}`);
51
+ if (tg?.scope === 'project') return t('skillsStudio.publish.targetProject', { runtime, project: tg.projectLabel || ctx.projects?.find((p) => p.path === tg.projectPath)?.label || tg.projectPath || '' });
52
+ if (tg?.accountId && tg.accountId !== 'default') return t('skillsStudio.publish.targetAccount', { runtime, account: tg.accountId });
53
+ return t('skillsStudio.publish.targetUser', { runtime });
54
+ }
55
+
56
+ /** The words of a target key (an install's result names targets by key). */
57
+ export const keyWords = (ctx, key) => targetWords(ctx, parseTargetKey(key));
58
+
59
+ /** A folder path, short: the home folder as ~ (the full path stays in the element's title). */
60
+ export function shortPath(path) {
61
+ return String(path || '').replace(/^\/Users\/[^/]+(?=\/)/, '~').replace(/^\/home\/[^/]+(?=\/)/, '~').replace(/^[A-Za-z]:\\Users\\[^\\]+(?=\\)/, '~');
62
+ }
63
+
64
+ /** The owner's assistants in effect (read with the Library: GET /v2/status `assistants`), or null on an older server. */
65
+ export function assistantsNow(ctx) { return assistantsOf(ctx.data?.status || null); }
66
+
67
+ /** The runtimes the resolver found on this computer (GET /v2/status `assistants.installed`), or null when not known. */
68
+ export function installedNow(ctx) { return installedRuntimesOf(ctx.data?.status || null); }
69
+
70
+ /**
71
+ * "Also install for Codex · OpenCode": the assistants in effect that a skill installed somewhere does not reach yet
72
+ * (sx-install.js alsoInstallTargets), with their words. Empty when there is nothing to add.
73
+ */
74
+ export function alsoInstall(ctx, { skill, row }) {
75
+ const targets = alsoInstallTargets({ skill, row, targets: targetsNow(ctx), assistants: assistantsNow(ctx), installed: installedNow(ctx) });
76
+ return { targets: targets.map(wholeTarget), words: targets.map((t) => ctx.t(`skillsStudio.runtime.${t.runtime}`)).join(' · ') };
77
+ }
78
+
79
+ /**
80
+ * One press: the version that is installed already (sx-install.js alsoInstallRevision) goes to the assistants that lack it, through the one flow (the plan, one question only when it asks one,
81
+ * then exactly that plan). The outcome's words come back for a toast or a banner; null when nothing was to add.
82
+ */
83
+ export async function alsoInstallNow(ctx, { skill, row }) {
84
+ const { t } = ctx;
85
+ const { targets } = alsoInstall(ctx, { skill, row });
86
+ if (!targets.length || ctx.guest) return null;
87
+ const revisionId = alsoInstallRevision(skill);
88
+ if (!revisionId) return { kind: 'error', line: t('skillsStudio.publish.noVersion'), lines: [] };
89
+ const release = acquireInstall(skill.skillId);
90
+ if (!release) return { kind: 'error', line: t('skillsStudio.publish.busyElsewhere'), lines: [] };
91
+ let out;
92
+ try { out = await installFlow(ctx, { revisionId, targets }); } catch (err) { out = { status: 'failed', error: studioErrorText(ctx.t, err) }; } finally { release(); }
93
+ return flowWords(ctx, out);
94
+ }
95
+
96
+ /** The targets a skill can be installed into now, with the shared folder as Settings has it (read with the Library). */
97
+ export function targetsNow(ctx, { projects = [] } = {}) {
98
+ const config = ctx.data?.config || { sharedAgentsRoot: false };
99
+ return installTargets({ roots: ctx.data?.catalog?.roots || [], config, projects: ctx.projects.filter((p) => projects.includes(p.path)) });
100
+ }
101
+
102
+ /**
103
+ * Where a review's install goes (sx-install.js reviewDestinations), from the same targets every surface uses: the
104
+ * label a button shows and the targets its click sends are one computation. `targets` as requests name them;
105
+ * `words` each destination in words; `missing` what the submission named that is not available here, with why.
106
+ */
107
+ export function reviewTargets(ctx, { review, skill, row }) {
108
+ const projects = [...new Set((review?.targets || []).filter((x) => x?.scope === 'project' && x.projectPath && ctx.projects.some((p) => p.path === x.projectPath)).map((x) => x.projectPath))];
109
+ const all = targetsNow(ctx, { projects });
110
+ const d = reviewDestinations({ review, targets: all, skill, row, assistants: assistantsNow(ctx) });
111
+ return {
112
+ targets: d.targets.map(wholeTarget), all, keys: d.keys, named: d.named, fallback: d.fallback,
113
+ words: all.filter((t) => d.keys.has(t.key)).map((t) => targetWords(ctx, t)),
114
+ missing: d.missing.map((m) => ({ ...m, words: targetWords(ctx, m.target), why: ctx.t(`skillsStudio.publish.unavailable.${m.reason}`) })),
115
+ };
116
+ }
117
+
118
+ /**
119
+ * The workspaces of a skill read it again (its installations, pointers) after an install from another surface, so
120
+ * their header pill, banners and Publish say what the Library row says. `resetPublish` starts their Publish over.
121
+ */
122
+ export async function refreshSkillWorkspaces(ctx, skillId, { resetPublish = false } = {}) {
123
+ if (!skillId) return;
124
+ let fresh = null;
125
+ try { fresh = (await ctx.api.fetchSkill(skillId)).skill; } catch { return; }
126
+ for (const ws of ctx.workspaces.values()) {
127
+ if (ws.skill?.skillId !== skillId) continue;
128
+ ws.skill = fresh;
129
+ if (resetPublish) { ws.pub = null; ws.publishReviewId = null; ws.publishRevisionId = null; }
130
+ }
131
+ }
132
+
133
+ /**
134
+ * The refresh hints of an outcome, worded (one per runtime that needs something). A runtime the resolver did not
135
+ * find on this computer is said as such: the copy waits in its folder, and nothing lists it until it is installed.
136
+ */
137
+ export function refreshWords(ctx, outcome) {
138
+ const installed = installedNow(ctx);
139
+ return (outcome?.refresh || []).map((x) => {
140
+ const kind = Array.isArray(installed) && ['claude', 'codex', 'opencode'].includes(x.runtime) && !installed.includes(x.runtime) ? 'not-installed' : x.kind;
141
+ return ctx.t(`skillsStudio.publish.refresh.${kind}`, { runtime: ctx.t(`skillsStudio.runtime.${x.runtime}`) });
142
+ });
143
+ }
144
+
145
+ /** Why a runtime's settings turn the skill off, in words (sx-plan.js eligibility reason). */
146
+ export function eligibilityWords(ctx, runtime, reason) {
147
+ const { t } = ctx;
148
+ const name = t(`skillsStudio.runtime.${['claude', 'codex', 'opencode', 'agents'].includes(runtime) ? runtime : 'claude'}`);
149
+ const file = shortPath(reason?.file || '');
150
+ const kind = ['codex-config', 'claude-override', 'claude-deny', 'opencode-permission', 'unreadable', 'invalid-json'].includes(reason?.kind) ? reason.kind : 'other';
151
+ return t(`skillsStudio.publish.eligibility.reason.${kind}`, { runtime: name, file, pattern: reason?.pattern || '*', rule: reason?.rule || reason?.key || '' });
152
+ }
153
+
154
+ /**
155
+ * The owner pressed "Enable for <runtime>" on a plan line: the runtime setting that turns the skill off is changed by
156
+ * the server over a backup (POST /v2/eligibility/enable); the words of what happened come back for a toast. A plan
157
+ * is made again by the caller: this changes no file of the skill.
158
+ * @returns {Promise<{ ok: boolean, line: string }>}
159
+ */
160
+ export async function enableFor(ctx, { runtime, line }) {
161
+ const { t } = ctx;
162
+ const name = t(`skillsStudio.runtime.${runtime}`);
163
+ const target = parseTargetKey(line.targetKey);
164
+ const sep = line.destination && line.destination.includes('\\') && !line.destination.includes('/') ? '\\' : '/';
165
+ const body = { runtime, name: line.destination ? line.destination.split(/[\\/]/).pop() : '', skillMd: `${line.destination}${sep}SKILL.md`, accountId: target.accountId, projectPath: target.projectPath };
166
+ const said = (out) => (out.changed === false ? { ok: true, line: t('skillsStudio.publish.enableNothing', { runtime: name }) } : { ok: out.ok !== false, line: t('skillsStudio.publish.enabled', { runtime: name, file: shortPath(out.file || ''), backup: shortPath(out.backup || '') }) });
167
+ try {
168
+ return said(await ctx.api.enableSkillsEligibility(body));
169
+ } catch (err) {
170
+ // OpenCode's rules deny every skill: loosened only after the owner's word (the server refuses first).
171
+ if (err?.code === 'deny_all' && runtime === 'opencode' && typeof ctx.ask === 'function') {
172
+ const answer = await ctx.ask({
173
+ title: t('skillsStudio.publish.enableDenyAllTitle', { runtime: name }),
174
+ lines: [t('skillsStudio.publish.enableDenyAllBody', { file: shortPath(err.body?.file || err.file || '') })],
175
+ buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'go', label: t('skillsStudio.publish.enableDenyAllGo'), kind: 'danger' }],
176
+ focus: 'cancel',
177
+ });
178
+ if (answer !== 'go') return { ok: false, line: t('skillsStudio.publish.enableFailed', { runtime: name, error: studioErrorText(ctx.t, err) }) };
179
+ try { return said(await ctx.api.enableSkillsEligibility({ ...body, confirmDenyAll: true })); } catch (e2) { return { ok: false, line: t('skillsStudio.publish.enableFailed', { runtime: name, error: studioErrorText(ctx.t, e2) }) }; }
180
+ }
181
+ return { ok: false, line: t('skillsStudio.publish.enableFailed', { runtime: name, error: studioErrorText(ctx.t, err) }) };
182
+ }
183
+ }
184
+
185
+ /** An outcome in words: the line, the hints for open sessions, what failed. */
186
+ export function outcomeWords(ctx, outcome) {
187
+ const { t } = ctx;
188
+ const where = outcome.ok.map((k) => keyWords(ctx, k)).join(', ');
189
+ const failed = outcome.failed.map((f) => keyWords(ctx, f.targetKey)).join(', ');
190
+ const reason = (f) => f?.code && BLOCK_CODES.includes(f.code) ? t(`skillsStudio.publish.blocked.${f.code}`) : f?.code || f?.reason ? studioErrorText(t, f) : f ? t(`skillsStudio.publish.result.${f.status}`) : failed;
191
+ const line = outcome.ok.length && !outcome.failed.length ? t('skillsStudio.publish.doneFor', { where })
192
+ : outcome.ok.length ? t('skillsStudio.publish.doneSome', { where, failed })
193
+ : t('skillsStudio.publish.doneNone', { reason: reason(outcome.failed[0]) });
194
+ return { line, refresh: refreshWords(ctx, outcome), failed: outcome.failed.map((f) => `${keyWords(ctx, f.targetKey)}: ${reason(f)}`) };
195
+ }
196
+
197
+ /** What blocks a plan's targets, in words (one line per target). */
198
+ export function blockedWords(ctx, rows) {
199
+ const { t } = ctx;
200
+ return (rows || []).map((r) => t('skillsStudio.publish.blockedFor', { target: keyWords(ctx, r.targetKey), reason: r.blocked.map((b) => t(`skillsStudio.publish.blocked.${b.code}`)).join(' ') }));
201
+ }
202
+
203
+ /**
204
+ * Ask what the plan asks, in one question, or nothing when it asks nothing.
205
+ * @returns {Promise<object|null>} the decisions to send (empty when nothing was asked), null when the owner said no
206
+ */
207
+ export async function askConfirmations(ctx, model) {
208
+ const { t } = ctx;
209
+ const c = confirmations(model);
210
+ if (!c.replace.length && !c.scan.length) return c.decisions;
211
+ const box = document.createElement('div');
212
+ box.className = 'sx-confirm-list';
213
+ setHTML(box, html`${c.replace.map((r) => html`<p class="sx-confirm-item"><strong>${keyWords(ctx, r.targetKey)}</strong>: <span class="sx-mono">${shortPath(r.destination)}</span> ${t(`skillsStudio.publish.confirmWhy.${r.replace.state === 'modified' ? 'modified' : 'unowned'}`)} ${t('skillsStudio.publish.confirmReplaced')}</p>`)}
214
+ ${c.scan.map((r) => html`<div class="sx-confirm-item"><p><strong>${keyWords(ctx, r.targetKey)}</strong>: ${t('skillsStudio.publish.confirmScanLine')}</p>
215
+ <ul class="sx-findings">${r.scanAck.findings.map((f) => html`<li class="sx-finding sx-finding--${f.severity}"><span class="sx-finding-sev">${t(`skillsStudio.review.severity.${['critical', 'high', 'medium', 'low'].includes(f.severity) ? f.severity : 'low'}`)}</span> ${scanFindingText(t, f)}${f.file ? html` <span class="sx-mono sx-dim">${f.file}${f.line ? `:${f.line}` : ''}</span>` : ''}</li>`)}</ul></div>`)}`);
216
+ const answer = await ctx.ask({
217
+ title: t('skillsStudio.publish.confirmTitle'),
218
+ lines: [t(c.replace.length ? 'skillsStudio.publish.confirmBodyReplace' : 'skillsStudio.publish.confirmBodyScan')],
219
+ extra: box,
220
+ buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'go', label: t(c.replace.length ? 'skillsStudio.publish.confirmGo' : 'skillsStudio.publish.confirmGoScan'), kind: c.replace.length ? 'danger' : 'primary' }],
221
+ focus: 'cancel',
222
+ wide: c.scan.length > 0,
223
+ });
224
+ return answer === 'go' ? c.decisions : null;
225
+ }
226
+
227
+ /**
228
+ * Plan, ask only what must be asked, install exactly that plan.
229
+ * @param {object} ctx the Studio's context
230
+ * @param {{ revisionId: string, targets: object[], reviewId?: string|null, plan?: object|null, onPlanned?: (model: object) => void }} o
231
+ * plan: a plan already made for exactly this ask (sx-plan.js planModel): used instead of asking for one
232
+ * @returns {Promise<{ status: 'done'|'blocked'|'cancelled'|'plan-failed'|'failed'|'gone', plan?: object, results?: object[], outcome?: object, blocked?: object[], error?: string }>}
233
+ */
234
+ export async function installFlow(ctx, { revisionId, targets, reviewId = null, plan = null, onPlanned = null }) {
235
+ const gen = ctx.gen();
236
+ let model = plan;
237
+ for (let attempt = 0; attempt < 2; attempt++) {
238
+ if (!model) {
239
+ try { model = planModel((await ctx.api.planSkillsInstall(revisionId, targets)).plan); } catch (err) { return { status: 'plan-failed', error: studioErrorText(ctx.t, err) }; }
240
+ if (!ctx.alive(gen)) return { status: 'gone' };
241
+ onPlanned?.(model);
242
+ }
243
+ const c = confirmations(model);
244
+ if (!c.ready.length) return { status: 'blocked', plan: model, blocked: c.blocked };
245
+ const decisions = await askConfirmations(ctx, model);
246
+ if (!ctx.alive(gen)) return { status: 'gone' };
247
+ if (decisions === null) return { status: 'cancelled', plan: model };
248
+ let result = null;
249
+ try {
250
+ result = await ctx.api.installSkillsRevision({ revisionId, targets, planDigest: model.planDigest, decisions, ...(reviewId ? { reviewId } : {}) });
251
+ } catch (err) {
252
+ if (!ctx.alive(gen)) return { status: 'gone' };
253
+ if (err?.status === 207 && err.body) result = err.body;
254
+ else if (err?.code === 'plan_changed' && err.body?.plan) { model = planModel(err.body.plan); onPlanned?.(model); continue; } // what is there changed meanwhile: planned again, asked again
255
+ else return { status: 'failed', error: studioErrorText(ctx.t, err), plan: model };
256
+ }
257
+ if (!ctx.alive(gen)) return { status: 'gone' };
258
+ const results = resultModel(result);
259
+ return { status: 'done', plan: model, results, outcome: installOutcome(results), blocked: c.blocked };
260
+ }
261
+ return { status: 'failed', error: ctx.t('skillsStudio.publish.planChanged'), plan: model };
262
+ }
263
+
264
+ /**
265
+ * The words a finished flow gets, for a toast and a notice: null when nothing is to be said (gone, cancelled).
266
+ * @returns {{ kind: 'info'|'error', line: string, lines: string[] }|null}
267
+ */
268
+ export function flowWords(ctx, out) {
269
+ const { t } = ctx;
270
+ if (!out || out.status === 'gone' || out.status === 'cancelled') return null;
271
+ if (out.status === 'done') {
272
+ const w = outcomeWords(ctx, out.outcome);
273
+ const extra = [...w.refresh, ...w.failed, ...blockedWords(ctx, out.blocked)];
274
+ return { kind: out.outcome.all ? 'info' : 'error', line: w.line, lines: extra };
275
+ }
276
+ if (out.status === 'blocked') return { kind: 'error', line: t('skillsStudio.publish.notInstalledBlocked'), lines: blockedWords(ctx, out.blocked) };
277
+ if (out.status === 'plan-failed') return { kind: 'error', line: t('skillsStudio.publish.planFailed', { error: out.error }), lines: [] };
278
+ return { kind: 'error', line: t('skillsStudio.publish.installFailed', { error: out.error || '' }), lines: [] };
279
+ }