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,212 @@
1
+ // ── The Review queue ──
2
+ // What the SynaBun Assistant (or any agent) makes goes here, never straight into a runtime (owner decision,
3
+ // 2026-10-22): a submission is one immutable revision of a library skill, with its digest, the targets it is meant
4
+ // for, the checks of exactly those bytes, what it can do compared with the revision approved before it (the scan's
5
+ // authority inventory, side by side), release notes, the author's assumptions, and who made it. The owner
6
+ // approves, asks for changes or rejects; only an approval moves the skill's `approved` pointer, and only an
7
+ // approved revision of an agent's is installed (lib/skills/deploy.js refuses the rest). New bytes need a new
8
+ // submission: an approval belongs to the digest it was given for.
9
+ //
10
+ // pending ──► approved ──► publishing ──► published | partial | publish_failed
11
+ // │ └──► changes_requested ──► (a new submission links back to it)
12
+ // ├──► rejected
13
+ // └──► withdrawn (the author, or the owner)
14
+ //
15
+ // DATA_HOME/data/skills/reviews.json (schema 2), written whole and atomically.
16
+
17
+ import { randomBytes } from 'node:crypto';
18
+ import { mkdirSync } from 'node:fs';
19
+ import { join } from 'node:path';
20
+ import { PathConfineError } from '../path-confine.js';
21
+ import { readConfinedFile, replaceFileAtomic } from '../confined-fs.js';
22
+ import { normalizeAuthor } from './library.js';
23
+
24
+ export const REVIEWS_SCHEMA = 2;
25
+ export const REVIEW_POLICY = 'review-policy-1';
26
+ export const REVIEW_STATES = Object.freeze(['pending', 'changes_requested', 'approved', 'publishing', 'published', 'partial', 'publish_failed', 'rejected', 'withdrawn']);
27
+ const OPEN = new Set(['pending', 'changes_requested']);
28
+ const MAX_REVIEWS = 2000;
29
+ const MAX_COMMENTS = 200;
30
+ const REVIEW_RE = /^rvw_[0-9a-f]{20}$/;
31
+
32
+ export class ReviewError extends PathConfineError {
33
+ constructor(status, message, code, extra = {}) {
34
+ super(status, message);
35
+ this.code = code;
36
+ Object.assign(this, extra);
37
+ }
38
+ }
39
+
40
+ const text = (v, n) => (typeof v === 'string' ? v.slice(0, n) : '');
41
+
42
+ /** What one revision can do that the other cannot (the scans' authority inventories compared). */
43
+ export function authorityDelta(before, after) {
44
+ const keys = ['scripts', 'binaries', 'dynamicContext', 'hooks', 'toolGrants', 'domains', 'sensitive', 'persistentWrites', 'installers'];
45
+ const label = (k, v) => {
46
+ if (k === 'scripts' || k === 'binaries') return v.path;
47
+ if (k === 'hooks') return `${v.event}`;
48
+ if (k === 'toolGrants') return `${v.key}: ${v.tool}`;
49
+ if (k === 'domains') return v;
50
+ if (k === 'dynamicContext') return `${v.file}: ${v.command}`;
51
+ return `${v.file}:${v.line}`;
52
+ };
53
+ const out = {};
54
+ for (const k of keys) {
55
+ const a = new Set((before?.[k] || []).map((v) => label(k, v)));
56
+ const b = new Set((after?.[k] || []).map((v) => label(k, v)));
57
+ const added = [...b].filter((x) => !a.has(x));
58
+ const removed = [...a].filter((x) => !b.has(x));
59
+ if (added.length || removed.length) out[k] = { added: added.slice(0, 100), removed: removed.slice(0, 100) };
60
+ }
61
+ return { changed: Object.keys(out).length > 0, byKind: out };
62
+ }
63
+
64
+ /**
65
+ * @param {object} o
66
+ * @param {string} o.dataDir DATA_HOME/data/skills
67
+ * @param {object} o.library lib/skills/library.js
68
+ */
69
+ export function createReviewQueue({ dataDir, library, emit = () => {}, now = () => new Date().toISOString(), newId = () => `rvw_${randomBytes(10).toString('hex')}` }) {
70
+ const storePath = join(dataDir, 'reviews.json');
71
+
72
+ function read() {
73
+ try {
74
+ const parsed = JSON.parse(readConfinedFile(dataDir, storePath, { maxBytes: 32 * 1024 * 1024, encoding: 'utf-8' }));
75
+ if (parsed?.schemaVersion === REVIEWS_SCHEMA && Array.isArray(parsed.records)) return parsed;
76
+ } catch { /* none yet */ }
77
+ return { schemaVersion: REVIEWS_SCHEMA, records: [] };
78
+ }
79
+ function write(store) {
80
+ mkdirSync(dataDir, { recursive: true, mode: 0o700 });
81
+ const records = store.records.length > MAX_REVIEWS ? store.records.slice(-MAX_REVIEWS) : store.records;
82
+ replaceFileAtomic(storePath, `${JSON.stringify({ schemaVersion: REVIEWS_SCHEMA, records }, null, 2)}\n`, { mode: 0o600 });
83
+ }
84
+ function get(reviewId) {
85
+ if (typeof reviewId !== 'string' || !REVIEW_RE.test(reviewId)) throw new ReviewError(400, 'Not a review id.', 'invalid_review');
86
+ const rec = read().records.find((r) => r.reviewId === reviewId);
87
+ if (!rec) throw new ReviewError(404, 'There is no such review.', 'review_not_found');
88
+ return rec;
89
+ }
90
+ function update(reviewId, change) {
91
+ const store = read();
92
+ const rec = store.records.find((r) => r.reviewId === reviewId);
93
+ if (!rec) throw new ReviewError(404, 'There is no such review.', 'review_not_found');
94
+ change(rec);
95
+ rec.updatedAt = now();
96
+ write(store);
97
+ emit({ type: 'review.changed', reviewId, skillId: rec.skillId, state: rec.state });
98
+ return rec;
99
+ }
100
+
101
+ function list({ state = null, skillId = null, open = null } = {}) {
102
+ return read().records.filter((r) => (!state || r.state === state) && (!skillId || r.skillId === skillId) && (open === null || OPEN.has(r.state) === open))
103
+ .sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1));
104
+ }
105
+
106
+ /**
107
+ * Submit a revision for review.
108
+ * @param {object} o
109
+ * @param {string} o.revisionId
110
+ * @param {object[]} [o.targets] where it is meant to go ({ runtime, scope, accountId?, projectPath? })
111
+ * @param {object} o.evidence { lint, scan, authority } of exactly that revision
112
+ */
113
+ function submit({ revisionId, targets = [], releaseNotes = '', assumptions = [], author, evidence = {}, previousReviewId = null }) {
114
+ const revision = library.getRevision(revisionId);
115
+ const skill = library.getSkill(revision.skillId);
116
+ const store = read();
117
+ const dup = store.records.find((r) => r.submittedRevisionId === revisionId && OPEN.has(r.state));
118
+ if (dup) return { review: dup, existing: true };
119
+ if (previousReviewId !== null) {
120
+ const prev = store.records.find((r) => r.reviewId === previousReviewId);
121
+ if (!prev || prev.skillId !== skill.skillId) throw new ReviewError(400, 'previousReviewId is not a review of this skill.', 'invalid_review');
122
+ }
123
+ const rec = {
124
+ schemaVersion: REVIEWS_SCHEMA, reviewId: newId(), skillId: skill.skillId, skillName: skill.name,
125
+ submittedRevisionId: revisionId, submittedDigest: revision.contentDigest, baseApprovedRevisionId: skill.pointers.approved || null,
126
+ state: 'pending',
127
+ targets: (Array.isArray(targets) ? targets : []).slice(0, 12).map((t) => ({ runtime: text(t?.runtime, 20), scope: text(t?.scope, 20) || 'user', accountId: text(t?.accountId, 64) || 'default', projectPath: text(t?.projectPath, 2000) || null })),
128
+ evidence, releaseNotes: text(releaseNotes, 8000), assumptions: (Array.isArray(assumptions) ? assumptions : []).filter((a) => typeof a === 'string').slice(0, 50).map((a) => a.slice(0, 1000)),
129
+ author: normalizeAuthor(author), reviewer: null, policyVersion: REVIEW_POLICY, decisionReason: null, exceptions: [],
130
+ comments: [], publication: { jobId: null, targetResults: [] }, previousReviewId, createdAt: now(), updatedAt: now(),
131
+ };
132
+ if (!REVIEW_RE.test(rec.reviewId)) throw new ReviewError(500, 'Bad review id.', 'bad_id');
133
+ store.records.push(rec);
134
+ if (previousReviewId) { const prev = store.records.find((r) => r.reviewId === previousReviewId); if (prev) prev.supersededBy = rec.reviewId; }
135
+ write(store);
136
+ try { library.setPointer(skill.skillId, 'candidate', revisionId, { by: rec.author.kind, reason: `submitted for review (${rec.reviewId})` }); } catch { /* the pointer is a convenience: the review stands */ }
137
+ emit({ type: 'review.changed', reviewId: rec.reviewId, skillId: rec.skillId, state: rec.state });
138
+ return { review: rec, existing: false };
139
+ }
140
+
141
+ function comment(reviewId, { by, body }) {
142
+ const t = text(body, 4000).trim();
143
+ if (!t) throw new ReviewError(400, 'A comment has text.', 'invalid_comment');
144
+ return update(reviewId, (rec) => {
145
+ if (rec.comments.length >= MAX_COMMENTS) throw new ReviewError(413, 'This review has as many comments as it can hold.', 'too_many_comments');
146
+ rec.comments.push({ at: now(), by: normalizeAuthor(by), body: t });
147
+ });
148
+ }
149
+
150
+ /** The author takes a submission back (or the owner closes it). */
151
+ function withdraw(reviewId, { by }) {
152
+ return update(reviewId, (rec) => {
153
+ if (!OPEN.has(rec.state)) throw new ReviewError(409, `A review that is ${rec.state} cannot be withdrawn.`, 'review_state');
154
+ rec.state = 'withdrawn';
155
+ rec.decisionReason = 'withdrawn';
156
+ rec.reviewer = normalizeAuthor(by);
157
+ });
158
+ }
159
+
160
+ /**
161
+ * The owner's decision. `approve` binds the approval to the submitted digest (the revision must still read as
162
+ * it was submitted, and its scan must not block) and moves the skill's `approved` pointer.
163
+ * @param {'approve'|'reject'|'request_changes'} decision
164
+ */
165
+ /**
166
+ * @param {object} o
167
+ * @param {string|null} [o.installName] the name the revision installs under (deploy.js effectiveInstallName): an
168
+ * approval covers that name only, and an install under another name goes back to review
169
+ */
170
+ function decide(reviewId, { decision, reason = '', by, scan = null, installName = null }) {
171
+ if (!['approve', 'reject', 'request_changes'].includes(decision)) throw new ReviewError(400, 'A decision is approve, reject or request_changes.', 'invalid_decision');
172
+ const rec = get(reviewId);
173
+ if (!OPEN.has(rec.state) || (decision === 'approve' && rec.state !== 'pending')) throw new ReviewError(409, `A review that is ${rec.state} cannot be ${decision === 'request_changes' ? 'sent back' : `${decision}d`}.`, 'review_state');
174
+ if (decision === 'approve') {
175
+ const revision = library.getRevision(rec.submittedRevisionId);
176
+ if (revision.contentDigest !== rec.submittedDigest) throw new ReviewError(409, 'The revision does not read as it was submitted.', 'digest_mismatch');
177
+ library.readRevisionFiles(rec.submittedRevisionId); // every blob there and intact
178
+ if (scan && scan.digest === rec.submittedDigest && (scan.gate === 'blocked' || scan.status === 'error')) {
179
+ throw new ReviewError(409, 'The scan of these files blocks them: they cannot be approved.', 'scan_blocked');
180
+ }
181
+ library.setPointer(rec.skillId, 'approved', rec.submittedRevisionId, { by: 'owner', reason: `review ${reviewId} approved`, allowProtected: 'review' });
182
+ }
183
+ return update(reviewId, (r) => {
184
+ r.state = decision === 'approve' ? 'approved' : decision === 'reject' ? 'rejected' : 'changes_requested';
185
+ r.reviewer = normalizeAuthor(by);
186
+ r.decisionReason = text(reason, 4000) || null;
187
+ r.decidedAt = now();
188
+ if (decision === 'approve') r.approvedInstallName = typeof installName === 'string' && installName ? installName : null;
189
+ });
190
+ }
191
+
192
+ /** The approval a revision has: the newest approved review of it, with the name it was approved to install as. */
193
+ function approvalOf(revisionId) {
194
+ const approved = new Set(['approved', 'publishing', 'published', 'partial', 'publish_failed']);
195
+ const found = read().records.filter((r) => r.submittedRevisionId === revisionId && approved.has(r.state))
196
+ .sort((a, b) => String(b.decidedAt || '').localeCompare(String(a.decidedAt || '')))[0];
197
+ return found ? { reviewId: found.reviewId, installName: found.approvedInstallName || null } : null;
198
+ }
199
+
200
+ /** A publication of an approved review: its per-target outcomes, never one success for all. */
201
+ function recordPublication(reviewId, { results, jobId = null }) {
202
+ return update(reviewId, (rec) => {
203
+ if (!['approved', 'publishing', 'published', 'partial', 'publish_failed'].includes(rec.state)) throw new ReviewError(409, 'Only an approved review is published.', 'review_state');
204
+ const list = Array.isArray(results) ? results : [];
205
+ rec.publication = { jobId, targetResults: [...(rec.publication?.targetResults || []), ...list.map((r) => ({ at: now(), targetKey: r.targetKey, status: r.status, installationId: r.installationId || null, error: r.error || null }))].slice(-200) };
206
+ const committed = list.filter((r) => r.status === 'committed').length;
207
+ rec.state = committed === list.length && list.length ? 'published' : committed ? 'partial' : 'publish_failed';
208
+ });
209
+ }
210
+
211
+ return { list, get, submit, comment, withdraw, decide, approvalOf, recordPublication, paths: { storePath } };
212
+ }
@@ -0,0 +1,80 @@
1
+ // ── Saving over the version that was opened ──
2
+ // A Skills Studio save names the revision the editor opened (lib/skills/digest.js revisionOf): when the file on
3
+ // disk is not that revision any more (another editor, a pull, an agent changed it), nothing is written and the
4
+ // answer says so (409, `revision_conflict`, with the revision that is there now). Otherwise the new bytes go to
5
+ // a temporary file in the same folder and are renamed over the old one, so an interrupted save leaves the old
6
+ // file whole; the file keeps its permissions, and a link to it inside the skill stays a link.
7
+
8
+ import { lstatSync } from 'node:fs';
9
+ import { PathConfineError } from '../path-confine.js';
10
+ import { canonicalInside, readConfinedFile, replaceFileAtomic, writeConfinedFile, MAX_FILE_BYTES } from '../confined-fs.js';
11
+ import { revisionOf } from './digest.js';
12
+ import { isEditableText } from '../../public/shared/skills/ss-doc.js';
13
+
14
+ export class RevisionConflictError extends PathConfineError {
15
+ constructor(currentRevision) {
16
+ super(409, currentRevision
17
+ ? 'This file was changed since it was opened. Nothing was written.'
18
+ : 'This file was removed since it was opened. Nothing was written.');
19
+ this.code = 'revision_conflict';
20
+ this.currentRevision = currentRevision;
21
+ }
22
+ }
23
+
24
+ /** A revision as a request may carry it: `sha256:<64 hex>`, or `absent` (the file must not exist yet). */
25
+ export const isRevision = (value) => value === 'absent' || (typeof value === 'string' && /^sha256:[0-9a-f]{64}$/.test(value));
26
+
27
+ /**
28
+ * Read one regular file inside `base`, with its revision.
29
+ * @returns {{ bytes: Buffer, binary: boolean, text: string | null, revision: string, size: number }}
30
+ */
31
+ export function readRevisioned(base, path, { maxBytes = MAX_FILE_BYTES } = {}) {
32
+ const bytes = readConfinedFile(base, path, { maxBytes });
33
+ const binary = !isEditableText(bytes);
34
+ return { bytes, binary, text: binary ? null : bytes.toString('utf-8'), revision: revisionOf(bytes), size: bytes.length };
35
+ }
36
+
37
+ /**
38
+ * Write one file inside `base` over the revision it was opened at.
39
+ * @param {string} base the folder the file must really be in
40
+ * @param {string} path
41
+ * @param {Buffer|string} data
42
+ * @param {object} [o]
43
+ * @param {string|null} [o.expectedRevision] what the file must hold now (`absent`: it must not exist). Without one
44
+ * (a page from before revisions) the file is written as before, but atomically.
45
+ * @param {number} [o.maxBytes] the most the new content may be (413 over it)
46
+ * @param {boolean} [o.refuseBinary] a file that is not text is not replaced (415)
47
+ * @param {boolean} [o.create] a file that does not exist may be created (exclusively)
48
+ * @returns {{ revision: string, created: boolean, path: string }}
49
+ */
50
+ export function writeRevisioned(base, path, data, { expectedRevision = null, maxBytes = MAX_FILE_BYTES, refuseBinary = false, create = true } = {}) {
51
+ if (expectedRevision !== null && expectedRevision !== undefined && !isRevision(expectedRevision)) throw new PathConfineError(400, 'Not a revision.');
52
+ const buf = Buffer.isBuffer(data) ? data : Buffer.from(String(data ?? ''), 'utf-8');
53
+ if (buf.length > maxBytes) throw new PathConfineError(413, `That is larger than ${Math.max(1, Math.round(maxBytes / (1024 * 1024)))} MB; it was not written.`);
54
+ const real = canonicalInside(base, path);
55
+ let st = null;
56
+ try { st = lstatSync(real); } catch { st = null; }
57
+ if (!st) {
58
+ if (expectedRevision && expectedRevision !== 'absent') throw new RevisionConflictError(null);
59
+ if (!create) throw new PathConfineError(404, 'File not found.');
60
+ writeConfinedFile(base, real, buf, { exclusive: true });
61
+ return { revision: revisionOf(buf), created: true, path: real };
62
+ }
63
+ if (!st.isFile()) throw new PathConfineError(400, 'Not a regular file.');
64
+ const current = readConfinedFile(base, real, { maxBytes: Math.max(maxBytes, MAX_FILE_BYTES) });
65
+ const currentRevision = revisionOf(current);
66
+ // "Create it again" while something made the file meanwhile: what is there is named, never replaced.
67
+ if (expectedRevision === 'absent') throw new RevisionConflictError(currentRevision);
68
+ if (expectedRevision && expectedRevision !== currentRevision) throw new RevisionConflictError(currentRevision);
69
+ if (refuseBinary && !isEditableText(current)) {
70
+ const err = new PathConfineError(415, 'This file is not text: it is not replaced by text. Nothing was written.');
71
+ err.code = 'binary_file';
72
+ throw err;
73
+ }
74
+ replaceFileAtomic(real, buf, {
75
+ mode: st.mode & 0o7777,
76
+ exactMode: true,
77
+ expect: { dev: st.dev, ino: st.ino, mtimeMs: st.mtimeMs, size: st.size },
78
+ });
79
+ return { revision: revisionOf(buf), created: false, path: real };
80
+ }
@@ -0,0 +1,269 @@
1
+ // ── Every folder a runtime reads skills, commands, agents or prompts from ──
2
+ // One list, for the catalog (lib/skills/catalog.js), the installs (lib/skills/deploy.js), the bridge migration
3
+ // (lib/skills/migration.js) and the staging check (an import is never staged inside one of these). Each root says
4
+ // which runtimes read it, for whom (a Claude or Codex account, a project), what lives there (skill folders, or
5
+ // command / agent / prompt files), whether SynaBun may write there and why, and how its discovery was verified.
6
+ //
7
+ // Where they are (profiles in public/shared/skills/ss-profiles.js; Codex and OpenCode verified on 2026-10-22 by
8
+ // starting codex-cli 0.163.0 and opencode 2.0.32 in a temporary home):
9
+ // Claude Code <config dir>/skills, /commands, /agents for each account (CLAUDE_CONFIG_DIR, ~/.claude by
10
+ // default; an added account's own folder); <config dir>/skills/synced (account skills,
11
+ // read-only); plugin skills and commands under <config dir>/plugins/cache (read-only); the managed
12
+ // settings folder's .claude/skills (read-only); each registered project's .claude/skills,
13
+ // /commands, /agents.
14
+ // Codex $CODEX_HOME/skills for each account (~/.codex by default; the installer's default and where
15
+ // SynaBun installs), $CODEX_HOME/skills/.system (Codex's own, read-only), $CODEX_HOME/prompts (the
16
+ // old bridges), /etc/codex/skills (admin, read-only), a project's .agents/skills (Codex and
17
+ // OpenCode) and .codex/skills (read by Codex, not documented: read-only here).
18
+ // OpenCode <config>/opencode/skills (XDG_CONFIG_HOME or ~/.config; where SynaBun installs), the singular
19
+ // <config>/opencode/skill (read), <config>/opencode/command and ~/.opencode/command (the old
20
+ // bridges), <config>/opencode/commands, a project's .opencode/skills. With OPENCODE_CONFIG_DIR set,
21
+ // OpenCode (2.0.32, checked) reads <dir>/skills and <dir>/skill instead of the <config>/opencode
22
+ // ones: <dir>/skills is then where SynaBun installs (and <dir> holds the settings it reads), and the
23
+ // <config>/opencode skill folders stay listed as read by no runtime.
24
+ // OpenCode also reads ~/.claude/skills and both .agents roots (not in SynaBun's own OpenCode panel,
25
+ // which turns the Claude ones off).
26
+ // Antigravity ~/.gemini/config/skills (Google Antigravity's `agy`; where SynaBun installs) and a project's
27
+ // .agents/skills (the Codex project folder, which Antigravity reads too). Not fixture-tested.
28
+ // Shared ~/.agents/skills: Codex and OpenCode (and other agents) read it. SynaBun writes there only when
29
+ // the owner turned that on (`sharedAgentsRoot`).
30
+ // SynaBun PACKAGE_ROOT/skills, the bundled skills: read-only.
31
+ //
32
+ // Nothing here reads a skill; `status` is only what the folder is (there, missing, not a folder, unreadable).
33
+
34
+ import { createHash } from 'node:crypto';
35
+ import { lstatSync, readdirSync, realpathSync } from 'node:fs';
36
+ import { homedir } from 'node:os';
37
+ import { join, resolve } from 'node:path';
38
+
39
+ export const RUNTIMES = Object.freeze(['claude', 'codex', 'opencode', 'antigravity']);
40
+
41
+ const realFs = { lstat: lstatSync, realpath: realpathSync, readdir: readdirSync };
42
+
43
+ /** A root's id: stable for the same kind of root at the same place. */
44
+ export function rootIdOf({ family, kind, scope, accountId = '', path }) {
45
+ return `root_${createHash('sha256').update([family, kind, scope, accountId || '', resolve(String(path))].join('\0')).digest('hex').slice(0, 20)}`;
46
+ }
47
+
48
+ /** The managed-settings folder of Claude Code on this platform (enterprise skills live in its .claude/skills). */
49
+ export function claudeManagedDir(platform = process.platform) {
50
+ if (platform === 'darwin') return '/Library/Application Support/ClaudeCode';
51
+ if (platform === 'win32') return 'C:\\Program Files\\ClaudeCode';
52
+ return '/etc/claude-code';
53
+ }
54
+
55
+ /**
56
+ * @param {object} o
57
+ * @param {Record<string,string|undefined>} [o.env]
58
+ * @param {string} [o.homeDir]
59
+ * @param {string} [o.platform]
60
+ * @param {() => {path: string, label?: string}[]} [o.projects] registered projects
61
+ * @param {() => {id: string, label?: string, home: string}[]} [o.claudeAccounts] every Claude account with its config dir
62
+ * @param {() => {id: string, label?: string, home: string}[]} [o.codexAccounts] every Codex account with its CODEX_HOME
63
+ * @param {string} [o.bundledDir] PACKAGE_ROOT/skills
64
+ * @param {() => { sharedAgentsRoot?: boolean }} [o.config]
65
+ * @param {object} [o.fs] lstat / realpath / readdir (tests)
66
+ */
67
+ export function createRootResolver({
68
+ env = process.env, homeDir = homedir(), platform = process.platform,
69
+ projects = () => [], claudeAccounts = null, codexAccounts = null,
70
+ bundledDir = null, config = () => ({}), fs = realFs, managedDir = null, codexAdminDir = '/etc/codex/skills',
71
+ } = {}) {
72
+ const home = resolve(homeDir);
73
+ const configHome = () => resolve(env.XDG_CONFIG_HOME || join(home, '.config'));
74
+
75
+ function statusOf(path) {
76
+ let st;
77
+ try { st = fs.lstat(path); } catch (err) { return { status: err?.code === 'EACCES' ? 'unreadable' : 'absent', resolvedPath: null, linked: false }; }
78
+ let real = null;
79
+ try { real = fs.realpath(path); } catch { return { status: 'unreadable', resolvedPath: null, linked: st.isSymbolicLink(), note: 'a link that leads nowhere' }; }
80
+ let target = st;
81
+ if (st.isSymbolicLink()) { try { target = fs.lstat(real); } catch { return { status: 'unreadable', resolvedPath: real, linked: true }; } }
82
+ if (!target.isDirectory()) return { status: 'not-a-folder', resolvedPath: real, linked: st.isSymbolicLink() };
83
+ try { fs.readdir(real); } catch { return { status: 'unreadable', resolvedPath: real, linked: st.isSymbolicLink() }; }
84
+ return { status: 'ok', resolvedPath: real, linked: st.isSymbolicLink() };
85
+ }
86
+
87
+ function claudeHomes() {
88
+ const rows = typeof claudeAccounts === 'function' ? (claudeAccounts() || []) : [];
89
+ const list = rows.filter((r) => r && typeof r.home === 'string' && r.home).map((r) => ({ id: r.id || 'default', label: r.label || r.id || 'Default', home: resolve(r.home) }));
90
+ if (!list.some((r) => r.id === 'default')) list.unshift({ id: 'default', label: 'Default', home: resolve(env.CLAUDE_CONFIG_DIR || join(home, '.claude')) });
91
+ return list;
92
+ }
93
+ function codexHomes() {
94
+ const rows = typeof codexAccounts === 'function' ? (codexAccounts() || []) : [];
95
+ const list = rows.filter((r) => r && typeof r.home === 'string' && r.home).map((r) => ({ id: r.id || 'default', label: r.label || r.id || 'Default', home: resolve(r.home) }));
96
+ if (!list.some((r) => r.id === 'default')) list.unshift({ id: 'default', label: 'Default', home: resolve(env.CODEX_HOME || join(home, '.codex')) });
97
+ return list;
98
+ }
99
+
100
+ /** Every root, with what it is now. */
101
+ function listRoots() {
102
+ const out = [];
103
+ const seenIds = new Set();
104
+ const add = (spec) => {
105
+ const rootId = rootIdOf(spec);
106
+ if (seenIds.has(rootId)) return;
107
+ seenIds.add(rootId);
108
+ const state = statusOf(spec.path);
109
+ out.push({
110
+ rootId, family: spec.family, kind: spec.kind, layout: spec.kind === 'skills' ? 'skill-folders' : 'md-files',
111
+ runtimes: spec.runtimes, scope: spec.scope, sourceKind: spec.sourceKind,
112
+ accountId: spec.accountId || null, accountLabel: spec.accountLabel || null, pluginId: spec.pluginId || null,
113
+ projectPath: spec.projectPath || null, projectLabel: spec.projectLabel || null,
114
+ path: resolve(spec.path), resolvedPath: state.resolvedPath, linked: state.linked, status: state.status,
115
+ policy: spec.policy, discovery: spec.discovery, anchor: spec.anchor || null, note: spec.note || state.note || '',
116
+ installTarget: spec.installTarget || null,
117
+ });
118
+ };
119
+ const owner = (reason) => ({ write: 'owner', reason });
120
+ const never = (reason) => ({ write: 'never', reason });
121
+ const sharedOn = !!config()?.sharedAgentsRoot;
122
+
123
+ // Claude Code, per account.
124
+ const claudeDefault = resolve(join(home, '.claude'));
125
+ for (const acct of claudeHomes()) {
126
+ const a = { accountId: acct.id, accountLabel: acct.label };
127
+ // OpenCode reads ~/.claude/skills itself (not another config dir, and not in SynaBun's own OpenCode panel).
128
+ const opencodeCompat = acct.home === claudeDefault;
129
+ add({ family: 'claude', kind: 'skills', scope: 'user', sourceKind: 'native', path: join(acct.home, 'skills'), runtimes: opencodeCompat ? ['claude', 'opencode'] : ['claude'], ...a,
130
+ policy: owner('The account\'s own skills folder.'), discovery: { contract: 'skills/<name>/SKILL.md', verified: 'docs' },
131
+ installTarget: { runtime: 'claude', scope: 'user' },
132
+ note: opencodeCompat ? 'A terminal OpenCode reads it too (Claude compatibility); SynaBun\'s own OpenCode serves (the panel, an Assistant brain) start with OPENCODE_DISABLE_CLAUDE_CODE=1 and do not.' : '' });
133
+ add({ family: 'claude', kind: 'skills', scope: 'synced', sourceKind: 'synced', path: join(acct.home, 'skills', 'synced'), runtimes: ['claude'], ...a,
134
+ policy: never('Skills the Claude account syncs down; a local change would be overwritten. Customize a copy instead.'), discovery: { contract: 'skills/synced/<name>/SKILL.md, /anthropic-skills:<name>', verified: 'docs' } });
135
+ add({ family: 'claude', kind: 'commands', scope: 'user', sourceKind: 'native', path: join(acct.home, 'commands'), runtimes: ['claude'], ...a,
136
+ policy: owner('The account\'s own commands.'), discovery: { contract: 'commands/<ns>/<name>.md → /ns:name', verified: 'docs' } });
137
+ add({ family: 'claude', kind: 'agents', scope: 'user', sourceKind: 'native', path: join(acct.home, 'agents'), runtimes: ['claude'], ...a,
138
+ policy: owner('The account\'s own agents.'), discovery: { contract: 'agents/<name>.md', verified: 'docs' } });
139
+ // Plugins: every installed plugin's skills and commands, read-only (they belong to the plugin).
140
+ const cache = join(acct.home, 'plugins', 'cache');
141
+ for (const plugin of pluginFolders(cache)) {
142
+ const p = { ...a, pluginId: `${plugin.plugin}@${plugin.marketplace}` };
143
+ add({ family: 'claude', kind: 'skills', scope: 'plugin', sourceKind: 'plugin', path: join(plugin.path, 'skills'), runtimes: ['claude'], ...p,
144
+ policy: never('Part of an installed plugin: updated through the plugin.'), discovery: { contract: `/${plugin.plugin}:<name>`, verified: 'docs' }, note: `Plugin ${plugin.plugin} (${plugin.marketplace}, ${plugin.version})` });
145
+ add({ family: 'claude', kind: 'commands', scope: 'plugin', sourceKind: 'plugin', path: join(plugin.path, 'commands'), runtimes: ['claude'], ...p,
146
+ policy: never('Part of an installed plugin.'), discovery: { contract: `/${plugin.plugin}:<name>`, verified: 'docs' }, note: `Plugin ${plugin.plugin} (${plugin.marketplace}, ${plugin.version})` });
147
+ }
148
+ }
149
+ add({ family: 'claude', kind: 'skills', scope: 'managed', sourceKind: 'managed', path: join(managedDir || claudeManagedDir(platform), '.claude', 'skills'), runtimes: ['claude'],
150
+ policy: never('Set by an administrator (managed settings).'), discovery: { contract: '<managed>/.claude/skills/<name>/SKILL.md', verified: 'docs' } });
151
+
152
+ // Codex, per account.
153
+ for (const acct of codexHomes()) {
154
+ const a = { accountId: acct.id, accountLabel: acct.label };
155
+ add({ family: 'codex', kind: 'skills', scope: 'user', sourceKind: 'native', path: join(acct.home, 'skills'), runtimes: ['codex'], ...a,
156
+ policy: owner('The Codex home\'s skills folder (the installer\'s default).'), discovery: { contract: '$CODEX_HOME/skills/<name>/SKILL.md', verified: 'fixture codex-cli 0.163.0' },
157
+ installTarget: { runtime: 'codex', scope: 'user' } });
158
+ add({ family: 'codex', kind: 'skills', scope: 'system', sourceKind: 'system', path: join(acct.home, 'skills', '.system'), runtimes: ['codex'], ...a,
159
+ policy: never('Codex\'s own skills.'), discovery: { contract: '$CODEX_HOME/skills/.system/<name>/SKILL.md', verified: 'fixture codex-cli 0.163.0' } });
160
+ add({ family: 'codex', kind: 'prompts', scope: 'user', sourceKind: 'legacy-bridge', path: join(acct.home, 'prompts'), runtimes: ['codex'], ...a,
161
+ policy: { write: 'migration', reason: 'Custom prompts are deprecated: SynaBun\'s old bridges move from here to native skill folders.' }, discovery: { contract: '/prompts:<name>', verified: 'docs' } });
162
+ }
163
+ add({ family: 'codex', kind: 'skills', scope: 'admin', sourceKind: 'admin', path: codexAdminDir, runtimes: ['codex'],
164
+ policy: never('Set by an administrator.'), discovery: { contract: '/etc/codex/skills/<name>/SKILL.md', verified: 'docs' } });
165
+
166
+ // OpenCode: its configuration folder is OPENCODE_CONFIG_DIR when that is set (2.0.32 then reads skills there,
167
+ // and not in <config>/opencode), else <config>/opencode.
168
+ const oc = join(configHome(), 'opencode');
169
+ const ocDir = opencodeConfigDir();
170
+ const overridden = ocDir !== oc;
171
+ add({ family: 'opencode', kind: 'skills', scope: 'user', sourceKind: 'native', path: join(ocDir, 'skills'), runtimes: ['opencode'],
172
+ policy: owner(overridden ? 'OpenCode\'s skills folder (OPENCODE_CONFIG_DIR).' : 'OpenCode\'s own skills folder.'),
173
+ discovery: { contract: overridden ? '$OPENCODE_CONFIG_DIR/skills/<name>/SKILL.md' : '<config>/opencode/skills/<name>/SKILL.md', verified: 'fixture opencode 2.0.32' },
174
+ installTarget: { runtime: 'opencode', scope: 'user' } });
175
+ add({ family: 'opencode', kind: 'skills', scope: 'user', sourceKind: 'compat', path: join(ocDir, 'skill'), runtimes: ['opencode'],
176
+ policy: never('The singular folder OpenCode still reads; new skills go to skills/.'),
177
+ discovery: { contract: overridden ? '$OPENCODE_CONFIG_DIR/skill/<name>/SKILL.md' : '<config>/opencode/skill/<name>/SKILL.md', verified: 'fixture opencode 2.0.32' } });
178
+ if (overridden) {
179
+ for (const folder of ['skills', 'skill']) {
180
+ add({ family: 'opencode', kind: 'skills', scope: 'user', sourceKind: 'inactive', path: join(oc, folder), runtimes: [],
181
+ policy: never('OpenCode does not read this folder while OPENCODE_CONFIG_DIR is set.'), discovery: { contract: 'not read while OPENCODE_CONFIG_DIR is set', verified: 'fixture opencode 2.0.32' } });
182
+ }
183
+ }
184
+ add({ family: 'opencode', kind: 'commands', scope: 'user', sourceKind: 'legacy-bridge', path: join(oc, 'command'), runtimes: ['opencode'],
185
+ policy: { write: 'migration', reason: 'Where SynaBun\'s old bridges were written.' }, discovery: { contract: '<config>/opencode/command/<name>.md', verified: 'fixture opencode 2.0.32' } });
186
+ add({ family: 'opencode', kind: 'commands', scope: 'user', sourceKind: 'native', path: join(oc, 'commands'), runtimes: ['opencode'],
187
+ policy: never('OpenCode commands: not managed by Skills Studio yet.'), discovery: { contract: '<config>/opencode/commands/<name>.md', verified: 'fixture opencode 2.0.32' } });
188
+ add({ family: 'opencode', kind: 'commands', scope: 'user', sourceKind: 'legacy-bridge', path: join(home, '.opencode', 'command'), runtimes: ['opencode'],
189
+ policy: { write: 'migration', reason: 'An old place SynaBun wrote bridges to.' }, discovery: { contract: '~/.opencode/command/<name>.md', verified: 'unverified' } });
190
+
191
+ // Antigravity (Google's `agy`): its own skills folder under ~/.gemini/config. It needs no refresh: it reads the folder again by itself.
192
+ add({ family: 'antigravity', kind: 'skills', scope: 'user', sourceKind: 'native', path: join(home, '.gemini', 'config', 'skills'), runtimes: ['antigravity'],
193
+ policy: owner('Antigravity\'s own skills folder.'), discovery: { contract: '~/.gemini/config/skills/<name>/SKILL.md', verified: 'docs' },
194
+ installTarget: { runtime: 'antigravity', scope: 'user' } });
195
+
196
+ // Shared.
197
+ add({ family: 'shared', kind: 'skills', scope: 'shared', sourceKind: 'shared', path: join(home, '.agents', 'skills'), runtimes: ['codex', 'opencode'],
198
+ policy: sharedOn ? owner('Shared by Codex, OpenCode and other agents; turned on by the owner.') : { write: 'owner-opt-in', reason: 'Shared by Codex, OpenCode and other agents: SynaBun writes here only when you turn it on.' },
199
+ discovery: { contract: '~/.agents/skills/<name>/SKILL.md', verified: 'fixture codex-cli 0.163.0, opencode 2.0.32' },
200
+ installTarget: { runtime: 'agents', scope: 'user' } });
201
+
202
+ // Projects.
203
+ let list = [];
204
+ try { list = projects() || []; } catch { list = []; }
205
+ for (const proj of list) {
206
+ if (!proj?.path) continue;
207
+ const p = { projectPath: resolve(proj.path), projectLabel: proj.label || proj.path, anchor: resolve(proj.path) };
208
+ const at = (...parts) => join(proj.path, ...parts);
209
+ add({ family: 'claude', kind: 'skills', scope: 'project', sourceKind: 'native', path: at('.claude', 'skills'), runtimes: ['claude', 'opencode'], ...p,
210
+ policy: owner('The project\'s Claude skills (shared with the repository).'), discovery: { contract: '.claude/skills/<name>/SKILL.md', verified: 'docs; OpenCode fixture' },
211
+ installTarget: { runtime: 'claude', scope: 'project' } });
212
+ add({ family: 'claude', kind: 'commands', scope: 'project', sourceKind: 'native', path: at('.claude', 'commands'), runtimes: ['claude'], ...p,
213
+ policy: owner('The project\'s commands.'), discovery: { contract: '.claude/commands/<ns>/<name>.md → /ns:name', verified: 'docs' } });
214
+ add({ family: 'claude', kind: 'agents', scope: 'project', sourceKind: 'native', path: at('.claude', 'agents'), runtimes: ['claude'], ...p,
215
+ policy: owner('The project\'s agents.'), discovery: { contract: '.claude/agents/<name>.md', verified: 'docs' } });
216
+ add({ family: 'codex', kind: 'skills', scope: 'project', sourceKind: 'native', path: at('.agents', 'skills'), runtimes: ['codex', 'opencode', 'antigravity'], ...p,
217
+ policy: owner('The project\'s shared skills (Codex\'s repository folder; OpenCode and Antigravity read it too).'), discovery: { contract: '.agents/skills/<name>/SKILL.md', verified: 'fixture codex-cli 0.163.0, opencode 2.0.32; Antigravity: docs' },
218
+ installTarget: { runtime: 'codex', scope: 'project' } });
219
+ add({ family: 'codex', kind: 'skills', scope: 'project', sourceKind: 'compat', path: at('.codex', 'skills'), runtimes: ['codex'], ...p,
220
+ policy: never('Read by Codex 0.163.0 but not in its guide: new skills go to .agents/skills.'), discovery: { contract: '.codex/skills/<name>/SKILL.md', verified: 'fixture codex-cli 0.163.0' } });
221
+ add({ family: 'opencode', kind: 'skills', scope: 'project', sourceKind: 'native', path: at('.opencode', 'skills'), runtimes: ['opencode'], ...p,
222
+ policy: owner('The project\'s OpenCode skills.'), discovery: { contract: '.opencode/skills/<name>/SKILL.md', verified: 'fixture opencode 2.0.32' },
223
+ installTarget: { runtime: 'opencode', scope: 'project' } });
224
+ }
225
+
226
+ // Bundled.
227
+ if (bundledDir) {
228
+ add({ family: 'synabun', kind: 'skills', scope: 'bundled', sourceKind: 'bundled', path: bundledDir, runtimes: [],
229
+ policy: never('Ships with SynaBun: install it, or customize a copy.'), discovery: { contract: 'not read by a runtime directly', verified: 'n/a' } });
230
+ }
231
+ return out;
232
+ }
233
+
234
+ /** Installed plugins under a plugin cache: <marketplace>/<plugin>/<version>, bounded. */
235
+ function pluginFolders(cache) {
236
+ const out = [];
237
+ const list = (dir) => { try { return fs.readdir(dir).filter((n) => !n.startsWith('.')).sort(); } catch { return []; } };
238
+ for (const marketplace of list(cache).slice(0, 50)) {
239
+ for (const plugin of list(join(cache, marketplace)).slice(0, 200)) {
240
+ for (const version of list(join(cache, marketplace, plugin)).slice(0, 20)) {
241
+ out.push({ marketplace, plugin, version, path: join(cache, marketplace, plugin, version) });
242
+ if (out.length >= 500) return out;
243
+ }
244
+ }
245
+ }
246
+ return out;
247
+ }
248
+
249
+ /** OpenCode's configuration folder: OPENCODE_CONFIG_DIR when set, else <config>/opencode (its settings and skills). */
250
+ function opencodeConfigDir() {
251
+ return env.OPENCODE_CONFIG_DIR ? resolve(String(env.OPENCODE_CONFIG_DIR)) : join(configHome(), 'opencode');
252
+ }
253
+
254
+ /** The roots that receive installs, by runtime and scope (and account / project). */
255
+ function installRoot({ runtime, scope = 'user', accountId = 'default', projectPath = null }) {
256
+ const roots = listRoots();
257
+ const wanted = roots.filter((r) => r.installTarget && r.installTarget.runtime === runtime && r.installTarget.scope === scope);
258
+ if (scope === 'project') return wanted.find((r) => projectPath && r.projectPath === resolve(projectPath)) || null;
259
+ if (runtime === 'claude' || runtime === 'codex') return wanted.find((r) => (r.accountId || 'default') === (accountId || 'default')) || null;
260
+ return wanted[0] || null;
261
+ }
262
+
263
+ /** Every folder a runtime reads skills from (the import staging is refused inside any of them). */
264
+ function liveRootPaths() {
265
+ return listRoots().filter((r) => r.family !== 'synabun').map((r) => r.path);
266
+ }
267
+
268
+ return { listRoots, installRoot, liveRootPaths, claudeHomes, codexHomes, opencodeConfigDir };
269
+ }