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,376 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: the frontmatter of a skill, a command or an agent
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // A skill's metadata is YAML between two `---` lines at the top of its file. It is read here with a real YAML
6
+ // parser (the `yaml` package), and changed one key at a time: an edit replaces the text of that one key and
7
+ // nothing else, so every key the edit does not touch keeps its bytes (order, quoting, folding, comments, keys
8
+ // no form knows). A change is checked by reading the result again before it is handed back: the key holds the
9
+ // new value, every other key what it held, or the change is refused and the text stays as it was.
10
+ //
11
+ // What is not plain YAML is said, never guessed: a syntax error, a key given twice, a root that is not a
12
+ // mapping, an alias, a tag the parser does not resolve. While the YAML has errors nothing is patched: the
13
+ // form waits, the raw text is still editable and saveable.
14
+ //
15
+ // No imports: the YAML library is handed in (the server gives it the `yaml` package, the page the copy in
16
+ // /vendor/yaml/), so `node --test` loads this module as it is.
17
+
18
+ /** A frontmatter longer than this is not parsed (a bound on what one request makes the parser do). */
19
+ export const FRONTMATTER_MAX_CHARS = 128 * 1024;
20
+ /** Aliases a document may expand while it is read (the `yaml` package's guard against alias bombs). */
21
+ export const MAX_ALIAS_COUNT = 100;
22
+
23
+ export const PARSE_OPTIONS = Object.freeze({
24
+ uniqueKeys: true, // a key given twice is an error, not "the last one wins"
25
+ strict: true,
26
+ prettyErrors: true, // line and column on every error
27
+ resolveKnownTags: false, // !!binary, !!set, !!omap… stay unresolved (a warning), never other types
28
+ merge: false, // `<<` is a key like any other
29
+ keepSourceTokens: false,
30
+ });
31
+
32
+ const BREAK = /\r\n|\n|\r/g;
33
+
34
+ /** The line ending at `index`: its length (0 at the end of the text). */
35
+ function breakAt(text, index) {
36
+ if (text.charCodeAt(index) === 13) return text.charCodeAt(index + 1) === 10 ? 2 : 1;
37
+ if (text.charCodeAt(index) === 10) return 1;
38
+ return 0;
39
+ }
40
+
41
+ /** The index of the next line break at or after `from` (the text's length when there is none). */
42
+ function nextBreak(text, from) {
43
+ for (let i = from; i < text.length; i++) {
44
+ const c = text.charCodeAt(i);
45
+ if (c === 10 || c === 13) return i;
46
+ }
47
+ return text.length;
48
+ }
49
+
50
+ /** 1-based line number of an index in a text. */
51
+ export function lineOfIndex(text, index) {
52
+ let line = 1;
53
+ const s = String(text ?? '');
54
+ BREAK.lastIndex = 0;
55
+ let m;
56
+ while ((m = BREAK.exec(s)) && m.index < index) line++;
57
+ return line;
58
+ }
59
+
60
+ /**
61
+ * Where the frontmatter is. It opens with a first line `---` (after a byte-order mark, if any) and closes at the
62
+ * next line that is `---`; trailing blanks on either line are allowed.
63
+ * @returns {{ bom: boolean, hasFrontmatter: boolean, unterminated: boolean, yamlStart: number, yamlEnd: number,
64
+ * bodyStart: number, yaml: string, body: string, startLine: number }} `yaml` is the text between the two lines
65
+ * (with the line ending of its last line), `startLine` the line it starts on (1-based), `bodyStart` where the
66
+ * text after the closing line starts.
67
+ */
68
+ export function splitFrontmatter(text) {
69
+ const s = String(text ?? '');
70
+ const bom = s.charCodeAt(0) === 0xFEFF;
71
+ const at = bom ? 1 : 0;
72
+ const none = (unterminated = false) => ({
73
+ bom, hasFrontmatter: false, unterminated, yamlStart: at, yamlEnd: at, bodyStart: at, yaml: '', body: s.slice(at), startLine: 1,
74
+ });
75
+ const firstEnd = nextBreak(s, at);
76
+ if (!/^---[ \t]*$/.test(s.slice(at, firstEnd)) || firstEnd >= s.length) return none(false);
77
+ const yamlStart = firstEnd + breakAt(s, firstEnd);
78
+ let line = yamlStart;
79
+ while (line <= s.length) {
80
+ const end = nextBreak(s, line);
81
+ if (/^---[ \t]*$/.test(s.slice(line, end))) {
82
+ const bodyStart = end + breakAt(s, end);
83
+ return {
84
+ bom, hasFrontmatter: true, unterminated: false, yamlStart, yamlEnd: line, bodyStart,
85
+ yaml: s.slice(yamlStart, line), body: s.slice(bodyStart), startLine: 2,
86
+ };
87
+ }
88
+ if (end >= s.length) break;
89
+ line = end + breakAt(s, end);
90
+ }
91
+ return none(true);
92
+ }
93
+
94
+ /** What kind of value a frontmatter key holds, for a form that shows only what it can write back. */
95
+ export function valueKind(value) {
96
+ if (value === undefined) return 'absent';
97
+ if (value === null) return 'null';
98
+ if (typeof value === 'string') return 'string';
99
+ if (typeof value === 'boolean') return 'boolean';
100
+ if (typeof value === 'number') return Number.isFinite(value) ? 'number' : 'other';
101
+ if (Array.isArray(value)) return value.every((v) => typeof v === 'string') ? 'string-list' : 'list';
102
+ return 'other';
103
+ }
104
+
105
+ function deepEqual(a, b) {
106
+ if (a === b) return true;
107
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== 'object') return Number.isNaN(a) && Number.isNaN(b);
108
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
109
+ const ka = Object.keys(a); const kb = Object.keys(b);
110
+ if (ka.length !== kb.length) return false;
111
+ return ka.every((k, i) => k === kb[i] && deepEqual(a[k], b[k]));
112
+ }
113
+
114
+ /** A copy of `data` whose keys are own data properties of objects without a prototype chain to reach. */
115
+ function plainCopy(value, depth = 0) {
116
+ if (depth > 64) return null;
117
+ if (Array.isArray(value)) return value.map((v) => plainCopy(v, depth + 1));
118
+ if (value && typeof value === 'object') {
119
+ if (typeof value.length === 'number' && typeof value.BYTES_PER_ELEMENT === 'number') return null; // typed arrays: not a skill value
120
+ const out = Object.create(null);
121
+ for (const key of Object.keys(value)) out[key] = plainCopy(value[key], depth + 1);
122
+ return out;
123
+ }
124
+ return value;
125
+ }
126
+
127
+ /**
128
+ * The frontmatter tools over one YAML library.
129
+ * @param {object} YAML the `yaml` package (parseDocument, Document, isMap, isScalar, isSeq, isCollection, isAlias, visit)
130
+ */
131
+ export function createFrontmatter(YAML) {
132
+ const diag = (ruleId, severity, message, line = null, column = null) => ({ ruleId, severity, message, line, column });
133
+
134
+ function fromYamlError(err, severity, startLine) {
135
+ const pos = Array.isArray(err?.linePos) ? err.linePos[0] : null;
136
+ const first = String(err?.message || 'YAML error').split('\n')[0].replace(/ at line \d+, column \d+:?$/, '');
137
+ const code = String(err?.code || '').toLowerCase().replace(/_/g, '-');
138
+ const ruleId = code === 'duplicate-key' ? 'yaml-duplicate-key' : (code === 'tag-resolve-failed' ? 'yaml-tag' : 'yaml-syntax');
139
+ return diag(ruleId, severity, first, pos ? pos.line + startLine - 1 : startLine, pos ? pos.col : null);
140
+ }
141
+
142
+ function parseYaml(yaml) {
143
+ return YAML.parseDocument(yaml, PARSE_OPTIONS);
144
+ }
145
+
146
+ const keyName = (pair) => (YAML.isScalar(pair?.key) && typeof pair.key.value === 'string' ? pair.key.value : null);
147
+
148
+ /**
149
+ * Read a document's frontmatter.
150
+ * @param {string} text the whole file (a byte-order mark and any line ending are fine)
151
+ * @returns {{ hasFrontmatter: boolean, data: object | null, diagnostics: object[], keys: object[],
152
+ * editable: boolean, body: string, bodyLine: number, split: object }} `data` is null when the YAML cannot
153
+ * be read as a mapping; `editable` says whether patch() may change it; `keys` lists the top-level keys in
154
+ * order: `{ key, line, kind, style, flow }`.
155
+ */
156
+ function parse(text) {
157
+ const s = String(text ?? '');
158
+ const split = splitFrontmatter(s);
159
+ const result = {
160
+ hasFrontmatter: split.hasFrontmatter, data: null, diagnostics: [], keys: [], editable: false,
161
+ body: split.hasFrontmatter ? split.body : s.slice(split.bom ? 1 : 0),
162
+ bodyLine: split.hasFrontmatter ? lineOfIndex(s, split.bodyStart) : 1,
163
+ split,
164
+ };
165
+ if (!split.hasFrontmatter) {
166
+ if (split.unterminated) result.diagnostics.push(diag('yaml-unterminated', 'error', 'The frontmatter opens with --- but no line closes it with ---.', 1));
167
+ result.data = Object.create(null);
168
+ result.editable = !split.unterminated;
169
+ return result;
170
+ }
171
+ if (split.yaml.length > FRONTMATTER_MAX_CHARS) {
172
+ result.diagnostics.push(diag('yaml-too-large', 'error', `The frontmatter is longer than ${Math.round(FRONTMATTER_MAX_CHARS / 1024)} KB and was not read.`, split.startLine));
173
+ return result;
174
+ }
175
+ let doc;
176
+ try { doc = parseYaml(split.yaml); } catch (err) {
177
+ result.diagnostics.push(diag('yaml-syntax', 'error', String(err?.message || 'The frontmatter could not be read.').split('\n')[0], split.startLine));
178
+ return result;
179
+ }
180
+ for (const err of doc.errors) result.diagnostics.push(fromYamlError(err, 'error', split.startLine));
181
+ for (const warn of doc.warnings) result.diagnostics.push(fromYamlError(warn, 'warning', split.startLine));
182
+ const root = doc.contents;
183
+ if (root === null || root === undefined) {
184
+ result.data = Object.create(null);
185
+ result.editable = doc.errors.length === 0;
186
+ return result;
187
+ }
188
+ if (!YAML.isMap(root)) {
189
+ result.diagnostics.push(diag('yaml-not-mapping', 'error', 'The frontmatter must be key: value lines (a YAML mapping).', split.startLine));
190
+ return result;
191
+ }
192
+ // YAML with errors is not read as data at all: what a parser makes of it is a guess, and no value of it is
193
+ // shown or checked as if it were what the file says.
194
+ if (doc.errors.length) return result;
195
+ let aliases = 0;
196
+ YAML.visit(doc, { Alias() { aliases++; } });
197
+ if (aliases) result.diagnostics.push(diag('yaml-alias', 'warning', `The frontmatter uses ${aliases === 1 ? 'an alias' : `${aliases} aliases`} (*name); the form leaves aliased values alone.`, split.startLine));
198
+ let data;
199
+ try { data = doc.toJS({ maxAliasCount: MAX_ALIAS_COUNT }); } catch (err) {
200
+ result.diagnostics.push(diag('yaml-alias-limit', 'error', String(err?.message || 'Too many aliases.').split('\n')[0], split.startLine));
201
+ return result;
202
+ }
203
+ result.data = plainCopy(data && typeof data === 'object' && !Array.isArray(data) ? data : {});
204
+ let complexKeys = 0;
205
+ for (const pair of root.items) {
206
+ const key = keyName(pair);
207
+ if (key === null) { complexKeys++; continue; }
208
+ const value = pair.value;
209
+ const start = pair.key.range?.[0] ?? 0;
210
+ result.keys.push({
211
+ key,
212
+ line: lineOfIndex(split.yaml, start) + split.startLine - 1,
213
+ kind: valueKind(result.data[key]),
214
+ style: YAML.isScalar(value) ? (value.type || null) : (YAML.isCollection(value) ? (value.flow ? 'flow' : 'block') : (YAML.isAlias(value) ? 'alias' : null)),
215
+ alias: YAML.isAlias(value),
216
+ });
217
+ }
218
+ if (complexKeys) result.diagnostics.push(diag('yaml-complex-key', 'warning', 'A key of the frontmatter is not plain text; the form leaves the frontmatter alone.', split.startLine));
219
+ result.editable = doc.errors.length === 0 && !root.flow && !complexKeys;
220
+ return result;
221
+ }
222
+
223
+ // ── Patching ──
224
+
225
+ /** How the value a key holds is written, so its new value is written the same way. */
226
+ function styleOf(yamlText, pair) {
227
+ const value = pair.value;
228
+ const style = { type: null, flow: undefined, indentSeq: true, indent: 2 };
229
+ if (YAML.isScalar(value)) style.type = value.type || null;
230
+ if (YAML.isCollection(value)) {
231
+ style.flow = !!value.flow;
232
+ if (!value.flow && value.range) {
233
+ const at = value.range[0];
234
+ const lineStart = yamlText.lastIndexOf('\n', at - 1) + 1;
235
+ const column = at - lineStart;
236
+ if (YAML.isSeq(value)) { style.indentSeq = column > 0; if (column > 0) style.indent = column; }
237
+ else if (column > 0) style.indent = column;
238
+ }
239
+ }
240
+ if (YAML.isScalar(value) && (value.type === 'BLOCK_FOLDED' || value.type === 'BLOCK_LITERAL') && value.range) {
241
+ // The indentation of the block's first line with text.
242
+ const block = yamlText.slice(value.range[0], value.range[1]);
243
+ const m = /\n( +)\S/.exec(block);
244
+ if (m) style.indent = Math.max(1, Math.min(8, m[1].length));
245
+ }
246
+ return style;
247
+ }
248
+
249
+ /** `key: value` as YAML, written in `style` where the value allows it. Ends with "\n". */
250
+ function serializePair(key, value, style = {}) {
251
+ const doc = new YAML.Document();
252
+ doc.contents = doc.createNode({ [key]: value });
253
+ const node = doc.contents.items[0].value;
254
+ if (style.type && YAML.isScalar(node) && typeof value === 'string') node.type = style.type;
255
+ if (style.flow !== undefined && YAML.isCollection(node)) node.flow = style.flow;
256
+ return doc.toString({
257
+ lineWidth: style.type === 'BLOCK_FOLDED' ? 80 : 0,
258
+ minContentWidth: 0,
259
+ indent: style.indent || 2,
260
+ indentSeq: style.indentSeq !== false,
261
+ flowCollectionPadding: false,
262
+ });
263
+ }
264
+
265
+ function findPair(root, key) {
266
+ if (!root || !YAML.isMap(root)) return null;
267
+ return root.items.find((pair) => keyName(pair) === key) || null;
268
+ }
269
+
270
+ // The text a pair spans: from the start of its key to the end of its value (a value written over several lines
271
+ // takes its last line ending with it). A comment after a one-line value is outside it, and stays.
272
+ function pairSpan(yamlText, pair) {
273
+ const start = pair.key.range[0];
274
+ const lineStart = yamlText.lastIndexOf('\n', start - 1) + 1;
275
+ if (yamlText.slice(lineStart, start).trim() !== '') return null; // the key is not at the start of its line
276
+ let end;
277
+ if (pair.value && pair.value.range) end = pair.value.range[1];
278
+ else {
279
+ const colon = yamlText.indexOf(':', pair.key.range[1]);
280
+ if (colon < 0) return null;
281
+ end = colon + 1;
282
+ }
283
+ return { start, end, lineStart, block: yamlText.charCodeAt(end - 1) === 10 };
284
+ }
285
+
286
+ // The whole lines a pair takes: what removing the key removes. A comment on its last line goes with it.
287
+ function pairLines(yamlText, pair) {
288
+ const span = pairSpan(yamlText, pair);
289
+ if (!span) return null;
290
+ let end = span.block ? span.end : Math.max(span.end, pair.value?.range?.[2] ?? span.end);
291
+ if (yamlText.charCodeAt(end - 1) !== 10) {
292
+ const nl = yamlText.indexOf('\n', end);
293
+ const rest = yamlText.slice(end, nl < 0 ? yamlText.length : nl);
294
+ if (rest.trim() !== '' && !rest.trim().startsWith('#')) return null;
295
+ end = nl < 0 ? yamlText.length : nl + 1;
296
+ }
297
+ return { start: span.lineStart, end };
298
+ }
299
+
300
+ function patchOne(text, key, value) {
301
+ const split = splitFrontmatter(text);
302
+ const lead = split.bom ? '' : '';
303
+ if (!split.hasFrontmatter) {
304
+ if (split.unterminated) return { ok: false, reason: 'unterminated' };
305
+ if (value === undefined) return { ok: true, text };
306
+ const pairText = serializePair(key, value);
307
+ return { ok: true, text: `${lead}---\n${pairText}---\n${text.slice(split.bom ? 1 : 0)}` };
308
+ }
309
+ const yamlText = split.yaml;
310
+ let doc;
311
+ try { doc = parseYaml(yamlText); } catch { return { ok: false, reason: 'invalid' }; }
312
+ if (doc.errors.length) return { ok: false, reason: 'invalid' };
313
+ const root = doc.contents;
314
+ if (root !== null && root !== undefined && (!YAML.isMap(root) || root.flow)) return { ok: false, reason: 'unsupported' };
315
+ if (root && root.items.some((pair) => keyName(pair) === null)) return { ok: false, reason: 'unsupported' };
316
+ const pair = findPair(root, key);
317
+ if (pair && YAML.isAlias(pair.value)) return { ok: false, reason: 'alias' };
318
+ let next;
319
+ if (value === undefined) {
320
+ if (!pair) return { ok: true, text };
321
+ const lines = pairLines(yamlText, pair);
322
+ if (!lines) return { ok: false, reason: 'unsupported' };
323
+ next = yamlText.slice(0, lines.start) + yamlText.slice(lines.end);
324
+ } else if (pair) {
325
+ const span = pairSpan(yamlText, pair);
326
+ if (!span) return { ok: false, reason: 'unsupported' };
327
+ let replacement = serializePair(key, value, styleOf(yamlText, pair));
328
+ if (!span.block) replacement = replacement.replace(/\n$/, '');
329
+ next = yamlText.slice(0, span.start) + replacement + yamlText.slice(span.end);
330
+ } else {
331
+ const base = yamlText === '' || yamlText.endsWith('\n') ? yamlText : `${yamlText}\n`;
332
+ next = base + serializePair(key, value);
333
+ }
334
+ return { ok: true, text: text.slice(0, split.yamlStart) + next + text.slice(split.yamlEnd) };
335
+ }
336
+
337
+ /**
338
+ * Change keys of a document's frontmatter, each by its own text, and check the result.
339
+ * @param {string} text the whole document
340
+ * @param {Record<string, any>} changes key → new value; `undefined` removes the key
341
+ * @returns {{ ok: true, text: string, changed: boolean } | { ok: false, text: string, reason: string, key: string }}
342
+ * `reason`: `invalid` (the YAML has errors), `unterminated`, `unsupported` (a layout this does not edit:
343
+ * a flow mapping, a key that is not plain text), `alias`, `mismatch` (the result did not read back as asked)
344
+ */
345
+ function patch(text, changes) {
346
+ const source = String(text ?? '');
347
+ const before = parse(source);
348
+ if (before.hasFrontmatter && !before.editable) return { ok: false, text: source, reason: before.diagnostics.some((d) => d.severity === 'error') ? 'invalid' : 'unsupported', key: null };
349
+ let current = source;
350
+ const entries = Object.entries(changes || {});
351
+ for (const [key, value] of entries) {
352
+ const step = patchOne(current, key, value);
353
+ if (!step.ok) return { ok: false, text: source, reason: step.reason, key };
354
+ current = step.text;
355
+ }
356
+ if (current === source) return { ok: true, text: source, changed: false };
357
+ // Read back: the keys asked for hold what was asked, every other key what it held, in the same order.
358
+ const after = parse(current);
359
+ if (!after.data || after.diagnostics.some((d) => d.severity === 'error')) return { ok: false, text: source, reason: 'mismatch', key: null };
360
+ const asked = new Map(entries);
361
+ for (const [key, value] of asked) {
362
+ const has = Object.prototype.hasOwnProperty.call(after.data, key);
363
+ if (value === undefined ? has : !(has && deepEqual(after.data[key], plainCopy(value)))) return { ok: false, text: source, reason: 'mismatch', key };
364
+ }
365
+ const kept = (before.keys || []).map((k) => k.key).filter((k) => !asked.has(k));
366
+ const keptAfter = after.keys.map((k) => k.key).filter((k) => !asked.has(k));
367
+ if (!deepEqual(kept, keptAfter)) return { ok: false, text: source, reason: 'mismatch', key: null };
368
+ for (const key of kept) {
369
+ if (!deepEqual(before.data[key], after.data[key])) return { ok: false, text: source, reason: 'mismatch', key };
370
+ }
371
+ if (before.body !== after.body) return { ok: false, text: source, reason: 'mismatch', key: null };
372
+ return { ok: true, text: current, changed: true };
373
+ }
374
+
375
+ return { parse, patch, serializePair, splitFrontmatter, PARSE_OPTIONS };
376
+ }
@@ -0,0 +1,32 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: artifact ids in a URL
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The library's ids (`skill:<folder>`, `command:<file.md>`, `agent:<file.md>`) travel in URLs as the base64url of
6
+ // their UTF-8 bytes; the server decodes them the same way (Buffer.from(id, 'base64url').toString('utf-8')).
7
+ // btoa() alone reads a string as Latin-1: a folder named "café" was sent as other bytes than the server reads,
8
+ // and "技能" threw. Every id is encoded from its UTF-8 bytes here. No DOM: the page and the tests load it.
9
+
10
+ /** Bytes as base64, in pieces (one String.fromCharCode call on a large array would overflow). */
11
+ function bytesToBase64(bytes) {
12
+ let bin = '';
13
+ for (let i = 0; i < bytes.length; i += 0x8000) bin += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
14
+ return btoa(bin);
15
+ }
16
+
17
+ /** An id as the server reads it: base64url (no padding) of its UTF-8 bytes. */
18
+ export function encodeArtifactId(id) {
19
+ const bytes = new TextEncoder().encode(String(id ?? ''));
20
+ return bytesToBase64(bytes).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
21
+ }
22
+
23
+ /** The id an encoded one names ('' when it is not base64url of UTF-8 text). */
24
+ export function decodeArtifactId(encoded) {
25
+ try {
26
+ const text = String(encoded ?? '');
27
+ if (!/^[A-Za-z0-9_-]*$/.test(text)) return '';
28
+ const padded = text.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (text.length % 4)) % 4);
29
+ const bin = atob(padded);
30
+ return new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(bin, (c) => c.charCodeAt(0)));
31
+ } catch { return ''; }
32
+ }
@@ -0,0 +1,149 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun Neural Interface — Skills Studio: what is wrong with a skill, a command or an agent
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The checks run on what ss-frontmatter.js parse() read. Each problem names its rule, its severity and its
6
+ // line. An error is a file a runtime will misread (YAML that does not parse, a name that is not text, an
7
+ // agent without a description); a warning is one it will read but that is likely wrong (no description, a
8
+ // name that does not match its folder). Nothing here is decided by truthiness: an empty string, a number or
9
+ // a list where text belongs are each said for what they are.
10
+ //
11
+ // Profile: Claude Code (the folders the Studio edits). The limits that the portable Agent Skills format and
12
+ // Codex enforce too (64-character names, 1024-character descriptions) are errors here as well, so a skill that
13
+ // passes can be carried to the other runtimes. No DOM.
14
+
15
+ import { findHiddenChars, inspectName } from './ss-unicode.js';
16
+
17
+ export const LINT_PROFILE = 'claude-code-2026-10';
18
+ export const NAME_MAX = 64;
19
+ export const DESCRIPTION_MAX = 1024;
20
+ const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
21
+
22
+ const BOOLEAN_KEYS = ['user-invocable', 'disable-model-invocation'];
23
+ const STRING_KEYS = ['argument-hint', 'model', 'color', 'license', 'compatibility', 'context', 'agent', 'effort', 'when_to_use', 'shell'];
24
+ const TOOL_KEYS = ['allowed-tools', 'disallowed-tools', 'tools'];
25
+
26
+ const kindOf = (value) => {
27
+ if (value === undefined) return 'absent';
28
+ if (value === null) return 'empty';
29
+ if (Array.isArray(value)) return 'a list';
30
+ if (typeof value === 'object') return 'a mapping';
31
+ return typeof value === 'string' ? 'text' : `a ${typeof value}`;
32
+ };
33
+
34
+ /**
35
+ * @param {object} o
36
+ * @param {'skill' | 'command' | 'agent'} o.type
37
+ * @param {object} o.parsed what ss-frontmatter.js parse() returned
38
+ * @param {string} [o.folderName] a skill's folder, or a command's / agent's file name without `.md`
39
+ * @param {string[]} [o.knownNames] names a skill must not pass for (the bundled ones)
40
+ * @returns {{ profile: string, errors: object[], warnings: object[], infos: object[], all: object[] }}
41
+ * each problem `{ ruleId, severity, message, line }`
42
+ */
43
+ export function lintArtifact({ type, parsed, folderName = '', knownNames = [] }) {
44
+ const all = [];
45
+ const add = (ruleId, severity, message, line = null) => all.push({ ruleId, severity, message, line });
46
+ const lineOf = (key) => parsed?.keys?.find((k) => k.key === key)?.line ?? (parsed?.split?.startLine || 1);
47
+
48
+ for (const d of parsed?.diagnostics || []) add(d.ruleId, d.severity, d.message, d.line);
49
+ const data = parsed?.data;
50
+ if (!parsed?.hasFrontmatter && !parsed?.split?.unterminated) {
51
+ if (type === 'agent') add('fm-missing', 'error', 'An agent needs frontmatter with a name and a description.', 1);
52
+ else if (type === 'skill') add('fm-missing', 'warning', 'No frontmatter: Claude takes the folder name and has no description to decide when to use the skill.', 1);
53
+ }
54
+ const has = (key) => !!data && Object.prototype.hasOwnProperty.call(data, key);
55
+ // Without frontmatter, "fm-missing" says it all: the fields it would hold are not named one by one.
56
+ const missingSaid = !parsed?.hasFrontmatter;
57
+
58
+ if (data) {
59
+ // name
60
+ if (has('name')) {
61
+ const name = data.name;
62
+ if (typeof name !== 'string') add('name-type', 'error', `"name" must be text; it is ${kindOf(name)}.`, lineOf('name'));
63
+ else if (!name.trim()) add('name-empty', 'error', '"name" is empty.', lineOf('name'));
64
+ else {
65
+ if (name.length > NAME_MAX) add('name-length', 'error', `"name" is ${name.length} characters; the limit is ${NAME_MAX}.`, lineOf('name'));
66
+ const look = inspectName(name, knownNames);
67
+ if (look.hidden.length) add('name-hidden-chars', 'error', `"name" holds characters nobody can see: ${look.hidden.map((h) => h.label).join(', ')}.`, lineOf('name'));
68
+ if (look.confusable) add('name-confusable', 'warning', `"name" mixes in letters of another script that look Latin (reads as "${look.skeleton}").`, lineOf('name'));
69
+ if (look.impersonates) add('name-impersonates', 'warning', `"name" looks like "${look.impersonates}" but is not it.`, lineOf('name'));
70
+ // Claude Code, Codex and the portable format ask for this; a name outside it may not be found everywhere.
71
+ if (type !== 'command' && !look.hidden.length && !NAME_RE.test(name)) add('name-format', 'warning', '"name" should use lowercase letters, digits and single hyphens only (no leading or trailing hyphen).', lineOf('name'));
72
+ if (type === 'skill' && folderName && name !== folderName) add('name-folder', 'warning', `"name" is "${name}" but the folder is "${folderName}": the skill is invoked by its name.`, lineOf('name'));
73
+ }
74
+ } else if (type === 'agent' && !missingSaid) {
75
+ add('name-missing', 'error', 'An agent needs a "name".', lineOf('name'));
76
+ }
77
+
78
+ // description
79
+ if (has('description')) {
80
+ const desc = data.description;
81
+ if (typeof desc !== 'string') add('description-type', 'error', `"description" must be text; it is ${kindOf(desc)}.`, lineOf('description'));
82
+ else if (!desc.trim()) add('description-empty', 'error', '"description" is empty.', lineOf('description'));
83
+ else {
84
+ // The 1024-character limit is the skill format's (Claude Code, Codex and the portable format refuse a longer
85
+ // SKILL.md). An agent or a command has none: a long description only costs context, which is no error.
86
+ if (desc.length > DESCRIPTION_MAX) {
87
+ if (type === 'skill') add('description-length', 'error', `"description" is ${desc.length} characters; the limit is ${DESCRIPTION_MAX}.`, lineOf('description'));
88
+ else add('description-length', 'info', `"description" is ${desc.length} characters: every session carries it, so a shorter one leaves more room.`, lineOf('description'));
89
+ }
90
+ const hidden = findHiddenChars(desc);
91
+ if (hidden.length) add('description-hidden-chars', 'error', `"description" holds characters nobody can see: ${hidden.slice(0, 5).map((h) => h.label).join(', ')}${hidden.length > 5 ? '…' : ''}.`, lineOf('description'));
92
+ }
93
+ } else if (missingSaid) {
94
+ // said by fm-missing
95
+ } else if (type === 'agent') {
96
+ add('description-missing', 'error', 'An agent needs a "description": it is how Claude decides to use it.', lineOf('description'));
97
+ } else if (type === 'skill') {
98
+ add('description-missing', 'warning', 'No "description": Claude cannot tell when to use this skill on its own.', lineOf('description'));
99
+ } else {
100
+ add('description-missing', 'info', 'No "description": the command list shows nothing for it.', lineOf('description'));
101
+ }
102
+
103
+ for (const key of BOOLEAN_KEYS) {
104
+ if (has(key) && typeof data[key] !== 'boolean') add(`${key}-type`, 'error', `"${key}" must be true or false; it is ${kindOf(data[key])}.`, lineOf(key));
105
+ }
106
+ for (const key of STRING_KEYS) {
107
+ // `argument-hint: [message]` is the form Claude Code's own documentation writes: YAML reads the brackets as a
108
+ // list of text and Claude Code shows it as written. Only another kind of value is an error.
109
+ if (key === 'argument-hint' && Array.isArray(data[key]) && data[key].every((v) => typeof v === 'string' || typeof v === 'number')) continue;
110
+ if (has(key) && data[key] !== null && typeof data[key] !== 'string') add(`${key}-type`, 'error', `"${key}" must be text; it is ${kindOf(data[key])}.`, lineOf(key));
111
+ }
112
+ for (const key of TOOL_KEYS) {
113
+ if (!has(key)) continue;
114
+ const value = data[key];
115
+ const ok = typeof value === 'string' || (Array.isArray(value) && value.every((t) => typeof t === 'string' && t.trim()));
116
+ if (!ok) add(`${key}-type`, 'error', `"${key}" must be text or a list of tool names; it is ${kindOf(value)}.`, lineOf(key));
117
+ }
118
+ if (has('maxTurns')) {
119
+ const turns = data.maxTurns;
120
+ if (!Number.isInteger(turns) || turns < 0) add('maxTurns-type', 'error', '"maxTurns" must be a whole number, 0 or more.', lineOf('maxTurns'));
121
+ }
122
+ if (has('hooks') && (data.hooks === null || typeof data.hooks !== 'object' || Array.isArray(data.hooks))) {
123
+ add('hooks-type', 'error', `"hooks" must be a mapping of events; it is ${kindOf(data.hooks)}.`, lineOf('hooks'));
124
+ }
125
+ if (has('metadata') && (data.metadata === null || typeof data.metadata !== 'object' || Array.isArray(data.metadata))) {
126
+ add('metadata-type', 'error', `"metadata" must be a mapping; it is ${kindOf(data.metadata)}.`, lineOf('metadata'));
127
+ }
128
+ }
129
+
130
+ const body = String(parsed?.body ?? '');
131
+ if (!body.trim()) add('body-empty', 'warning', 'The body is empty: there are no instructions.', parsed?.bodyLine || 1);
132
+ else {
133
+ const hidden = findHiddenChars(body);
134
+ if (hidden.length) {
135
+ const first = hidden[0];
136
+ add('body-hidden-chars', 'warning', `The instructions hold ${hidden.length === 1 ? 'a character' : `${hidden.length} characters`} nobody can see (${first.label}, line ${first.line + (parsed?.bodyLine || 1) - 1}).`, first.line + (parsed?.bodyLine || 1) - 1);
137
+ }
138
+ }
139
+
140
+ const rank = { error: 0, warning: 1, info: 2 };
141
+ all.sort((a, b) => (rank[a.severity] - rank[b.severity]) || ((a.line || 0) - (b.line || 0)));
142
+ return {
143
+ profile: LINT_PROFILE,
144
+ errors: all.filter((d) => d.severity === 'error'),
145
+ warnings: all.filter((d) => d.severity === 'warning'),
146
+ infos: all.filter((d) => d.severity === 'info'),
147
+ all,
148
+ };
149
+ }