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,165 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: what a skill refers to, and its arguments
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // A skill is its SKILL.md and the files it points the model to. This reads those pointers and checks them
6
+ // against the folder (rule L-05): a link that leaves the skill, a file that is not there, one an export leaves
7
+ // out (hidden files, node_modules), an absolute path that will not travel with it, a chain of references more
8
+ // than one level deep, and files nothing refers to. Markdown links are references by definition; a path in
9
+ // `code` counts only when it starts where the skill's own files do (./, a top-level folder of the skill, or the
10
+ // skill-folder variables), so a skill about a repository can name that repository's files freely.
11
+ //
12
+ // It also reads the argument placeholders of the body for each runtime (rule L-08): Claude Code counts $0 as the
13
+ // first argument; Codex and OpenCode read a native skill body as written, so $ARGUMENTS reaches the model as
14
+ // text there. $SKILL_DIR is a convention of SynaBun's old bridges, not a variable any runtime fills.
15
+ //
16
+ // No DOM, no I/O: the caller hands in the files and their text.
17
+
18
+ const LINK_RE = /!?\[[^\]\n]*\]\(\s*<?([^)\s>]+)>?(?:\s+(?:"[^"]*"|'[^']*'))?\s*\)/g;
19
+ const CODE_RE = /`([^`\n]{1,300})`/g;
20
+ const SCHEME_RE = /^[A-Za-z][A-Za-z0-9+.-]*:/;
21
+ const VARIABLE_PREFIX_RE = /^(?:\$SKILL_DIR|\$\{CLAUDE_SKILL_DIR\}|\$CLAUDE_SKILL_DIR)(?=\/|$)/;
22
+ const ALWAYS_KEPT = /^(?:SKILL\.md|icon\.(?:png|svg|jpe?g|webp)|LICEN[CS]E(?:\.[A-Za-z]+)?|README(?:\.md)?|agents\/openai\.yaml|evals?\/.*)$/i;
23
+ const TEXT_EXT = /\.(?:md|markdown|mdx|txt)$/i;
24
+
25
+ function lineAt(text, index) {
26
+ let line = 1;
27
+ for (let i = 0; i < index && i < text.length; i++) if (text.charCodeAt(i) === 10) line++;
28
+ return line;
29
+ }
30
+
31
+ /** Join a reference onto the folder of the file it is in, as the skill's own relative path; `escapes` when it climbs out. */
32
+ function resolveRef(fromDir, target) {
33
+ const parts = fromDir ? fromDir.split('/') : [];
34
+ for (const seg of target.split('/')) {
35
+ if (seg === '' || seg === '.') continue;
36
+ if (seg === '..') { if (!parts.length) return { escapes: true, path: null }; parts.pop(); continue; }
37
+ parts.push(seg);
38
+ }
39
+ return { escapes: false, path: parts.join('/') };
40
+ }
41
+
42
+ /**
43
+ * The references of a skill folder, checked.
44
+ * @param {object} o
45
+ * @param {{ path: string, size?: number, text?: string|null }[]} o.files every file (text null for a binary)
46
+ * @param {string[]} [o.dirs] every folder
47
+ * @param {{ path: string, reason: string }[]} [o.excluded] what an export leaves out
48
+ * @param {string} [o.headPath]
49
+ * @returns {{ references: object[], orphans: string[], diagnostics: object[] }}
50
+ */
51
+ export function analyzeResources({ files = [], dirs = [], excluded = [], headPath = 'SKILL.md' } = {}) {
52
+ const byPath = new Map(files.map((f) => [f.path, f]));
53
+ const dirSet = new Set(dirs);
54
+ for (const f of files) { const p = f.path.split('/'); for (let i = 1; i < p.length; i++) dirSet.add(p.slice(0, i).join('/')); }
55
+ const excludedSet = new Map(excluded.map((e) => [e.path, e.reason]));
56
+ const topLevel = new Set([...dirSet].filter((d) => !d.includes('/')));
57
+ const references = [];
58
+ const diagnostics = [];
59
+ const add = (ruleId, severity, message, file, line) => diagnostics.push({ ruleId, severity, message, file, line });
60
+ const referenced = new Set([headPath]);
61
+
62
+ const read = (path, depth, seen) => {
63
+ const file = byPath.get(path);
64
+ if (!file || typeof file.text !== 'string' || seen.has(path)) return;
65
+ seen.add(path);
66
+ const text = file.text;
67
+ const fromDir = path.includes('/') ? path.slice(0, path.lastIndexOf('/')) : '';
68
+ const found = [];
69
+ for (const m of text.matchAll(LINK_RE)) {
70
+ const raw = m[1];
71
+ if (SCHEME_RE.test(raw) || raw.startsWith('#') || raw.startsWith('//')) continue;
72
+ found.push({ raw: raw.split('#')[0].split('?')[0], kind: 'link', index: m.index });
73
+ }
74
+ for (const m of text.matchAll(CODE_RE)) {
75
+ const raw = m[1].trim();
76
+ if (/\s/.test(raw) || SCHEME_RE.test(raw)) continue;
77
+ if (VARIABLE_PREFIX_RE.test(raw)) { found.push({ raw, kind: 'variable', index: m.index }); continue; }
78
+ const first = raw.replace(/^\.\//, '').split('/')[0];
79
+ if (raw.startsWith('./') || (raw.includes('/') && topLevel.has(first))) found.push({ raw, kind: 'code', index: m.index });
80
+ else if (raw.startsWith('/') || /^[A-Za-z]:[\\/]/.test(raw) || raw.startsWith('~/')) {
81
+ if (/(?:^|\/)(?:skills?|\.claude|\.codex|\.agents|\.opencode)\//.test(raw)) found.push({ raw, kind: 'code', index: m.index });
82
+ }
83
+ }
84
+ for (const ref of found) {
85
+ if (!ref.raw) continue;
86
+ const line = lineAt(text, ref.index);
87
+ const entry = { from: path, raw: ref.raw, kind: ref.kind, line, depth, to: null, status: 'ok' };
88
+ if (ref.raw.startsWith('/') || /^[A-Za-z]:[\\/]/.test(ref.raw) || ref.raw.startsWith('~/')) {
89
+ entry.status = 'absolute';
90
+ add('L-05-absolute', 'warning', `${ref.raw} is a path on this computer: it does not travel with the skill. Point to a file inside the skill instead.`, path, line);
91
+ references.push(entry);
92
+ continue;
93
+ }
94
+ let target = ref.raw;
95
+ let base = fromDir;
96
+ if (ref.kind === 'variable') { target = ref.raw.replace(VARIABLE_PREFIX_RE, '').replace(/^\//, ''); base = ''; }
97
+ let decoded = target;
98
+ try { decoded = decodeURIComponent(target); } catch { decoded = target; }
99
+ const resolved = resolveRef(base, decoded.replace(/\\/g, '/'));
100
+ if (resolved.escapes) {
101
+ entry.status = 'escapes';
102
+ add('L-05-escape', 'error', `${ref.raw} leads out of the skill's folder.`, path, line);
103
+ references.push(entry);
104
+ continue;
105
+ }
106
+ entry.to = resolved.path;
107
+ const hiddenPart = resolved.path.split('/').find((seg) => seg.startsWith('.') || seg === 'node_modules');
108
+ if (excludedSet.has(resolved.path) || hiddenPart) {
109
+ entry.status = 'excluded';
110
+ add('L-05-excluded', 'error', `${ref.raw} points to ${resolved.path}, which an export leaves out (${excludedSet.get(resolved.path) || (hiddenPart === 'node_modules' ? 'node_modules' : 'hidden')}): the skill would refer to a file it does not carry.`, path, line);
111
+ } else if (byPath.has(resolved.path)) {
112
+ referenced.add(resolved.path);
113
+ if (depth >= 1 && TEXT_EXT.test(resolved.path) && resolved.path !== headPath) {
114
+ add('L-05-deep', 'info', `${path} refers on to ${resolved.path}: references more than one level deep may be read only in part. Link it from ${headPath} instead.`, path, line);
115
+ }
116
+ if (depth < 2 && TEXT_EXT.test(resolved.path)) read(resolved.path, depth + 1, seen);
117
+ } else if (dirSet.has(resolved.path) || resolved.path === '') {
118
+ entry.status = 'directory';
119
+ for (const f of files) if (resolved.path === '' || f.path.startsWith(`${resolved.path}/`)) referenced.add(f.path);
120
+ } else {
121
+ entry.status = 'missing';
122
+ add('L-05-missing', ref.kind === 'link' ? 'error' : 'warning', `${ref.raw} is not in the skill.`, path, line);
123
+ }
124
+ references.push(entry);
125
+ }
126
+ };
127
+ read(headPath, 0, new Set());
128
+
129
+ const orphans = files.map((f) => f.path).filter((p) => !referenced.has(p) && !ALWAYS_KEPT.test(p) && !p.split('/').some((s) => s.startsWith('.')));
130
+ if (orphans.length) {
131
+ const shown = orphans.slice(0, 12).join(', ') + (orphans.length > 12 ? `, and ${orphans.length - 12} more` : '');
132
+ add('L-05-orphan', 'info', `${orphans.length === 1 ? 'One file is' : `${orphans.length} files are`} not referred to from ${headPath} or the files it links: ${shown}. A model finds a file only when it is pointed to it.`, headPath, null);
133
+ }
134
+ return { references, orphans, diagnostics };
135
+ }
136
+
137
+ /**
138
+ * The argument placeholders of a body, for one runtime.
139
+ * @param {string} runtime 'claude' | 'codex' | 'opencode' | 'portable' | 'claude-cloud'
140
+ * @param {{ body: string, bodyLine?: number, headPath?: string, declared?: string[] }} o
141
+ */
142
+ export function argumentDiagnostics(runtime, { body = '', bodyLine = 1, headPath = 'SKILL.md', declared = [] } = {}) {
143
+ const out = [];
144
+ const text = String(body);
145
+ const add = (ruleId, severity, message, index) => out.push({ ruleId, severity, message, file: headPath, line: index === null ? null : lineAt(text, index) + bodyLine - 1 });
146
+ const first = (re) => { const m = re.exec(text); return m ? m.index : -1; };
147
+ const skillDir = first(/(?<!\\)\$SKILL_DIR\b/);
148
+ if (skillDir >= 0) add('L-08-bridge-token', 'warning', '$SKILL_DIR is a convention of SynaBun\'s old bridges, not a variable a runtime fills. Use a path relative to the skill (Claude Code also has ${CLAUDE_SKILL_DIR}).', skillDir);
149
+ const args = first(/(?<!\\)\$ARGUMENTS\b/);
150
+ const positional = first(/(?<!\\)\$\d+\b/);
151
+ if (runtime === 'claude') {
152
+ const one = first(/(?<!\\)\$1\b/);
153
+ if (one >= 0 && first(/(?<!\\)\$0\b/) < 0) add('L-08-zero-based', 'info', 'Claude Code counts arguments from $0: $1 is the second argument.', one);
154
+ } else if (runtime === 'codex' || runtime === 'opencode' || runtime === 'portable' || runtime === 'claude-cloud') {
155
+ const where = args >= 0 ? args : positional;
156
+ if (where >= 0) {
157
+ const who = { codex: 'Codex', opencode: 'OpenCode', portable: 'The portable format', 'claude-cloud': 'Claude.ai and the API' }[runtime];
158
+ add('L-08-arguments-literal', 'info', `${who} reads a skill body as written: ${args >= 0 ? '$ARGUMENTS' : 'a $N placeholder'} reaches the model as text, not filled.`, where);
159
+ }
160
+ const claudeVar = first(/\$\{CLAUDE_[A-Z_]+\}/);
161
+ if (claudeVar >= 0 && runtime !== 'portable') add('L-08-claude-variable', 'info', 'A ${CLAUDE_*} variable is filled by Claude Code only; elsewhere it stays as written.', claudeVar);
162
+ }
163
+ if (declared.length && runtime !== 'claude') add('L-08-named-arguments', 'info', `"arguments" (${declared.join(', ')}) is a Claude Code field; other runtimes do not fill $${declared[0]}.`, null);
164
+ return out;
165
+ }
@@ -0,0 +1,190 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: the Review queue and install plans, as words
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // What the Review queue (lib/skills/reviews.js) and an install plan (lib/skills/deploy.js) say, turned into what
6
+ // the owner reads and decides on: a card per submission (who made it, what it does, what it can do that the
7
+ // approved revision could not, the checks of exactly its bytes), the targets an install can choose, a row per
8
+ // target of a plan (where, who sees it, what is there, what it needs confirmed), the decisions the confirmations
9
+ // make, and the outcome per target. No DOM: the tests read it (Skills Studio 2.0 draws Review with studio/sx-review-view.js).
10
+
11
+ export const REVIEW_STATE_LABEL = Object.freeze({
12
+ pending: 'Waiting for your review',
13
+ changes_requested: 'Changes asked for',
14
+ approved: 'Approved, not installed yet',
15
+ publishing: 'Installing',
16
+ published: 'Installed',
17
+ partial: 'Installed in part',
18
+ publish_failed: 'Install failed',
19
+ rejected: 'Rejected',
20
+ withdrawn: 'Withdrawn',
21
+ });
22
+
23
+ export const RUNTIME_LABEL = Object.freeze({ claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity', agents: 'Shared (~/.agents/skills)' });
24
+
25
+ /** A target as a person reads it: the runtime, and the project or the account when it is not the default one. */
26
+ export function targetLabel(t) {
27
+ const runtime = RUNTIME_LABEL[t?.runtime] || t?.runtime || '?';
28
+ if (t?.scope === 'project' && t.projectPath) return `${runtime} (project ${t.projectPath})`;
29
+ if (t?.accountId && t.accountId !== 'default') return `${runtime} (account ${t.accountId})`;
30
+ return runtime;
31
+ }
32
+
33
+ /** A target as an install sends it: only the fields a target has (runtime, scope, projectPath, accountId). */
34
+ function wholeTarget(t) {
35
+ if (typeof t === 'string') return { runtime: t };
36
+ const out = { runtime: t?.runtime };
37
+ for (const key of ['scope', 'projectPath', 'accountId']) if (typeof t?.[key] === 'string' && t[key]) out[key] = t[key];
38
+ return out;
39
+ }
40
+ const plainTarget = (t) => (!t.scope || t.scope === 'user') && !t.projectPath && (!t.accountId || t.accountId === 'default');
41
+
42
+ const AUTHORITY_WORDS = {
43
+ scripts: 'scripts', binaries: 'files that are not text', dynamicContext: 'commands run when it loads', hooks: 'session hooks',
44
+ toolGrants: 'pre-approved tools', domains: 'web addresses', sensitive: 'reads of private data', persistentWrites: 'lasting configuration changes', installers: 'software installs',
45
+ };
46
+
47
+ /** What a revision can do that the approved one could not, in words. */
48
+ export function authorityLines(delta) {
49
+ if (!delta || !delta.byKind) return [];
50
+ const out = [];
51
+ for (const [kind, change] of Object.entries(delta.byKind)) {
52
+ const words = AUTHORITY_WORDS[kind] || kind;
53
+ if (change.added?.length) out.push(`Adds ${words}: ${change.added.slice(0, 6).join(', ')}${change.added.length > 6 ? ` and ${change.added.length - 6} more` : ''}`);
54
+ if (change.removed?.length) out.push(`No longer has ${words}: ${change.removed.slice(0, 6).join(', ')}${change.removed.length > 6 ? ` and ${change.removed.length - 6} more` : ''}`);
55
+ }
56
+ return out;
57
+ }
58
+
59
+ function authorLine(author) {
60
+ if (!author) return 'Unknown author';
61
+ if (author.kind === 'owner') return 'You';
62
+ const who = author.kind === 'assistant' ? 'The SynaBun Assistant' : 'An agent';
63
+ const bits = [author.provider, author.model, author.runId ? `run ${author.runId}` : null].filter(Boolean);
64
+ return bits.length ? `${who} (${bits.join(', ')})` : who;
65
+ }
66
+
67
+ function scanLine(scan) {
68
+ if (!scan) return { text: 'Not scanned', severity: 'medium' };
69
+ const counts = scan.counts || {};
70
+ const found = Object.values(counts).reduce((n, c) => n + (Number(c) || 0), 0);
71
+ if (scan.status === 'error') return { text: 'The scan failed: it cannot be approved', severity: 'high' };
72
+ if (scan.gate === 'blocked') return { text: `The scan blocks it (${found} finding${found === 1 ? '' : 's'})`, severity: 'high' };
73
+ if (scan.status === 'no_findings') return { text: 'No findings in the scanned content', severity: 'none' };
74
+ return { text: `${found} finding${found === 1 ? '' : 's'} to review${scan.status === 'incomplete' ? '; part of it could not be read' : ''}`, severity: scan.gate === 'review' ? 'medium' : 'none' };
75
+ }
76
+
77
+ /** One submission as a card. `actions`: what the owner can do with it now. */
78
+ export function reviewCard(review) {
79
+ const ev = review.evidence || {};
80
+ const lintErrors = Object.values(ev.lint?.profiles || {}).reduce((n, p) => n + (p.errors || 0), 0);
81
+ const changed = ev.diff?.files || [];
82
+ const scan = scanLine(ev.scan);
83
+ const actions = review.state === 'pending' ? ['approve', 'request_changes', 'reject']
84
+ : review.state === 'changes_requested' ? ['reject']
85
+ : ['approved', 'partial', 'publish_failed', 'published'].includes(review.state) ? ['install'] : [];
86
+ return {
87
+ id: review.reviewId,
88
+ title: review.skillName || review.skillId,
89
+ state: review.state,
90
+ stateLabel: REVIEW_STATE_LABEL[review.state] || review.state,
91
+ author: authorLine(review.author),
92
+ notes: review.releaseNotes || '',
93
+ assumptions: Array.isArray(review.assumptions) ? review.assumptions : [],
94
+ targets: (review.targets || []).map(targetLabel),
95
+ scan,
96
+ lint: lintErrors ? `${lintErrors} problem${lintErrors === 1 ? '' : 's'} for its targets` : 'No problems for its targets',
97
+ lintSeverity: lintErrors ? 'high' : 'none',
98
+ authority: authorityLines(ev.authority),
99
+ changes: ev.diff ? `${changed.length} file${changed.length === 1 ? '' : 's'} changed since the approved revision` : (review.baseApprovedRevisionId ? '' : 'A new skill'),
100
+ reason: review.decisionReason || '',
101
+ comments: (review.comments || []).slice(-5).map((c) => `${c.by?.kind === 'owner' ? 'You' : authorLine(c.by)}: ${c.body}`),
102
+ revisionId: review.submittedRevisionId,
103
+ // The folder name it installs under (its SKILL.md's), and the scan's findings for exactly its bytes.
104
+ installAs: typeof ev.installName === 'string' && ev.installName ? ev.installName : null,
105
+ findings: (ev.scan?.findings || []).slice(0, 5).map((f) => ({ severity: ['critical', 'high', 'medium', 'low'].includes(f.severity) ? f.severity : 'low', text: `${f.severity}: ${f.message}` })),
106
+ actions,
107
+ };
108
+ }
109
+
110
+ /**
111
+ * The targets an install can choose: the ones a submission named, whole (scope, project, account) and turned on,
112
+ * then each runtime's own folder for the default account, off (Claude Code on when nothing was named). Each choice
113
+ * carries the `target` an install sends.
114
+ */
115
+ export function targetChoices({ sharedAgentsRoot = false } = {}, suggested = []) {
116
+ const asked = (suggested || []).map(wholeTarget).filter((t) => typeof t.runtime === 'string' && t.runtime);
117
+ const seen = new Set();
118
+ const choices = [];
119
+ for (const target of asked) {
120
+ const key = JSON.stringify(target);
121
+ if (seen.has(key)) continue;
122
+ seen.add(key);
123
+ choices.push({ key, runtime: target.runtime, target, label: targetLabel(target), on: true });
124
+ }
125
+ const plainAsked = new Set(asked.filter(plainTarget).map((t) => t.runtime));
126
+ for (const runtime of ['claude', 'codex', 'opencode', 'antigravity', ...(sharedAgentsRoot ? ['agents'] : [])]) {
127
+ if (plainAsked.has(runtime)) continue;
128
+ const target = { runtime };
129
+ choices.push({ key: JSON.stringify(target), runtime, target, label: targetLabel(target), on: !asked.length && runtime === 'claude' });
130
+ }
131
+ return choices;
132
+ }
133
+
134
+ /** One row per target of a plan: where, who sees it, and what it needs. */
135
+ export function planRows(plan) {
136
+ return (plan?.targets || []).map((t) => {
137
+ const replace = (t.needs || []).find((n) => n.code === 'confirm_replace') || null;
138
+ const scanAck = (t.needs || []).find((n) => n.code === 'acknowledge_scan') || null;
139
+ const status = t.blocked?.length ? 'blocked' : (t.needs?.length ? 'needs' : 'ready');
140
+ const lines = [];
141
+ if (t.destination) lines.push(`Goes to ${t.destination}`);
142
+ if (t.affectedRuntimes?.length > 1) lines.push(`Read by ${t.affectedRuntimes.map((r) => RUNTIME_LABEL[r] || r).join(' and ')}`);
143
+ for (const b of t.blocked || []) lines.push(b.message);
144
+ for (const tr of t.export?.transforms || []) lines.push(`Changed for this runtime only: ${tr.reason}`);
145
+ for (const o of t.overlap || []) lines.push(o.why);
146
+ if (t.lint?.warnings) lines.push(`${t.lint.warnings} warning${t.lint.warnings === 1 ? '' : 's'} for this runtime`);
147
+ return {
148
+ targetKey: t.targetKey,
149
+ label: targetLabel(t),
150
+ status, lines,
151
+ replace: replace ? { currentDigest: replace.currentDigest, message: replace.message } : null,
152
+ scanAck: scanAck ? { digest: scanAck.digest, message: scanAck.message, findings: (t.scan?.findings || []).slice(0, 10).map((f) => `${f.severity}: ${f.message}${f.file ? ` (${f.file}${f.line ? `:${f.line}` : ''})` : ''}`) } : null,
153
+ refresh: (t.refresh || []).map((r) => r.message).filter(Boolean),
154
+ };
155
+ });
156
+ }
157
+
158
+ /**
159
+ * The decisions an install sends: for each target, exactly what the owner confirmed (the digest of what is there
160
+ * now, the digest whose scan findings they reviewed). Nothing is confirmed by default.
161
+ * @param {object} plan @param {{ replace: Iterable<string>, scan: Iterable<string> }} confirmed target keys
162
+ */
163
+ export function decisionsFor(plan, { replace = [], scan = [] } = {}) {
164
+ const r = new Set(replace); const s = new Set(scan);
165
+ const out = {};
166
+ for (const row of planRows(plan)) {
167
+ const d = {};
168
+ if (row.replace && r.has(row.targetKey)) d.expectedDestinationDigest = row.replace.currentDigest;
169
+ if (row.scanAck && s.has(row.targetKey)) d.acknowledgedDigest = row.scanAck.digest;
170
+ if (Object.keys(d).length) out[row.targetKey] = d;
171
+ }
172
+ return out;
173
+ }
174
+
175
+ /** Whether an install of this plan would do anything with these confirmations. */
176
+ export function installable(plan, confirmed = {}) {
177
+ const r = new Set(confirmed.replace || []); const s = new Set(confirmed.scan || []);
178
+ return planRows(plan).some((row) => row.status !== 'blocked' && (!row.replace || r.has(row.targetKey)) && (!row.scanAck || s.has(row.targetKey)));
179
+ }
180
+
181
+ const RESULT_WORDS = { committed: 'Installed', skipped: 'Not installed', 'needs-confirmation': 'Not installed: it needs your confirmation', failed: 'Failed' };
182
+
183
+ /** The outcome per target, never one word for all. */
184
+ export function resultLines(result) {
185
+ return (result?.results || []).map((r) => {
186
+ const why = r.error || (r.reasons || []).map((x) => x.message).filter(Boolean).join(' ');
187
+ const refresh = (r.refresh || []).map((x) => x.message).filter(Boolean).join(' ');
188
+ return { targetKey: r.targetKey, ok: r.status === 'committed', text: `${RESULT_WORDS[r.status] || r.status}${r.unchanged ? ' (it was already there)' : ''}${why ? `: ${why}` : ''}${r.status === 'committed' && refresh ? ` ${refresh}` : ''}` };
189
+ });
190
+ }
@@ -0,0 +1,79 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: an open document
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // A tab holds the file as it was last read or saved (its exact text and the revision the server gave it) and
6
+ // the text being edited ("\n" line breaks, no byte-order mark). It is unsaved when that text is not the saved
7
+ // text, line endings aside: opening a CRLF file or switching tabs never makes one. A save sends a snapshot,
8
+ // the text as it is when Save is pressed, with the file's own line endings put back; when the answer comes,
9
+ // the file is that snapshot, and anything typed meanwhile is still unsaved. No DOM and no imports beyond
10
+ // ss-doc.js.
11
+
12
+ import { toEditing, fromEditing, sameAsSaved, looksDecodedFromBinary } from './ss-doc.js';
13
+
14
+ /** The key of a tab's copy of unsaved text: the same for the same file, whichever tab opens it. */
15
+ export const draftKey = (artifactId, path) => `skills:${artifactId}::${path ?? ''}`;
16
+
17
+ /**
18
+ * A tab from what the server answered.
19
+ * @param {object} o
20
+ * @param {object} o.artifact the library entry
21
+ * @param {object} o.artifactContent what GET /artifact/:id answered (subFiles…)
22
+ * @param {string|null} o.path null for the artifact's own file, else the sub-file
23
+ * @param {string} o.raw the file's text as read
24
+ * @param {string|null} [o.revision] what a save names (null: a server from before revisions)
25
+ * @param {boolean} [o.binary] not text: shown, never edited. What the server said, as it said it: `undefined`
26
+ * is a server from before binary files were told apart
27
+ * @param {boolean} [o.readOnly] a bundled skill's source
28
+ */
29
+ export function createDocTab({ artifact, artifactContent, path = null, raw = '', revision = null, binary, size = 0, readOnly = false, label }) {
30
+ const text = String(raw ?? '');
31
+ // The server's word decides. One from before binary files were told apart (it says nothing) decoded every
32
+ // file as text, the artifact's own file too: a text that holds what that decoding replaced is not edited.
33
+ const isBinary = binary === true || (binary !== false && looksDecodedFromBinary(text));
34
+ const editing = toEditing(isBinary ? '' : text);
35
+ return {
36
+ id: path === null ? `main:${artifact.id}` : `${artifact.id}:${path}`,
37
+ artifactId: artifact.id, artifact, artifactContent, path,
38
+ label: label ?? (path === null ? artifact.name : String(path).split('/').pop()),
39
+ saved: { raw: isBinary ? '' : text, revision: revision ?? null },
40
+ content: editing.text,
41
+ bom: editing.bom, eol: editing.eol, mixedEol: editing.mixed,
42
+ dirty: false, binary: isBinary, size: Number(size) || text.length,
43
+ readOnly: !!readOnly || isBinary,
44
+ saving: null,
45
+ };
46
+ }
47
+
48
+ /** Whether the tab's text differs from the file as saved (line endings and the byte-order mark aside). */
49
+ export function refreshDirty(tab) {
50
+ tab.dirty = !tab.binary && !tab.readOnly && !sameAsSaved(tab.saved.raw, tab.content);
51
+ return tab.dirty;
52
+ }
53
+
54
+ /** What a save sends: the text as it is now, written the way the file is written. */
55
+ export function saveSnapshot(tab) {
56
+ const text = tab.content;
57
+ return { text, raw: fromEditing(tab.saved.raw, text), expectedRevision: tab.saved.revision };
58
+ }
59
+
60
+ /** The save of `sent` arrived: the file is that now. Returns whether the tab is still unsaved (typed meanwhile). */
61
+ export function applySaved(tab, sent, revision) {
62
+ tab.saved = { raw: sent.raw, revision: revision ?? null };
63
+ return refreshDirty(tab);
64
+ }
65
+
66
+ /** Give the unsaved text up: the tab holds the file as saved again. */
67
+ export function revertTab(tab) {
68
+ tab.content = toEditing(tab.saved.raw).text;
69
+ return refreshDirty(tab);
70
+ }
71
+
72
+ /** The file was read again (another save won, or "reload"): the tab holds what is there now. */
73
+ export function replaceSaved(tab, raw, revision) {
74
+ tab.saved = { raw: String(raw ?? ''), revision: revision ?? null };
75
+ const editing = toEditing(tab.saved.raw);
76
+ tab.content = editing.text;
77
+ tab.bom = editing.bom; tab.eol = editing.eol; tab.mixedEol = editing.mixed;
78
+ return refreshDirty(tab);
79
+ }
@@ -0,0 +1,135 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: characters a reader cannot see
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // Text that hides something from the person reading it: bidirectional controls that reorder what is shown,
6
+ // zero-width and other invisible characters, Unicode "tag" characters that spell text nobody sees, control
7
+ // characters. Found and shown escaped (U+202E, never removed: the evidence stays as it was written), so a
8
+ // review sees what a model reads. Ordinary multilingual text is left alone: a zero-width joiner inside an emoji
9
+ // or between letters of a script that uses it is not flagged; one between two ASCII letters is.
10
+ //
11
+ // Also: names that look like other names (Cyrillic "а" in "synаbun"). No DOM and no imports.
12
+
13
+ const NAMES = {
14
+ 0x00AD: 'SOFT HYPHEN', 0x034F: 'COMBINING GRAPHEME JOINER', 0x061C: 'ARABIC LETTER MARK', 0x115F: 'HANGUL CHOSEONG FILLER',
15
+ 0x1160: 'HANGUL JUNGSEONG FILLER', 0x17B4: 'KHMER VOWEL INHERENT AQ', 0x17B5: 'KHMER VOWEL INHERENT AA', 0x180E: 'MONGOLIAN VOWEL SEPARATOR',
16
+ 0x200B: 'ZERO WIDTH SPACE', 0x200C: 'ZERO WIDTH NON-JOINER', 0x200D: 'ZERO WIDTH JOINER', 0x200E: 'LEFT-TO-RIGHT MARK',
17
+ 0x200F: 'RIGHT-TO-LEFT MARK', 0x202A: 'LEFT-TO-RIGHT EMBEDDING', 0x202B: 'RIGHT-TO-LEFT EMBEDDING', 0x202C: 'POP DIRECTIONAL FORMATTING',
18
+ 0x202D: 'LEFT-TO-RIGHT OVERRIDE', 0x202E: 'RIGHT-TO-LEFT OVERRIDE', 0x2060: 'WORD JOINER', 0x2061: 'FUNCTION APPLICATION',
19
+ 0x2062: 'INVISIBLE TIMES', 0x2063: 'INVISIBLE SEPARATOR', 0x2064: 'INVISIBLE PLUS', 0x2066: 'LEFT-TO-RIGHT ISOLATE',
20
+ 0x2067: 'RIGHT-TO-LEFT ISOLATE', 0x2068: 'FIRST STRONG ISOLATE', 0x2069: 'POP DIRECTIONAL ISOLATE', 0x3164: 'HANGUL FILLER',
21
+ 0xFEFF: 'ZERO WIDTH NO-BREAK SPACE', 0xFFA0: 'HALFWIDTH HANGUL FILLER',
22
+ };
23
+
24
+ const isBidi = (cp) => (cp >= 0x202A && cp <= 0x202E) || (cp >= 0x2066 && cp <= 0x2069) || cp === 0x200E || cp === 0x200F || cp === 0x061C;
25
+ const isTag = (cp) => cp >= 0xE0000 && cp <= 0xE007F;
26
+ const isVariationSupplement = (cp) => cp >= 0xE0100 && cp <= 0xE01EF;
27
+ const isControl = (cp) => (cp < 0x20 && cp !== 0x09 && cp !== 0x0A && cp !== 0x0D) || (cp >= 0x7F && cp <= 0x9F);
28
+ const isJoiner = (cp) => cp === 0x200C || cp === 0x200D;
29
+ const isInvisible = (cp) => cp in NAMES && !isBidi(cp) && !isJoiner(cp);
30
+ const isAsciiLetter = (cp) => (cp >= 0x41 && cp <= 0x5A) || (cp >= 0x61 && cp <= 0x7A) || (cp >= 0x30 && cp <= 0x39);
31
+
32
+ /** The name of a code point, for a message ("U+202E RIGHT-TO-LEFT OVERRIDE"). */
33
+ export function describeCodePoint(cp) {
34
+ const hex = `U+${cp.toString(16).toUpperCase().padStart(4, '0')}`;
35
+ if (NAMES[cp]) return `${hex} ${NAMES[cp]}`;
36
+ if (isTag(cp)) return `${hex} TAG CHARACTER`;
37
+ if (isVariationSupplement(cp)) return `${hex} VARIATION SELECTOR`;
38
+ if (isControl(cp)) return `${hex} CONTROL CHARACTER`;
39
+ return hex;
40
+ }
41
+
42
+ /**
43
+ * The hidden characters of a text.
44
+ * @param {string} text
45
+ * @param {{ identifier?: boolean, limit?: number, onHit?: (hit: object) => void }} [o] `identifier`: the text is a name (every invisible
46
+ * character counts, a joiner too); `limit`: retain this many; with `onHit`, visit every hit without retaining the whole result
47
+ * @returns {{ index: number, line: number, column: number, codePoint: number, kind: 'bidi'|'tag'|'invisible'|'control'|'joiner', label: string }[]}
48
+ */
49
+ export function findHiddenChars(text, { identifier = false, limit = 200, onHit = null } = {}) {
50
+ const s = String(text ?? '');
51
+ const out = [];
52
+ let line = 1; let column = 1;
53
+ let prev = 0;
54
+ let index = 0;
55
+ for (const ch of s) {
56
+ const cp = ch.codePointAt(0);
57
+ let kind = null;
58
+ if (isBidi(cp)) kind = 'bidi';
59
+ else if (isTag(cp) || isVariationSupplement(cp)) kind = 'tag';
60
+ else if (isControl(cp)) kind = 'control';
61
+ else if (cp === 0xFEFF && index === 0 && !identifier) kind = null; // a byte-order mark where one belongs
62
+ else if (isInvisible(cp)) kind = 'invisible';
63
+ else if (isJoiner(cp)) {
64
+ const next = s.codePointAt(index + ch.length) || 0;
65
+ if (identifier || (isAsciiLetter(prev) && isAsciiLetter(next))) kind = 'joiner';
66
+ }
67
+ if (kind) {
68
+ const hit = { index, line, column, codePoint: cp, kind, label: describeCodePoint(cp) };
69
+ if (onHit) onHit(hit);
70
+ if (out.length < limit) out.push(hit);
71
+ if (!onHit && out.length >= limit) break;
72
+ }
73
+ if (cp === 10) { line++; column = 1; } else column++;
74
+ prev = cp;
75
+ index += cp > 0xFFFF ? 2 : 1;
76
+ }
77
+ return out;
78
+ }
79
+
80
+ /** The text with every hidden character written out (`⟦U+202E⟧`), for showing a name or a line as evidence. */
81
+ export function revealHidden(text, { identifier = true } = {}) {
82
+ const s = String(text ?? '');
83
+ const found = findHiddenChars(s, { identifier, limit: 10000 });
84
+ if (!found.length) return s;
85
+ let out = '';
86
+ let at = 0;
87
+ for (const hit of found) {
88
+ out += s.slice(at, hit.index) + `⟦U+${hit.codePoint.toString(16).toUpperCase().padStart(4, '0')}⟧`;
89
+ at = hit.index + (hit.codePoint > 0xFFFF ? 2 : 1);
90
+ }
91
+ return out + s.slice(at);
92
+ }
93
+
94
+ // Letters of other scripts that are drawn like Latin ones (the common skeleton cases).
95
+ const CONFUSABLES = {
96
+ 'а': 'a', 'в': 'b', 'е': 'e', 'ё': 'e', 'к': 'k', 'м': 'm', 'н': 'h', 'о': 'o', 'р': 'p', 'с': 'c', 'т': 't', 'у': 'y', 'х': 'x',
97
+ 'ѕ': 's', 'і': 'i', 'ї': 'i', 'ј': 'j', 'ԁ': 'd', 'ԛ': 'q', 'ԝ': 'w', 'һ': 'h', 'ɡ': 'g', 'ɩ': 'i', 'ʏ': 'y', 'ʟ': 'l',
98
+ 'α': 'a', 'ο': 'o', 'ν': 'v', 'ρ': 'p', 'τ': 't', 'υ': 'u', 'ι': 'i', 'κ': 'k', 'χ': 'x', 'ϲ': 'c', 'ϳ': 'j',
99
+ 'А': 'A', 'В': 'B', 'Е': 'E', 'К': 'K', 'М': 'M', 'Н': 'H', 'О': 'O', 'Р': 'P', 'С': 'C', 'Т': 'T', 'Х': 'X', 'Ѕ': 'S', 'І': 'I', 'Ј': 'J',
100
+ 'Α': 'A', 'Β': 'B', 'Ε': 'E', 'Ζ': 'Z', 'Η': 'H', 'Ι': 'I', 'Κ': 'K', 'Μ': 'M', 'Ν': 'N', 'Ο': 'O', 'Ρ': 'P', 'Τ': 'T', 'Υ': 'Y', 'Χ': 'X',
101
+ 'ı': 'i', 'ℓ': 'l', '0': '0', '1': '1', 'ⅼ': 'l', 'ⅰ': 'i',
102
+ };
103
+
104
+ /** A name as it is drawn: confusable letters swapped for the Latin ones they look like, compatibility forms folded, lower case. */
105
+ export function nameSkeleton(name) {
106
+ const folded = String(name ?? '').normalize('NFKC');
107
+ return Array.from(folded, (ch) => CONFUSABLES[ch] ?? ch).join('').toLowerCase();
108
+ }
109
+
110
+ /**
111
+ * What is misleading about a name: hidden characters, letters of another script that look Latin, a look-alike of
112
+ * a known name.
113
+ * @param {string} name
114
+ * @param {string[]} [known] names it must not pass for (the bundled skills, say)
115
+ * @returns {{ hidden: object[], confusable: boolean, skeleton: string, impersonates: string | null, scripts: string[] }}
116
+ */
117
+ export function inspectName(name, known = []) {
118
+ const s = String(name ?? '');
119
+ const hidden = findHiddenChars(s, { identifier: true });
120
+ const skeleton = nameSkeleton(s);
121
+ const confusable = Array.from(s).some((ch) => ch in CONFUSABLES);
122
+ const scripts = new Set();
123
+ for (const ch of s) {
124
+ if (/\p{Script=Latin}/u.test(ch)) scripts.add('Latin');
125
+ else if (/\p{Script=Cyrillic}/u.test(ch)) scripts.add('Cyrillic');
126
+ else if (/\p{Script=Greek}/u.test(ch)) scripts.add('Greek');
127
+ else if (/\p{L}/u.test(ch)) scripts.add('other');
128
+ }
129
+ const plain = s.toLowerCase();
130
+ const impersonates = (known || []).find((k) => {
131
+ const ref = String(k ?? '').toLowerCase();
132
+ return ref && ref !== plain && nameSkeleton(ref) === skeleton;
133
+ }) || null;
134
+ return { hidden, confusable, skeleton, impersonates, scripts: [...scripts] };
135
+ }