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
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: what can be done about each health problem
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// Every health item the Library shows (sx-library.js healthOf), and the few problems only this module finds (two
|
|
6
|
+
// library records of one skill, an installation older than the skill's latest version, folders the catalog could not
|
|
7
|
+
// read), get a resolution path here:
|
|
8
|
+
// - `auto`: one press, no choice to make, and nothing lost (frontmatter added to a SKILL.md that has none, a
|
|
9
|
+
// description taken from its own first paragraph, a second library record archived: Restore brings it back);
|
|
10
|
+
// - `guided`: a choice the owner makes (which copy to keep, rename or leave, update or keep mine): the panel says
|
|
11
|
+
// what each choice does and opens the place where it is made;
|
|
12
|
+
// - `dismiss`: the owner has seen it and it is fine as it is; kept per item and per problem (its fingerprint), so a
|
|
13
|
+
// problem that changes (another count, another runtime) is said again.
|
|
14
|
+
// Writes are never made here: the view (sx-fixes-view.js) sends them through the Studio's API (X-SynaBun-UI), and an
|
|
15
|
+
// install location is changed only through the existing install / remove flows. No DOM and no imports but the lint's
|
|
16
|
+
// name rule.
|
|
17
|
+
|
|
18
|
+
import { NAME_MAX } from '../ss-lint.js';
|
|
19
|
+
|
|
20
|
+
export const DISMISSED_KEY = 'skills-studio-dismissed-issues';
|
|
21
|
+
|
|
22
|
+
/** Every issue kind, with its default resolution mode (a kind may still be guided for one row: see issuesOf). */
|
|
23
|
+
export const ISSUE_KINDS = Object.freeze({
|
|
24
|
+
'not-text': 'guided',
|
|
25
|
+
invalid: 'guided',
|
|
26
|
+
warnings: 'guided',
|
|
27
|
+
update: 'guided',
|
|
28
|
+
shadowed: 'guided',
|
|
29
|
+
twice: 'guided',
|
|
30
|
+
'same-description': 'guided',
|
|
31
|
+
bridge: 'guided',
|
|
32
|
+
'duplicate-record': 'auto',
|
|
33
|
+
'stale-install': 'guided',
|
|
34
|
+
unreadable: 'guided',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/** The text rules a safe fix knows how to repair (sx-fixes-view reads the artifact's diagnostics to find them). */
|
|
38
|
+
export const TEXT_FIX_RULES = Object.freeze(['fm-missing', 'description-missing']);
|
|
39
|
+
|
|
40
|
+
const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
41
|
+
const lower = (v) => (typeof v === 'string' ? v.toLowerCase() : '');
|
|
42
|
+
|
|
43
|
+
/** What makes one problem of one row itself: a different count or runtime is a different problem. */
|
|
44
|
+
export function fingerprintOf(rowKey, h) {
|
|
45
|
+
const detail = h.code === 'invalid' || h.code === 'warnings' ? String(h.count ?? '')
|
|
46
|
+
: h.code === 'shadowed' || h.code === 'twice' ? String(h.runtime || '')
|
|
47
|
+
: h.code === 'same-description' ? (h.names || []).map(lower).sort().join(',')
|
|
48
|
+
: h.code === 'bridge' ? (h.runtimes || []).slice().sort().join(',')
|
|
49
|
+
: h.code === 'duplicate-record' ? String(h.of || '')
|
|
50
|
+
: h.code === 'stale-install' ? String(h.latest || '')
|
|
51
|
+
: h.code === 'unreadable' ? String(h.count ?? '')
|
|
52
|
+
: '';
|
|
53
|
+
return `${rowKey}|${h.code}|${detail}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The dismissed problems, as a set of fingerprints, from what storage kept (anything unreadable is nothing). */
|
|
57
|
+
export function readDismissed(raw) {
|
|
58
|
+
try {
|
|
59
|
+
const o = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
60
|
+
return new Set(o && typeof o === 'object' && !Array.isArray(o) ? Object.keys(o) : []);
|
|
61
|
+
} catch { return new Set(); }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Storage text with these fingerprints added (each with when), at most 500 kept (the oldest go first). */
|
|
65
|
+
export function addDismissed(raw, fingerprints, now = new Date().toISOString()) {
|
|
66
|
+
let o = {};
|
|
67
|
+
try { const p = typeof raw === 'string' ? JSON.parse(raw) : raw; if (p && typeof p === 'object' && !Array.isArray(p)) o = p; } catch { o = {}; }
|
|
68
|
+
for (const f of fingerprints) o[f] = now;
|
|
69
|
+
const kept = Object.entries(o).sort((a, b) => String(b[1]).localeCompare(String(a[1]))).slice(0, 500);
|
|
70
|
+
return JSON.stringify(Object.fromEntries(kept));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Storage text without these fingerprints (Show again). */
|
|
74
|
+
export function removeDismissed(raw, fingerprints) {
|
|
75
|
+
let o = {};
|
|
76
|
+
try { const p = typeof raw === 'string' ? JSON.parse(raw) : raw; if (p && typeof p === 'object' && !Array.isArray(p)) o = p; } catch { o = {}; }
|
|
77
|
+
for (const f of fingerprints) delete o[f];
|
|
78
|
+
return JSON.stringify(o);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const SEVERITY_RANK = { err: 0, warn: 1, info: 2, off: 3, ok: 4 };
|
|
82
|
+
const DUP_WEIGHT = 250;
|
|
83
|
+
const STALE_WEIGHT = 60;
|
|
84
|
+
|
|
85
|
+
/** Who keeps the name when two library records hold one skill: installed, adopted, more versions, then the oldest. */
|
|
86
|
+
function originalFirst(a, b) {
|
|
87
|
+
const ia = (a.skill.installations || []).length; const ib = (b.skill.installations || []).length;
|
|
88
|
+
if (!!ia !== !!ib) return ib - ia;
|
|
89
|
+
const oa = a.skill.origin?.kind === 'adopted' ? 0 : 1; const ob = b.skill.origin?.kind === 'adopted' ? 0 : 1;
|
|
90
|
+
if (oa !== ob) return oa - ob;
|
|
91
|
+
if ((b.skill.revisions || 0) !== (a.skill.revisions || 0)) return (b.skill.revisions || 0) - (a.skill.revisions || 0);
|
|
92
|
+
return String(a.skill.createdAt || '').localeCompare(String(b.skill.createdAt || '')) || a.key.localeCompare(b.key);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Library records that are one skill twice (the same name, neither shipped by SynaBun): every record but the one that
|
|
97
|
+
* keeps the name, with `safe` when archiving it loses nothing (not installed, no open review, and no version or the
|
|
98
|
+
* very content of the original's latest). Archived, a record comes back with Restore.
|
|
99
|
+
* @returns {{ row: object, original: object, safe: boolean, why: string }[]}
|
|
100
|
+
*/
|
|
101
|
+
export function duplicateRecords(rows) {
|
|
102
|
+
const groups = new Map();
|
|
103
|
+
for (const row of rows) {
|
|
104
|
+
const s = row.skill;
|
|
105
|
+
if (!s?.skillId || s.origin?.kind === 'bundled' || !s.name) continue;
|
|
106
|
+
const k = lower(s.name);
|
|
107
|
+
if (!groups.has(k)) groups.set(k, []);
|
|
108
|
+
groups.get(k).push(row);
|
|
109
|
+
}
|
|
110
|
+
const out = [];
|
|
111
|
+
for (const list of groups.values()) {
|
|
112
|
+
if (list.length < 2) continue;
|
|
113
|
+
const sorted = list.slice().sort(originalFirst);
|
|
114
|
+
const original = sorted[0];
|
|
115
|
+
for (const row of sorted.slice(1)) {
|
|
116
|
+
const s = row.skill;
|
|
117
|
+
const why = (s.installations || []).length ? 'installed'
|
|
118
|
+
: row.review ? 'review'
|
|
119
|
+
: s.revisions && !(s.latest?.contentDigest && s.latest.contentDigest === original.skill.latest?.contentDigest) ? 'different'
|
|
120
|
+
: '';
|
|
121
|
+
out.push({ row, original, safe: !why, why });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The rows with what only this module finds added to their health (a duplicate record, an installation older than the
|
|
129
|
+
* latest version), the dismissed problems taken out, and attention counted again. Returns the same rows (changed).
|
|
130
|
+
* @param {object[]} rows built by sx-library.js buildRows
|
|
131
|
+
* @param {Set<string>} [dismissed] fingerprints
|
|
132
|
+
*/
|
|
133
|
+
export function applyFixState(rows, dismissed = new Set()) {
|
|
134
|
+
for (const d of duplicateRecords(rows)) {
|
|
135
|
+
if (!d.row.health.some((h) => h.code === 'duplicate-record')) d.row.health.push({ code: 'duplicate-record', sev: 'warn', of: d.original.key, name: d.original.label || d.original.name || '', safe: d.safe, why: d.why });
|
|
136
|
+
}
|
|
137
|
+
for (const row of rows) {
|
|
138
|
+
const s = row.skill;
|
|
139
|
+
const latest = s?.pointers?.latest || s?.latest?.revisionId || null;
|
|
140
|
+
if (latest && s.origin?.kind !== 'bundled' && !row.health.some((h) => h.code === 'stale-install')) {
|
|
141
|
+
const older = (s.installations || []).filter((i) => i.sourceRevisionId && i.sourceRevisionId !== latest);
|
|
142
|
+
if (older.length) row.health.push({ code: 'stale-install', sev: 'info', latest, count: older.length });
|
|
143
|
+
}
|
|
144
|
+
if (dismissed.size) row.health = row.health.filter((h) => h.sev === 'ok' || h.sev === 'off' || !dismissed.has(fingerprintOf(row.key, h)));
|
|
145
|
+
row.health = row.health.filter((h) => h.code !== 'ok');
|
|
146
|
+
if (!row.health.some((h) => h.sev === 'err' || h.sev === 'warn' || h.sev === 'info')) row.health.push({ code: 'ok', sev: 'ok' });
|
|
147
|
+
row.health.sort((a, b) => SEVERITY_RANK[a.sev] - SEVERITY_RANK[b.sev]);
|
|
148
|
+
const extra = row.health.reduce((n, h) => n + (h.code === 'duplicate-record' ? DUP_WEIGHT : h.code === 'stale-install' ? STALE_WEIGHT : 0), 0);
|
|
149
|
+
const base = dismissed.size ? recount(row) : row.attention || 0;
|
|
150
|
+
row.attention = base + extra;
|
|
151
|
+
row.needsAttention = row.attention > 0;
|
|
152
|
+
}
|
|
153
|
+
return rows;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// sx-library.js ATTENTION, for a row whose health lost dismissed items.
|
|
157
|
+
const ATTENTION = { 'not-text': 1000, invalid: 1000, update: 400, shadowed: 300, twice: 300, 'same-description': 200, bridge: 150, warnings: 100, review: 80, draft: 50 };
|
|
158
|
+
function recount(row) {
|
|
159
|
+
return row.health.reduce((n, h) => n + (ATTENTION[h.code] || 0) * (h.code === 'invalid' || h.code === 'warnings' ? Math.max(1, Math.min(9, Number(h.count) || 0)) : 1), 0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The issues of one row: each health item that is a problem, with what can be done about it.
|
|
164
|
+
* `can` says what the row allows: `edit` (its text can be changed here), `open` (it has a workspace), `archive` (the
|
|
165
|
+
* duplicate's record can leave the library), `dismiss` (not a guest).
|
|
166
|
+
* Actions: 'auto-text' (repair the text: which rules is learnt from the artifact's diagnostics), 'archive-duplicate',
|
|
167
|
+
* 'choose-copy', 'open-edit', 'open-publish', 'open-update', 'keep-mine', 'open-review', 'move-bridges',
|
|
168
|
+
* 'rescan', 'delete', 'dismiss'.
|
|
169
|
+
* @returns {{ code: string, sev: string, mode: 'auto'|'guided', fingerprint: string, health: object, actions: string[] }[]}
|
|
170
|
+
*/
|
|
171
|
+
export function issuesOf(row, can = {}) {
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const h of row.health || []) {
|
|
174
|
+
if (!(h.code in ISSUE_KINDS)) continue;
|
|
175
|
+
const actions = [];
|
|
176
|
+
let mode = ISSUE_KINDS[h.code];
|
|
177
|
+
switch (h.code) {
|
|
178
|
+
case 'not-text':
|
|
179
|
+
if (can.open) actions.push('open-edit');
|
|
180
|
+
if (can.delete) actions.push('delete');
|
|
181
|
+
break;
|
|
182
|
+
case 'invalid':
|
|
183
|
+
case 'warnings':
|
|
184
|
+
// Whether a repair is safe is known once the text is read (the panel asks): until then it is guided.
|
|
185
|
+
if (can.edit) actions.push('auto-text');
|
|
186
|
+
if (can.open) actions.push('open-edit');
|
|
187
|
+
break;
|
|
188
|
+
case 'update':
|
|
189
|
+
if (can.open) actions.push('open-update', 'keep-mine');
|
|
190
|
+
break;
|
|
191
|
+
case 'shadowed':
|
|
192
|
+
case 'twice':
|
|
193
|
+
case 'same-description':
|
|
194
|
+
if (can.open) actions.push('open-edit');
|
|
195
|
+
break;
|
|
196
|
+
case 'bridge':
|
|
197
|
+
if (can.dismiss) actions.push('move-bridges');
|
|
198
|
+
break;
|
|
199
|
+
case 'duplicate-record':
|
|
200
|
+
if (!h.safe) mode = 'guided';
|
|
201
|
+
if (can.archive) actions.push(h.safe ? 'archive-duplicate' : 'choose-copy');
|
|
202
|
+
if (can.open) actions.push('open-edit');
|
|
203
|
+
break;
|
|
204
|
+
case 'stale-install':
|
|
205
|
+
if (can.open) actions.push('open-publish');
|
|
206
|
+
break;
|
|
207
|
+
default: break;
|
|
208
|
+
}
|
|
209
|
+
if (can.dismiss) actions.push('dismiss');
|
|
210
|
+
out.push({ code: h.code, sev: h.sev, mode, fingerprint: fingerprintOf(row.key, h), health: h, actions });
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Text repairs ──
|
|
216
|
+
|
|
217
|
+
const INVISIBLE = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f----]/g;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* A description taken from a body's first paragraph of prose (not a heading, a list, a table, a quote or code),
|
|
221
|
+
* markdown marks removed, on one line, at most 280 characters cut at a word; null when there is none of 20 or more.
|
|
222
|
+
*/
|
|
223
|
+
export function descriptionFromBody(body) {
|
|
224
|
+
const lines = String(body || '').replace(/\r\n?/g, '\n').split('\n');
|
|
225
|
+
let inCode = false;
|
|
226
|
+
let para = [];
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
const tr = line.trim();
|
|
229
|
+
if (/^(```|~~~)/.test(tr)) { inCode = !inCode; if (para.length) break; continue; }
|
|
230
|
+
if (inCode) continue;
|
|
231
|
+
if (!tr) { if (para.length) break; continue; }
|
|
232
|
+
if (/^(#|[-*+]\s|\d+[.)]\s|\||>|<|---|===)/.test(tr)) { if (para.length) break; continue; }
|
|
233
|
+
para.push(tr);
|
|
234
|
+
}
|
|
235
|
+
let text = para.join(' ')
|
|
236
|
+
.replace(INVISIBLE, '')
|
|
237
|
+
.replace(/!\[[^\]]*\]\([^)]*\)/g, '')
|
|
238
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')
|
|
239
|
+
.replace(/(\*\*|__|\*|_|`)(\S(?:.*?\S)?)\1/g, '$2')
|
|
240
|
+
.replace(/\s+/g, ' ')
|
|
241
|
+
.trim();
|
|
242
|
+
if (text.length < 20) return null;
|
|
243
|
+
if (text.length > 280) {
|
|
244
|
+
const cut = text.slice(0, 280);
|
|
245
|
+
const sentence = cut.lastIndexOf('. ');
|
|
246
|
+
text = sentence >= 120 ? cut.slice(0, sentence + 1) : `${cut.slice(0, cut.lastIndexOf(' ') > 200 ? cut.lastIndexOf(' ') : 280).trimEnd()}…`;
|
|
247
|
+
}
|
|
248
|
+
return text;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** A YAML scalar that reads back as exactly this text (a JSON string is a YAML double-quoted scalar). */
|
|
252
|
+
const yamlString = (s) => JSON.stringify(String(s));
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The safe repair of a skill's or an agent's head file, from the problems its check found: frontmatter added to a file
|
|
256
|
+
* that has none (name from the folder when it is a valid name, description from its first paragraph), or a description
|
|
257
|
+
* added to frontmatter that lacks one. Line endings and a BOM are kept; nothing else changes. null when there is
|
|
258
|
+
* nothing safe to do (an agent needs both a name and a description; a description that cannot be taken from the text).
|
|
259
|
+
* @param {object} o
|
|
260
|
+
* @param {string} o.raw the file as read
|
|
261
|
+
* @param {{ ruleId: string }[]} o.diagnostics what the server's check said (GET artifact `diagnostics`)
|
|
262
|
+
* @param {'skill'|'agent'|'command'} o.type
|
|
263
|
+
* @param {string} [o.folderName]
|
|
264
|
+
* @returns {{ text: string, changes: { ruleId: string, added: string }[] } | null}
|
|
265
|
+
*/
|
|
266
|
+
export function planTextFix({ raw, diagnostics = [], type, folderName = '' }) {
|
|
267
|
+
if (typeof raw !== 'string' || type === 'command') return null;
|
|
268
|
+
const rules = new Set((diagnostics || []).map((d) => d?.ruleId));
|
|
269
|
+
const eol = raw.includes('\r\n') ? '\r\n' : '\n';
|
|
270
|
+
const bom = raw.startsWith('') ? '' : '';
|
|
271
|
+
const text = bom ? raw.slice(1) : raw;
|
|
272
|
+
if (rules.has('fm-missing')) {
|
|
273
|
+
if (/^---[ \t]*\r?\n/.test(text)) return null; // an unterminated block is the owner's to read
|
|
274
|
+
const name = NAME_RE.test(folderName) && folderName.length <= NAME_MAX ? folderName : '';
|
|
275
|
+
const description = descriptionFromBody(text);
|
|
276
|
+
if (type === 'agent' && (!name || !description)) return null;
|
|
277
|
+
if (!name && !description) return null;
|
|
278
|
+
const lines = ['---', ...(name ? [`name: ${name}`] : []), ...(description ? [`description: ${yamlString(description)}`] : []), '---', ''];
|
|
279
|
+
const changes = [];
|
|
280
|
+
if (name) changes.push({ ruleId: 'fm-missing', added: `name: ${name}` });
|
|
281
|
+
if (description) changes.push({ ruleId: 'fm-missing', added: `description: ${yamlString(description)}` });
|
|
282
|
+
return { text: `${bom}${lines.join(eol)}${text}`, changes };
|
|
283
|
+
}
|
|
284
|
+
if (rules.has('description-missing') && type === 'skill') {
|
|
285
|
+
const m = /^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/.exec(text);
|
|
286
|
+
if (!m) return null;
|
|
287
|
+
const head = m[1];
|
|
288
|
+
if (/^description\s*:/m.test(head)) return null; // present but empty or of another type: the owner's to word
|
|
289
|
+
const body = text.slice(m[0].length);
|
|
290
|
+
const description = descriptionFromBody(body);
|
|
291
|
+
if (!description) return null;
|
|
292
|
+
const line = `description: ${yamlString(description)}`;
|
|
293
|
+
const headLines = head.split(/\r?\n/);
|
|
294
|
+
const at = headLines.findIndex((l) => /^name\s*:/.test(l));
|
|
295
|
+
headLines.splice(at >= 0 ? at + 1 : 0, 0, line);
|
|
296
|
+
const start = m[0].slice(0, m[0].indexOf('\n') + 1);
|
|
297
|
+
const close = m[0].slice(m[0].lastIndexOf(head) + head.length);
|
|
298
|
+
return { text: `${bom}${start}${headLines.join(eol)}${close}${body}`, changes: [{ ruleId: 'description-missing', added: line }] };
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* What "Fix all safe issues" would do, before it is done: one step per change, with the rows it skips and why.
|
|
305
|
+
* Text repairs are candidates until their file is read (`needsRead`), so the preview reads them first.
|
|
306
|
+
* @param {object[]} rows
|
|
307
|
+
* @param {(row: object) => object} canOf what each row allows (as for issuesOf)
|
|
308
|
+
* @returns {{ steps: { kind: 'text'|'archive', row: object, issue: object }[] }}
|
|
309
|
+
*/
|
|
310
|
+
export function safeFixCandidates(rows, canOf) {
|
|
311
|
+
const steps = [];
|
|
312
|
+
for (const row of rows) {
|
|
313
|
+
const can = canOf(row);
|
|
314
|
+
for (const issue of issuesOf(row, can)) {
|
|
315
|
+
if (issue.actions.includes('archive-duplicate')) steps.push({ kind: 'archive', row, issue });
|
|
316
|
+
else if (issue.actions.includes('auto-text') && !steps.some((s) => s.kind === 'text' && s.row === row)) steps.push({ kind: 'text', row, issue, needsRead: true });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return { steps };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** How many issues the Library shows now, and how many of them have a one-press fix (text repairs not yet known). */
|
|
323
|
+
export function issueCounts(rows, canOf) {
|
|
324
|
+
let total = 0; let auto = 0;
|
|
325
|
+
for (const row of rows) for (const issue of issuesOf(row, canOf(row))) { total++; if (issue.actions.includes('archive-duplicate')) auto++; }
|
|
326
|
+
return { total, auto };
|
|
327
|
+
}
|
|
@@ -21,7 +21,7 @@ import { html, raw, setHTML, ICONS } from './sx-dom.js';
|
|
|
21
21
|
import { descriptionHelp, keptKeys, frontmatterText } from './sx-editor-model.js';
|
|
22
22
|
import { snippetsFor, applySnippet, snippetMatches, SNIPPET_GROUPS } from './sx-snippets.js';
|
|
23
23
|
|
|
24
|
-
const OTHER_PROFILES = ['portable-six-field', 'codex-native', 'opencode-native'];
|
|
24
|
+
const OTHER_PROFILES = ['portable-six-field', 'codex-native', 'opencode-native', 'antigravity-native'];
|
|
25
25
|
let _life = null;
|
|
26
26
|
|
|
27
27
|
/** The checks of a head document: the Studio's (gutter and list) and, for a skill, each other runtime's. */
|
|
@@ -15,7 +15,7 @@ import { studioErrorText } from './sx-errors.js';
|
|
|
15
15
|
import { html, setHTML } from './sx-dom.js';
|
|
16
16
|
import { BLOCK_CODES, planModel, resultModel } from './sx-plan.js';
|
|
17
17
|
import { installTargets, wholeTarget } from './sx-subject.js';
|
|
18
|
-
import { confirmations, installOutcome, parseTargetKey, reviewDestinations, assistantsOf, installedRuntimesOf, alsoInstallTargets, alsoInstallRevision } from './sx-install.js';
|
|
18
|
+
import { confirmations, installOutcome, parseTargetKey, reviewDestinations, assistantsOf, installedRuntimesOf, alsoInstallTargets, alsoInstallRevision, alsoInstallScope } from './sx-install.js';
|
|
19
19
|
|
|
20
20
|
// ── One install at a time per skill ──
|
|
21
21
|
// Every surface that installs (the Publish tab's button, Review's card, the workspace banner) takes this lock for
|
|
@@ -47,7 +47,7 @@ function tell(skillId, busy) { for (const fn of _watchers) { try { fn({ skillId,
|
|
|
47
47
|
/** The words of a target: "Claude Code · all projects", "Codex · project Repo", "Claude Code · account work". */
|
|
48
48
|
export function targetWords(ctx, tg) {
|
|
49
49
|
const { t } = ctx;
|
|
50
|
-
const runtime = t(`skillsStudio.runtime.${['claude', 'codex', 'opencode', 'agents'].includes(tg?.runtime) ? tg.runtime : 'claude'}`);
|
|
50
|
+
const runtime = t(`skillsStudio.runtime.${['claude', 'codex', 'opencode', 'antigravity', 'agents'].includes(tg?.runtime) ? tg.runtime : 'claude'}`);
|
|
51
51
|
if (tg?.scope === 'project') return t('skillsStudio.publish.targetProject', { runtime, project: tg.projectLabel || ctx.projects?.find((p) => p.path === tg.projectPath)?.label || tg.projectPath || '' });
|
|
52
52
|
if (tg?.accountId && tg.accountId !== 'default') return t('skillsStudio.publish.targetAccount', { runtime, account: tg.accountId });
|
|
53
53
|
return t('skillsStudio.publish.targetUser', { runtime });
|
|
@@ -72,8 +72,11 @@ export function installedNow(ctx) { return installedRuntimesOf(ctx.data?.status
|
|
|
72
72
|
* (sx-install.js alsoInstallTargets), with their words. Empty when there is nothing to add.
|
|
73
73
|
*/
|
|
74
74
|
export function alsoInstall(ctx, { skill, row }) {
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// A skill installed for one project only adds to that project (sx-install.js alsoInstallScope); its words say so.
|
|
76
|
+
const scope = alsoInstallScope(skill);
|
|
77
|
+
const project = scope.startsWith('project:') ? scope.slice(8) : null;
|
|
78
|
+
const targets = alsoInstallTargets({ skill, row, targets: targetsNow(ctx, { projects: project ? [project] : [] }), assistants: assistantsNow(ctx), installed: installedNow(ctx) });
|
|
79
|
+
return { targets: targets.map(wholeTarget), words: targets.map((t) => (project ? targetWords(ctx, t) : ctx.t(`skillsStudio.runtime.${t.runtime}`))).join(' · ') };
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
/**
|
|
@@ -137,7 +140,7 @@ export async function refreshSkillWorkspaces(ctx, skillId, { resetPublish = fals
|
|
|
137
140
|
export function refreshWords(ctx, outcome) {
|
|
138
141
|
const installed = installedNow(ctx);
|
|
139
142
|
return (outcome?.refresh || []).map((x) => {
|
|
140
|
-
const kind = Array.isArray(installed) && ['claude', 'codex', 'opencode'].includes(x.runtime) && !installed.includes(x.runtime) ? 'not-installed' : x.kind;
|
|
143
|
+
const kind = Array.isArray(installed) && ['claude', 'codex', 'opencode', 'antigravity'].includes(x.runtime) && !installed.includes(x.runtime) ? 'not-installed' : x.kind;
|
|
141
144
|
return ctx.t(`skillsStudio.publish.refresh.${kind}`, { runtime: ctx.t(`skillsStudio.runtime.${x.runtime}`) });
|
|
142
145
|
});
|
|
143
146
|
}
|
|
@@ -145,7 +148,7 @@ export function refreshWords(ctx, outcome) {
|
|
|
145
148
|
/** Why a runtime's settings turn the skill off, in words (sx-plan.js eligibility reason). */
|
|
146
149
|
export function eligibilityWords(ctx, runtime, reason) {
|
|
147
150
|
const { t } = ctx;
|
|
148
|
-
const name = t(`skillsStudio.runtime.${['claude', 'codex', 'opencode', 'agents'].includes(runtime) ? runtime : 'claude'}`);
|
|
151
|
+
const name = t(`skillsStudio.runtime.${['claude', 'codex', 'opencode', 'antigravity', 'agents'].includes(runtime) ? runtime : 'claude'}`);
|
|
149
152
|
const file = shortPath(reason?.file || '');
|
|
150
153
|
const kind = ['codex-config', 'claude-override', 'claude-deny', 'opencode-permission', 'unreadable', 'invalid-json'].includes(reason?.kind) ? reason.kind : 'other';
|
|
151
154
|
return t(`skillsStudio.publish.eligibility.reason.${kind}`, { runtime: name, file, pattern: reason?.pattern || '*', rule: reason?.rule || reason?.key || '' });
|
|
@@ -39,11 +39,22 @@ export function installedRuntimesOf(source) {
|
|
|
39
39
|
export function alsoInstallTargets({ skill = null, row = null, targets = [], assistants = null, installed = null } = {}) {
|
|
40
40
|
if (!skill || row?.source === 'synabun' || !list(skill.installations).length) return [];
|
|
41
41
|
const wanted = Array.isArray(assistants) && assistants.length ? assistants : [];
|
|
42
|
+
const scope = alsoInstallScope(skill);
|
|
42
43
|
return SIMPLE_RUNTIMES.filter((r) => wanted.includes(r) && !(Array.isArray(installed) && !installed.includes(r)))
|
|
43
|
-
.map((r) => list(targets).find((t) => t.key === keyIn(r,
|
|
44
|
+
.map((r) => list(targets).find((t) => t.key === keyIn(r, scope)) || null)
|
|
44
45
|
.filter((t) => t && !t.off && !['installed', 'outdated', 'present'].includes(targetState(t, { skill, row }).state));
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* The scope "Also install for …" adds to: the one project every installation is in (a skill installed for one
|
|
50
|
+
* project only is never offered to every project), else the owner's ('user').
|
|
51
|
+
*/
|
|
52
|
+
export function alsoInstallScope(skill) {
|
|
53
|
+
const installs = list(skill?.installations);
|
|
54
|
+
const projects = new Set(installs.map((i) => (i?.scope === 'project' && i.projectPath ? i.projectPath : '')));
|
|
55
|
+
return projects.size === 1 && !projects.has('') ? `project:${[...projects][0]}` : 'user';
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
/**
|
|
48
59
|
* The version "Also install for …" sends: the one the installations hold (the one most of them hold when they
|
|
49
60
|
* differ; on a tie the approved one, then the latest), else the approved one, else the latest; null for none.
|
|
@@ -58,7 +69,11 @@ export function alsoInstallRevision(skill) {
|
|
|
58
69
|
return [...count.entries()].sort((a, b) => b[1] - a[1] || rank(b[0]) - rank(a[0]) || held.indexOf(a[0]) - held.indexOf(b[0]))[0][0];
|
|
59
70
|
}
|
|
60
71
|
export const BUTTON_CODES = Object.freeze(['install', 'update', 'installed', 'checking', 'installing', 'nothing', 'blocked']);
|
|
61
|
-
export const SIMPLE_RUNTIMES = Object.freeze(['claude', 'codex', 'opencode']);
|
|
72
|
+
export const SIMPLE_RUNTIMES = Object.freeze(['claude', 'codex', 'opencode', 'antigravity']);
|
|
73
|
+
// The runtimes with a project folder of their own. Antigravity has none: in a project it reads .agents/skills, the
|
|
74
|
+
// Codex tile's folder, so a project scope shows no Antigravity tile (the plan says Antigravity reads that folder).
|
|
75
|
+
const PROJECT_RUNTIMES = new Set(['claude', 'codex', 'opencode']);
|
|
76
|
+
const inProject = (scope) => String(scope || '').startsWith('project:');
|
|
62
77
|
const APPROVED_STATES = ['approved', 'partial', 'publish_failed'];
|
|
63
78
|
|
|
64
79
|
const list = (v) => (Array.isArray(v) ? v : []);
|
|
@@ -132,7 +147,7 @@ export const keyIn = (runtime, scope) => (scope && scope.startsWith('project:')
|
|
|
132
147
|
* @param {{ targets: object[], checked: Set<string>, scope: string, skill?: object|null, row?: object|null, revisionId?: string|null }} o
|
|
133
148
|
*/
|
|
134
149
|
export function simplePicker({ targets = [], checked = new Set(), scope = 'user', skill = null, row = null, revisionId = null, installed = null } = {}) {
|
|
135
|
-
const tiles = SIMPLE_RUNTIMES.map((runtime) => {
|
|
150
|
+
const tiles = SIMPLE_RUNTIMES.filter((runtime) => !inProject(scope) || PROJECT_RUNTIMES.has(runtime)).map((runtime) => {
|
|
136
151
|
const key = keyIn(runtime, scope);
|
|
137
152
|
const target = list(targets).find((t) => t.key === key) || null;
|
|
138
153
|
const st = target ? targetState(target, { skill, row, revisionId }) : { state: 'none', path: null };
|
|
@@ -159,6 +174,8 @@ export function moveScope(checked, from, to) {
|
|
|
159
174
|
const next = new Set();
|
|
160
175
|
for (const key of checked || []) {
|
|
161
176
|
const runtime = SIMPLE_RUNTIMES.find((r) => key === keyIn(r, from));
|
|
177
|
+
// A runtime with no folder of its own in the new scope (Antigravity in a project) is not ticked there.
|
|
178
|
+
if (runtime && inProject(to) && !PROJECT_RUNTIMES.has(runtime)) continue;
|
|
162
179
|
next.add(runtime ? keyIn(runtime, to) : key);
|
|
163
180
|
}
|
|
164
181
|
return next;
|