synabun 2.1.0 → 2.2.1

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 (332) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +1 -1
  3. package/docs/whatsapp.md +11 -11
  4. package/lib/client-config-repair.js +13 -9
  5. package/lib/external-tools.js +6 -5
  6. package/lib/update-snapshot.js +77 -3
  7. package/lib/virustotal.mjs +12 -1
  8. package/mcp-server/dist/index.js +1 -1
  9. package/mcp-server/dist/index.js.map +1 -1
  10. package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
  11. package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
  12. package/mcp-server/dist/services/budget-judgments.js +5 -3
  13. package/mcp-server/dist/services/budget-judgments.js.map +1 -1
  14. package/mcp-server/dist/services/identity.d.ts +2 -2
  15. package/mcp-server/dist/services/identity.js +2 -2
  16. package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
  17. package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
  18. package/mcp-server/dist/services/memory-sessions.js +28 -2
  19. package/mcp-server/dist/services/memory-sessions.js.map +1 -1
  20. package/mcp-server/dist/services/neural-interface.d.ts +2 -1
  21. package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
  22. package/mcp-server/dist/services/neural-interface.js.map +1 -1
  23. package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
  24. package/mcp-server/dist/services/server-instructions.js +2 -1
  25. package/mcp-server/dist/services/server-instructions.js.map +1 -1
  26. package/mcp-server/dist/services/tool-catalog.js +1 -1
  27. package/mcp-server/dist/services/tool-catalog.js.map +1 -1
  28. package/mcp-server/dist/services/tool-list.d.ts +1 -1
  29. package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
  30. package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
  31. package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
  32. package/mcp-server/dist/services/tool-runtime.js +1 -1
  33. package/mcp-server/dist/services/tool-runtime.js.map +1 -1
  34. package/mcp-server/dist/services/typesafe-config.js +1 -1
  35. package/mcp-server/dist/services/typesafe-config.js.map +1 -1
  36. package/mcp-server/dist/tools/agents.d.ts +8 -8
  37. package/mcp-server/dist/tools/agents.d.ts.map +1 -1
  38. package/mcp-server/dist/tools/agents.js +10 -9
  39. package/mcp-server/dist/tools/agents.js.map +1 -1
  40. package/mcp-server/dist/tools/choice.d.ts +6 -0
  41. package/mcp-server/dist/tools/choice.d.ts.map +1 -1
  42. package/mcp-server/dist/tools/choice.js +18 -13
  43. package/mcp-server/dist/tools/choice.js.map +1 -1
  44. package/mcp-server/dist/tools/git-tools.js +2 -2
  45. package/mcp-server/dist/tools/git-tools.js.map +1 -1
  46. package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
  47. package/mcp-server/dist/tools/morelogin.js +9 -6
  48. package/mcp-server/dist/tools/morelogin.js.map +1 -1
  49. package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
  50. package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
  51. package/mcp-server/dist/tools/skill-tools.js +11 -8
  52. package/mcp-server/dist/tools/skill-tools.js.map +1 -1
  53. package/mcp-server/package-lock.json +2 -2
  54. package/mcp-server/package.json +1 -1
  55. package/mcp-server/src/index.ts +1 -1
  56. package/mcp-server/src/services/budget-judgments.ts +5 -3
  57. package/mcp-server/src/services/identity.ts +2 -2
  58. package/mcp-server/src/services/memory-sessions.ts +24 -3
  59. package/mcp-server/src/services/neural-interface.ts +2 -1
  60. package/mcp-server/src/services/server-instructions.ts +2 -1
  61. package/mcp-server/src/services/tool-catalog.ts +1 -1
  62. package/mcp-server/src/services/tool-list.ts +1 -1
  63. package/mcp-server/src/services/tool-runtime.ts +2 -2
  64. package/mcp-server/src/services/typesafe-config.ts +1 -1
  65. package/mcp-server/src/tools/agents.ts +10 -9
  66. package/mcp-server/src/tools/choice.ts +17 -12
  67. package/mcp-server/src/tools/git-tools.ts +2 -2
  68. package/mcp-server/src/tools/morelogin.ts +6 -4
  69. package/mcp-server/src/tools/skill-tools.ts +11 -8
  70. package/neural-interface/i18n/en.json +504 -39
  71. package/neural-interface/i18n/pt-BR.json +504 -39
  72. package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
  73. package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
  74. package/neural-interface/lib/antigravity/acp-connection.js +299 -0
  75. package/neural-interface/lib/antigravity/bypass.js +100 -0
  76. package/neural-interface/lib/antigravity/catalog.js +273 -0
  77. package/neural-interface/lib/antigravity/home.js +260 -0
  78. package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
  79. package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
  80. package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
  81. package/neural-interface/lib/antigravity/policy.js +74 -0
  82. package/neural-interface/lib/antigravity/tool-call.js +178 -0
  83. package/neural-interface/lib/antigravity/transcripts.js +252 -0
  84. package/neural-interface/lib/antigravity/usage.js +43 -0
  85. package/neural-interface/lib/assistant-api.js +1 -0
  86. package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
  87. package/neural-interface/lib/assistant-brains/codex.js +6 -1
  88. package/neural-interface/lib/assistant-budget-planner.js +7 -3
  89. package/neural-interface/lib/assistant-budget-split.js +42 -15
  90. package/neural-interface/lib/assistant-catalog.js +76 -9
  91. package/neural-interface/lib/assistant-config.js +6 -3
  92. package/neural-interface/lib/assistant-dispatch.js +211 -50
  93. package/neural-interface/lib/assistant-envelope.js +153 -1
  94. package/neural-interface/lib/assistant-evidence.js +80 -0
  95. package/neural-interface/lib/assistant-hidden-models.js +7 -5
  96. package/neural-interface/lib/assistant-persona.js +16 -11
  97. package/neural-interface/lib/assistant-plan-permissions.js +15 -0
  98. package/neural-interface/lib/assistant-router.js +237 -35
  99. package/neural-interface/lib/assistant-runtime.js +158 -12
  100. package/neural-interface/lib/assistant-task-prompt.js +10 -8
  101. package/neural-interface/lib/assistant-tools.js +2 -2
  102. package/neural-interface/lib/assistant-usage.js +29 -0
  103. package/neural-interface/lib/backup-service.js +6 -1
  104. package/neural-interface/lib/browser-guard-cdp.js +5 -4
  105. package/neural-interface/lib/browser-network-guard.js +274 -82
  106. package/neural-interface/lib/browser-tool-policy.js +12 -7
  107. package/neural-interface/lib/claude-agent-bridge.js +7 -0
  108. package/neural-interface/lib/claude-history-cache.js +210 -0
  109. package/neural-interface/lib/claude-model-catalog.js +5 -1
  110. package/neural-interface/lib/cli-install-api.js +1 -1
  111. package/neural-interface/lib/cli-install.js +160 -31
  112. package/neural-interface/lib/cli-resolver.js +9 -2
  113. package/neural-interface/lib/cli-update-planner.js +25 -15
  114. package/neural-interface/lib/codex-rollout-reader.js +52 -0
  115. package/neural-interface/lib/codex-worker-app-server.js +25 -6
  116. package/neural-interface/lib/editor-git-api.js +3 -20
  117. package/neural-interface/lib/exec-loop-command.js +9 -2
  118. package/neural-interface/lib/file-explorer-api.js +74 -40
  119. package/neural-interface/lib/file-explorer-watch.js +9 -3
  120. package/neural-interface/lib/git-bash/install.js +52 -6
  121. package/neural-interface/lib/git-bash/manager.js +59 -17
  122. package/neural-interface/lib/git-bash/pins.js +12 -0
  123. package/neural-interface/lib/git-read.js +2 -2
  124. package/neural-interface/lib/git-status-runner.js +390 -0
  125. package/neural-interface/lib/git-status-watchdog.js +40 -0
  126. package/neural-interface/lib/greeting/budgets.js +1 -0
  127. package/neural-interface/lib/greeting/engine.js +2 -2
  128. package/neural-interface/lib/greeting/sources.js +10 -12
  129. package/neural-interface/lib/greeting/writer.js +1 -1
  130. package/neural-interface/lib/mcp-client-config.js +37 -6
  131. package/neural-interface/lib/mcp-external-servers.js +40 -7
  132. package/neural-interface/lib/mcp-installer.js +2 -1
  133. package/neural-interface/lib/memory-session-capture.js +17 -3
  134. package/neural-interface/lib/morelogin.js +147 -25
  135. package/neural-interface/lib/native-loop-providers.js +423 -23
  136. package/neural-interface/lib/native-loop-runtime.js +23 -2
  137. package/neural-interface/lib/opencode-native-boundary.js +165 -22
  138. package/neural-interface/lib/owner-authority.js +1 -1
  139. package/neural-interface/lib/path-confine.js +12 -9
  140. package/neural-interface/lib/peer-identity.js +80 -6
  141. package/neural-interface/lib/pty-host/core.js +28 -1
  142. package/neural-interface/lib/pty-host/lifetime.js +12 -0
  143. package/neural-interface/lib/pty-host/manager.js +12 -1
  144. package/neural-interface/lib/remote-policy.js +1 -1
  145. package/neural-interface/lib/request-gate.js +3 -0
  146. package/neural-interface/lib/rulesets/api.js +6 -5
  147. package/neural-interface/lib/rulesets/fs-safe.js +1 -1
  148. package/neural-interface/lib/rulesets/installer.js +89 -10
  149. package/neural-interface/lib/rulesets/render.js +1 -1
  150. package/neural-interface/lib/session-cache-builder.js +64 -1
  151. package/neural-interface/lib/skills/catalog.js +5 -1
  152. package/neural-interface/lib/skills/creation-brief.js +1 -1
  153. package/neural-interface/lib/skills/creation-sources.js +19 -1
  154. package/neural-interface/lib/skills/deploy.js +9 -4
  155. package/neural-interface/lib/skills/draft-confinement.js +3 -1
  156. package/neural-interface/lib/skills/eval-confinement.js +3 -1
  157. package/neural-interface/lib/skills/eval-trace.js +16 -0
  158. package/neural-interface/lib/skills/export.js +5 -2
  159. package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
  160. package/neural-interface/lib/skills/ownership.js +1 -1
  161. package/neural-interface/lib/skills/roots.js +10 -3
  162. package/neural-interface/lib/skills/runtime-checks.js +6 -3
  163. package/neural-interface/lib/skills/service.js +15 -7
  164. package/neural-interface/lib/skills/telemetry.js +6 -0
  165. package/neural-interface/lib/synabun-version.js +1 -1
  166. package/neural-interface/lib/system-activity.js +1 -1
  167. package/neural-interface/lib/whatsapp/brain.js +7 -3
  168. package/neural-interface/lib/whatsapp/cards.js +22 -9
  169. package/neural-interface/lib/whatsapp/config.js +1 -1
  170. package/neural-interface/lib/worker-confinement.js +77 -1
  171. package/neural-interface/lib/worker-question.js +74 -0
  172. package/neural-interface/lib/worker-resume.js +13 -4
  173. package/neural-interface/lib/worker-sandbox.js +46 -16
  174. package/neural-interface/package-lock.json +18 -2
  175. package/neural-interface/package.json +2 -1
  176. package/neural-interface/public/onboarding.html +1 -1
  177. package/neural-interface/public/shared/agent-runtime-options.js +89 -11
  178. package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
  179. package/neural-interface/public/shared/agy/agy-client.js +266 -0
  180. package/neural-interface/public/shared/agy/agy-commands.js +120 -0
  181. package/neural-interface/public/shared/agy/agy-copy.js +326 -0
  182. package/neural-interface/public/shared/agy/agy-diff.js +161 -0
  183. package/neural-interface/public/shared/agy/agy-events.js +469 -0
  184. package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
  185. package/neural-interface/public/shared/agy/agy-options.js +232 -0
  186. package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
  187. package/neural-interface/public/shared/agy/agy-render.js +392 -0
  188. package/neural-interface/public/shared/agy/agy-setup.js +149 -0
  189. package/neural-interface/public/shared/agy/agy-styles.js +604 -0
  190. package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
  191. package/neural-interface/public/shared/api.js +4 -4
  192. package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
  193. package/neural-interface/public/shared/assistant/asst-api.js +15 -3
  194. package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
  195. package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
  196. package/neural-interface/public/shared/assistant/asst-computer.js +26 -2
  197. package/neural-interface/public/shared/assistant/asst-control.js +48 -0
  198. package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
  199. package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
  200. package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
  201. package/neural-interface/public/shared/assistant/asst-motion-gate.js +80 -0
  202. package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
  203. package/neural-interface/public/shared/assistant/asst-render.js +112 -18
  204. package/neural-interface/public/shared/assistant/asst-route.js +237 -57
  205. package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
  206. package/neural-interface/public/shared/assistant/asst-scene-styles.js +55 -23
  207. package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
  208. package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
  209. package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
  210. package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
  211. package/neural-interface/public/shared/assistant/asst-state.js +76 -9
  212. package/neural-interface/public/shared/assistant/asst-style-cache.js +44 -0
  213. package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
  214. package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
  215. package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
  216. package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
  217. package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
  218. package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
  219. package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
  220. package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
  221. package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
  222. package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
  223. package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
  224. package/neural-interface/public/shared/cli-install-controller.js +218 -0
  225. package/neural-interface/public/shared/cli-install-view.js +7 -4
  226. package/neural-interface/public/shared/cli-status.js +9 -8
  227. package/neural-interface/public/shared/constants.js +3 -2
  228. package/neural-interface/public/shared/context-menu-model.js +1 -0
  229. package/neural-interface/public/shared/cp/cp-permissions.js +12 -0
  230. package/neural-interface/public/shared/cp/cp-restore.js +162 -0
  231. package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
  232. package/neural-interface/public/shared/desktop-bridge.js +17 -0
  233. package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
  234. package/neural-interface/public/shared/desktop-titlebar.js +13 -4
  235. package/neural-interface/public/shared/desktop-update-view.js +1 -0
  236. package/neural-interface/public/shared/explorer/fx-api.js +4 -4
  237. package/neural-interface/public/shared/explorer/fx-links.js +3 -3
  238. package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
  239. package/neural-interface/public/shared/explorer/fx-main.js +48 -16
  240. package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
  241. package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
  242. package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
  243. package/neural-interface/public/shared/html-shell.js +21 -4
  244. package/neural-interface/public/shared/loading-teardown.js +34 -0
  245. package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
  246. package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -7
  247. package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
  248. package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +17 -1
  249. package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
  250. package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
  251. package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
  252. package/neural-interface/public/shared/provider-icons.js +4 -2
  253. package/neural-interface/public/shared/settings/settings-rules.js +2 -2
  254. package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
  255. package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
  256. package/neural-interface/public/shared/showcase-privacy.js +6 -1
  257. package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
  258. package/neural-interface/public/shared/skills/ss-creation.js +6 -3
  259. package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
  260. package/neural-interface/public/shared/skills/ss-lint.js +9 -1
  261. package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
  262. package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
  263. package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
  264. package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
  265. package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
  266. package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
  267. package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
  268. package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
  269. package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
  270. package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
  271. package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
  272. package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
  273. package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
  274. package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
  275. package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
  276. package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
  277. package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
  278. package/neural-interface/public/shared/state.js +1 -1
  279. package/neural-interface/public/shared/storage-retry.js +47 -0
  280. package/neural-interface/public/shared/storage.js +25 -4
  281. package/neural-interface/public/shared/streaming-markdown.js +58 -0
  282. package/neural-interface/public/shared/styles.css +362 -35
  283. package/neural-interface/public/shared/synabun-mascot.js +27 -8
  284. package/neural-interface/public/shared/synabun-ticker.js +147 -12
  285. package/neural-interface/public/shared/text-cache.js +29 -0
  286. package/neural-interface/public/shared/tool-list-picker.js +2 -2
  287. package/neural-interface/public/shared/tool-list-view.js +2 -2
  288. package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
  289. package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
  290. package/neural-interface/public/shared/ui-assistant.js +2 -0
  291. package/neural-interface/public/shared/ui-automation-studio.js +20 -12
  292. package/neural-interface/public/shared/ui-claude-panel.js +374 -134
  293. package/neural-interface/public/shared/ui-cli-banner.js +23 -167
  294. package/neural-interface/public/shared/ui-file-explorer.js +48 -6
  295. package/neural-interface/public/shared/ui-file-links.js +2 -2
  296. package/neural-interface/public/shared/ui-games.js +2 -2
  297. package/neural-interface/public/shared/ui-judgments.js +1 -1
  298. package/neural-interface/public/shared/ui-keybinds.js +8 -2
  299. package/neural-interface/public/shared/ui-link.js +3 -3
  300. package/neural-interface/public/shared/ui-loading.js +5 -4
  301. package/neural-interface/public/shared/ui-menubar.js +2 -2
  302. package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
  303. package/neural-interface/public/shared/ui-navbar.js +45 -0
  304. package/neural-interface/public/shared/ui-notifications.js +1 -0
  305. package/neural-interface/public/shared/ui-resume.js +9 -7
  306. package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
  307. package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
  308. package/neural-interface/public/shared/ui-sessions.js +22 -12
  309. package/neural-interface/public/shared/ui-settings.js +90 -65
  310. package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
  311. package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
  312. package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
  313. package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
  314. package/neural-interface/public/shared/ui-terminal.js +146 -10
  315. package/neural-interface/public/shared/ui-tool-updates.js +3 -3
  316. package/neural-interface/public/shared/ui-tooltip.js +71 -46
  317. package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
  318. package/neural-interface/public/shared/ui-whiteboard.js +1 -1
  319. package/neural-interface/public/shared/ui-worker-end.js +1 -1
  320. package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
  321. package/neural-interface/public/shared/worker-questions.js +34 -0
  322. package/neural-interface/public/sidepanel-session.html +1 -1
  323. package/neural-interface/server.js +731 -466
  324. package/neural-interface/templates/rulesets/README.md +1 -1
  325. package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
  326. package/neural-interface/templates/rulesets/manifest.json +1 -1
  327. package/npm-shrinkwrap.json +18 -2
  328. package/package.json +2 -1
  329. package/skills/skill-forge/SKILL.md +4 -4
  330. package/skills/synabun/SKILL.md +1 -1
  331. package/skills/synabun/modules/schedule.md +3 -3
  332. /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
