synabun 2.0.10 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (656) hide show
  1. package/CHANGELOG.md +207 -0
  2. package/README.md +25 -2
  3. package/THIRD-PARTY-LICENSES.md +22 -0
  4. package/bin/synabun.js +71 -24
  5. package/docs/whatsapp.md +13 -13
  6. package/hooks/claude-code/pre-websearch.mjs +12 -1
  7. package/hooks/claude-code/shared.mjs +5 -0
  8. package/hooks/claude-code/stop.mjs +1 -1
  9. package/lib/claude-hooks.js +172 -18
  10. package/lib/client-config-repair.js +30 -17
  11. package/lib/desktop-app.js +7 -1
  12. package/lib/external-tools.js +7 -6
  13. package/lib/first-launch-protection.js +57 -12
  14. package/lib/mcp-dist.js +176 -0
  15. package/lib/release-npm-ci.mjs +138 -0
  16. package/lib/release-publish.mjs +91 -0
  17. package/lib/release-scan.mjs +50 -0
  18. package/lib/server-crash-record.js +123 -0
  19. package/lib/server-exit.js +318 -0
  20. package/lib/server-supervisor.js +508 -0
  21. package/lib/start-launcher.js +37 -4
  22. package/lib/tools-cli.js +45 -0
  23. package/lib/update-snapshot.js +302 -0
  24. package/lib/virustotal.mjs +203 -0
  25. package/lib/windows-crash-event.js +269 -0
  26. package/mcp-server/dist/http.d.ts +20 -1
  27. package/mcp-server/dist/http.d.ts.map +1 -1
  28. package/mcp-server/dist/http.js +59 -12
  29. package/mcp-server/dist/http.js.map +1 -1
  30. package/mcp-server/dist/index.d.ts +33 -27
  31. package/mcp-server/dist/index.d.ts.map +1 -1
  32. package/mcp-server/dist/index.js +128 -143
  33. package/mcp-server/dist/index.js.map +1 -1
  34. package/mcp-server/dist/preload.js +1 -1
  35. package/mcp-server/dist/preload.js.map +1 -1
  36. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  37. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  38. package/mcp-server/dist/services/budget-judgments.js +5 -3
  39. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  40. package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
  41. package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
  42. package/mcp-server/dist/services/codex-config-heal.js +81 -28
  43. package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
  44. package/mcp-server/dist/services/identity.d.ts +2 -2
  45. package/mcp-server/dist/services/identity.js +2 -2
  46. package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
  47. package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
  48. package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
  49. package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
  50. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  51. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  52. package/mcp-server/dist/services/memory-sessions.js +28 -2
  53. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  54. package/mcp-server/dist/services/neural-interface.d.ts +18 -11
  55. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  56. package/mcp-server/dist/services/neural-interface.js +67 -11
  57. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  58. package/mcp-server/dist/services/server-instructions.d.ts +7 -2
  59. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  60. package/mcp-server/dist/services/server-instructions.js +22 -63
  61. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  62. package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
  63. package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
  64. package/mcp-server/dist/services/tool-catalog.js +339 -0
  65. package/mcp-server/dist/services/tool-catalog.js.map +1 -0
  66. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
  67. package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
  68. package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
  69. package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
  70. package/mcp-server/dist/services/tool-list.d.ts +108 -0
  71. package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
  72. package/mcp-server/dist/services/tool-list.js +334 -0
  73. package/mcp-server/dist/services/tool-list.js.map +1 -0
  74. package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
  75. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
  76. package/mcp-server/dist/services/tool-runtime.js +400 -0
  77. package/mcp-server/dist/services/tool-runtime.js.map +1 -0
  78. package/mcp-server/dist/services/typesafe-config.js +1 -1
  79. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  80. package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
  81. package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
  82. package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
  83. package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
  84. package/mcp-server/dist/tools/agents.d.ts +14 -14
  85. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  86. package/mcp-server/dist/tools/agents.js +29 -19
  87. package/mcp-server/dist/tools/agents.js.map +1 -1
  88. package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
  89. package/mcp-server/dist/tools/choice.d.ts +6 -0
  90. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  91. package/mcp-server/dist/tools/choice.js +18 -13
  92. package/mcp-server/dist/tools/choice.js.map +1 -1
  93. package/mcp-server/dist/tools/git-tools.js +2 -2
  94. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  95. package/mcp-server/dist/tools/image-tools.d.ts +1 -1
  96. package/mcp-server/dist/tools/image-tools.js +1 -1
  97. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  98. package/mcp-server/dist/tools/morelogin.js +9 -6
  99. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  100. package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
  101. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
  102. package/mcp-server/dist/tools/skill-tools.js +642 -0
  103. package/mcp-server/dist/tools/skill-tools.js.map +1 -0
  104. package/mcp-server/dist/tools/skill.d.ts +7 -0
  105. package/mcp-server/dist/tools/skill.d.ts.map +1 -0
  106. package/mcp-server/dist/tools/skill.js +11 -0
  107. package/mcp-server/dist/tools/skill.js.map +1 -0
  108. package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
  109. package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
  110. package/mcp-server/dist/tools/tool-groups.js +147 -0
  111. package/mcp-server/dist/tools/tool-groups.js.map +1 -0
  112. package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
  113. package/mcp-server/dist/tools/whiteboard-format.js +7 -4
  114. package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
  115. package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
  116. package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
  117. package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
  118. package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
  119. package/mcp-server/package-lock.json +2 -2
  120. package/mcp-server/package.json +1 -1
  121. package/mcp-server/src/http.ts +75 -12
  122. package/mcp-server/src/index.ts +147 -154
  123. package/mcp-server/src/preload.ts +12 -12
  124. package/mcp-server/src/services/budget-judgments.ts +5 -3
  125. package/mcp-server/src/services/codex-config-heal.ts +80 -29
  126. package/mcp-server/src/services/identity.ts +2 -2
  127. package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
  128. package/mcp-server/src/services/memory-sessions.ts +24 -3
  129. package/mcp-server/src/services/neural-interface.ts +81 -21
  130. package/mcp-server/src/services/server-instructions.ts +29 -64
  131. package/mcp-server/src/services/tool-catalog.ts +403 -0
  132. package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
  133. package/mcp-server/src/services/tool-list.ts +410 -0
  134. package/mcp-server/src/services/tool-runtime.ts +457 -0
  135. package/mcp-server/src/services/typesafe-config.ts +1 -1
  136. package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
  137. package/mcp-server/src/tools/agents.ts +29 -20
  138. package/mcp-server/src/tools/choice.ts +17 -12
  139. package/mcp-server/src/tools/git-tools.ts +2 -2
  140. package/mcp-server/src/tools/image-tools.ts +1 -1
  141. package/mcp-server/src/tools/morelogin.ts +6 -4
  142. package/mcp-server/src/tools/skill-tools.ts +592 -0
  143. package/mcp-server/src/tools/skill.ts +13 -0
  144. package/mcp-server/src/tools/tool-groups.ts +164 -0
  145. package/mcp-server/src/tools/whiteboard-format.ts +9 -3
  146. package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
  147. package/neural-interface/i18n/en.json +3953 -228
  148. package/neural-interface/i18n/pt-BR.json +3901 -179
  149. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  150. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  151. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  152. package/neural-interface/lib/antigravity/bypass.js +100 -0
  153. package/neural-interface/lib/antigravity/catalog.js +273 -0
  154. package/neural-interface/lib/antigravity/home.js +260 -0
  155. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  156. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  157. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  158. package/neural-interface/lib/antigravity/policy.js +74 -0
  159. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  160. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  161. package/neural-interface/lib/antigravity/usage.js +43 -0
  162. package/neural-interface/lib/assistant-api.js +128 -28
  163. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  164. package/neural-interface/lib/assistant-brains/claude.js +4 -3
  165. package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
  166. package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
  167. package/neural-interface/lib/assistant-brains/codex.js +9 -5
  168. package/neural-interface/lib/assistant-brains/opencode.js +120 -54
  169. package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
  170. package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
  171. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  172. package/neural-interface/lib/assistant-budget-split.js +42 -15
  173. package/neural-interface/lib/assistant-catalog.js +81 -15
  174. package/neural-interface/lib/assistant-config.js +6 -3
  175. package/neural-interface/lib/assistant-dispatch.js +1073 -200
  176. package/neural-interface/lib/assistant-envelope.js +156 -2
  177. package/neural-interface/lib/assistant-evidence.js +80 -0
  178. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  179. package/neural-interface/lib/assistant-persona.js +93 -19
  180. package/neural-interface/lib/assistant-plan-permissions.js +25 -9
  181. package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
  182. package/neural-interface/lib/assistant-route-gate.js +6 -4
  183. package/neural-interface/lib/assistant-router.js +245 -38
  184. package/neural-interface/lib/assistant-runtime.js +214 -30
  185. package/neural-interface/lib/assistant-task-prompt.js +87 -21
  186. package/neural-interface/lib/assistant-tools.js +51 -0
  187. package/neural-interface/lib/assistant-usage.js +29 -0
  188. package/neural-interface/lib/backup-service.js +626 -4
  189. package/neural-interface/lib/backup-zip-reader.js +10 -5
  190. package/neural-interface/lib/backup-zip-writer.js +257 -0
  191. package/neural-interface/lib/browser-execution.js +10 -2
  192. package/neural-interface/lib/browser-guard-cdp.js +84 -0
  193. package/neural-interface/lib/browser-network-guard.js +476 -0
  194. package/neural-interface/lib/browser-policy-roots.js +64 -0
  195. package/neural-interface/lib/browser-tool-policy.js +198 -38
  196. package/neural-interface/lib/claude-agent-bridge.js +11 -2
  197. package/neural-interface/lib/claude-history-cache.js +210 -0
  198. package/neural-interface/lib/claude-hooks-status.js +103 -0
  199. package/neural-interface/lib/claude-model-catalog.js +5 -1
  200. package/neural-interface/lib/claude-temporary.js +5 -11
  201. package/neural-interface/lib/cli-install-api.js +1 -1
  202. package/neural-interface/lib/cli-install.js +163 -154
  203. package/neural-interface/lib/cli-resolver.js +13 -3
  204. package/neural-interface/lib/cli-update-planner.js +52 -41
  205. package/neural-interface/lib/cloudflared/tunnel.js +42 -2
  206. package/neural-interface/lib/codex-mcp-approvals.js +11 -0
  207. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  208. package/neural-interface/lib/codex-worker-app-server.js +191 -0
  209. package/neural-interface/lib/confined-fs.js +30 -3
  210. package/neural-interface/lib/crash-notice-api.js +98 -0
  211. package/neural-interface/lib/editor-completion-opencode.js +68 -68
  212. package/neural-interface/lib/editor-git-api.js +3 -20
  213. package/neural-interface/lib/exec-loop-command.js +13 -10
  214. package/neural-interface/lib/file-explorer-api.js +74 -40
  215. package/neural-interface/lib/file-explorer-watch.js +9 -3
  216. package/neural-interface/lib/git-bash/install.js +52 -6
  217. package/neural-interface/lib/git-bash/manager.js +59 -17
  218. package/neural-interface/lib/git-bash/pins.js +12 -0
  219. package/neural-interface/lib/git-read.js +2 -2
  220. package/neural-interface/lib/git-status-runner.js +390 -0
  221. package/neural-interface/lib/git-status-watchdog.js +40 -0
  222. package/neural-interface/lib/greeting/budgets.js +1 -0
  223. package/neural-interface/lib/greeting/engine.js +2 -2
  224. package/neural-interface/lib/greeting/sources.js +10 -12
  225. package/neural-interface/lib/greeting/writer.js +1 -1
  226. package/neural-interface/lib/http-guards.js +8 -2
  227. package/neural-interface/lib/install-details.js +4 -1
  228. package/neural-interface/lib/mcp-agent-servers.js +67 -0
  229. package/neural-interface/lib/mcp-client-config.js +35 -33
  230. package/neural-interface/lib/mcp-external-servers.js +332 -0
  231. package/neural-interface/lib/mcp-installer.js +2 -1
  232. package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
  233. package/neural-interface/lib/memory-session-capture.js +17 -3
  234. package/neural-interface/lib/morelogin.js +177 -25
  235. package/neural-interface/lib/native-loop-providers.js +965 -66
  236. package/neural-interface/lib/native-loop-runtime.js +290 -42
  237. package/neural-interface/lib/opencode-config.js +557 -0
  238. package/neural-interface/lib/opencode-db-schema.js +17 -34
  239. package/neural-interface/lib/opencode-history.js +48 -218
  240. package/neural-interface/lib/opencode-mcp-ready.js +108 -0
  241. package/neural-interface/lib/opencode-native-boundary.js +53 -0
  242. package/neural-interface/lib/opencode-protocol.js +249 -112
  243. package/neural-interface/lib/opencode-runtime-config.js +32 -22
  244. package/neural-interface/lib/opencode-serve-records.js +39 -21
  245. package/neural-interface/lib/opencode-v2-client.js +48 -153
  246. package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
  247. package/neural-interface/lib/opencode-worker-data.js +27 -0
  248. package/neural-interface/lib/opencode2-client.js +73 -37
  249. package/neural-interface/lib/opencode2-proxy.js +23 -63
  250. package/neural-interface/lib/opencode2-translate.js +51 -2
  251. package/neural-interface/lib/owner-authority.js +249 -0
  252. package/neural-interface/lib/page-session.js +275 -0
  253. package/neural-interface/lib/path-confine.js +12 -9
  254. package/neural-interface/lib/peer-identity.js +793 -0
  255. package/neural-interface/lib/pty-host/core.js +28 -1
  256. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  257. package/neural-interface/lib/pty-host/manager.js +12 -1
  258. package/neural-interface/lib/remote-policy.js +1 -1
  259. package/neural-interface/lib/request-gate.js +20 -2
  260. package/neural-interface/lib/rulesets/api.js +7 -6
  261. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  262. package/neural-interface/lib/rulesets/installer.js +180 -36
  263. package/neural-interface/lib/rulesets/json-edit.js +1 -1
  264. package/neural-interface/lib/rulesets/render.js +1 -1
  265. package/neural-interface/lib/session-cache-builder.js +81 -31
  266. package/neural-interface/lib/session-title-generator.js +58 -36
  267. package/neural-interface/lib/skills/api-v2.js +495 -0
  268. package/neural-interface/lib/skills/bundle.js +418 -0
  269. package/neural-interface/lib/skills/catalog.js +298 -0
  270. package/neural-interface/lib/skills/creation-api.js +191 -0
  271. package/neural-interface/lib/skills/creation-brief.js +247 -0
  272. package/neural-interface/lib/skills/creation-sources.js +285 -0
  273. package/neural-interface/lib/skills/creation.js +843 -0
  274. package/neural-interface/lib/skills/deploy.js +569 -0
  275. package/neural-interface/lib/skills/digest.js +119 -0
  276. package/neural-interface/lib/skills/discover.js +111 -0
  277. package/neural-interface/lib/skills/draft-confinement.js +216 -0
  278. package/neural-interface/lib/skills/eval-confinement.js +399 -0
  279. package/neural-interface/lib/skills/eval-sandbox.js +280 -0
  280. package/neural-interface/lib/skills/eval-trace.js +135 -0
  281. package/neural-interface/lib/skills/evals-api.js +212 -0
  282. package/neural-interface/lib/skills/evals.js +1573 -0
  283. package/neural-interface/lib/skills/export.js +203 -0
  284. package/neural-interface/lib/skills/frontmatter.js +64 -0
  285. package/neural-interface/lib/skills/health.js +75 -0
  286. package/neural-interface/lib/skills/json-edit.js +165 -0
  287. package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
  288. package/neural-interface/lib/skills/known-bridges.js +96 -0
  289. package/neural-interface/lib/skills/library.js +823 -0
  290. package/neural-interface/lib/skills/local-store.js +21 -0
  291. package/neural-interface/lib/skills/migration.js +433 -0
  292. package/neural-interface/lib/skills/operations.js +45 -0
  293. package/neural-interface/lib/skills/ownership.js +427 -0
  294. package/neural-interface/lib/skills/packages.js +88 -0
  295. package/neural-interface/lib/skills/reviews.js +212 -0
  296. package/neural-interface/lib/skills/revision.js +80 -0
  297. package/neural-interface/lib/skills/roots.js +269 -0
  298. package/neural-interface/lib/skills/runtime-checks.js +358 -0
  299. package/neural-interface/lib/skills/scan-assets.js +252 -0
  300. package/neural-interface/lib/skills/scan-context.js +26 -0
  301. package/neural-interface/lib/skills/scan-shell.js +324 -0
  302. package/neural-interface/lib/skills/scan.js +364 -0
  303. package/neural-interface/lib/skills/scanners.js +54 -0
  304. package/neural-interface/lib/skills/service.js +530 -0
  305. package/neural-interface/lib/skills/telemetry.js +119 -0
  306. package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
  307. package/neural-interface/lib/synabun-version.js +1 -1
  308. package/neural-interface/lib/system-activity.js +1 -1
  309. package/neural-interface/lib/tool-lists.js +124 -0
  310. package/neural-interface/lib/whatsapp/brain.js +7 -3
  311. package/neural-interface/lib/whatsapp/bridge.js +6 -3
  312. package/neural-interface/lib/whatsapp/cards.js +22 -9
  313. package/neural-interface/lib/whatsapp/config.js +1 -1
  314. package/neural-interface/lib/worker-confinement.js +1268 -0
  315. package/neural-interface/lib/worker-guard-setting.js +56 -0
  316. package/neural-interface/lib/worker-mcp-channel.js +175 -0
  317. package/neural-interface/lib/worker-question.js +74 -0
  318. package/neural-interface/lib/worker-relay-ports.js +227 -0
  319. package/neural-interface/lib/worker-resume.js +238 -0
  320. package/neural-interface/lib/worker-sandbox.js +329 -0
  321. package/neural-interface/package-lock.json +34 -12
  322. package/neural-interface/package.json +3 -2
  323. package/neural-interface/public/index.html +3 -1
  324. package/neural-interface/public/index2d.html +19 -11
  325. package/neural-interface/public/onboarding.html +3 -3
  326. package/neural-interface/public/shared/account-menu.js +8 -5
  327. package/neural-interface/public/shared/agent-runtime-options.js +125 -31
  328. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  329. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  330. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  331. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  332. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  333. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  334. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  335. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  336. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  337. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  338. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  339. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  340. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  341. package/neural-interface/public/shared/api.js +1527 -1336
  342. package/neural-interface/public/shared/app-frame.js +192 -0
  343. package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
  344. package/neural-interface/public/shared/assistant/asst-api.js +1 -1
  345. package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
  346. package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
  347. package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
  348. package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
  349. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  350. package/neural-interface/public/shared/assistant/asst-control.js +205 -26
  351. package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
  352. package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
  353. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  354. package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
  355. package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
  356. package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
  357. package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
  358. package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
  359. package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
  360. package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
  361. package/neural-interface/public/shared/assistant/asst-render.js +445 -82
  362. package/neural-interface/public/shared/assistant/asst-route.js +304 -53
  363. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  364. package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
  365. package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
  366. package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
  367. package/neural-interface/public/shared/assistant/asst-state.js +87 -24
  368. package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
  369. package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
  370. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
  371. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  372. package/neural-interface/public/shared/automation-context.js +5 -2
  373. package/neural-interface/public/shared/cc-dropdown.js +89 -19
  374. package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
  375. package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
  376. package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
  377. package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
  378. package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
  379. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  380. package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
  381. package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
  382. package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
  383. package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
  384. package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
  385. package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
  386. package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
  387. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  388. package/neural-interface/public/shared/cli-install-view.js +11 -12
  389. package/neural-interface/public/shared/cli-status.js +11 -10
  390. package/neural-interface/public/shared/constants.js +5 -4
  391. package/neural-interface/public/shared/context-menu-areas.js +34 -0
  392. package/neural-interface/public/shared/context-menu-logic.js +243 -0
  393. package/neural-interface/public/shared/context-menu-model.js +367 -0
  394. package/neural-interface/public/shared/context-menu.js +372 -0
  395. package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
  396. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  397. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  398. package/neural-interface/public/shared/crash-notice-view.js +168 -0
  399. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  400. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  401. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  402. package/neural-interface/public/shared/desktop-update-view.js +21 -0
  403. package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
  404. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  405. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  406. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  407. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  408. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  409. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  410. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  411. package/neural-interface/public/shared/html-shell.js +30 -15
  412. package/neural-interface/public/shared/i18n.js +2 -0
  413. package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
  414. package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
  415. package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
  416. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
  417. package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
  418. package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
  419. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
  420. package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
  421. package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
  422. package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
  423. package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
  424. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
  425. package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
  426. package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
  427. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  428. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
  429. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
  430. package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
  431. package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
  432. package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
  433. package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
  434. package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
  435. package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
  436. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
  437. package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
  438. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
  439. package/neural-interface/public/shared/page-session.js +96 -0
  440. package/neural-interface/public/shared/provider-icons.js +4 -2
  441. package/neural-interface/public/shared/registry.js +5 -3
  442. package/neural-interface/public/shared/retired-2d.js +3 -0
  443. package/neural-interface/public/shared/schedules-view.js +195 -61
  444. package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
  445. package/neural-interface/public/shared/settings/settings-data.js +3 -1
  446. package/neural-interface/public/shared/settings/settings-ia.js +6 -6
  447. package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
  448. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  449. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
  450. package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
  451. package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
  452. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  453. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  454. package/neural-interface/public/shared/skills/ss-args.js +96 -0
  455. package/neural-interface/public/shared/skills/ss-creation.js +222 -0
  456. package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
  457. package/neural-interface/public/shared/skills/ss-doc.js +173 -0
  458. package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
  459. package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
  460. package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
  461. package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
  462. package/neural-interface/public/shared/skills/ss-evals.js +417 -0
  463. package/neural-interface/public/shared/skills/ss-form.js +159 -0
  464. package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
  465. package/neural-interface/public/shared/skills/ss-ids.js +32 -0
  466. package/neural-interface/public/shared/skills/ss-lint.js +149 -0
  467. package/neural-interface/public/shared/skills/ss-presets.js +117 -0
  468. package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
  469. package/neural-interface/public/shared/skills/ss-resources.js +165 -0
  470. package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
  471. package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
  472. package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
  473. package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
  474. package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
  475. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
  476. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
  477. package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
  478. package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
  479. package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
  480. package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
  481. package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
  482. package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
  483. package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
  484. package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
  485. package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
  486. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  487. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  488. package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
  489. package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
  490. package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
  491. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
  492. package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
  493. package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
  494. package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
  495. package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
  496. package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
  497. package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
  498. package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
  499. package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
  500. package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
  501. package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
  502. package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
  503. package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
  504. package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
  505. package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
  506. package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
  507. package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
  508. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
  509. package/neural-interface/public/shared/state.js +1 -1
  510. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  511. package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
  512. package/neural-interface/public/shared/styles.css +1822 -1756
  513. package/neural-interface/public/shared/synabun-mascot.js +1115 -9
  514. package/neural-interface/public/shared/text-cache.js +29 -0
  515. package/neural-interface/public/shared/tool-list-picker.js +55 -0
  516. package/neural-interface/public/shared/tool-list-storage.js +24 -0
  517. package/neural-interface/public/shared/tool-list-view.js +112 -0
  518. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  519. package/neural-interface/public/shared/tour/tour-actor.js +268 -0
  520. package/neural-interface/public/shared/tour/tour-beats.js +336 -0
  521. package/neural-interface/public/shared/tour/tour-engine.js +884 -0
  522. package/neural-interface/public/shared/tour/tour-ink.js +390 -0
  523. package/neural-interface/public/shared/tour/tour-stage.js +440 -0
  524. package/neural-interface/public/shared/tour/tour-state.js +40 -0
  525. package/neural-interface/public/shared/tour/tour-tips.js +99 -0
  526. package/neural-interface/public/shared/tour/tour.css +144 -0
  527. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  528. package/neural-interface/public/shared/ui-assistant.js +2 -0
  529. package/neural-interface/public/shared/ui-automation-studio.js +111 -140
  530. package/neural-interface/public/shared/ui-claude-panel.js +376 -82
  531. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  532. package/neural-interface/public/shared/ui-command-runner.js +6 -3
  533. package/neural-interface/public/shared/ui-context-menu.js +295 -0
  534. package/neural-interface/public/shared/ui-cost-widget.js +57 -59
  535. package/neural-interface/public/shared/ui-crash-notice.js +187 -0
  536. package/neural-interface/public/shared/ui-detail.js +15 -3
  537. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  538. package/neural-interface/public/shared/ui-file-links.js +2 -2
  539. package/neural-interface/public/shared/ui-games.js +2 -2
  540. package/neural-interface/public/shared/ui-image-gallery.js +50 -47
  541. package/neural-interface/public/shared/ui-judgments.js +1 -1
  542. package/neural-interface/public/shared/ui-keybinds.js +33 -2
  543. package/neural-interface/public/shared/ui-link.js +3 -3
  544. package/neural-interface/public/shared/ui-menubar.js +10 -20
  545. package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
  546. package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
  547. package/neural-interface/public/shared/ui-navbar.js +49 -24
  548. package/neural-interface/public/shared/ui-notifications.js +1 -0
  549. package/neural-interface/public/shared/ui-panels.js +96 -65
  550. package/neural-interface/public/shared/ui-resume.js +9 -7
  551. package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
  552. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  553. package/neural-interface/public/shared/ui-sessions.js +27 -15
  554. package/neural-interface/public/shared/ui-settings.js +683 -526
  555. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  556. package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
  557. package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
  558. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  559. package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
  560. package/neural-interface/public/shared/ui-skills.js +33 -2830
  561. package/neural-interface/public/shared/ui-styleguide.js +5 -2
  562. package/neural-interface/public/shared/ui-sync.js +15 -5
  563. package/neural-interface/public/shared/ui-terminal.js +270 -190
  564. package/neural-interface/public/shared/ui-tool-updates.js +6 -4
  565. package/neural-interface/public/shared/ui-tooltip.js +81 -51
  566. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  567. package/neural-interface/public/shared/ui-trash.js +6 -4
  568. package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
  569. package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
  570. package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
  571. package/neural-interface/public/shared/ui-tutorial.css +47 -100
  572. package/neural-interface/public/shared/ui-tutorial.js +285 -509
  573. package/neural-interface/public/shared/ui-update.js +38 -2
  574. package/neural-interface/public/shared/ui-whiteboard.js +312 -59
  575. package/neural-interface/public/shared/ui-worker-end.js +5 -1
  576. package/neural-interface/public/shared/ui-workspaces.js +3 -2
  577. package/neural-interface/public/shared/view-variant.js +66 -0
  578. package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
  579. package/neural-interface/public/shared/whiteboard-list.js +172 -0
  580. package/neural-interface/public/shared/worker-end.js +10 -3
  581. package/neural-interface/public/shared/worker-questions.js +34 -0
  582. package/neural-interface/public/shared/worker-restart.js +12 -0
  583. package/neural-interface/public/sidepanel-session.html +2 -2
  584. package/neural-interface/public/variant/2d/background.js +2 -1
  585. package/neural-interface/public/variant/2d/context-menu.js +8 -2
  586. package/neural-interface/public/variant/2d/graph.js +42 -20
  587. package/neural-interface/public/variant/2d/hulls.js +14 -9
  588. package/neural-interface/public/variant/2d/lasso.js +11 -7
  589. package/neural-interface/public/variant/2d/main.js +32 -1
  590. package/neural-interface/public/variant/2d/minimap.js +11 -13
  591. package/neural-interface/public/variant/3d/main.js +69 -6
  592. package/neural-interface/public/variant/3d/map-controls.js +24 -9
  593. package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
  594. package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
  595. package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
  596. package/neural-interface/public/vendor/caveat/caveat.css +17 -0
  597. package/neural-interface/public/vendor/caveat/manifest.json +12 -0
  598. package/neural-interface/public/vendor/yaml/LICENSE +13 -0
  599. package/neural-interface/public/vendor/yaml/manifest.json +4 -0
  600. package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
  601. package/neural-interface/scripts/build-vendor.mjs +32 -0
  602. package/neural-interface/server.js +4553 -2855
  603. package/neural-interface/templates/rulesets/README.md +1 -1
  604. package/neural-interface/templates/rulesets/core.md +1 -1
  605. package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
  606. package/neural-interface/templates/rulesets/manifest.json +8 -8
  607. package/npm-shrinkwrap.json +34 -12
  608. package/package.json +17 -3
  609. package/postinstall.js +9 -4
  610. package/preuninstall.js +1 -1
  611. package/setup.js +97 -322
  612. package/skills/skill-forge/SKILL.md +177 -0
  613. package/skills/skill-forge/evals/evals.json +54 -0
  614. package/skills/skill-forge/references/package.md +105 -0
  615. package/skills/skill-forge/references/review-and-recovery.md +63 -0
  616. package/skills/skill-forge/references/runtime-profiles.md +20 -0
  617. package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
  618. package/skills/synabun/SKILL.md +10 -7
  619. package/skills/synabun/modules/audit.md +1 -5
  620. package/skills/synabun/modules/changelog.md +1 -5
  621. package/skills/synabun/modules/idea.md +1 -1
  622. package/skills/synabun/modules/memorize.md +6 -9
  623. package/skills/synabun/modules/schedule.md +6 -6
  624. package/mcp-server/dist/services/profiles.d.ts +0 -120
  625. package/mcp-server/dist/services/profiles.d.ts.map +0 -1
  626. package/mcp-server/dist/services/profiles.js +0 -387
  627. package/mcp-server/dist/services/profiles.js.map +0 -1
  628. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
  629. package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
  630. package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
  631. package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
  632. package/mcp-server/dist/tools/leonardo.d.ts +0 -13
  633. package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
  634. package/mcp-server/dist/tools/leonardo.js +0 -21
  635. package/mcp-server/dist/tools/leonardo.js.map +0 -1
  636. package/mcp-server/dist/tools/profile.d.ts +0 -17
  637. package/mcp-server/dist/tools/profile.d.ts.map +0 -1
  638. package/mcp-server/dist/tools/profile.js +0 -114
  639. package/mcp-server/dist/tools/profile.js.map +0 -1
  640. package/mcp-server/src/services/profiles.ts +0 -428
  641. package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
  642. package/mcp-server/src/tools/leonardo.ts +0 -30
  643. package/mcp-server/src/tools/profile.ts +0 -122
  644. package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
  645. package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
  646. package/neural-interface/lib/mcp-profile-servers.js +0 -120
  647. package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
  648. package/skills/leonardo/SKILL.md +0 -168
  649. package/skills/leonardo/icon.svg +0 -1
  650. package/skills/leonardo/modules/browser-guide.md +0 -378
  651. package/skills/leonardo/modules/image-prompter.md +0 -283
  652. package/skills/leonardo/modules/model-advisor.md +0 -100
  653. package/skills/leonardo/modules/prompt-library.md +0 -110
  654. package/skills/leonardo/modules/style-guide.md +0 -186
  655. package/skills/leonardo/modules/video-prompter.md +0 -317
  656. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,237 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: drafting a skill with AI (P3), what decides
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Create's AI box: the owner's words, an example of a good result, the sources (a session transcript, a web page, a
6
+ // registered project, memories, an existing skill), and the options (name, who invokes it, the runtimes, the worker
7
+ // that drafts it, its work time). This module keeps that form as plain data (it survives the window: storage), turns
8
+ // it into the request the server checks with the same rules (../ss-creation.js), and says what a creation shows:
9
+ // its steps, its state, what it made or why it stopped. No DOM; the view (sx-create-view.js) draws it.
10
+
11
+ import { CREATION_LIMITS, DRAFT_PROVIDERS, isActiveCreation, normalizeCreationRequest, sourceSummary } from '../ss-creation.js';
12
+
13
+ export const AI_FORM_KEY = 'skills-studio-create-ai';
14
+ /** The places a source is added from (describe is the prompt itself). */
15
+ export const AI_SOURCE_KINDS = Object.freeze(['conversation', 'url', 'repo', 'memory', 'import']);
16
+ /** The runtimes a drafted skill is meant for (its install targets, ss-creation.js CREATION_TARGET_RUNTIMES), not who drafts it. */
17
+ export const DRAFT_RUNTIMES = Object.freeze(['claude', 'codex', 'opencode', 'antigravity']);
18
+ export const WORK_MINUTES = Object.freeze([20, 30, 45, 60, 90]);
19
+ /** The steps a creation shows, in order. */
20
+ export const CREATION_STEPS = Object.freeze(['sources', 'drafting', 'checking', 'review']);
21
+ /** Error codes the view words (`skillsStudio.create.ai.error.<code>`); anything else is said as `other`. */
22
+ export const CREATION_ERRORS = Object.freeze(['needs_browser', 'needs_input', 'worker_blocked', 'worker_failed', 'worker_ended', 'draft_invalid', 'draft_unchanged', 'draft_unsafe', 'budget_exceeded', 'provider_unsupported', 'request_too_long', 'source_failed', 'source_not_found', 'source_empty', 'source_unavailable', 'dispatch_failed', 'drafting_unavailable', 'too_many_creations', 'interrupted', 'invalid_request', 'other']);
23
+
24
+ export function emptyAiForm() {
25
+ return { prompt: '', example: '', showExample: false, sources: [], name: '', invocation: 'auto', runtimes: ['claude'], target: '', minutes: CREATION_LIMITS.maxMinutes.default };
26
+ }
27
+
28
+ /** The form as it was kept (a bad or old copy gives the empty form, its prompt kept when it can be). */
29
+ export function readAiForm(text, legacyPrompt = '') {
30
+ const base = emptyAiForm();
31
+ let saved = null;
32
+ try { saved = text ? JSON.parse(text) : null; } catch { saved = null; }
33
+ if (!saved || typeof saved !== 'object') return { ...base, prompt: typeof legacyPrompt === 'string' ? legacyPrompt : '' };
34
+ const runtimes = Array.isArray(saved.runtimes) ? saved.runtimes.filter((r) => DRAFT_RUNTIMES.includes(r)) : base.runtimes;
35
+ return {
36
+ prompt: typeof saved.prompt === 'string' ? saved.prompt.slice(0, CREATION_LIMITS.promptChars) : '',
37
+ example: typeof saved.example === 'string' ? saved.example.slice(0, CREATION_LIMITS.exampleChars) : '',
38
+ showExample: saved.showExample === true || (typeof saved.example === 'string' && saved.example !== ''),
39
+ sources: Array.isArray(saved.sources) ? saved.sources.filter((s) => s && AI_SOURCE_KINDS.includes(s.kind)).slice(0, CREATION_LIMITS.sources) : [],
40
+ name: typeof saved.name === 'string' ? saved.name.slice(0, CREATION_LIMITS.nameChars) : '',
41
+ invocation: saved.invocation === 'manual' ? 'manual' : 'auto',
42
+ runtimes: runtimes.length ? runtimes : base.runtimes,
43
+ // A worker kept from before that can no longer draft (only Claude Code can) goes back to the default.
44
+ target: typeof saved.target === 'string' && DRAFT_PROVIDERS.includes(targetOf(saved.target)?.provider) ? saved.target.slice(0, 260) : '',
45
+ minutes: WORK_MINUTES.includes(Number(saved.minutes)) ? Number(saved.minutes) : base.minutes,
46
+ };
47
+ }
48
+
49
+ export const writeAiForm = (form) => JSON.stringify(form);
50
+
51
+ /** A source the same as one already there (the same session, address, project and paths, query, skill). */
52
+ export function sameSource(a, b) {
53
+ if (!a || !b || a.kind !== b.kind) return false;
54
+ switch (a.kind) {
55
+ case 'conversation': return a.provider === b.provider && a.sessionId === b.sessionId;
56
+ case 'url': return a.url === b.url;
57
+ case 'repo': return a.project === b.project && JSON.stringify(a.paths || []) === JSON.stringify(b.paths || []);
58
+ case 'memory': return a.query === b.query && (a.project || '') === (b.project || '');
59
+ case 'import': return (a.stagingId || a.skillId || a.artifactId) === (b.stagingId || b.skillId || b.artifactId);
60
+ default: return false;
61
+ }
62
+ }
63
+
64
+ /** The form with one more source: 'added', 'duplicate' or 'full' says what happened. */
65
+ export function addSource(form, source) {
66
+ if (form.sources.some((s) => sameSource(s, source))) return { form, outcome: 'duplicate' };
67
+ if (form.sources.length >= CREATION_LIMITS.sources) return { form, outcome: 'full' };
68
+ return { form: { ...form, sources: [...form.sources, source] }, outcome: 'added' };
69
+ }
70
+
71
+ export const removeSource = (form, index) => ({ ...form, sources: form.sources.filter((_, i) => i !== index) });
72
+
73
+ /** A source's chip: its kind and a short label. */
74
+ export function sourceChip(source) {
75
+ return { kind: source.kind, label: sourceSummary(source) || source.kind, title: source.kind === 'url' ? source.url : (source.project || source.query || source.label || '') };
76
+ }
77
+
78
+ /** "provider|model" of the model dropdown → a target ({} for the owner's default route). */
79
+ export function targetOf(value) {
80
+ const s = String(value || '');
81
+ if (!s) return null;
82
+ const at = s.indexOf('|');
83
+ const provider = at < 0 ? s : s.slice(0, at);
84
+ const model = at < 0 ? '' : s.slice(at + 1);
85
+ return { provider, ...(model ? { model } : {}) };
86
+ }
87
+
88
+ /**
89
+ * The request the form sends (POST /v2/creations), checked with the server's own rules.
90
+ * @returns {{ ok: boolean, body: object, problems: { field: string, code: string }[] }}
91
+ */
92
+ export function buildCreationRequest(form) {
93
+ const body = {
94
+ prompt: form.prompt, ...(form.example && form.showExample ? { example: form.example } : {}), ...(form.name ? { name: form.name } : {}),
95
+ invocation: form.invocation, sources: form.sources.map(({ label, ...rest }) => ({ ...rest, ...(label ? { label } : {}) })),
96
+ targets: (form.runtimes.length ? form.runtimes : ['claude']).map((runtime) => ({ runtime })),
97
+ ...(targetOf(form.target) ? { target: targetOf(form.target) } : {}),
98
+ maxMinutes: form.minutes, submit: true,
99
+ };
100
+ const { ok, problems } = normalizeCreationRequest(body);
101
+ return { ok, body, problems };
102
+ }
103
+
104
+ /** The key of what is wrong with a request, for the view (`skillsStudio.create.ai.problem.<key>`). */
105
+ export function problemKey(problem) {
106
+ const code = String(problem?.code || '');
107
+ if (code === 'prompt-required') return 'prompt';
108
+ if (code === 'name-format') return 'name';
109
+ if (code.startsWith('url-')) return 'url';
110
+ if (code.startsWith('repo-')) return 'repo';
111
+ if (code.startsWith('memory-')) return 'memory';
112
+ if (code.startsWith('conversation-')) return 'conversation';
113
+ if (code.startsWith('import-')) return 'import';
114
+ if (code === 'target-provider') return 'provider';
115
+ if (code.startsWith('targets-')) return 'targets';
116
+ if (code.endsWith('-long')) return 'long';
117
+ return 'other';
118
+ }
119
+ export const PROBLEM_KEYS = Object.freeze(['prompt', 'name', 'url', 'repo', 'memory', 'conversation', 'import', 'provider', 'targets', 'long', 'other']);
120
+
121
+ /**
122
+ * The steps of a creation: each 'done', 'active', 'failed' or 'waiting'.
123
+ * @param {object} job a creation (GET /v2/creations/:id)
124
+ */
125
+ export function creationSteps(job) {
126
+ const order = { queued: 0, gathering: 0, drafting: 1, checking: 2, submitting: 3, done: 4 };
127
+ const state = String(job?.state || 'queued');
128
+ // Where it stopped: the last step it reached.
129
+ const reached = state in order ? order[state] : (job?.result ? 4 : job?.run ? (job?.files?.length ? 2 : 1) : (job?.sources?.length ? 1 : 0));
130
+ return CREATION_STEPS.map((id, i) => {
131
+ if (state === 'done') return { id, state: id === 'review' && !job?.result?.reviewId ? 'skipped' : 'done' };
132
+ if (i < reached) return { id, state: 'done' };
133
+ if (i === reached) return { id, state: isActiveCreation(state) ? 'active' : (state === 'failed' || state === 'interrupted' ? 'failed' : 'waiting') };
134
+ return { id, state: 'waiting' };
135
+ });
136
+ }
137
+
138
+ /** What a creation is called in lists: the name it made, the name asked for, or the first words of the prompt. */
139
+ export function creationTitle(job) {
140
+ const name = job?.result?.name || job?.partial?.name || job?.request?.name;
141
+ if (name) return name;
142
+ const words = String(job?.request?.prompt || '').replace(/\s+/g, ' ').trim();
143
+ return words ? (words.length > 60 ? `${words.slice(0, 59)}…` : words) : (job?.request?.sources?.[0] ? sourceSummary(job.request.sources[0]) : job?.jobId || '');
144
+ }
145
+
146
+ /** The error code the view words. */
147
+ export function errorCode(job) {
148
+ const code = String(job?.error?.code || '');
149
+ if (CREATION_ERRORS.includes(code)) return code;
150
+ if (code.startsWith('dispatch_')) return 'dispatch_failed';
151
+ return 'other';
152
+ }
153
+
154
+ /** Seconds since a time (for "2 min"); null without one. */
155
+ export function elapsedSeconds(job, now = Date.now()) {
156
+ const from = Date.parse(job?.createdAt || '');
157
+ if (!Number.isFinite(from)) return null;
158
+ const to = job?.finishedAt ? Date.parse(job.finishedAt) : now;
159
+ return Math.max(0, Math.round((to - from) / 1000));
160
+ }
161
+
162
+ /** "1:05" or "12 s". */
163
+ export function clockText(seconds) {
164
+ if (seconds === null || seconds === undefined) return '';
165
+ if (seconds < 60) return `${seconds} s`;
166
+ const m = Math.floor(seconds / 60);
167
+ const s = String(seconds % 60).padStart(2, '0');
168
+ return m < 60 ? `${m}:${s}` : `${Math.floor(m / 60)}:${String(m % 60).padStart(2, '0')}:${s}`;
169
+ }
170
+
171
+ /** A size in bytes as the file list says it. */
172
+ export function sizeText(bytes) {
173
+ const n = Number(bytes) || 0;
174
+ if (n < 1024) return `${n} B`;
175
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(n < 10 * 1024 ? 1 : 0)} KB`;
176
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
177
+ }
178
+
179
+ /** How long to wait before asking for a running creation again (a long poll holds most of it). */
180
+ export function pollSeconds(job) {
181
+ if (!isActiveCreation(job?.state)) return null;
182
+ return job.state === 'queued' || job.state === 'gathering' ? 5 : 20;
183
+ }
184
+
185
+ /**
186
+ * The providers that draft, as the server lists them (GET /creations/options `drafting.providers`, Claude Code first
187
+ * and the default) with what it says of each (`providerStatus`: one that cannot start here now is greyed out with its
188
+ * plain reason and cannot be picked). A server without the list: Claude Code alone. A provider the server says nothing
189
+ * of on its own takes what it says of drafting as a whole (an older server: `available: false` and why).
190
+ * @returns {{ provider: string, available: boolean, reason: string|null }[]}
191
+ */
192
+ export function draftingProviders(drafting) {
193
+ const listed = Array.isArray(drafting?.providers) ? drafting.providers.filter((p) => typeof p === 'string' && DRAFT_PROVIDERS.includes(p)) : [];
194
+ const providers = listed.length ? [...new Set(listed)] : ['claude-code'];
195
+ const status = new Map((Array.isArray(drafting?.providerStatus) ? drafting.providerStatus : []).filter((p) => p && typeof p.provider === 'string').map((p) => [p.provider, p]));
196
+ const allOff = drafting?.available === false;
197
+ return providers.sort((a, b) => (a === 'claude-code' ? -1 : b === 'claude-code' ? 1 : 0)).map((provider) => {
198
+ const st = status.get(provider);
199
+ if (st) return { provider, available: st.available === true, reason: st.available === true ? null : String(st.reason || '') };
200
+ return allOff ? { provider, available: false, reason: String(drafting.reason || '') } : { provider, available: true, reason: null };
201
+ });
202
+ }
203
+
204
+
205
+ /**
206
+ * The model dropdown: the owner's route first (when it is one that drafts and can start here), then the models of
207
+ * each provider the server lists from the Assistant's catalog; a provider that cannot draft here now is one greyed
208
+ * option with its reason, never selectable. The default (Claude Code's own model, when there is no route) is greyed
209
+ * too when Claude Code cannot start here, so the first provider that can is what the list then shows chosen.
210
+ */
211
+ export function modelEntries({ defaultTarget = null, catalog = null, selected = '', words, providers = null }) {
212
+ const list_ = Array.isArray(providers) && providers.length ? providers : [{ provider: 'claude-code', available: true, reason: null }];
213
+ const can = (p) => list_.some((x) => x.provider === p && x.available);
214
+ const routed = defaultTarget?.provider && can(defaultTarget.provider) && defaultTarget.route === true;
215
+ const defaultOff = !routed && !can('claude-code');
216
+ const entries = [{ value: '', label: routed ? words.route(defaultTarget) : words.fallback, selected: !selected && !defaultOff, ...(defaultOff ? { disabled: true } : {}) }];
217
+ for (const { provider, available, reason } of list_) {
218
+ if (!available) { entries.push({ group: words.provider(provider), options: [{ value: provider, label: words.providerOff(provider, reason || ''), disabled: true, selected: false }] }); continue; }
219
+ const rows = Array.isArray(catalog?.[provider]) ? catalog[provider] : [];
220
+ const options = [{ value: provider, label: words.providerDefault(provider), selected: selected === provider }];
221
+ for (const row of rows.slice(0, 60)) {
222
+ if (!row?.id || row.status === 'unavailable') continue;
223
+ const value = `${provider}|${row.id}`;
224
+ options.push({ value, label: String(row.label || row.id), selected: selected === value });
225
+ }
226
+ entries.push({ group: words.provider(provider), options });
227
+ }
228
+ // A choice kept from before that the catalog no longer lists stays visible (and chosen), never silently swapped;
229
+ // one of a provider that cannot draft here now is shown greyed and the default is chosen instead.
230
+ if (selected && !entries.some((e) => (e.options || [e]).some((o) => o.value === selected))) {
231
+ const t = targetOf(selected);
232
+ const off = !can(t.provider);
233
+ entries.push({ value: selected, label: `${words.provider(t.provider)} · ${t.model || ''}`.trim(), selected: !off, disabled: off });
234
+ if (off && !defaultOff) entries[0].selected = true;
235
+ }
236
+ return entries;
237
+ }
@@ -0,0 +1,254 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: the Create place
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // AI first: one box to describe the skill, with the sources it is drafted from (a session transcript, a web page, a
6
+ // registered project, memories, an existing skill); one worker drafts the package and the result waits in Review
7
+ // (sx-create-ai-view.js, P3). One step away, without AI: a blank skill from a name (checked as it is typed, a
8
+ // folder-safe name, a starting layout, the description written into its frontmatter), a pasted SKILL.md, and Import.
9
+ // A new skill starts as a draft in SynaBun's library and reaches a runtime only when it is published; an agent or a
10
+ // command is one file, written where the owner picks.
11
+
12
+ import { studioErrorText } from './sx-errors.js';
13
+ import { dropdownHTML, wireDropdowns } from '../../cc-dropdown.js';
14
+ import { decodeArtifactId } from '../ss-ids.js';
15
+ import { html, raw, setHTML, ICONS, attr } from './sx-dom.js';
16
+ import { slugify, checkName, scaffold, layoutsFor, pastedName } from './sx-create.js';
17
+ import { triggerImport } from './sx-import.js';
18
+ import { aiAreaHTML, wireAi, leaveAi, openCreation } from './sx-create-ai-view.js';
19
+
20
+ let _life = null;
21
+
22
+ export { openCreation };
23
+
24
+ function state(ctx) {
25
+ if (!ctx.lib.create) ctx.lib.create = { name: '', kind: 'skill', layout: 'blank', description: '', invocation: 'auto', scope: 'global', projectPath: '' };
26
+ return ctx.lib.create;
27
+ }
28
+
29
+ /** Start an import from anywhere (the menu's "Import…", a link). */
30
+ export function startImport(ctx) { triggerImport(ctx, ctx.capabilities); }
31
+
32
+ function nameChecks(ctx, st) {
33
+ const shipped = ctx.rows.filter((r) => r.source === 'synabun').map((r) => r.folderName);
34
+ const taken = ctx.rows.filter((r) => r.kind === st.kind).map((r) => r.folderName);
35
+ return checkName(st.name, { shipped, taken, kind: st.kind });
36
+ }
37
+
38
+ function blankHTML(ctx, st) {
39
+ const { t } = ctx;
40
+ const check = nameChecks(ctx, st);
41
+ const layouts = layoutsFor(st.kind);
42
+ const typed = st.name !== '';
43
+ const projects = [{ value: '', label: t('skillsStudio.import.chooseProject'), disabled: true, selected: !st.projectPath }, ...ctx.projects.map((p) => ({ value: p.path, label: p.label, selected: st.projectPath === p.path }))];
44
+ return html`<div class="sx-blank" role="group" aria-labelledby="sx-blank-title">
45
+ <h4 class="sx-section-title" id="sx-blank-title">${t('skillsStudio.create.blankTitle')}</h4>
46
+ <div class="sx-field">
47
+ <label class="sx-field-label" for="sx-cr-name">${t('skillsStudio.create.nameLabel')}</label>
48
+ <input class="sx-input" id="sx-cr-name" value="${st.name}" autocomplete="off" spellcheck="false" aria-describedby="sx-cr-name-help"${attr('aria-invalid', typed && !check.ok ? 'true' : null)} placeholder="${t('skillsStudio.create.namePlaceholder')}">
49
+ <p class="sx-field-note${typed && !check.ok ? ' sx-err-text' : check.warnings.length ? ' sx-warn-text' : ''}" id="sx-cr-name-help">${typed && !check.ok ? t(`skillsStudio.create.name.${check.errors[0]}`) : check.warnings.length ? t(`skillsStudio.create.name.${check.warnings[0]}`) : t('skillsStudio.create.nameHelp', { slug: st.name ? `/${st.name}` : '/name' })}</p>
50
+ </div>
51
+ <fieldset class="sx-field sx-choices"><legend class="sx-field-label">${t('skillsStudio.create.kindLabel')}</legend>
52
+ ${['skill', 'agent', 'command'].map((k) => html`<label class="sx-choice"><input type="radio" name="sx-cr-kind" value="${k}"${st.kind === k ? ' checked' : ''}> ${t(`skillsStudio.create.kind.${k}`)}</label>`)}
53
+ <p class="sx-help">${t(`skillsStudio.create.kindHelp.${st.kind}`)}</p>
54
+ </fieldset>
55
+ <fieldset class="sx-field"><legend class="sx-field-label">${t('skillsStudio.create.layoutLabel')}</legend>
56
+ <div class="sx-layouts">${layouts.map((l) => html`<label class="sx-layout${st.layout === l ? ' is-on' : ''}"><input type="radio" name="sx-cr-layout" value="${l}"${st.layout === l ? ' checked' : ''}>
57
+ <span class="sx-layout-name">${t(`skillsStudio.create.layout.${st.kind}.${l}.name`)}</span><span class="sx-layout-desc">${t(`skillsStudio.create.layout.${st.kind}.${l}.desc`)}</span></label>`)}</div>
58
+ </fieldset>
59
+ <div class="sx-field">
60
+ <label class="sx-field-label" for="sx-cr-desc">${t('skillsStudio.create.descriptionLabel')} <span class="sx-count" id="sx-cr-desc-count">${st.description.length} / 1024</span></label>
61
+ <textarea class="sx-input sx-textarea" id="sx-cr-desc" rows="3" maxlength="1024" placeholder="${t('skillsStudio.create.descriptionPlaceholder')}">${st.description}</textarea>
62
+ <p class="sx-help">${t('skillsStudio.create.descriptionHelp')}</p>
63
+ </div>
64
+ ${st.kind === 'skill' ? html`<fieldset class="sx-field sx-choices"><legend class="sx-field-label">${t('skillsStudio.form.invocation')}</legend>
65
+ <label class="sx-choice"><input type="radio" name="sx-cr-inv" value="auto"${st.invocation === 'auto' ? ' checked' : ''}> ${t('skillsStudio.form.invocationAuto', { name: st.name ? `/${st.name}` : '/name' })}</label>
66
+ <label class="sx-choice"><input type="radio" name="sx-cr-inv" value="manual"${st.invocation === 'manual' ? ' checked' : ''}> ${t('skillsStudio.form.invocationManual', { name: st.name ? `/${st.name}` : '/name' })}</label>
67
+ <p class="sx-help">${t('skillsStudio.create.whereSkill')}</p></fieldset>`
68
+ : html`<fieldset class="sx-field sx-choices"><legend class="sx-field-label">${t('skillsStudio.create.whereLabel')}</legend>
69
+ <label class="sx-choice"><input type="radio" name="sx-cr-scope" value="global"${st.scope === 'global' ? ' checked' : ''}> ${t(`skillsStudio.create.whereGlobal.${st.kind}`)}</label>
70
+ <label class="sx-choice"><input type="radio" name="sx-cr-scope" value="project"${st.scope === 'project' ? ' checked' : ''}${ctx.projects.length ? '' : ' disabled'}> ${t('skillsStudio.create.whereProject')}</label>
71
+ ${st.scope === 'project' ? html`<div class="sx-field"><span class="sx-field-label" id="sx-cr-project-label">${t('skillsStudio.import.project')}</span>${raw(dropdownHTML('sx-cr-project', projects, { labelledBy: 'sx-cr-project-label' }))}</div>` : ''}
72
+ </fieldset>`}
73
+ <div class="sx-row">
74
+ <button type="button" class="sx-btn sx-btn--primary" data-create="blank" aria-keyshortcuts="${ctx.mac ? 'Meta+Enter' : 'Control+Enter'}">${t(`skillsStudio.create.createButton.${st.kind}`)} <kbd>${ctx.mac ? '⌘↵' : 'Ctrl+Enter'}</kbd></button>
75
+ <button type="button" class="sx-btn" data-create="paste">${t('skillsStudio.create.paste')}</button>
76
+ <button type="button" class="sx-btn sx-btn--ghost" data-create="import">${ICONS.upload}<span>${t('skillsStudio.create.import')}</span></button>
77
+ </div>
78
+ </div>`;
79
+ }
80
+
81
+ /** Draw the Create place. */
82
+ /** Create is left for another place: its listeners on the shared main area end. */
83
+ export function leaveCreatePlace() { _life?.abort(); _life = null; leaveAi(); }
84
+
85
+ export function renderCreatePlace(host, ctx) {
86
+ const { t } = ctx;
87
+ _life?.abort();
88
+ _life = new AbortController();
89
+ const signal = _life.signal;
90
+ const st = state(ctx);
91
+ const guest = ctx.guest;
92
+ setHTML(host, html`<section class="sx-place sx-place--create" aria-labelledby="sx-create-title">
93
+ <div class="sx-place-scroll"><div class="sx-create">
94
+ <h3 class="sx-place-title" id="sx-create-title" tabindex="-1">${t('skillsStudio.create.title')}</h3>
95
+ ${guest ? html`<p class="sx-note" role="note">${t('skillsStudio.create.guest')}</p>` : html`
96
+ ${aiAreaHTML(ctx)}
97
+ ${blankHTML(ctx, st)}`}
98
+ <p class="sx-explain">${t('skillsStudio.create.explain')}</p>
99
+ <div><p class="sx-help">${t('skillsStudio.create.examplesTitle')}</p><ul class="sx-plain-list sx-examples">
100
+ <li>${t('skillsStudio.create.example1')}</li><li>${t('skillsStudio.create.example2')}</li><li>${t('skillsStudio.create.example3')}</li></ul></div>
101
+ </div></div>
102
+ </section>`);
103
+ if (guest) return;
104
+ wireDropdowns(host, { signal });
105
+ // The AI box (its own listeners on the same host, ended by the same signal).
106
+ wireAi(host, ctx, signal);
107
+ host.addEventListener('input', (e) => {
108
+ const el = e.target;
109
+ if (el.id === 'sx-cr-name') {
110
+ const slug = slugify(el.value);
111
+ // Spaces and capitals turn into the folder-safe name as it is typed.
112
+ if (slug !== el.value && /[\sA-Z_]/.test(el.value)) { const at = el.selectionStart; el.value = slug; el.setSelectionRange(Math.min(at, slug.length), Math.min(at, slug.length)); }
113
+ st.name = el.value;
114
+ updateName(host, ctx, st);
115
+ return;
116
+ }
117
+ if (el.id === 'sx-cr-desc') { st.description = el.value; const c = host.querySelector('#sx-cr-desc-count'); if (c) c.textContent = `${el.value.length} / 1024`; }
118
+ }, { signal });
119
+ host.addEventListener('change', (e) => {
120
+ const el = e.target;
121
+ if (el.name === 'sx-cr-kind') { st.kind = el.value; st.layout = 'blank'; redraw(host, ctx, st); host.querySelector(`input[name="sx-cr-kind"][value="${el.value}"]`)?.focus(); }
122
+ else if (el.name === 'sx-cr-layout') { st.layout = el.value; host.querySelectorAll('.sx-layout').forEach((l) => l.classList.toggle('is-on', l.querySelector('input')?.checked)); }
123
+ else if (el.name === 'sx-cr-inv') st.invocation = el.value;
124
+ else if (el.name === 'sx-cr-scope') { st.scope = el.value; redraw(host, ctx, st); host.querySelector(`input[name="sx-cr-scope"][value="${el.value}"]`)?.focus(); }
125
+ else if (el.id === 'sx-cr-project') st.projectPath = el.value;
126
+ }, { signal });
127
+ host.addEventListener('click', (e) => {
128
+ const btn = e.target.closest?.('[data-create]');
129
+ if (!btn || btn.disabled) return;
130
+ if (btn.dataset.create === 'blank') createBlank(host, ctx, st);
131
+ else if (btn.dataset.create === 'paste') pasteSkill(ctx);
132
+ else if (btn.dataset.create === 'import') startImport(ctx);
133
+ }, { signal });
134
+ host.addEventListener('keydown', (e) => {
135
+ if ((ctx.mac ? e.metaKey : e.ctrlKey) && e.key === 'Enter' && e.target.closest?.('.sx-blank')) { e.preventDefault(); createBlank(host, ctx, st); }
136
+ }, { signal });
137
+ }
138
+
139
+ function updateName(host, ctx, st) {
140
+ const { t } = ctx;
141
+ const check = nameChecks(ctx, st);
142
+ const input = host.querySelector('#sx-cr-name');
143
+ const help = host.querySelector('#sx-cr-name-help');
144
+ const typed = st.name !== '';
145
+ if (input) { if (typed && !check.ok) input.setAttribute('aria-invalid', 'true'); else input.removeAttribute('aria-invalid'); }
146
+ if (help) {
147
+ help.className = `sx-field-note${typed && !check.ok ? ' sx-err-text' : check.warnings.length ? ' sx-warn-text' : ''}`;
148
+ help.textContent = typed && !check.ok ? t(`skillsStudio.create.name.${check.errors[0]}`) : check.warnings.length ? t(`skillsStudio.create.name.${check.warnings[0]}`) : t('skillsStudio.create.nameHelp', { slug: st.name ? `/${st.name}` : '/name' });
149
+ }
150
+ }
151
+
152
+ function redraw(host, ctx, st) {
153
+ const blank = host.querySelector('.sx-blank');
154
+ if (!blank) return;
155
+ const tmp = document.createElement('div');
156
+ setHTML(tmp, blankHTML(ctx, st));
157
+ // (the dropdowns of the new form are served by the host's delegated listeners: wiring them again would toggle twice)
158
+ blank.replaceWith(tmp.firstElementChild);
159
+ }
160
+
161
+ async function createBlank(host, ctx, st) {
162
+ const { t } = ctx;
163
+ const check = nameChecks(ctx, st);
164
+ if (!check.ok) {
165
+ // Said where it is: the field is marked, its note says why, and the keyboard goes there.
166
+ const input = host.querySelector('#sx-cr-name');
167
+ const help = host.querySelector('#sx-cr-name-help');
168
+ input?.setAttribute('aria-invalid', 'true');
169
+ if (help) { help.textContent = t(`skillsStudio.create.name.${check.errors[0]}`); help.className = 'sx-field-note sx-err-text'; }
170
+ input?.focus();
171
+ return;
172
+ }
173
+ if (st.kind !== 'skill' && st.scope === 'project' && !st.projectPath) { ctx.toast(t('skillsStudio.import.chooseProjectError'), 'error'); host.querySelector('#sx-cr-project-trigger')?.focus(); return; }
174
+ const files = scaffold({ kind: st.kind, name: st.name, description: st.description, layout: st.layout, invocation: st.invocation }).files;
175
+ const btn = host.querySelector('[data-create="blank"]');
176
+ if (btn) btn.disabled = true;
177
+ const gen = ctx.gen();
178
+ try {
179
+ if (st.kind === 'skill') {
180
+ const res = await ctx.api.createLibrarySkill({ name: st.name, files }, { idempotencyKey: ctx.key('create') });
181
+ if (!ctx.alive(gen)) return;
182
+ ctx.toast(t('skillsStudio.create.created', { name: st.name }));
183
+ ctx.lib.create = null;
184
+ await ctx.reload();
185
+ if (ctx.alive(gen)) await ctx.openRow(`lib:${res.skillId}`, { tab: 'edit' });
186
+ } else {
187
+ const res = await ctx.api.createSkillsArtifact({ type: st.kind, scope: st.scope, projectPath: st.scope === 'project' ? st.projectPath : '', name: st.name, rawContent: files[0].content });
188
+ if (!ctx.alive(gen)) return;
189
+ ctx.toast(t('skillsStudio.create.created', { name: st.name }));
190
+ ctx.lib.create = null;
191
+ await ctx.reload();
192
+ const id = decodeArtifactId(res?.id || '');
193
+ const path = id.slice(id.indexOf(':') + 1);
194
+ const row = ctx.rows.find((r) => r.entries.some((e) => e.path === path));
195
+ if (row && ctx.alive(gen)) await ctx.openRow(row.key, { tab: 'edit' });
196
+ }
197
+ } catch (err) {
198
+ if (!ctx.alive(gen)) return;
199
+ if (btn) btn.disabled = false;
200
+ ctx.toast(t('skillsStudio.create.failed', { error: studioErrorText(ctx.t, err) }), 'error');
201
+ }
202
+ }
203
+
204
+ async function pasteSkill(ctx) {
205
+ const { t } = ctx;
206
+ const box = document.createElement('div');
207
+ box.className = 'sx-dlg-form';
208
+ const label = document.createElement('label');
209
+ label.className = 'sx-field-label';
210
+ label.htmlFor = 'sx-paste';
211
+ label.textContent = t('skillsStudio.create.pasteLabel');
212
+ const area = document.createElement('textarea');
213
+ area.id = 'sx-paste';
214
+ area.className = 'sx-input sx-textarea sx-mono';
215
+ area.rows = 12;
216
+ area.spellcheck = false;
217
+ const nameLabel = document.createElement('label');
218
+ nameLabel.className = 'sx-field-label';
219
+ nameLabel.htmlFor = 'sx-paste-name';
220
+ nameLabel.textContent = t('skillsStudio.create.nameLabel');
221
+ const nameInput = document.createElement('input');
222
+ nameInput.id = 'sx-paste-name';
223
+ nameInput.className = 'sx-input';
224
+ nameInput.autocomplete = 'off';
225
+ area.addEventListener('input', () => { if (!nameInput.dataset.touched) nameInput.value = slugify(pastedName(area.value)); });
226
+ nameInput.addEventListener('input', () => { nameInput.dataset.touched = '1'; });
227
+ const err = document.createElement('p');
228
+ err.className = 'ss-dlg-error';
229
+ err.setAttribute('role', 'alert');
230
+ box.append(label, area, nameLabel, nameInput, err);
231
+ setTimeout(() => area.focus(), 0);
232
+ let chosen = null;
233
+ const answer = await ctx.ask({
234
+ title: t('skillsStudio.create.pasteTitle'), extra: box, wide: true,
235
+ buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'ok', label: t('skillsStudio.create.pasteConfirm'), kind: 'primary' }],
236
+ accept: (id) => {
237
+ if (id !== 'ok') return true;
238
+ if (!area.value.trim()) { err.textContent = t('skillsStudio.create.pasteEmpty'); return false; }
239
+ const check = checkName(nameInput.value.trim(), { shipped: ctx.rows.filter((r) => r.source === 'synabun').map((r) => r.folderName) });
240
+ if (!check.ok) { err.textContent = t(`skillsStudio.create.name.${check.errors[0]}`); return false; }
241
+ chosen = { name: nameInput.value.trim(), text: area.value };
242
+ return true;
243
+ },
244
+ });
245
+ if (answer !== 'ok' || !chosen) return;
246
+ const gen = ctx.gen();
247
+ try {
248
+ const res = await ctx.api.createLibrarySkill({ name: chosen.name, files: [{ path: 'SKILL.md', content: chosen.text }] }, { idempotencyKey: ctx.key('paste') });
249
+ if (!ctx.alive(gen)) return;
250
+ ctx.toast(t('skillsStudio.create.created', { name: chosen.name }));
251
+ await ctx.reload();
252
+ if (ctx.alive(gen)) await ctx.openRow(`lib:${res.skillId}`, { tab: 'edit' });
253
+ } catch (e) { if (ctx.alive(gen)) ctx.toast(t('skillsStudio.create.failed', { error: studioErrorText(ctx.t, e) }), 'error'); }
254
+ }
@@ -0,0 +1,126 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio 2.0: a new skill, agent or command from a name
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The fast path of Create ("Blank skill"): a name, checked as it is typed and turned into a folder-safe slug, a
6
+ // kind, an optional description that goes into the frontmatter (the old wizard dropped it, SK-063), and a starting
7
+ // layout. A layout with references writes those files too, and SKILL.md points to them by a relative path, the
8
+ // way every runtime resolves it (SK-064); nothing names `$SKILL_DIR`. What comes out is a list of files for the
9
+ // library (POST /v2/skills { name, files }) or, for an agent or a command, the one file the P0 create route writes.
10
+ // No DOM and no imports.
11
+
12
+ export const NAME_MAX = 64;
13
+ const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
14
+
15
+ /** A name as a folder can carry it everywhere: lowercase ASCII letters, digits, single hyphens, at most 64. */
16
+ export function slugify(input) {
17
+ return String(input ?? '')
18
+ .normalize('NFKD').replace(/[̀-ͯ]/g, '')
19
+ .toLowerCase()
20
+ .replace(/[\s_./\\]+/g, '-')
21
+ .replace(/[^a-z0-9-]/g, '')
22
+ .replace(/-{2,}/g, '-')
23
+ .replace(/^-+|-+$/g, '')
24
+ .slice(0, NAME_MAX)
25
+ .replace(/-+$/, '');
26
+ }
27
+
28
+ /**
29
+ * What is wrong with a name, as codes the view words (`skillsStudio.create.name.*`).
30
+ * @param {string} name
31
+ * @param {{ taken?: string[], shipped?: string[], kind?: string }} [o] names already used (a warning), names SynaBun ships (an error)
32
+ * @returns {{ ok: boolean, errors: string[], warnings: string[] }}
33
+ */
34
+ export function checkName(name, { taken = [], shipped = [], kind = 'skill' } = {}) {
35
+ const n = String(name ?? '');
36
+ const errors = [];
37
+ const warnings = [];
38
+ if (!n.trim()) errors.push('required');
39
+ else {
40
+ if (n.length > NAME_MAX) errors.push('too-long');
41
+ if (!NAME_RE.test(n)) errors.push('format');
42
+ const lower = n.toLowerCase();
43
+ if (kind === 'skill' && shipped.some((s) => String(s).toLowerCase() === lower)) errors.push('shipped');
44
+ else if (taken.some((s) => String(s).toLowerCase() === lower)) warnings.push('taken');
45
+ }
46
+ return { ok: errors.length === 0, errors, warnings };
47
+ }
48
+
49
+ /** A YAML scalar for a text value: JSON's double-quoted string is valid YAML and keeps every character. */
50
+ const yamlText = (text) => JSON.stringify(String(text ?? ''));
51
+
52
+ const TODO_DESCRIPTION = 'TODO: say what this skill does and when to use it.';
53
+
54
+ /** The layouts a skill can start from. `files`: what each writes besides SKILL.md. */
55
+ export const SKILL_LAYOUTS = Object.freeze(['blank', 'steps', 'router', 'references', 'script']);
56
+ export const AGENT_LAYOUTS = Object.freeze(['blank', 'specialist', 'researcher']);
57
+ export const COMMAND_LAYOUTS = Object.freeze(['blank', 'analysis', 'workflow']);
58
+
59
+ export const layoutsFor = (kind) => (kind === 'agent' ? AGENT_LAYOUTS : kind === 'command' ? COMMAND_LAYOUTS : SKILL_LAYOUTS);
60
+
61
+ function skillHead({ name, description, invocation }) {
62
+ const lines = ['---', `name: ${name}`, `description: ${yamlText(description || TODO_DESCRIPTION)}`];
63
+ if (invocation === 'manual') lines.push('disable-model-invocation: true');
64
+ return lines;
65
+ }
66
+
67
+ const SKILL_BODIES = {
68
+ blank: (n) => [`# ${n}`, '', 'Write the instructions here: what to do, in which order, and what a good result looks like.', ''],
69
+ steps: (n) => [`# ${n}`, '', 'The user asked for this with: $ARGUMENTS', '', '## Steps', '', '1. Read what the user gave and check it is complete.', '2. Do the task.', '3. Check the result before you report it.', '4. Report what changed, briefly.', ''],
70
+ router: (n) => [`# ${n}`, '', 'Read the first word of $ARGUMENTS and follow its section. With no word, list the sections and ask which one.', '', '| Word | What it does |', '|------|--------------|', '| `first` | Does the first thing |', '| `second` | Does the second thing |', '', '## first', '', 'Instructions for the first thing.', '', '## second', '', 'Instructions for the second thing.', ''],
71
+ references: (n) => [`# ${n}`, '', 'Keep this file short. Read a reference only when the task needs it:', '', '- [references/guide.md](references/guide.md): the detailed guide.', '', '## Steps', '', '1. Work out what the user needs.', '2. When the details matter, read the guide above.', '3. Follow it and report the result.', ''],
72
+ script: (n) => [`# ${n}`, '', 'Run the script instead of reading it:', '', '```bash', 'bash scripts/run.sh', '```', '', 'It prints what it did. Report that output to the user.', ''],
73
+ };
74
+
75
+ const AGENT_FILES = {
76
+ blank: (n, d) => ['---', `name: ${n}`, `description: ${yamlText(d || `Use this agent when the task needs ${n}.`)}`, '---', '', `You are the ${n} agent.`, '', 'Follow the instructions you are given and report what you did.', ''],
77
+ specialist: (n, d) => ['---', `name: ${n}`, `description: ${yamlText(d || `Use this agent when the task needs ${n} expertise.`)}`, 'model: sonnet', 'tools: Read, Grep, Glob, Bash', '---', '', `You are an expert in ${n}.`, '', '## How you work', '', '1. Understand the current state before you change anything.', '2. Explain what you found.', '3. Recommend concrete next steps.', ''],
78
+ researcher: (n, d) => ['---', `name: ${n}`, `description: ${yamlText(d || `Use this agent to research ${n}.`)}`, 'tools: Read, Grep, Glob, WebFetch, WebSearch', '---', '', `You research ${n}.`, '', '1. Search the code for context.', '2. Look up current documentation.', '3. Return a short summary with sources.', ''],
79
+ };
80
+
81
+ const COMMAND_FILES = {
82
+ blank: (n, d) => ['---', `description: ${yamlText(d || `What /${n} does.`)}`, '---', '', `# ${n}`, '', 'Instructions for the command. The user\'s words: $ARGUMENTS', ''],
83
+ analysis: (n, d) => ['---', `description: ${yamlText(d || `Analyze code or content for ${n}.`)}`, '---', '', `# ${n}`, '', 'Target: $ARGUMENTS', '', '1. Read the target.', '2. Look for problems and improvements.', '3. Report them, most important first.', ''],
84
+ workflow: (n, d) => ['---', `description: ${yamlText(d || `Run the ${n} workflow.`)}`, '---', '', `# ${n}`, '', '$ARGUMENTS', '', '1. Gather what is needed.', '2. Propose a plan and wait for a yes.', '3. Do it.', '4. Check the result.', ''],
85
+ };
86
+
87
+ /**
88
+ * The files of a new skill, agent or command.
89
+ * @param {{ kind?: 'skill'|'agent'|'command', name: string, description?: string, layout?: string, invocation?: 'auto'|'manual' }} o
90
+ * @returns {{ files: { path: string, content: string, executable?: boolean }[], head: string }}
91
+ */
92
+ export function scaffold({ kind = 'skill', name, description = '', layout = 'blank', invocation = 'auto' }) {
93
+ const n = String(name ?? '');
94
+ const d = String(description ?? '').replace(/\s+/g, ' ').trim();
95
+ if (kind === 'agent') {
96
+ const gen = AGENT_FILES[layout] || AGENT_FILES.blank;
97
+ return { files: [{ path: `${n}.md`, content: gen(n, d).join('\n') }], head: `${n}.md` };
98
+ }
99
+ if (kind === 'command') {
100
+ const gen = COMMAND_FILES[layout] || COMMAND_FILES.blank;
101
+ return { files: [{ path: `${n}.md`, content: gen(n, d).join('\n') }], head: `${n}.md` };
102
+ }
103
+ const body = (SKILL_BODIES[layout] || SKILL_BODIES.blank)(n);
104
+ const hint = layout === 'router' ? ['argument-hint: "[first|second]"'] : layout === 'steps' ? ['argument-hint: "[what to work on]"'] : [];
105
+ const head = [...skillHead({ name: n, description: d, invocation }), ...hint, '---', '', ...body];
106
+ const files = [{ path: 'SKILL.md', content: head.join('\n') }];
107
+ if (layout === 'references') {
108
+ files.push({ path: 'references/guide.md', content: [`# ${n}: the detailed guide`, '', 'Everything SKILL.md does not need to say every time: background, examples, edge cases.', '', '## Examples', '', '- An example of a request and what a good answer looks like.', ''].join('\n') });
109
+ }
110
+ if (layout === 'script') {
111
+ files.push({ path: 'scripts/run.sh', content: ['#!/usr/bin/env bash', 'set -euo pipefail', '', `echo "${n}: replace this line with the work the script does"`, ''].join('\n'), executable: true });
112
+ }
113
+ return { files, head: 'SKILL.md' };
114
+ }
115
+
116
+ /** The name a pasted SKILL.md gives itself (its frontmatter `name`), or ''. YAML is not parsed here: a plain line only. */
117
+ export function pastedName(text) {
118
+ const m = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---/.exec(String(text ?? '').replace(/^/, ''));
119
+ if (!m) return '';
120
+ const line = /^name:[ \t]*(.+?)[ \t]*$/m.exec(m[1]);
121
+ if (!line) return '';
122
+ return line[1].replace(/^(['"])(.*)\1$/, '$2');
123
+ }
124
+
125
+ /** The AI sources of Create (P3: sx-create-ai.js drafts from them; `describe` is the prompt itself). */
126
+ export const AI_SOURCES = Object.freeze(['describe', 'conversation', 'url', 'repo', 'memory', 'import']);