synabun 2.1.0 → 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.
- package/CHANGELOG.md +121 -0
- package/README.md +1 -1
- package/docs/whatsapp.md +11 -11
- package/lib/client-config-repair.js +13 -9
- package/lib/external-tools.js +6 -5
- package/lib/update-snapshot.js +77 -3
- package/lib/virustotal.mjs +12 -1
- package/mcp-server/dist/index.js +1 -1
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +2 -1
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +2 -1
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.js +1 -1
- package/mcp-server/dist/services/tool-catalog.js.map +1 -1
- package/mcp-server/dist/services/tool-list.d.ts +1 -1
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.js +1 -1
- package/mcp-server/dist/services/tool-runtime.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +8 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +10 -9
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +8 -8
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/skill-tools.js +11 -8
- package/mcp-server/dist/tools/skill-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/index.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +2 -1
- package/mcp-server/src/services/server-instructions.ts +2 -1
- package/mcp-server/src/services/tool-catalog.ts +1 -1
- package/mcp-server/src/services/tool-list.ts +1 -1
- package/mcp-server/src/services/tool-runtime.ts +2 -2
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/tools/agents.ts +10 -9
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +11 -8
- package/neural-interface/i18n/en.json +501 -39
- package/neural-interface/i18n/pt-BR.json +501 -39
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +1 -0
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +76 -9
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +207 -49
- package/neural-interface/lib/assistant-envelope.js +153 -1
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +16 -11
- package/neural-interface/lib/assistant-plan-permissions.js +15 -0
- package/neural-interface/lib/assistant-router.js +237 -35
- package/neural-interface/lib/assistant-runtime.js +158 -12
- package/neural-interface/lib/assistant-task-prompt.js +10 -8
- package/neural-interface/lib/assistant-tools.js +2 -2
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +6 -1
- package/neural-interface/lib/browser-guard-cdp.js +5 -4
- package/neural-interface/lib/browser-network-guard.js +274 -82
- package/neural-interface/lib/browser-tool-policy.js +12 -7
- package/neural-interface/lib/claude-agent-bridge.js +7 -0
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +160 -31
- package/neural-interface/lib/cli-resolver.js +9 -2
- package/neural-interface/lib/cli-update-planner.js +25 -15
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +25 -6
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +9 -2
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/mcp-client-config.js +37 -6
- package/neural-interface/lib/mcp-external-servers.js +13 -4
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +147 -25
- package/neural-interface/lib/native-loop-providers.js +420 -21
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +11 -2
- package/neural-interface/lib/owner-authority.js +1 -1
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +32 -2
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +3 -0
- package/neural-interface/lib/rulesets/api.js +6 -5
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +89 -10
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +64 -1
- package/neural-interface/lib/skills/catalog.js +5 -1
- package/neural-interface/lib/skills/creation-brief.js +1 -1
- package/neural-interface/lib/skills/creation-sources.js +19 -1
- package/neural-interface/lib/skills/deploy.js +9 -4
- package/neural-interface/lib/skills/draft-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-trace.js +16 -0
- package/neural-interface/lib/skills/export.js +5 -2
- package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
- package/neural-interface/lib/skills/ownership.js +1 -1
- package/neural-interface/lib/skills/roots.js +10 -3
- package/neural-interface/lib/skills/runtime-checks.js +6 -3
- package/neural-interface/lib/skills/service.js +15 -7
- package/neural-interface/lib/skills/telemetry.js +6 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +77 -1
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-resume.js +13 -4
- package/neural-interface/lib/worker-sandbox.js +46 -16
- package/neural-interface/package-lock.json +18 -2
- package/neural-interface/package.json +2 -1
- package/neural-interface/public/onboarding.html +1 -1
- package/neural-interface/public/shared/agent-runtime-options.js +89 -11
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +4 -4
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +48 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
- package/neural-interface/public/shared/assistant/asst-panel.js +45 -8
- package/neural-interface/public/shared/assistant/asst-render.js +51 -12
- package/neural-interface/public/shared/assistant/asst-route.js +237 -57
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +54 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
- package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
- package/neural-interface/public/shared/assistant/asst-state.js +76 -9
- package/neural-interface/public/shared/assistant/asst-styles.js +34 -0
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
- package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
- package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +7 -4
- package/neural-interface/public/shared/cli-status.js +9 -8
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/context-menu-model.js +1 -0
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +1 -0
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +21 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +27 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
- package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-creation.js +6 -3
- package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
- package/neural-interface/public/shared/skills/ss-lint.js +9 -1
- package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
- package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
- package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
- package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
- package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
- package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +354 -34
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +2 -2
- package/neural-interface/public/shared/tool-list-view.js +2 -2
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +20 -12
- package/neural-interface/public/shared/ui-claude-panel.js +299 -77
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +8 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +2 -2
- package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
- package/neural-interface/public/shared/ui-navbar.js +45 -0
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +22 -12
- package/neural-interface/public/shared/ui-settings.js +90 -65
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
- package/neural-interface/public/shared/ui-terminal.js +146 -10
- package/neural-interface/public/shared/ui-tool-updates.js +3 -3
- package/neural-interface/public/shared/ui-tooltip.js +71 -46
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1 -1
- package/neural-interface/public/shared/ui-worker-end.js +1 -1
- package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/server.js +644 -426
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
- package/neural-interface/templates/rulesets/manifest.json +1 -1
- package/npm-shrinkwrap.json +18 -2
- package/package.json +2 -1
- package/skills/skill-forge/SKILL.md +4 -4
- package/skills/synabun/SKILL.md +1 -1
- package/skills/synabun/modules/schedule.md +3 -3
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
// claude owned file ~/.claude/rules/synabun.md (and each Claude account's config dir)
|
|
9
9
|
// opencode owned file <opencode config dir>/synabun.md + one `instructions` entry in opencode.json
|
|
10
10
|
// codex block ~/.codex/AGENTS.md (account homes link to it, or get their own block)
|
|
11
|
-
//
|
|
11
|
+
// antigravity block ~/.gemini/config/GEMINI.md (Antigravity's global customization root; the block an
|
|
12
|
+
// older SynaBun wrote for Gemini CLI in ~/.gemini/GEMINI.md, which Antigravity also
|
|
13
|
+
// reads, is moved here so the rules are never loaded twice)
|
|
12
14
|
// cursor none User Rules live inside the app: copy and paste ("manual")
|
|
13
15
|
//
|
|
14
16
|
// What it will not do: create the config directory of a CLI that is not
|
|
@@ -83,11 +85,18 @@ const HOSTS = Object.freeze({
|
|
|
83
85
|
claude: { label: 'Claude Code', carrier: 'file' },
|
|
84
86
|
codex: { label: 'Codex CLI', carrier: 'block' },
|
|
85
87
|
opencode: { label: 'OpenCode', carrier: 'file' },
|
|
86
|
-
|
|
88
|
+
// `legacyHosts`: the stamps an earlier SynaBun wrote for what this host replaced (Gemini CLI's rules, host=gemini).
|
|
89
|
+
antigravity: { label: 'Antigravity', carrier: 'block', legacyHosts: Object.freeze(['gemini']) },
|
|
87
90
|
cursor: { label: 'Cursor', carrier: 'none' },
|
|
88
91
|
});
|
|
89
92
|
const MANAGED_HOSTS = Object.freeze(RULESET_HOSTS.filter((host) => HOSTS[host].carrier !== 'none'));
|
|
90
|
-
const PROJECT_FILES = Object.freeze([['CLAUDE.md', 'claude'], ['AGENTS.md', 'codex'], ['GEMINI.md', '
|
|
93
|
+
const PROJECT_FILES = Object.freeze([['CLAUDE.md', 'claude'], ['AGENTS.md', 'codex'], ['GEMINI.md', 'antigravity']]);
|
|
94
|
+
// State rows an earlier SynaBun kept under a host this one replaced: read as the new host's row (readState).
|
|
95
|
+
const RENAMED_HOSTS = Object.freeze({ gemini: 'antigravity' });
|
|
96
|
+
/** Whether a block's stamp is this host's own: its name, or one it replaced. */
|
|
97
|
+
function ownStamp(host, stamped) {
|
|
98
|
+
return stamped === host || (HOSTS[host]?.legacyHosts || []).includes(stamped);
|
|
99
|
+
}
|
|
91
100
|
const RANK = Object.freeze({ missing: 0, current: 1, newer: 2, outdated: 3, modified: 4, conflict: 5, error: 6 });
|
|
92
101
|
const MAX_SCAN_BYTES = 2 * 1024 * 1024;
|
|
93
102
|
|
|
@@ -259,6 +268,17 @@ export function createRulesetInstaller({
|
|
|
259
268
|
return view();
|
|
260
269
|
}
|
|
261
270
|
if (!isPlainObject(saved)) return view();
|
|
271
|
+
// A row kept under a host this SynaBun replaced (Gemini CLI → Antigravity) is the new host's row, marked
|
|
272
|
+
// `movedFrom` until reconcile has put the rules where the new host reads them (see reconcileHosts).
|
|
273
|
+
for (const [old, now] of Object.entries(RENAMED_HOSTS)) {
|
|
274
|
+
const hosts = isPlainObject(saved.hosts) ? saved.hosts : null;
|
|
275
|
+
if (hosts && isPlainObject(hosts[old]) && !isPlainObject(hosts[now])) {
|
|
276
|
+
saved.hosts = { ...hosts, [now]: { ...hosts[old], ...(hosts[old].managed === true ? { movedFrom: old } : {}) } };
|
|
277
|
+
delete saved.hosts[old];
|
|
278
|
+
}
|
|
279
|
+
if (Array.isArray(saved.offerAcked)) saved.offerAcked = saved.offerAcked.map((host) => (host === old ? now : host));
|
|
280
|
+
if (isPlainObject(saved.notice) && Array.isArray(saved.notice.hosts)) saved.notice = { ...saved.notice, hosts: unique(saved.notice.hosts.map((host) => (host === old ? now : host))) };
|
|
281
|
+
}
|
|
262
282
|
if (Number.isSafeInteger(saved.revision) && saved.revision > 0) state.revision = saved.revision;
|
|
263
283
|
if (typeof saved.autoUpdate === 'boolean') state.autoUpdate = saved.autoUpdate;
|
|
264
284
|
if (isPlainObject(saved.notice)) state.notice = saved.notice;
|
|
@@ -352,7 +372,7 @@ export function createRulesetInstaller({
|
|
|
352
372
|
|
|
353
373
|
// ── Where things live ──────────────────────────────────────────────────────
|
|
354
374
|
// Resolved the way the MCP toggle routes resolve them: Claude, Codex and
|
|
355
|
-
//
|
|
375
|
+
// Antigravity under the home directory, OpenCode in its configuration folder
|
|
356
376
|
// (OPENCODE_CONFIG_DIR, else XDG_CONFIG_HOME/opencode, else ~/.config/opencode),
|
|
357
377
|
// whose opencode.json OpenCode 2 reads (lib/opencode-config.js).
|
|
358
378
|
function layout() {
|
|
@@ -363,6 +383,9 @@ export function createRulesetInstaller({
|
|
|
363
383
|
return {
|
|
364
384
|
claudeDir: join(home, '.claude'),
|
|
365
385
|
codexHome: join(home, '.codex'),
|
|
386
|
+
// Antigravity's global customization root (rules, skills, mcp_config.json), and the folder above it, where
|
|
387
|
+
// Gemini CLI's GEMINI.md lives (Antigravity reads that file too).
|
|
388
|
+
antigravityDir: join(home, '.gemini', 'config'),
|
|
366
389
|
geminiDir: join(home, '.gemini'),
|
|
367
390
|
opencodeDir: dirname(opencodeConfig),
|
|
368
391
|
opencodeConfig,
|
|
@@ -416,7 +439,7 @@ export function createRulesetInstaller({
|
|
|
416
439
|
...codexAccounts().filter((account) => account.kind === 'file').map(({ path, dir }) => ({ path, dir })),
|
|
417
440
|
];
|
|
418
441
|
}
|
|
419
|
-
if (host === '
|
|
442
|
+
if (host === 'antigravity') return [{ path: join(where.antigravityDir, 'GEMINI.md'), dir: where.antigravityDir, legacyBlock: join(where.geminiDir, 'GEMINI.md') }];
|
|
420
443
|
return [];
|
|
421
444
|
}
|
|
422
445
|
function projectPaths() {
|
|
@@ -428,7 +451,10 @@ export function createRulesetInstaller({
|
|
|
428
451
|
const where = layout();
|
|
429
452
|
try {
|
|
430
453
|
if (host === 'claude') return !!JSON.parse(readFileSync(join(home, '.claude.json'), 'utf8'))?.mcpServers?.SynaBun;
|
|
431
|
-
if (host === '
|
|
454
|
+
if (host === 'antigravity') {
|
|
455
|
+
const text = readFileSync(join(where.antigravityDir, 'mcp_config.json'), 'utf8').replace(/^\uFEFF/, '');
|
|
456
|
+
return text.trim() ? !!JSON.parse(text)?.mcpServers?.SynaBun : false;
|
|
457
|
+
}
|
|
432
458
|
if (host === 'codex') return /^\[mcp_servers\.SynaBun\]/m.test(readFileSync(join(where.codexHome, 'config.toml'), 'utf8'));
|
|
433
459
|
if (host === 'opencode') return !!mcpServersOf(mergedOpencodeConfig(readOpencodeConfigDocuments(where.opencodeDir))).SynaBun;
|
|
434
460
|
} catch { /* no config, no registration */ }
|
|
@@ -481,7 +507,7 @@ export function createRulesetInstaller({
|
|
|
481
507
|
}
|
|
482
508
|
const block = blocks[0] || null;
|
|
483
509
|
// A stamp for another tool is not this host's copy, however untouched it is: never updated, never removed.
|
|
484
|
-
if (block && block.host
|
|
510
|
+
if (block && !ownStamp(host, block.host)) return { status: 'conflict', text, bytes, detail: `${foreignBlock(host, block, target.path)} Replace does not apply here: take that block out by hand, then install again.` };
|
|
485
511
|
if (HOSTS[host].carrier === 'file') {
|
|
486
512
|
if (normalise(text) === expected.block) return { status: 'current', text, bytes, block };
|
|
487
513
|
if (!block) return { status: 'modified', text, bytes, block, detail: `${target.path} has no SynaBun markers, so SynaBun treats it as your own file and never replaces it. Move or delete it, then install again.` };
|
|
@@ -756,6 +782,45 @@ export function createRulesetInstaller({
|
|
|
756
782
|
}
|
|
757
783
|
tally.changed = true;
|
|
758
784
|
}
|
|
785
|
+
/**
|
|
786
|
+
* The block an earlier SynaBun wrote where this host's tool also reads rules, under a name this host replaced
|
|
787
|
+
* (Gemini CLI's ~/.gemini/GEMINI.md, host=gemini, which Antigravity reads beside its own ~/.gemini/config/GEMINI.md):
|
|
788
|
+
* once the rules are where the new host keeps them, an untouched old block goes, or the tool would load them twice.
|
|
789
|
+
* An edited one is the user's: it stays, with a warning. A file that held nothing else and that SynaBun created is
|
|
790
|
+
* deleted; any other file keeps everything but the block. Never forced.
|
|
791
|
+
*/
|
|
792
|
+
function moveLegacyBlock(host, file, row, tally) {
|
|
793
|
+
let bytes;
|
|
794
|
+
try { bytes = readBytes(file); } catch { return; }
|
|
795
|
+
if (bytes === null) return;
|
|
796
|
+
const text = bytes.toString('utf8');
|
|
797
|
+
const { blocks, conflict } = findBlocks(text);
|
|
798
|
+
if (conflict) {
|
|
799
|
+
tally.warnings.push(`${conflict.message} in ${file}, which ${HOSTS[host].label} also reads, so SynaBun left it as it is: remove the old SynaBun marker lines by hand.`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const block = blocks[0];
|
|
803
|
+
if (!block || !ownStamp(host, block.host)) return;
|
|
804
|
+
if (!isPristine(block)) {
|
|
805
|
+
tally.warnings.push(`The SynaBun block an earlier version wrote in ${file} was edited, so it stays there. ${HOSTS[host].label} reads that file as well: take that block out by hand, or the rules are loaded twice.`);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const next = removeBlock(text);
|
|
809
|
+
const created = Array.isArray(row.created) && row.created.some((path) => samePath(path, file));
|
|
810
|
+
try {
|
|
811
|
+
tally.backup(backedUp(file, host, () => {
|
|
812
|
+
if (created && !isSymlink(file) && withoutBom(next).trim() === '') unlinkUnchanged(file, text);
|
|
813
|
+
else commit(file, next, text);
|
|
814
|
+
}));
|
|
815
|
+
} catch (error) {
|
|
816
|
+
if (error?.code !== 'CHANGED_UNDERNEATH') throw error;
|
|
817
|
+
tally.warnings.push(`${file} changed while SynaBun was moving its old rules out of it, so it was left as it is.`);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
tally.changed = true;
|
|
821
|
+
row.paths = row.paths.filter((path) => !samePath(path, file));
|
|
822
|
+
row.created = row.created.filter((path) => !samePath(path, file));
|
|
823
|
+
}
|
|
759
824
|
/**
|
|
760
825
|
* One carrier. Returns the status it ended in; writes only when the copy is
|
|
761
826
|
* missing, untouched and old, or (forced) one well-formed copy that was
|
|
@@ -951,7 +1016,7 @@ export function createRulesetInstaller({
|
|
|
951
1016
|
};
|
|
952
1017
|
for (const target of targets('claude')) add(target.legacyFile, 'claude', 'global');
|
|
953
1018
|
for (const target of targets('codex')) add(target.path, 'codex', 'global');
|
|
954
|
-
for (const target of targets('
|
|
1019
|
+
for (const target of targets('antigravity')) { add(target.path, 'antigravity', 'global'); add(target.legacyBlock, 'antigravity', 'global'); }
|
|
955
1020
|
for (const target of targets('opencode')) add(target.legacyFile, 'opencode', 'global');
|
|
956
1021
|
for (const dir of projectPaths()) {
|
|
957
1022
|
for (const [name, host] of PROJECT_FILES) add(join(dir, name), host, 'project');
|
|
@@ -1047,6 +1112,7 @@ export function createRulesetInstaller({
|
|
|
1047
1112
|
}
|
|
1048
1113
|
}
|
|
1049
1114
|
if (host === 'opencode' && !moveOpencode1Entry(row, tally)) addOpencodeEntry(row, tally);
|
|
1115
|
+
for (const target of list) if (target.legacyBlock) moveLegacyBlock(host, target.legacyBlock, row, tally);
|
|
1050
1116
|
|
|
1051
1117
|
Object.assign(row, {
|
|
1052
1118
|
managed: true,
|
|
@@ -1181,7 +1247,7 @@ export function createRulesetInstaller({
|
|
|
1181
1247
|
const created = new Set(row.created);
|
|
1182
1248
|
// Every place a copy can be: the host's carriers, the row's paths, and what an unfinished removal recorded.
|
|
1183
1249
|
const recorded = Array.isArray(row.removal?.paths) ? row.removal.paths.filter((path) => typeof path === 'string') : [];
|
|
1184
|
-
const paths = unique([...list.map((target) => target.path), ...row.paths, ...recorded]);
|
|
1250
|
+
const paths = unique([...list.map((target) => target.path), ...list.map((target) => target.legacyBlock).filter(Boolean), ...row.paths, ...recorded]);
|
|
1185
1251
|
/** Intent first: the paths about to go are saved before the first of them is touched. */
|
|
1186
1252
|
const recordIntent = (planned) => {
|
|
1187
1253
|
row.removal = { at: stamp(), error: INTERRUPTED, paths: unique(planned) };
|
|
@@ -1256,7 +1322,7 @@ export function createRulesetInstaller({
|
|
|
1256
1322
|
if (conflict) { refuse(path, 'conflict', `${conflict.message} in ${path}. SynaBun will not guess: remove the SynaBun marker lines by hand, then remove again.`); continue; }
|
|
1257
1323
|
const block = blocks[0];
|
|
1258
1324
|
if (!block) continue;
|
|
1259
|
-
if (block.host
|
|
1325
|
+
if (!ownStamp(host, block.host)) { refuse(path, 'conflict', `${foreignBlock(host, block, path)} Take that block out by hand if you no longer want it.`); continue; }
|
|
1260
1326
|
const pristine = isPristine(block);
|
|
1261
1327
|
if (!force && !pristine) {
|
|
1262
1328
|
left.push({ path, reason: 'edited', message: `The SynaBun block in ${path} was edited, so SynaBun left it in place and no longer manages it.` });
|
|
@@ -1413,6 +1479,19 @@ export function createRulesetInstaller({
|
|
|
1413
1479
|
// config.json does not list any more is not moved: the user took it out, which is the opt-out below.
|
|
1414
1480
|
const older = config && first.status !== 'missing' && !config.hasEntry ? opencode1Record(row) : null;
|
|
1415
1481
|
const moving = older !== null && opencode1Listed(older);
|
|
1482
|
+
if (first.status === 'missing' && row.movedFrom && isDir(primary.dir)) {
|
|
1483
|
+
// A host this SynaBun replaced (Gemini CLI → Antigravity) was managed: its rules go where the new host reads
|
|
1484
|
+
// them, and the old block goes (installHost moves it). Not an opt-out: nothing was deleted by the user.
|
|
1485
|
+
const moved = installHost(state, host, { force: false }, call);
|
|
1486
|
+
if (moved.ok) { delete row.movedFrom; updated.push(host); to = expected.version; from = from || row.installedVersion || null; }
|
|
1487
|
+
states[host] = hostStatus(host, state).state;
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1490
|
+
if (first.status === 'missing' && row.movedFrom) {
|
|
1491
|
+
// The new host's folder is not there yet (Antigravity not installed): nothing to move to; asked again later.
|
|
1492
|
+
states[host] = 'not-installed';
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
1416
1495
|
if (first.status === 'missing' || (config && !config.hasEntry && !moving)) {
|
|
1417
1496
|
// The user deleted it: an opt-out, never a reason to put it back. (A copy SynaBun's own removal took is
|
|
1418
1497
|
// not this case: that removal is on record and was dealt with above.)
|
|
@@ -18,7 +18,7 @@ import { normalise, sha12 } from './normalise.js';
|
|
|
18
18
|
|
|
19
19
|
export { normalise, sha12 };
|
|
20
20
|
|
|
21
|
-
export const RULESET_HOSTS = Object.freeze(['claude', 'codex', 'opencode', '
|
|
21
|
+
export const RULESET_HOSTS = Object.freeze(['claude', 'codex', 'opencode', 'antigravity', 'cursor']);
|
|
22
22
|
export const RULESETS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', 'templates', 'rulesets');
|
|
23
23
|
|
|
24
24
|
const HOST_SLOT = '<!-- host -->';
|
|
@@ -3,7 +3,7 @@ import { normalizeMemoryEvents, ingestMemoryEvents } from '../../mcp-server/dist
|
|
|
3
3
|
import { detectProject } from '../../mcp-server/dist/config.js';
|
|
4
4
|
/**
|
|
5
5
|
* Session Cache Builder — populates session_cache + session_fts for
|
|
6
|
-
* Claude Code, Codex CLI, and
|
|
6
|
+
* Claude Code, Codex CLI, OpenCode and Antigravity so the Resume surfaces can do
|
|
7
7
|
* full-body keyword search via FTS5.
|
|
8
8
|
*
|
|
9
9
|
* Per-provider rebuilders walk the provider's session storage, extract
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { parseLine } from './session-chunker.js';
|
|
26
26
|
import { listCodexSessionsFromAccounts } from './codex-session-catalog.js';
|
|
27
27
|
import { OPENCODE_SESSION_TABLES, hasOpencodeSessionTables, v2MessageText } from './opencode-db-schema.js';
|
|
28
|
+
import { listAntigravityConversations, parseAntigravityTranscript, readTranscriptText } from './antigravity/transcripts.js';
|
|
28
29
|
|
|
29
30
|
const MAX_BODY = 256 * 1024;
|
|
30
31
|
const MAX_MSG_CHARS = 8 * 1024;
|
|
@@ -493,6 +494,68 @@ export function rebuildOpencodeCache({ getOpencodeDb, onProgress } = {}) {
|
|
|
493
494
|
return { sessions: total, updated };
|
|
494
495
|
}
|
|
495
496
|
|
|
497
|
+
// --- Antigravity ---
|
|
498
|
+
|
|
499
|
+
// agy keeps one transcript.jsonl per conversation (lib/antigravity/transcripts.js). Every conversation it holds,
|
|
500
|
+
// SynaBun's brain and run homes included, is queued for memory capture; the user's own (the panel's and a terminal
|
|
501
|
+
// agy's, the only ones the panel can load again) also go into the Resume index, titled as Antigravity titles them.
|
|
502
|
+
// A conversation unchanged since the last pass (size, time, folder, title) is not read again. `only`: one
|
|
503
|
+
// conversation (a panel turn just ended), the others left as they are. Request/timer paths read at most
|
|
504
|
+
// MAX_BODY bytes per transcript: the index is bounded; full transcripts remain available to memory capture.
|
|
505
|
+
export function rebuildAntigravityCache({ stores = [], only = null, onProgress, list = listAntigravityConversations, read = readTranscriptText } = {}) {
|
|
506
|
+
const emit = onProgress || (() => {});
|
|
507
|
+
let conversations;
|
|
508
|
+
try { conversations = list(stores); } catch { return { sessions: 0, updated: 0 }; }
|
|
509
|
+
if (only) conversations = conversations.filter((c) => c.sessionId === only);
|
|
510
|
+
const listed = new Set(conversations.filter((c) => c.store === 'user').map((c) => c.sessionId));
|
|
511
|
+
const total = listed.size;
|
|
512
|
+
let updated = 0;
|
|
513
|
+
for (const c of conversations) {
|
|
514
|
+
try {
|
|
515
|
+
queueMemoryCapture({ host: 'antigravity', sessionId: c.sessionId, filePath: c.transcript, cwd: c.cwd || undefined, project: c.cwd ? detectProject(c.cwd) : 'global', size: c.size });
|
|
516
|
+
} catch { /* memory capture is best effort */ }
|
|
517
|
+
if (c.store !== 'user') continue;
|
|
518
|
+
const mtimeIso = new Date(c.mtimeMs).toISOString();
|
|
519
|
+
const existing = getSessionCacheEntry(c.sessionId, 'antigravity');
|
|
520
|
+
if (existing && !existing.deleted && Number(existing.file_size) === c.size && existing.file_mtime === mtimeIso
|
|
521
|
+
&& (existing.project_path || null) === (c.cwd || null) && (!c.title || existing.first_prompt === c.title)) continue;
|
|
522
|
+
const parsed = parseAntigravityTranscript(read(c.transcript, { maxBytes: MAX_BODY }));
|
|
523
|
+
let body = '';
|
|
524
|
+
for (const message of parsed.messages) {
|
|
525
|
+
if (message.role !== 'user' && message.role !== 'assistant') continue;
|
|
526
|
+
if (message.text) body = appendBody(body, message.text.slice(0, MAX_MSG_CHARS));
|
|
527
|
+
}
|
|
528
|
+
const firstPrompt = c.title || parsed.firstPrompt;
|
|
529
|
+
const project = c.cwd ? basename(c.cwd) : null;
|
|
530
|
+
upsertSessionCache({
|
|
531
|
+
session_id: c.sessionId,
|
|
532
|
+
provider: 'antigravity',
|
|
533
|
+
project,
|
|
534
|
+
project_path: c.cwd,
|
|
535
|
+
git_branch: null,
|
|
536
|
+
first_prompt: firstPrompt,
|
|
537
|
+
message_count: parsed.messageCount,
|
|
538
|
+
created: parsed.created,
|
|
539
|
+
modified: c.updatedAt || parsed.modified || mtimeIso,
|
|
540
|
+
file_path: c.transcript,
|
|
541
|
+
file_size: c.size,
|
|
542
|
+
file_mtime: mtimeIso,
|
|
543
|
+
body_size: body.length,
|
|
544
|
+
deleted: 0,
|
|
545
|
+
});
|
|
546
|
+
upsertSessionFts({ session_id: c.sessionId, provider: 'antigravity', project, first_prompt: firstPrompt, body });
|
|
547
|
+
updated++;
|
|
548
|
+
emit({ type: 'cache:session', provider: 'antigravity', sessionId: c.sessionId, done: updated, total });
|
|
549
|
+
}
|
|
550
|
+
// A conversation Antigravity no longer has (deleted there, or its folder cleared) leaves Resume.
|
|
551
|
+
if (!only) {
|
|
552
|
+
for (const cached of listSessionCache('antigravity', { limit: 100000 })) {
|
|
553
|
+
if (!listed.has(cached.session_id) && !cached.deleted) markSessionDeleted(cached.session_id, 'antigravity', true);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return { sessions: total, updated };
|
|
557
|
+
}
|
|
558
|
+
|
|
496
559
|
// --- Single-session refresh (for piggyback from session-indexer) ---
|
|
497
560
|
|
|
498
561
|
export function updateSessionCacheEntry(provider, sessionId, opts = {}) {
|
|
@@ -178,8 +178,12 @@ export function createSkillsCatalog({ roots, readHead, describe, ownership = ()
|
|
|
178
178
|
});
|
|
179
179
|
}
|
|
180
180
|
} else {
|
|
181
|
+
// Antigravity's rule for two skills of one name (its folder and a project's .agents/skills) is not known here.
|
|
182
|
+
const why = runtime === 'antigravity'
|
|
183
|
+
? 'Antigravity sees more than one skill of this name; which one it uses is not certain. Give them different names.'
|
|
184
|
+
: `${runtime === 'codex' ? 'Codex' : 'OpenCode'} lists every skill of this name; which one the model picks is not fixed. Give them different names.`;
|
|
181
185
|
for (const e of list) {
|
|
182
|
-
(e.collisions ||= []).push({ runtime, with: list.filter((x) => x !== e).map((x) => x.id), outcome: 'both-listed', why
|
|
186
|
+
(e.collisions ||= []).push({ runtime, with: list.filter((x) => x !== e).map((x) => x.id), outcome: 'both-listed', why });
|
|
183
187
|
}
|
|
184
188
|
}
|
|
185
189
|
}
|
|
@@ -82,7 +82,7 @@ const literal = (value, max) => { const s = String(value ?? ''); return JSON.str
|
|
|
82
82
|
*/
|
|
83
83
|
export function buildDraftingBrief({ jobId, request, sources = [] }) {
|
|
84
84
|
const name = request?.name || '';
|
|
85
|
-
const runtimes = [...new Set((request?.targets || []).map((t) => ({ claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', agents: 'Codex and OpenCode (shared folder)' }[t.runtime])).filter(Boolean))];
|
|
85
|
+
const runtimes = [...new Set((request?.targets || []).map((t) => ({ claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity', agents: 'Codex and OpenCode (shared folder)' }[t.runtime])).filter(Boolean))];
|
|
86
86
|
const prompt = request?.prompt ? quoteBounded(request.prompt, PROMPT_IN_BRIEF) : null;
|
|
87
87
|
const example = request?.example ? quoteBounded(request.example, EXAMPLE_IN_BRIEF) : null;
|
|
88
88
|
const lines = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ── AI skill creation: the sources a draft is made from (P3) ──
|
|
2
2
|
// What lib/skills/creation.js puts in a drafting job's workspace (`sources/`) before the worker starts: one session
|
|
3
|
-
// transcript of Claude Code, Codex or
|
|
3
|
+
// transcript of Claude Code, Codex, OpenCode or Antigravity, SynaBun memories, a read-only snapshot of a registered project, the
|
|
4
4
|
// files of an existing skill. Every file opens with a line saying it is untrusted data, secrets are taken out
|
|
5
5
|
// (lib/log-redact.js: before anything is cut short, so half a secret never survives), and every source has a
|
|
6
6
|
// budget: what does not fit is cut, and the manifest says so. A web page is read by the server with the SynaBun
|
|
@@ -91,6 +91,24 @@ export function opencodeRowsToMessages(rows) {
|
|
|
91
91
|
return out;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Antigravity's transcript as lib/antigravity/transcripts.js parses it (parseAntigravityTranscript().messages:
|
|
96
|
+
* { role: user | assistant | tool_result, text, tools }) → messages: the person's words, the model's answers and
|
|
97
|
+
* the tools it called (never its reasoning), and what the tools gave back.
|
|
98
|
+
*/
|
|
99
|
+
export function antigravityMessagesToMessages(messages) {
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const m of Array.isArray(messages) ? messages : []) {
|
|
102
|
+
if (!m || typeof m !== 'object') continue;
|
|
103
|
+
if (m.role === 'user' && typeof m.text === 'string' && m.text.trim()) out.push({ role: 'user', text: m.text });
|
|
104
|
+
else if (m.role === 'assistant') {
|
|
105
|
+
if (typeof m.text === 'string' && m.text.trim()) out.push({ role: 'assistant', text: m.text });
|
|
106
|
+
for (const t of Array.isArray(m.tools) ? m.tools : []) out.push({ role: 'tool', tool: String(t?.name || 'tool'), text: safeJson(t?.input) });
|
|
107
|
+
} else if (m.role === 'tool_result' && typeof m.text === 'string' && m.text.trim()) out.push({ role: 'tool_result', text: m.text, error: m.error === true });
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
function safeJson(value) {
|
|
95
113
|
try { return typeof value === 'string' ? value : JSON.stringify(value ?? {}); } catch { return ''; }
|
|
96
114
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// ── Installing library revisions into runtime folders ──
|
|
2
2
|
// An install puts one revision of a library skill (lib/skills/library.js) into one or more runtime folders as a
|
|
3
3
|
// native skill folder: Claude Code (<config dir>/skills or a project's .claude/skills), Codex ($CODEX_HOME/skills
|
|
4
|
-
// or a project's .agents/skills), OpenCode (<config>/opencode/skills or a project's
|
|
5
|
-
// shared ~/.agents/skills only when the owner
|
|
4
|
+
// or a project's .agents/skills, which Antigravity reads too), OpenCode (<config>/opencode/skills or a project's
|
|
5
|
+
// .opencode/skills), Antigravity (~/.gemini/config/skills), and the shared ~/.agents/skills only when the owner
|
|
6
|
+
// turned it on. It is two steps:
|
|
6
7
|
//
|
|
7
8
|
// plan(…) what each target would receive (the export of the revision for that runtime, with its digest),
|
|
8
9
|
// where, which runtimes see that folder (and what else of that name they already see), what is there
|
|
@@ -39,7 +40,7 @@ import { PORTABLE_NAME_RE, TARGET_PROFILE, getProfile, lintForProfile } from '..
|
|
|
39
40
|
import { analyzeResources, argumentDiagnostics } from '../../public/shared/skills/ss-resources.js';
|
|
40
41
|
import { isEditableText } from '../../public/shared/skills/ss-doc.js';
|
|
41
42
|
|
|
42
|
-
export const INSTALL_TARGETS = Object.freeze(['claude', 'codex', 'opencode', 'agents']);
|
|
43
|
+
export const INSTALL_TARGETS = Object.freeze(['claude', 'codex', 'opencode', 'antigravity', 'agents']);
|
|
43
44
|
export const INSTALLATIONS_SCHEMA = 2;
|
|
44
45
|
const MAX_INSTALLATIONS = 2000;
|
|
45
46
|
|
|
@@ -66,9 +67,11 @@ const REFRESH = {
|
|
|
66
67
|
opencode: (rootExisted, root) => (root?.family === 'claude'
|
|
67
68
|
? { kind: 'terminal-only', message: 'An OpenCode open in a terminal reads Claude Code\'s folder (new session); SynaBun\'s own OpenCode panel and an OpenCode Assistant do not: install for OpenCode for those.' }
|
|
68
69
|
: { kind: 'panel-reload', message: 'SynaBun\'s OpenCode panel lists it now; an OpenCode open in a terminal needs a new session.' }),
|
|
70
|
+
// Antigravity reads its skill folders again by itself: there is no command to run.
|
|
71
|
+
antigravity: () => ({ kind: 'automatic', message: 'Antigravity picks up the change by itself.' }),
|
|
69
72
|
};
|
|
70
73
|
|
|
71
|
-
const RUNTIME_LABEL = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', agents: 'the shared agents folder' };
|
|
74
|
+
const RUNTIME_LABEL = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode', antigravity: 'Antigravity', agents: 'the shared agents folder' };
|
|
72
75
|
|
|
73
76
|
/** A target as a request may name it, checked. */
|
|
74
77
|
export function normalizeTarget(t) {
|
|
@@ -78,6 +81,8 @@ export function normalizeTarget(t) {
|
|
|
78
81
|
const scope = t.scope === undefined ? 'user' : t.scope;
|
|
79
82
|
if (scope !== 'user' && scope !== 'project') throw new DeployError(400, 'A target\'s scope is user or project.', 'invalid_target');
|
|
80
83
|
if (runtime === 'agents' && scope !== 'user') throw new DeployError(400, 'The shared agents folder is a user folder; a project\'s .agents/skills is the codex target with scope project.', 'invalid_target');
|
|
84
|
+
// Antigravity has no project folder of its own: it reads the project's .agents/skills, the Codex project folder.
|
|
85
|
+
if (runtime === 'antigravity' && scope !== 'user') throw new DeployError(400, 'Antigravity reads a project\'s .agents/skills: that is the codex target with scope project.', 'invalid_target');
|
|
81
86
|
const accountId = t.accountId === undefined || t.accountId === null || t.accountId === '' ? 'default' : t.accountId;
|
|
82
87
|
if (typeof accountId !== 'string' || !/^[A-Za-z0-9_-]{1,64}$/.test(accountId)) throw new DeployError(400, 'Not an account id.', 'invalid_target');
|
|
83
88
|
if (scope === 'project' && (typeof t.projectPath !== 'string' || !t.projectPath)) throw new DeployError(400, 'A project target names its project (projectPath).', 'invalid_target');
|
|
@@ -139,7 +139,9 @@ export function draftConfinementSupport(provider, { platform = process.platform
|
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
141
|
if (name === 'opencode') return { ok: true, level: 'rules', note: 'OpenCode with its shell, web, subagent and skill tools switched off, no MCP server, and every edit answered inside its folder only.' };
|
|
142
|
-
|
|
142
|
+
// Antigravity (a worker of the Assistant) has no drafting confinement in SynaBun: a drafting run never starts unconfined.
|
|
143
|
+
if (name === 'antigravity') return { ok: false, reason: 'Antigravity cannot be held to a drafting folder: skill drafting runs on Claude Code, Codex and OpenCode.' };
|
|
144
|
+
return { ok: false, reason: `${name || 'That provider'} cannot be held to a drafting folder: skill drafting runs on Claude Code, Codex and OpenCode.` };
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
/** The MCP server names a Codex config.toml declares ([mcp_servers.NAME] tables), for disabling them one by one. */
|
|
@@ -96,7 +96,9 @@ export function evalConfinementSupport(provider, { platform = process.platform }
|
|
|
96
96
|
if (name === 'claude-code') return { ok: true, level: 'strict', shell: platform === 'darwin' ? 'sandbox' : 'never', optIn: false, note: 'Read, Write, Edit, Glob, Grep and Skill inside its folder; no settings file, hook or plugin but the skill under test; a shell only under the evaluation\'s macOS sandbox (no network, no home folder); no web, no MCP tools, no subagents.' };
|
|
97
97
|
if (name === 'codex') return { ok: true, level: 'sandbox', shell: 'own-sandbox', optIn: true, note: `Codex keeps its shell under its ${platform === 'win32' ? 'Windows' : platform === 'darwin' ? 'Seatbelt' : 'Linux'} sandbox: writes inside its folder only, no network, no web search, no MCP server; its shell can read files anywhere on this computer, and what it reads can reach its model provider.` };
|
|
98
98
|
if (name === 'opencode') return { ok: true, level: 'rules', shell: platform === 'darwin' ? 'sandbox' : 'never', optIn: false, note: 'OpenCode with no project configuration or plugin of the folder, its web, subagent and todo tools off, no MCP server, reads and edits inside its folder only; a shell only under the evaluation\'s macOS sandbox (no network, no home folder).' };
|
|
99
|
-
|
|
99
|
+
// Antigravity (a worker of the Assistant) has no evaluation confinement in SynaBun: an evaluation never runs unconfined.
|
|
100
|
+
if (name === 'antigravity') return { ok: false, reason: 'Antigravity cannot be held to an evaluation folder: skill evaluations run on Claude Code, Codex and OpenCode.' };
|
|
101
|
+
return { ok: false, reason: `${name || 'That provider'} cannot be held to an evaluation folder: skill evaluations run on Claude Code, Codex and OpenCode.` };
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
/**
|
|
@@ -60,6 +60,22 @@ export function toolCallsOf(payload = {}) {
|
|
|
60
60
|
}
|
|
61
61
|
return out;
|
|
62
62
|
}
|
|
63
|
+
if (payload.provider === 'antigravity') {
|
|
64
|
+
// An ACP tool call (session/update tool_call, then tool_call_update with its status). Antigravity names its
|
|
65
|
+
// tool (read_file, run_command, call_mcp_tool …) and its arguments in its own words: the path a read names
|
|
66
|
+
// and a command's line are kept under `path` / `command`; an MCP call is <server>.<tool>.
|
|
67
|
+
if ((eventType === 'tool_call' || eventType === 'tool_call_update') && event.toolCallId) {
|
|
68
|
+
const raw = isObject(event.rawInput) ? event.rawInput : {};
|
|
69
|
+
const mcp = String(event.name || '') === 'call_mcp_tool' && raw.ServerName && raw.ToolName ? `${raw.ServerName}.${raw.ToolName}` : '';
|
|
70
|
+
const location = Array.isArray(event.locations) ? event.locations.find((l) => typeof l?.path === 'string' && l.path)?.path : '';
|
|
71
|
+
const path = [raw.AbsolutePath, raw.Path, raw.TargetFile, raw.path, raw.file_path, location].find((v) => typeof v === 'string' && v) || '';
|
|
72
|
+
const command = typeof raw.CommandLine === 'string' ? raw.CommandLine : typeof raw.command === 'string' ? raw.command : '';
|
|
73
|
+
const input = { ...(path ? { path } : {}), ...(command ? { command } : {}) };
|
|
74
|
+
const status = String(event.status || '');
|
|
75
|
+
out.push({ id: String(event.toolCallId), tool: mcp || String(event.name || event.kind || ''), input, phase: status, ok: status === 'completed' ? true : status === 'failed' ? false : null });
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
63
79
|
return out;
|
|
64
80
|
}
|
|
65
81
|
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
// - Claude Code: `disable-model-invocation: true` / `user-invocable: false` in SKILL.md (that key only);
|
|
8
8
|
// - Codex: `policy.allow_implicit_invocation: false` in agents/openai.yaml (made when there is none; otherwise
|
|
9
9
|
// that one value is set and every other value, comment and order is kept);
|
|
10
|
-
// - OpenCode: metadata `opencode/autoinvoke: "false"` in SKILL.md (the metadata key only)
|
|
10
|
+
// - OpenCode: metadata `opencode/autoinvoke: "false"` in SKILL.md (the metadata key only);
|
|
11
|
+
// - Antigravity: `disable-slash-command: true` in SKILL.md (that key only) for a skill the person may not start by
|
|
12
|
+
// name. Antigravity has no setting that keeps its model from starting a skill: an export of an explicit-only
|
|
13
|
+
// skill for it is refused (L-08-invocation-unrepresentable), never written wider.
|
|
11
14
|
// A Codex export also carries the harness metadata Codex shows in its skill picker (agents/openai.yaml
|
|
12
15
|
// `interface`: display_name, short_description, default_prompt, from the skill's own name and description; the
|
|
13
16
|
// 02-standards-runtimes report, §6.3). A sidecar that is already there keeps every value it has: only the interface
|
|
@@ -140,7 +143,7 @@ export function buildExport({ files, dirs = [] }, profileId) {
|
|
|
140
143
|
}
|
|
141
144
|
const next = patchHead(headText, changes);
|
|
142
145
|
if (next === null) {
|
|
143
|
-
diagnostics.push({ ruleId: 'L-08-adapt-failed', severity: 'error', message: `The SKILL.md frontmatter could not be changed to keep ${profile?.title || profileId} from ${t.key === 'user-invocable' ? 'showing it to the person' : 'starting it on its own'}: fix the YAML first.`, file: 'SKILL.md', line: null });
|
|
146
|
+
diagnostics.push({ ruleId: 'L-08-adapt-failed', severity: 'error', message: `The SKILL.md frontmatter could not be changed to keep ${profile?.title || profileId} from ${t.key === 'user-invocable' || t.key === 'disable-slash-command' ? 'showing it to the person' : 'starting it on its own'}: fix the YAML first.`, file: 'SKILL.md', line: null });
|
|
144
147
|
continue;
|
|
145
148
|
}
|
|
146
149
|
headText = next;
|
|
@@ -83,7 +83,8 @@
|
|
|
83
83
|
],
|
|
84
84
|
"skillMd": [
|
|
85
85
|
"78ebb9f149ff",
|
|
86
|
-
"
|
|
86
|
+
"870138a74151",
|
|
87
|
+
"bb2df5d23b7d"
|
|
87
88
|
],
|
|
88
89
|
"firstCommit": "ae9acec31b71",
|
|
89
90
|
"firstDate": "2026-10-23"
|
|
@@ -96,7 +97,8 @@
|
|
|
96
97
|
],
|
|
97
98
|
"skillMd": [
|
|
98
99
|
"78ebb9f149ff",
|
|
99
|
-
"
|
|
100
|
+
"870138a74151",
|
|
101
|
+
"bb2df5d23b7d"
|
|
100
102
|
],
|
|
101
103
|
"firstCommit": "ae9acec31b71",
|
|
102
104
|
"firstDate": "2026-10-23"
|
|
@@ -147,6 +149,7 @@
|
|
|
147
149
|
"g3"
|
|
148
150
|
],
|
|
149
151
|
"skillMd": [
|
|
152
|
+
"68a8323dfd0d",
|
|
150
153
|
"8d262753dd16",
|
|
151
154
|
"a9ac8e43510e",
|
|
152
155
|
"ea38ed171e73",
|
|
@@ -163,6 +166,7 @@
|
|
|
163
166
|
"g3"
|
|
164
167
|
],
|
|
165
168
|
"skillMd": [
|
|
169
|
+
"68a8323dfd0d",
|
|
166
170
|
"8d262753dd16",
|
|
167
171
|
"a9ac8e43510e",
|
|
168
172
|
"ea38ed171e73",
|
|
@@ -182,7 +186,8 @@
|
|
|
182
186
|
"1019fc5ef755",
|
|
183
187
|
"2a5ae45388b6",
|
|
184
188
|
"740fdb3985fd",
|
|
185
|
-
"b5380300a1f5"
|
|
189
|
+
"b5380300a1f5",
|
|
190
|
+
"f6a41a558efe"
|
|
186
191
|
],
|
|
187
192
|
"firstCommit": "636f18afbe6b",
|
|
188
193
|
"firstDate": "2026-07-21"
|
|
@@ -198,7 +203,8 @@
|
|
|
198
203
|
"1019fc5ef755",
|
|
199
204
|
"2a5ae45388b6",
|
|
200
205
|
"740fdb3985fd",
|
|
201
|
-
"b5380300a1f5"
|
|
206
|
+
"b5380300a1f5",
|
|
207
|
+
"f6a41a558efe"
|
|
202
208
|
],
|
|
203
209
|
"firstCommit": "636f18afbe6b",
|
|
204
210
|
"firstDate": "2026-07-21"
|
|
@@ -246,6 +252,30 @@
|
|
|
246
252
|
"firstCommit": "ae9acec31b71",
|
|
247
253
|
"firstDate": "2026-10-23"
|
|
248
254
|
},
|
|
255
|
+
{
|
|
256
|
+
"digest": "b052a36caf1bbbebe994c5b8c029696306c64824837ae632968a94f0d97a8888",
|
|
257
|
+
"sep": "any",
|
|
258
|
+
"generators": [
|
|
259
|
+
"g3"
|
|
260
|
+
],
|
|
261
|
+
"skillMd": [
|
|
262
|
+
"870138a74151"
|
|
263
|
+
],
|
|
264
|
+
"firstCommit": "0a8f77317114",
|
|
265
|
+
"firstDate": "2026-10-25"
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"digest": "7da57892ab385c2668a1afdb2da670ddcc732927103812c46a635eb476d34781",
|
|
269
|
+
"sep": "any",
|
|
270
|
+
"generators": [
|
|
271
|
+
"g3"
|
|
272
|
+
],
|
|
273
|
+
"skillMd": [
|
|
274
|
+
"bb2df5d23b7d"
|
|
275
|
+
],
|
|
276
|
+
"firstCommit": "52d9d1712c34",
|
|
277
|
+
"firstDate": "2026-10-31"
|
|
278
|
+
},
|
|
249
279
|
{
|
|
250
280
|
"digest": "4b619a432fc38e95310399f2d59a0f1501a3984b67007febd8ff92634afffa8f",
|
|
251
281
|
"sep": "any",
|
|
@@ -380,6 +410,30 @@
|
|
|
380
410
|
],
|
|
381
411
|
"firstCommit": "6776e42b885c",
|
|
382
412
|
"firstDate": "2026-10-23"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"digest": "6765902e8854b8d4a859e96aa9233ba61cc0fe6c33d197b9815c01668a92612c",
|
|
416
|
+
"sep": "any",
|
|
417
|
+
"generators": [
|
|
418
|
+
"g3"
|
|
419
|
+
],
|
|
420
|
+
"skillMd": [
|
|
421
|
+
"68a8323dfd0d"
|
|
422
|
+
],
|
|
423
|
+
"firstCommit": "2be15e00f29b",
|
|
424
|
+
"firstDate": "2026-11-02"
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"digest": "9f95ee55bf80bd711b191e1e94c74bd2808bcf82291b02bb20f52cab91f14c3b",
|
|
428
|
+
"sep": "any",
|
|
429
|
+
"generators": [
|
|
430
|
+
"g3"
|
|
431
|
+
],
|
|
432
|
+
"skillMd": [
|
|
433
|
+
"f6a41a558efe"
|
|
434
|
+
],
|
|
435
|
+
"firstCommit": "20ad54882a26",
|
|
436
|
+
"firstDate": "2026-11-03"
|
|
383
437
|
}
|
|
384
438
|
]
|
|
385
439
|
}
|
|
@@ -272,7 +272,7 @@ export function createSkillInstaller({ dataDir, now = () => new Date().toISOStri
|
|
|
272
272
|
/**
|
|
273
273
|
* Install `payload` at `destination` (a plain name in `root`).
|
|
274
274
|
* @param {object} o
|
|
275
|
-
* @param {'claude'|'codex'|'opencode'} o.target @param {string} o.name @param {string} o.root @param {string} o.destination
|
|
275
|
+
* @param {'claude'|'codex'|'opencode'|'antigravity'|'agents'} o.target @param {string} o.name @param {string} o.root @param {string} o.destination
|
|
276
276
|
* @param {object} o.payload from payloadFromFolder / payloadFromText
|
|
277
277
|
* @param {object} [o.source] where the payload came from ({ kind: 'bundled', name, digest })
|
|
278
278
|
* @param {{ expectedDestinationDigest?: string }} [o.decision] the person confirmed replacing what is there now
|