@@ -0,0 +1,326 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Antigravity panel: every sentence it says (no DOM)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // The English text lives here, once; `antigravityPanel.*` in i18n/en.json must equal it and
6
+ // i18n/pt-BR.json must have the same keys and placeholders (tests/antigravity-panel-tabs.test.mjs,
7
+ // tests/antigravity-panel-copy.test.mjs). A detached window or a test renders before the locale bundle
8
+ // is ready: the English is the fallback.
9
+
10
+ export const AGY_COPY = Object.freeze({
11
+ title: 'Antigravity',
12
+ toolbar: 'Antigravity side panel',
13
+ transcript: 'Antigravity conversation',
14
+ newSession: 'New session',
15
+ newTab: 'New tab',
16
+ closeTab: 'Close tab',
17
+ minimize: 'Minimize to pill',
18
+ rename: 'Rename session',
19
+ sessions: 'Sessions',
20
+ sessionsEmpty: 'No Antigravity conversations in this project yet.',
21
+ sessionsLoading: 'Loading conversations…',
22
+ sessionsFailed: 'Could not list conversations: {error}',
23
+ sessionsSearch: 'Search conversations',
24
+ sessionsMore: 'Load more',
25
+ delete: 'Delete',
26
+ deleteConfirm: 'Delete "{title}"?',
27
+ cancel: 'Cancel',
28
+ fork: 'Fork',
29
+ forkTitle: 'Fork of {title}',
30
+ project: 'Project',
31
+ chooseProject: 'Choose a project',
32
+ branch: 'Branch',
33
+ branchStopFirst: 'Stop the running turn before switching branches.',
34
+ branchSwitched: 'Switched the working tree to {branch}.',
35
+ branchConfirm: 'Switch from {from} to {to} with {count} uncommitted changes? Git keeps them when it can.',
36
+ branchFailed: 'Could not switch branches: {error}',
37
+ noProject: 'Choose a project folder to start.',
38
+ folderAdd: 'Add a folder to this session',
39
+ folderHelp: 'Antigravity can also read and edit these registered projects in this session.',
40
+ folderNone: 'Every registered project is already in this session.',
41
+ folderRemove: 'Remove {name} from this session',
42
+ foldersApplied: 'The session now includes the folders you chose.',
43
+ foldersNext: 'The folders apply when the next session starts.',
44
+ placeholder: 'Ask Antigravity…',
45
+ placeholderQueue: 'Queue a follow-up…',
46
+ placeholderAutomation: 'A dispatched run is using this tab.',
47
+ send: 'Send',
48
+ stop: 'Stop',
49
+ attach: 'Attach files',
50
+ attachmentRemove: 'Remove {name}',
51
+ attachmentTooMany: 'At most {count} attachments per message.',
52
+ attachmentTooLarge: '{name} is larger than {size} MB.',
53
+ attachmentType: '{name} is a file type Antigravity cannot take.',
54
+ mic: 'Record a voice message',
55
+ micFailed: 'The microphone could not start: {error}',
56
+ model: 'Model',
57
+ thinking: 'Thinking',
58
+ mode: 'Mode',
59
+ permissions: 'Permissions',
60
+ permDefault: 'Default',
61
+ permDefaultHint: 'Ask before each tool Antigravity wants approved',
62
+ permBypass: 'Bypass',
63
+ permBypassHint: 'Run everything without asking',
64
+ permBypassRestart: 'Bypass needs the SynaBun server restart: until then choosing it here does nothing.',
65
+ permBypassWaiting: 'Bypass · unavailable: this tab\'s Bypass pick waits for the SynaBun server restart, and until then Antigravity asks as usual.',
66
+ permBypassOnTitle: 'Bypass: every tool runs without asking in this tab, until you pick another mode. SynaBun still refuses browsers other than its own and its protected folders.',
67
+ permBypassStrip: 'Bypass is on: Antigravity runs every tool in this tab without asking.',
68
+ permBypassConfirmTitle: 'Turn on Bypass for this tab?',
69
+ permBypassConfirmBody: 'Antigravity will edit files, run commands and call tools without asking, until you pick Default again. SynaBun still refuses other browsers than its own and anything in its protected folders (credentials, SynaBun\'s data, the runtimes\' settings).',
70
+ permBypassConfirmYes: 'Turn on Bypass',
71
+ permBypassFailed: 'Could not change Bypass: {error}',
72
+ permAutoAllowed: 'Bypass allowed: {tool}',
73
+ permAutoRefused: 'Bypass refused {tool}: {reason}',
74
+ permTool: 'a tool',
75
+ modelDefault: 'Default',
76
+ working: 'Working…',
77
+ workingFor: 'Working… {seconds}s',
78
+ needsYou: 'Waiting for you',
79
+ thought: 'Thinking',
80
+ thoughtFor: 'Thought for {duration}',
81
+ thinkingNow: 'Thinking…',
82
+ plan: 'Plan',
83
+ planProgress: '{done} of {total} done',
84
+ planDone: 'Done',
85
+ priorityHigh: 'High',
86
+ toolPending: 'Waiting',
87
+ toolRunning: 'Running',
88
+ toolDone: 'Done',
89
+ toolFailed: 'Failed',
90
+ kind_read: 'Read',
91
+ kind_edit: 'Edit',
92
+ kind_delete: 'Delete',
93
+ kind_move: 'Move',
94
+ kind_search: 'Search',
95
+ kind_execute: 'Command',
96
+ kind_think: 'Think',
97
+ kind_fetch: 'Fetch',
98
+ kind_switch_mode: 'Mode switch',
99
+ kind_other: 'Tool',
100
+ rawInput: 'Input',
101
+ rawOutput: 'Output',
102
+ diffFor: 'Changes to {path}',
103
+ diffAdded: 'New file',
104
+ diffModified: 'Modified',
105
+ diffDeleted: 'Deleted',
106
+ diffIdentical: 'No changes in the text.',
107
+ diffTooLarge: 'Too many changes to compare line by line: the old text is shown removed and the new text added.',
108
+ diffTruncated: 'The file is longer than the part compared here.',
109
+ diffShowAll: 'Show the whole diff ({count} lines)',
110
+ copyPatch: 'Copy patch',
111
+ terminalWaiting: 'Waiting for output…',
112
+ terminalRunning: 'Running',
113
+ terminalExit: 'Exit code {code}',
114
+ terminalSignal: 'Stopped by {signal}',
115
+ terminalTruncated: 'Earlier output was cut.',
116
+ compaction: 'Conversation summary',
117
+ compactionRunning: 'Summarizing',
118
+ compactionDone: 'Summarized',
119
+ compactionFailed: 'Failed',
120
+ compactionCancelled: 'Cancelled',
121
+ compact: 'Compact',
122
+ subagent: 'Subagent',
123
+ subagentRunning: 'Running',
124
+ subagentIdle: 'Idle',
125
+ subagentNeedsYou: 'Needs you',
126
+ subagentUnknown: 'Unknown',
127
+ relayedMessage: 'Message between sessions',
128
+ usage_in: '{value} in',
129
+ usage_out: '{value} out',
130
+ usage_thinking: '{value} thinking',
131
+ usage_cached: '{value} cached',
132
+ copy: 'Copy',
133
+ copied: 'Copied',
134
+ copyAnswer: 'Copy answer',
135
+ copyCommand: 'Copy the command',
136
+ retry: 'Retry',
137
+ permissionTitle: 'Antigravity wants to {verb}',
138
+ verb_read: 'read a file',
139
+ verb_edit: 'edit a file',
140
+ verb_delete: 'delete a file',
141
+ verb_move: 'move a file',
142
+ verb_search: 'search',
143
+ verb_execute: 'run a command',
144
+ verb_think: 'think',
145
+ verb_fetch: 'fetch from the web',
146
+ verb_switch_mode: 'switch mode',
147
+ verb_other: 'use a tool',
148
+ permissionDeny: 'Deny',
149
+ permissionAnswered: 'Answered',
150
+ permissionCancelled: 'No longer needed',
151
+ permissionSending: 'Sending…',
152
+ permissionKeys: 'Enter allows once · Esc denies',
153
+ elicitTitle: 'Antigravity asks',
154
+ elicitSubmit: 'Send',
155
+ elicitDecline: 'Decline',
156
+ elicitCancel: 'Cancel',
157
+ elicitOpen: 'Open the link',
158
+ elicitDone: 'Done',
159
+ elicitAnswered: 'Answered',
160
+ elicitDeclined: 'Declined',
161
+ elicitBadUrl: 'The link is not an address the panel opens (https only).',
162
+ elicitUnsupported: 'This field cannot be answered here.',
163
+ elicitUnsupportedMode: 'The panel cannot show this kind of question.',
164
+ elicitRequired: 'Required.',
165
+ elicitNumber: 'Enter a number.',
166
+ elicitRange: 'The number is out of range.',
167
+ elicitLength: 'The length is out of range.',
168
+ elicitEmail: 'Enter an email address.',
169
+ elicitUrl: 'Enter an address.',
170
+ elicitChoices: 'Pick a different number of options.',
171
+ signIn: 'Sign in',
172
+ signInTitle: 'Sign in to Antigravity',
173
+ signInBody: 'Antigravity needs your Google sign-in before it can start a session.',
174
+ signInTerminal: 'Sign in from a terminal',
175
+ signInTerminalBody: 'Finish signing in in the terminal that opened, then check again.',
176
+ signInCheck: 'Check again',
177
+ signInWaiting: 'Finish signing in in the browser window that opened…',
178
+ signInFailed: 'Sign-in did not finish: {error}',
179
+ signOut: 'Sign out',
180
+ setupMissingTitle: 'Antigravity CLI is not installed',
181
+ setupMissingBody: 'Install Google\'s Antigravity CLI to use it here. SynaBun runs the official installer, then signs you in.',
182
+ setupMissingBodyBrew: 'Install Google\'s Antigravity CLI to use it here. SynaBun installs it with Homebrew, then signs you in.',
183
+ setupMissingManual: 'Install it with this command, then check again.',
184
+ setupMissingGuide: 'Follow the install guide, then check again.',
185
+ setupInstallingTitle: 'Installing Antigravity…',
186
+ setupStep_check: 'Checking this computer…',
187
+ setupStep_install: 'Installing the Antigravity CLI…',
188
+ setupStep_verify: 'Checking that agy starts…',
189
+ setupBrewRunning: 'Homebrew is installing antigravity-cli…',
190
+ setupFailedTitle: 'Antigravity could not be installed',
191
+ setupBrokenTitle: 'Antigravity CLI failed to start',
192
+ setupUpdateTitle: 'Update the Antigravity CLI',
193
+ setupUpdateBody: 'This version of agy has no ACP server (agy acp), which the panel needs. Update it with this command (or brew upgrade --cask antigravity-cli), then check again.',
194
+ setupConnectingTitle: 'Starting Antigravity…',
195
+ setupConnectingBody: 'The CLI is installed. SynaBun is starting it.',
196
+ setupReadyTitle: 'Antigravity is ready',
197
+ setupReadyBody: 'Ask anything about this project to start.',
198
+ setupPathTitle: 'Use agy in your other terminals',
199
+ setupInstall: 'Install Antigravity',
200
+ setupRetry: 'Try again',
201
+ setupRecheck: 'Check again',
202
+ setupChecking: 'Checking…',
203
+ setupDismiss: 'Dismiss',
204
+ setupLog: 'Installer output',
205
+ setupStepsLabel: 'Setup steps',
206
+ step_check: 'Checking',
207
+ step_install: 'Installing',
208
+ step_verify: 'Verifying',
209
+ step_signin: 'Sign in',
210
+ stepState_todo: 'not started',
211
+ stepState_active: 'in progress',
212
+ stepState_done: 'done',
213
+ stepState_failed: 'failed',
214
+ addToPath: 'Add to PATH',
215
+ installGuide: 'Install guide',
216
+ cliReference: 'Antigravity CLI reference',
217
+ blockedMissing: 'Install Antigravity first',
218
+ blockedInstalling: 'Antigravity is being installed',
219
+ blockedBroken: 'Antigravity failed to start',
220
+ blockedSignIn: 'Sign in first',
221
+ blockedConnecting: 'Antigravity is starting',
222
+ blockedConnection: 'Not connected to SynaBun',
223
+ agentStopped: 'Antigravity stopped{detail}. Send again to start it.',
224
+ disconnected: 'The connection to SynaBun dropped. Reconnecting…',
225
+ connOffline: 'SynaBun is not answering. Retrying…',
226
+ connRetryNow: 'Reconnect now',
227
+ connRetry: 'Try again',
228
+ connAutomation: 'SynaBun\'s automation browser cannot open the Antigravity panel. Open it from your own browser or the SynaBun app.',
229
+ connGuest: 'The Antigravity panel is for the owner of this SynaBun.',
230
+ connRefused: 'SynaBun refused this window\'s connection to the Antigravity panel. If your browser reaches SynaBun through a proxy, add localhost to its bypass list.',
231
+ connRestart: 'This SynaBun server started before the Antigravity panel existed. Restart SynaBun to use it.',
232
+ errorPrefix: 'Error: {message}',
233
+ stopped: 'Stopped.',
234
+ refusal: 'The model declined to continue.',
235
+ maxTokens: 'The reply reached the model\'s output limit.',
236
+ maxTurns: 'The turn reached its request limit.',
237
+ context: 'Context {label}',
238
+ contextUnknown: 'Not reported yet',
239
+ automation: 'Dispatched run',
240
+ automationBanner: 'A dispatched run of the Assistant is working in this tab.',
241
+ automationEnded: 'This run has ended. Send a message to continue here.',
242
+ commands: 'Commands and files',
243
+ hintsPanel: 'Panel',
244
+ hintsAgent: 'Antigravity',
245
+ mentionLoading: 'Looking for files…',
246
+ queueTitle: 'Queue',
247
+ queueAdd: 'Add to queue',
248
+ queuePause: 'Pause the queue',
249
+ queueResume: 'Resume the queue',
250
+ queueClear: 'Clear the queue',
251
+ queueEdit: 'Edit',
252
+ queueRemove: 'Remove',
253
+ queueFull: 'The queue holds at most {count} messages.',
254
+ jumpLatest: 'Latest',
255
+ emptyTitle: 'Antigravity',
256
+ emptyBody: 'Google\'s coding agent, with SynaBun\'s memory. Ask about this project, or paste a picture.',
257
+ emptyKeyCommands: 'commands',
258
+ emptyKeyFiles: 'files',
259
+ loadingHistory: 'Loading the conversation…',
260
+ historyFailed: 'Could not load this conversation: {error}',
261
+ runHomePruned: 'This dispatched run\'s conversation folder was already cleaned up, so the conversation can no longer be continued here. Start a new tab instead.',
262
+ cmdNew: 'Start a new session',
263
+ cmdSessions: 'Open the session list',
264
+ cmdRename: 'Rename this session',
265
+ cmdFork: 'Fork this session into a new tab',
266
+ cmdRetry: 'Send the last message again',
267
+ cmdCopy: 'Copy the last answer',
268
+ cmdModel: 'Choose the model',
269
+ cmdMode: 'Choose the mode',
270
+ cmdThinking: 'Choose the thinking level',
271
+ cmdDetail: 'Change how much of each turn is shown',
272
+ cmdSettings: 'Open Session settings',
273
+ cmdLogin: 'Sign in to Antigravity',
274
+ cmdLogout: 'Sign out of Antigravity',
275
+ cmdHelp: 'List commands and keys',
276
+ helpPanel: 'Panel commands:',
277
+ helpAgent: 'Antigravity commands:',
278
+ helpKeys: 'Keys: Enter sends · Shift+Enter adds a line · ↑ recalls the last message · Esc stops a running turn · @ attaches a file',
279
+ settingsTitle: 'Session settings',
280
+ settingsContext: 'Context',
281
+ settingsTokens: 'Tokens in context',
282
+ settingsSession: 'Session',
283
+ settingsSessionId: 'Session id',
284
+ settingsNoSession: 'Not started',
285
+ settingsFolders: 'Extra folders',
286
+ settingsTurns: 'Turns',
287
+ settingsOptions: 'Session options',
288
+ settingsNoOptions: 'Antigravity offers no options for this session.',
289
+ settingsStartSession: 'The options appear once the session starts.',
290
+ settingsPanel: 'Panel',
291
+ settingsDetail: 'Detail',
292
+ detail_compact: 'Compact',
293
+ detail_standard: 'Standard',
294
+ detail_verbose: 'Verbose',
295
+ detailHelp_compact: 'Answers only: thoughts hidden, every tool call closed.',
296
+ detailHelp_standard: 'Thoughts folded; file changes open, other tool calls closed.',
297
+ detailHelp_verbose: 'Everything open, with each tool call\'s raw input and output.',
298
+ settingsClientFs: 'Files through SynaBun',
299
+ settingsClientFsHelp: 'Antigravity reads and writes files through SynaBun. Applies when the agent restarts, which this switch does.',
300
+ settingsClientTerminal: 'Commands in SynaBun',
301
+ settingsClientTerminalHelp: 'Antigravity runs its commands through SynaBun, with their output live in the tool card. Applies when the agent restarts.',
302
+ settingsNeedsRestart: 'Needs a restart of the SynaBun server.',
303
+ settingsAccount: 'Account',
304
+ settingsSignIn: 'Sign-in',
305
+ settingsSignedIn: 'Signed in',
306
+ settingsSignedOut: 'Signed out',
307
+ settingsAgentOff: 'Antigravity is not running',
308
+ settingsMethods: 'Methods',
309
+ settingsAbout: 'About',
310
+ settingsAgent: 'Agent',
311
+ settingsProtocol: 'Protocol',
312
+ settingsLinks: 'Docs',
313
+ });
314
+
315
+ let translate = null;
316
+ export function setAntigravityTranslator(fn) { translate = typeof fn === 'function' ? fn : null; }
317
+
318
+ /** The sentence for `key`, translated when the locale has it, else the English, with {name} placeholders filled. */
319
+ export function agyText(key, params) {
320
+ const path = `antigravityPanel.${key}`;
321
+ let value = path;
322
+ try { value = translate?.(path, params) || path; } catch { value = path; }
323
+ if (value !== path) return value;
324
+ const english = Object.hasOwn(AGY_COPY, key) ? AGY_COPY[key] : key;
325
+ return english.replace(/\{(\w+)\}/g, (_, name) => (params?.[name] ?? `{${name}}`));
326
+ }
@@ -0,0 +1,161 @@
1
+ // ═══════════════════════════════════════════
2
+ // SynaBun — Antigravity panel: a file change as unified diff rows (no DOM)
3
+ // ═══════════════════════════════════════════
4
+ //
5
+ // An ACP tool call reports a change as `{ type: 'diff', path, oldText, newText }`
6
+ // (oldText null for a new file). This module turns one into the rows the panel
7
+ // draws: hunks with three lines of context, each row with its old and new line
8
+ // number, a sign and its text, plus the counts. Myers' algorithm on the lines
9
+ // between the common head and tail; a change too large to diff within the
10
+ // bounds is shown as the old text removed and the new text added, and says so.
11
+ // Imported by the page and by `node --test`.
12
+
13
+ export const DIFF_LIMITS = Object.freeze({
14
+ maxChars: 200_000, // per side; more is shown truncated
15
+ maxLines: 2000, // per side
16
+ maxEdits: 1000, // Myers' D: beyond it the change is "replace everything"
17
+ context: 3,
18
+ previewRows: 60, // rows drawn before "Show the whole diff"
19
+ });
20
+
21
+ const splitLines = (text) => {
22
+ const value = String(text ?? '');
23
+ if (!value) return [];
24
+ const lines = value.split(/\r?\n/);
25
+ if (lines.length && lines[lines.length - 1] === '') lines.pop();
26
+ return lines;
27
+ };
28
+
29
+ /**
30
+ * Myers' O(ND) diff of two line arrays. Returns the edit script as
31
+ * [{ op: 'equal' | 'delete' | 'insert', a, b }] (a / b: indexes into the
32
+ * arrays), or null when it needs more than `maxEdits` edits.
33
+ */
34
+ export function myersDiff(a, b, maxEdits = DIFF_LIMITS.maxEdits) {
35
+ const n = a.length;
36
+ const m = b.length;
37
+ const max = n + m;
38
+ if (max === 0) return [];
39
+ const limit = Math.min(max, maxEdits);
40
+ const offset = limit + 1;
41
+ let v = new Int32Array(2 * limit + 3);
42
+ const trace = [];
43
+ let found = -1;
44
+ for (let d = 0; d <= limit; d += 1) {
45
+ trace.push(v.slice(offset - d - 1 < 0 ? 0 : offset - d - 1, offset + d + 2));
46
+ for (let k = -d; k <= d; k += 2) {
47
+ let x;
48
+ if (k === -d || (k !== d && v[offset + k - 1] < v[offset + k + 1])) x = v[offset + k + 1];
49
+ else x = v[offset + k - 1] + 1;
50
+ let y = x - k;
51
+ while (x < n && y < m && a[x] === b[y]) { x += 1; y += 1; }
52
+ v[offset + k] = x;
53
+ if (x >= n && y >= m) { found = d; break; }
54
+ }
55
+ if (found >= 0) break;
56
+ }
57
+ if (found < 0) return null;
58
+ // Walk the trace back from (n, m).
59
+ const script = [];
60
+ let x = n;
61
+ let y = m;
62
+ for (let d = found; d > 0; d -= 1) {
63
+ const prev = trace[d];
64
+ const base = offset - d - 1 < 0 ? 0 : offset - d - 1;
65
+ const at = (k) => prev[offset + k - base];
66
+ const k = x - y;
67
+ const prevK = (k === -d || (k !== d && at(k - 1) < at(k + 1))) ? k + 1 : k - 1;
68
+ const prevX = at(prevK);
69
+ const prevY = prevX - prevK;
70
+ while (x > prevX && y > prevY) { x -= 1; y -= 1; script.push({ op: 'equal', a: x, b: y }); }
71
+ if (x === prevX) { y -= 1; script.push({ op: 'insert', a: x, b: y }); }
72
+ else { x -= 1; script.push({ op: 'delete', a: x, b: y }); }
73
+ }
74
+ while (x > 0 && y > 0) { x -= 1; y -= 1; script.push({ op: 'equal', a: x, b: y }); }
75
+ return script.reverse();
76
+ }
77
+
78
+ function clip(text, maxChars) {
79
+ const value = String(text ?? '');
80
+ return value.length > maxChars ? { text: value.slice(0, maxChars), clipped: true } : { text: value, clipped: false };
81
+ }
82
+
83
+ /**
84
+ * One ACP diff as rows:
85
+ * { path, status: 'added' | 'modified' | 'deleted', added, removed, rows: [{ kind: 'hunk' | 'context' | 'add' |
86
+ * 'delete' | 'note', old, new, text }], truncated, fallback, identical }
87
+ */
88
+ export function diffView(item, limits = DIFF_LIMITS) {
89
+ const path = String(item?.path || '');
90
+ const oldRaw = item?.oldText;
91
+ const isNew = oldRaw === null || oldRaw === undefined;
92
+ const oldClip = clip(isNew ? '' : oldRaw, limits.maxChars);
93
+ const newClip = clip(item?.newText ?? '', limits.maxChars);
94
+ let oldLines = splitLines(oldClip.text);
95
+ let newLines = splitLines(newClip.text);
96
+ let truncated = oldClip.clipped || newClip.clipped;
97
+ if (oldLines.length > limits.maxLines) { oldLines = oldLines.slice(0, limits.maxLines); truncated = true; }
98
+ if (newLines.length > limits.maxLines) { newLines = newLines.slice(0, limits.maxLines); truncated = true; }
99
+ const status = isNew ? 'added' : (!newLines.length && oldLines.length ? 'deleted' : 'modified');
100
+
101
+ // The common head and tail never take part in the edit search.
102
+ let head = 0;
103
+ while (head < oldLines.length && head < newLines.length && oldLines[head] === newLines[head]) head += 1;
104
+ let tail = 0;
105
+ while (tail < oldLines.length - head && tail < newLines.length - head
106
+ && oldLines[oldLines.length - 1 - tail] === newLines[newLines.length - 1 - tail]) tail += 1;
107
+ const midA = oldLines.slice(head, oldLines.length - tail);
108
+ const midB = newLines.slice(head, newLines.length - tail);
109
+ let middle = myersDiff(midA, midB, limits.maxEdits);
110
+ const fallback = middle === null;
111
+ if (fallback) middle = [...midA.map((_, a) => ({ op: 'delete', a, b: 0 })), ...midB.map((_, b) => ({ op: 'insert', a: midA.length, b }))];
112
+ const ops = [];
113
+ for (let i = 0; i < head; i += 1) ops.push({ op: 'equal', a: i, b: i });
114
+ for (const step of middle) ops.push({ op: step.op, a: step.a + head, b: step.b + head });
115
+ for (let i = 0; i < tail; i += 1) ops.push({ op: 'equal', a: oldLines.length - tail + i, b: newLines.length - tail + i });
116
+
117
+ let added = 0;
118
+ let removed = 0;
119
+ for (const step of ops) { if (step.op === 'insert') added += 1; else if (step.op === 'delete') removed += 1; }
120
+ const identical = added === 0 && removed === 0;
121
+
122
+ // Hunks: every change with `context` equal lines around it; runs closer than twice that merge.
123
+ const rows = [];
124
+ const changeAt = ops.map((step) => step.op !== 'equal');
125
+ const keep = new Array(ops.length).fill(false);
126
+ for (let i = 0; i < ops.length; i += 1) {
127
+ if (!changeAt[i]) continue;
128
+ for (let j = Math.max(0, i - limits.context); j <= Math.min(ops.length - 1, i + limits.context); j += 1) keep[j] = true;
129
+ }
130
+ let i = 0;
131
+ while (i < ops.length) {
132
+ if (!keep[i]) { i += 1; continue; }
133
+ let end = i;
134
+ while (end + 1 < ops.length && keep[end + 1]) end += 1;
135
+ const first = ops[i];
136
+ let oldStart = first.op === 'insert' ? first.a : first.a;
137
+ let newStart = first.op === 'delete' ? first.b : first.b;
138
+ let oldCount = 0;
139
+ let newCount = 0;
140
+ for (let j = i; j <= end; j += 1) {
141
+ if (ops[j].op !== 'insert') oldCount += 1;
142
+ if (ops[j].op !== 'delete') newCount += 1;
143
+ }
144
+ rows.push({ kind: 'hunk', old: null, new: null, text: `@@ -${oldCount ? oldStart + 1 : oldStart},${oldCount} +${newCount ? newStart + 1 : newStart},${newCount} @@` });
145
+ for (let j = i; j <= end; j += 1) {
146
+ const step = ops[j];
147
+ if (step.op === 'equal') rows.push({ kind: 'context', old: step.a + 1, new: step.b + 1, text: oldLines[step.a] });
148
+ else if (step.op === 'delete') rows.push({ kind: 'delete', old: step.a + 1, new: null, text: oldLines[step.a] });
149
+ else rows.push({ kind: 'add', old: null, new: step.b + 1, text: newLines[step.b] });
150
+ }
151
+ i = end + 1;
152
+ }
153
+ if (truncated) rows.push({ kind: 'note', old: null, new: null, text: 'truncated' });
154
+ return { path, status, added, removed, rows, truncated, fallback, identical };
155
+ }
156
+
157
+ /** Counts over several diffs: { files, added, removed }. */
158
+ export function diffTotals(views) {
159
+ const list = Array.isArray(views) ? views : [];
160
+ return list.reduce((sum, view) => ({ files: sum.files + 1, added: sum.added + (view?.added || 0), removed: sum.removed + (view?.removed || 0) }), { files: 0, added: 0, removed: 0 });
161
+ }