synabun 2.0.10 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +207 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +13 -13
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +30 -17
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +7 -6
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +302 -0
- package/lib/virustotal.mjs +203 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.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/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.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-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- 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 +18 -11
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +22 -63
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- 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/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +14 -14
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +29 -19
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- 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/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +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 +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +642 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +81 -21
- package/mcp-server/src/services/server-instructions.ts +29 -64
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +29 -20
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +592 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3953 -228
- package/neural-interface/i18n/pt-BR.json +3901 -179
- 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 +128 -28
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- 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 +81 -15
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +1073 -200
- package/neural-interface/lib/assistant-envelope.js +156 -2
- 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 +93 -19
- package/neural-interface/lib/assistant-plan-permissions.js +25 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +245 -38
- package/neural-interface/lib/assistant-runtime.js +214 -30
- package/neural-interface/lib/assistant-task-prompt.js +87 -21
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +626 -4
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +84 -0
- package/neural-interface/lib/browser-network-guard.js +476 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +198 -38
- package/neural-interface/lib/claude-agent-bridge.js +11 -2
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +163 -154
- package/neural-interface/lib/cli-resolver.js +13 -3
- package/neural-interface/lib/cli-update-planner.js +52 -41
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +191 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +68 -68
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +13 -10
- 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/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +35 -33
- package/neural-interface/lib/mcp-external-servers.js +332 -0
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +177 -25
- package/neural-interface/lib/native-loop-providers.js +965 -66
- package/neural-interface/lib/native-loop-runtime.js +290 -42
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +53 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +793 -0
- 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 +20 -2
- package/neural-interface/lib/rulesets/api.js +7 -6
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +180 -36
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +81 -31
- package/neural-interface/lib/session-title-generator.js +58 -36
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +298 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +285 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +569 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +216 -0
- package/neural-interface/lib/skills/eval-confinement.js +399 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +135 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +203 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +269 -0
- package/neural-interface/lib/skills/runtime-checks.js +358 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +530 -0
- package/neural-interface/lib/skills/telemetry.js +119 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/bridge.js +6 -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 +1268 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +238 -0
- package/neural-interface/lib/worker-sandbox.js +329 -0
- package/neural-interface/package-lock.json +34 -12
- package/neural-interface/package.json +3 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +3 -3
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +125 -31
- 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 +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +205 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +445 -82
- package/neural-interface/public/shared/assistant/asst-route.js +304 -53
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
- package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
- package/neural-interface/public/shared/assistant/asst-state.js +87 -24
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
- package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
- package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +11 -12
- package/neural-interface/public/shared/cli-status.js +11 -10
- package/neural-interface/public/shared/constants.js +5 -4
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +367 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- 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/crash-notice-view.js +168 -0
- 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 +21 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- 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 +30 -15
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- 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-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +222 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +149 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- 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-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1822 -1756
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -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 +111 -140
- package/neural-interface/public/shared/ui-claude-panel.js +376 -82
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +15 -3
- 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-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +33 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +10 -20
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
- package/neural-interface/public/shared/ui-navbar.js +49 -24
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +27 -15
- package/neural-interface/public/shared/ui-settings.js +683 -526
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +270 -190
- package/neural-interface/public/shared/ui-tool-updates.js +6 -4
- package/neural-interface/public/shared/ui-tooltip.js +81 -51
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-trash.js +6 -4
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +312 -59
- package/neural-interface/public/shared/ui-worker-end.js +5 -1
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +2 -2
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +32 -0
- package/neural-interface/server.js +4553 -2855
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +34 -12
- package/package.json +17 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +6 -6
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: the Review place
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What waits for the owner (sx-review.js): submissions of the Review queue (a new skill or a change from the SynaBun
|
|
6
|
+
// Assistant or another agent: what it does, who made it, its checks, its scan findings, what it can do that the
|
|
7
|
+
// approved revision could not, the name it installs as, every file it changes with its lines) and the updates
|
|
8
|
+
// SynaBun ships for skills that are installed. Approving installs: "Approve & install" approves and installs to the
|
|
9
|
+
// targets the submission named (sx-install-flow.js: the service's plan, one question only when the plan asks one,
|
|
10
|
+
// then exactly that plan), and the card then says where it went and what a running session needs; "Approve only"
|
|
11
|
+
// leaves it approved with Install on its card. Ask for changes (the Assistant reads why) or reject. An update is
|
|
12
|
+
// read file by file before it is applied; "Keep mine" leaves the copy as it is until SynaBun ships another version.
|
|
13
|
+
|
|
14
|
+
import { scanFindingText } from './sx-scan.js';
|
|
15
|
+
import { studioErrorText } from './sx-errors.js';
|
|
16
|
+
import { html, setHTML, ICONS, relativeTime, fullTime, plural } from './sx-dom.js';
|
|
17
|
+
import { reviewCardModel, sortReviews, updateTargetKeys } from './sx-review.js';
|
|
18
|
+
import { serviceDiff, invertDiff, lineDiff } from './sx-diff.js';
|
|
19
|
+
import { diffHTML, textDiffModel } from './sx-diff-view.js';
|
|
20
|
+
import { targetWords, keyWords, reviewTargets, installFlow, flowWords, installBusy, acquireInstall, refreshSkillWorkspaces } from './sx-install-flow.js';
|
|
21
|
+
|
|
22
|
+
let _life = null;
|
|
23
|
+
const FILTERS = ['waiting', 'all', 'decided'];
|
|
24
|
+
/** Scan codes whose words carry the number of findings. */
|
|
25
|
+
const SCAN_COUNTED = ['blocked', 'incomplete', 'review', 'findings'];
|
|
26
|
+
/** What an install from a card said, by review id, for as long as the page lives: the card shows it after the reload. */
|
|
27
|
+
const _said = new Map();
|
|
28
|
+
|
|
29
|
+
function authorWords(t, a) {
|
|
30
|
+
if (a.kind === 'owner') return t('skillsStudio.review.author.owner');
|
|
31
|
+
const who = a.kind === 'assistant' ? t('skillsStudio.review.author.assistant') : t('skillsStudio.review.author.agent');
|
|
32
|
+
const bits = [a.provider, a.model].filter(Boolean).join(' · ');
|
|
33
|
+
return bits ? `${who} (${bits})` : who;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Where a card's install goes, said whole: what the submission named, what of that is not available here and why,
|
|
38
|
+
* where it then goes (never a silent fallback), or that nothing of it is available (then Publish is the way).
|
|
39
|
+
*/
|
|
40
|
+
function destinationHTML(ctx, card, dest) {
|
|
41
|
+
const { t } = ctx;
|
|
42
|
+
if (!dest) return '';
|
|
43
|
+
const lines = [];
|
|
44
|
+
if (card.targets.length) lines.push(html`<li>${t('skillsStudio.review.forTargets', { targets: card.targets.map((tg) => targetWords(ctx, tg)).join(', ') })}</li>`);
|
|
45
|
+
for (const m of dest.missing) lines.push(html`<li class="sx-warn-text">${t('skillsStudio.review.targetUnavailable', { target: m.words, reason: m.why })}</li>`);
|
|
46
|
+
if (!dest.targets.length) lines.push(html`<li class="sx-warn-text">${t('skillsStudio.review.noDestination')}</li>`);
|
|
47
|
+
else if (dest.missing.length || !card.targets.length) {
|
|
48
|
+
const why = dest.fallback === 'installed' ? t('skillsStudio.review.fallbackInstalled') : dest.fallback === 'claude' ? t('skillsStudio.review.fallbackDefault') : dest.fallback === 'assistants' ? t('skillsStudio.review.fallbackAssistants') : '';
|
|
49
|
+
lines.push(html`<li>${t('skillsStudio.review.installsFor', { targets: dest.words.join(', ') })}${why ? html` <span class="sx-dim">${why}</span>` : ''}</li>`);
|
|
50
|
+
}
|
|
51
|
+
// One tile per runtime, as Publish shows them: where this card's install goes (Codex and OpenCode are first-class).
|
|
52
|
+
if (dest.targets.length) {
|
|
53
|
+
const picked = (rt) => [...dest.keys].some((k) => String(k).startsWith(`${rt}:`));
|
|
54
|
+
lines.push(html`<li class="sx-dest-tiles" role="group" aria-label="${t('skillsStudio.review.destTilesLabel')}">${['claude', 'codex', 'opencode', 'antigravity'].map((rt) => html`<span class="sx-dest-tile${picked(rt) ? ' is-on' : ''}" title="${picked(rt) ? t('skillsStudio.review.destTileOn', { runtime: t(`skillsStudio.runtime.${rt}`) }) : t('skillsStudio.review.destTileOff', { runtime: t(`skillsStudio.runtime.${rt}`) })}">${picked(rt) ? ICONS.ok : ''}<span>${t(`skillsStudio.runtime.${rt}`)}</span></span>`)}</li>`);
|
|
55
|
+
}
|
|
56
|
+
return html`${lines}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function cardHTML(ctx, card, focused) {
|
|
60
|
+
const { t, locale } = ctx;
|
|
61
|
+
const installs = card.actions.includes('approve_install') || card.actions.includes('install');
|
|
62
|
+
const review = installs ? (ctx.data?.reviews || []).find((r) => r.reviewId === card.id) : null;
|
|
63
|
+
const row = review ? ctx.rows.find((r) => r.skill?.skillId === review.skillId) || null : null;
|
|
64
|
+
const dest = review ? reviewTargets(ctx, { review, skill: row?.skill || null, row }) : null;
|
|
65
|
+
const canInstall = !!dest && dest.targets.length > 0;
|
|
66
|
+
// An install of this skill on its way from any surface: its buttons are off here too, however the card is drawn again.
|
|
67
|
+
const busy = installs && installBusy(card.skillId);
|
|
68
|
+
const authority = card.authority.map((a) => html`<li>${a.added.length ? t('skillsStudio.review.authorityAdds', { what: t(`skillsStudio.review.authority.${a.kind}`), items: a.added.slice(0, 6).join(', ') }) : ''}${a.removed.length ? t('skillsStudio.review.authorityRemoves', { what: t(`skillsStudio.review.authority.${a.kind}`), items: a.removed.slice(0, 6).join(', ') }) : ''}</li>`);
|
|
69
|
+
return html`<article class="sx-card sx-card--${card.state}${focused ? ' is-focused' : ''}" id="sx-review-${card.id}" aria-labelledby="sx-review-${card.id}-h" tabindex="-1">
|
|
70
|
+
<header class="sx-card-top">
|
|
71
|
+
<h4 class="sx-card-kind" id="sx-review-${card.id}-h"><span class="sx-card-mark" aria-hidden="true">${card.kind === 'new' ? ICONS.spark : ICONS.edit}</span>${t(`skillsStudio.review.kind.${card.kind}`)} · <span class="sx-mono">/${card.installAs || card.name}</span></h4>
|
|
72
|
+
<span class="sx-state-chip sx-state-chip--review-${card.state}">${t(`skillsStudio.review.state.${card.state}`)}</span>
|
|
73
|
+
<span class="sx-spacer"></span>
|
|
74
|
+
<span class="sx-card-from">${authorWords(t, card.author)}${card.createdAt ? html` · <time datetime="${card.createdAt}" title="${fullTime(card.createdAt, locale)}">${relativeTime(card.createdAt, locale)}</time>` : ''}</span>
|
|
75
|
+
</header>
|
|
76
|
+
${card.notes ? html`<p class="sx-card-notes">${card.notes}</p>` : ''}
|
|
77
|
+
${card.assumptions.length ? html`<ul class="sx-plain-list sx-card-assumptions">${card.assumptions.map((a) => html`<li>${t('skillsStudio.review.assumed', { text: a })}</li>`)}</ul>` : ''}
|
|
78
|
+
<ul class="sx-facts sx-card-facts">
|
|
79
|
+
${card.installAs ? html`<li>${t('skillsStudio.review.installsAs')} <span class="sx-mono">/${card.installAs}</span></li>` : ''}
|
|
80
|
+
${dest ? destinationHTML(ctx, card, dest) : card.targets.length ? html`<li>${t('skillsStudio.review.forTargets', { targets: card.targets.map((tg) => targetWords(ctx, tg)).join(', ') })}</li>` : ''}
|
|
81
|
+
<li class="${card.lint.errors ? 'sx-err-text' : ''}">${card.lint.errors ? ctx.tp('skillsStudio.review.lintErrors', card.lint.errors) : t('skillsStudio.review.lintOk')}${card.lint.warnings ? ` · ${ctx.tp('skillsStudio.review.lintWarnings', card.lint.warnings)}` : ''}</li>
|
|
82
|
+
<li class="sx-scan-${card.scan.sev}">${SCAN_COUNTED.includes(card.scan.code) ? plural(t, `skillsStudio.review.scan.${card.scan.code}`, card.scan.found) : t(`skillsStudio.review.scan.${card.scan.code}`)}</li>
|
|
83
|
+
</ul>
|
|
84
|
+
${card.scan.findings.length ? html`<details class="sx-details"><summary>${ctx.tp('skillsStudio.review.findings', card.scan.findings.length)}</summary><ul class="sx-findings">${card.scan.findings.map((f) => html`<li class="sx-finding sx-finding--${f.severity}"><span class="sx-finding-sev">${t(`skillsStudio.review.severity.${f.severity === 'info' ? 'low' : f.severity}`)}</span> ${scanFindingText(t, f)}${f.file ? html` <span class="sx-mono sx-dim">${f.file}${f.line ? `:${f.line}` : ''}</span>` : ''}</li>`)}</ul></details>` : ''}
|
|
85
|
+
${authority.length ? html`<div class="sx-authority"><p class="sx-help">${t('skillsStudio.review.authorityTitle')}</p><ul class="sx-plain-list">${authority}</ul></div>` : ''}
|
|
86
|
+
<details class="sx-details" data-review-diff="${card.id}"><summary>${card.files.length ? ctx.tp('skillsStudio.review.filesChanged', card.files.length) : t('skillsStudio.review.showFiles')}</summary><div class="sx-diff-box" data-diff-box="${card.id}"><p class="sx-help">${t('skillsStudio.review.loadingDiff')}</p></div></details>
|
|
87
|
+
${card.comments.length ? html`<ul class="sx-plain-list sx-card-comments">${card.comments.map((c) => html`<li><span class="sx-dim">${c.by === 'owner' ? t('skillsStudio.review.author.owner') : t('skillsStudio.review.author.assistant')}:</span> ${c.body}</li>`)}</ul>` : ''}
|
|
88
|
+
${card.reason ? html`<p class="sx-help">${t('skillsStudio.review.yourNote', { text: card.reason })}</p>` : ''}
|
|
89
|
+
${saidHTML(ctx, card)}
|
|
90
|
+
${card.publication.length && !_said.has(card.id) ? html`<ul class="sx-plain-list">${card.publication.map((p) => html`<li class="${p.status === 'committed' ? '' : 'sx-warn-text'}">${keyWords(ctx, p.targetKey)}: ${t(`skillsStudio.publish.result.${['committed', 'skipped', 'needs-confirmation', 'failed'].includes(p.status) ? p.status : 'failed'}`)}${p.error ? ` · ${p.error}` : ''}</li>`)}</ul>` : ''}
|
|
91
|
+
${card.actions.length ? html`<div class="sx-card-actions" data-card-actions="${card.id}" data-busy="${busy ? '1' : '0'}">
|
|
92
|
+
${card.actions.includes('approve_install') && canInstall ? html`<button type="button" class="sx-btn sx-btn--primary" data-review="approve_install" data-id="${card.id}"${busy ? ' disabled' : ''}>${ICONS.publish}<span>${busy ? t('skillsStudio.review.installing') : t('skillsStudio.review.approveInstall')}</span></button>` : ''}
|
|
93
|
+
${card.actions.includes('install') && canInstall ? html`<button type="button" class="sx-btn sx-btn--primary" data-review="install" data-id="${card.id}"${busy ? ' disabled' : ''}>${ICONS.publish}<span>${busy ? t('skillsStudio.review.installing') : t('skillsStudio.review.installNow')}</span></button>` : ''}
|
|
94
|
+
${card.actions.includes('approve') ? html`<button type="button" class="sx-btn${canInstall ? '' : ' sx-btn--primary'}" data-review="approve" data-id="${card.id}"${busy ? ' disabled' : ''}>${canInstall ? t('skillsStudio.review.approveOnly') : t('skillsStudio.review.approveOnlyPrimary')}</button>` : ''}
|
|
95
|
+
${card.actions.includes('request_changes') ? html`<button type="button" class="sx-btn" data-review="request_changes" data-id="${card.id}"${busy ? ' disabled' : ''}>${t('skillsStudio.review.requestChanges')}</button>` : ''}
|
|
96
|
+
${card.actions.includes('reject') ? html`<button type="button" class="sx-btn sx-btn--ghost sx-btn--danger" data-review="reject" data-id="${card.id}"${busy ? ' disabled' : ''}>${t('skillsStudio.review.reject')}</button>` : ''}
|
|
97
|
+
${card.actions.includes('install') ? html`<button type="button" class="sx-btn${canInstall ? ' sx-btn--ghost' : ' sx-btn--primary'}" data-review="publish" data-id="${card.id}"${busy ? ' disabled' : ''}>${t('skillsStudio.review.openPublish')}</button>` : ''}
|
|
98
|
+
<button type="button" class="sx-btn sx-btn--ghost" data-review="open" data-id="${card.id}">${t('skillsStudio.review.open')}</button>
|
|
99
|
+
</div>` : ''}
|
|
100
|
+
</article>`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** What the last install from this card said: where it went and what a running session needs, or why it did not go. */
|
|
104
|
+
function saidHTML(ctx, card) {
|
|
105
|
+
const said = _said.get(card.id);
|
|
106
|
+
if (!said) return '';
|
|
107
|
+
return html`<div class="sx-outcome sx-outcome--${said.kind}" role="status" data-card-said="${card.id}">
|
|
108
|
+
<p class="sx-outcome-line">${said.kind === 'info' ? ICONS.ok : ICONS.warn}<strong>${said.line}</strong></p>
|
|
109
|
+
${said.lines.length ? html`<ul class="sx-plain-list sx-outcome-notes">${said.lines.map((x) => html`<li>${x}</li>`)}</ul>` : ''}
|
|
110
|
+
${said.kind !== 'info' ? html`<p class="sx-help">${ctx.t('skillsStudio.review.notInstalledHelp')}</p>` : ''}
|
|
111
|
+
</div>`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function updateCardHTML(ctx, u) {
|
|
115
|
+
const { t } = ctx;
|
|
116
|
+
const where = [...new Set([...u.legacyCopies.map((c) => c.path), ...(u.installations.length ? [plural(t, 'skillsStudio.review.libraryInstalls', u.installations.length)] : [])])];
|
|
117
|
+
const hid = `sx-update-${String(u.name).replace(/[^A-Za-z0-9_-]/g, '_')}-h`;
|
|
118
|
+
return html`<article class="sx-card sx-card--update" aria-labelledby="${hid}">
|
|
119
|
+
<header class="sx-card-top">
|
|
120
|
+
<h4 class="sx-card-kind" id="${hid}"><span class="sx-card-mark" aria-hidden="true">${ICONS.update}</span>${t('skillsStudio.review.kind.update')} · <span class="sx-mono">/${u.label}</span></h4>
|
|
121
|
+
<span class="sx-spacer"></span><span class="sx-card-from">${t('skillsStudio.review.fromSynabun')}</span>
|
|
122
|
+
</header>
|
|
123
|
+
<p class="sx-card-notes">${t('skillsStudio.review.updateBody', { name: u.label })}</p>
|
|
124
|
+
<ul class="sx-facts">${where.map((w) => html`<li class="sx-mono">${w}</li>`)}${u.runtimes.length ? html`<li>${t('skillsStudio.review.updateRuntimes', { runtimes: u.runtimes.map((r) => t(`skillsStudio.runtime.${r}`)).join(', ') })}</li>` : ''}</ul>
|
|
125
|
+
<details class="sx-details" data-update-diff="${u.key}"><summary>${t('skillsStudio.review.reviewChanges')}</summary><div class="sx-diff-box" data-update-box="${u.key}"><p class="sx-help">${t('skillsStudio.review.loadingDiff')}</p></div></details>
|
|
126
|
+
${ctx.guest ? '' : html`<div class="sx-card-actions">
|
|
127
|
+
<button type="button" class="sx-btn sx-btn--primary" data-update="apply" data-key="${u.key}">${t('skillsStudio.review.update')}</button>
|
|
128
|
+
<button type="button" class="sx-btn sx-btn--ghost" data-update="keep" data-key="${u.key}">${t('skillsStudio.review.keepMine')}</button>
|
|
129
|
+
</div>`}
|
|
130
|
+
</article>`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Draw the Review place. */
|
|
134
|
+
/** Review is left for another place: its listeners on the shared main area end. */
|
|
135
|
+
export function leaveReviewPlace() { _life?.abort(); _life = null; }
|
|
136
|
+
|
|
137
|
+
/** The window closed: what the cards said of their installs goes with it. */
|
|
138
|
+
export function forgetReviewOutcomes() { _said.clear(); }
|
|
139
|
+
|
|
140
|
+
export function renderReviewPlace(host, ctx) {
|
|
141
|
+
const { t } = ctx;
|
|
142
|
+
_life?.abort();
|
|
143
|
+
_life = new AbortController();
|
|
144
|
+
const signal = _life.signal;
|
|
145
|
+
const filter = ctx.lib.reviewFilter || 'waiting';
|
|
146
|
+
const { open, decided } = sortReviews(ctx.data?.reviews || []);
|
|
147
|
+
// A submission installed from its card stays in the waiting list, saying where it went, until the window closes.
|
|
148
|
+
const justInstalled = decided.filter((r) => _said.has(r.reviewId));
|
|
149
|
+
const cards = (filter === 'decided' ? decided : filter === 'all' ? [...open, ...decided] : [...open, ...justInstalled]).map((r) => reviewCardModel(r, { guest: ctx.guest }));
|
|
150
|
+
const updates = filter === 'decided' ? [] : ctx.updates;
|
|
151
|
+
const focus = ctx.reviewFocus;
|
|
152
|
+
setHTML(host, html`<section class="sx-place sx-place--review" aria-labelledby="sx-review-title">
|
|
153
|
+
<div class="sx-place-head">
|
|
154
|
+
<h3 class="sx-place-title" id="sx-review-title" tabindex="-1" data-autofocus>${t('skillsStudio.review.title')}</h3>
|
|
155
|
+
<p class="sx-place-sub">${t('skillsStudio.review.subtitle')}</p>
|
|
156
|
+
<span class="sx-spacer"></span>
|
|
157
|
+
<div class="sx-seg" role="group" aria-label="${t('skillsStudio.review.filterLabel')}">${FILTERS.map((f) => html`<button type="button" data-review-filter="${f}" aria-pressed="${filter === f ? 'true' : 'false'}">${t(`skillsStudio.review.filter.${f}`)}</button>`)}</div>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="sx-place-scroll">
|
|
160
|
+
<div class="sx-cards">
|
|
161
|
+
${updates.length ? html`<h4 class="sx-section-title">${ctx.tp('skillsStudio.review.updatesTitle', updates.length)}</h4>${updates.map((u) => updateCardHTML(ctx, u))}` : ''}
|
|
162
|
+
${cards.length ? html`<h4 class="sx-section-title">${filter === 'decided' ? t('skillsStudio.review.decidedTitle') : ctx.tp('skillsStudio.review.waitingTitle', cards.length)}</h4>${cards.map((c) => cardHTML(ctx, c, c.id === focus))}` : ''}
|
|
163
|
+
${!cards.length && !updates.length ? html`<div class="sx-empty"><span class="sx-placeholder-icon" aria-hidden="true">${ICONS.review}</span><h4 class="sx-empty-title">${t('skillsStudio.review.emptyTitle')}</h4><p>${t('skillsStudio.review.emptyBody')}</p></div>` : ''}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</section>`);
|
|
167
|
+
if (focus) { ctx.clearReviewFocus(); requestAnimationFrame(() => { const el = host.querySelector(`#sx-review-${CSS.escape(focus)}`); el?.scrollIntoView({ block: 'nearest' }); el?.focus(); }); }
|
|
168
|
+
host.addEventListener('click', (e) => onClick(e, host, ctx), { signal });
|
|
169
|
+
host.addEventListener('toggle', (e) => {
|
|
170
|
+
const d = e.target;
|
|
171
|
+
if (!d.open) return;
|
|
172
|
+
if (d.dataset?.reviewDiff) loadReviewDiff(host, ctx, d.dataset.reviewDiff);
|
|
173
|
+
if (d.dataset?.updateDiff) loadUpdateDiff(host, ctx, d.dataset.updateDiff);
|
|
174
|
+
}, { signal, capture: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function loadReviewDiff(host, ctx, id) {
|
|
178
|
+
const { t } = ctx;
|
|
179
|
+
const box = host.querySelector(`[data-diff-box="${CSS.escape(id)}"]`);
|
|
180
|
+
const review = (ctx.data?.reviews || []).find((r) => r.reviewId === id);
|
|
181
|
+
if (!box || !review || box.dataset.loaded) return;
|
|
182
|
+
box.dataset.loaded = '1';
|
|
183
|
+
const card = reviewCardModel(review);
|
|
184
|
+
const gen = ctx.gen();
|
|
185
|
+
try {
|
|
186
|
+
if (card.fromRevisionId && card.revisionId) {
|
|
187
|
+
const res = await ctx.api.fetchSkillsDiff(card.fromRevisionId, card.revisionId);
|
|
188
|
+
if (!ctx.alive(gen) || !box.isConnected) return;
|
|
189
|
+
setHTML(box, diffHTML(t, serviceDiff(res.diff)));
|
|
190
|
+
} else if (card.revisionId) {
|
|
191
|
+
// A new skill: every file is new, each shown as added.
|
|
192
|
+
const rev = (await ctx.api.fetchSkillsRevision(card.revisionId)).revision;
|
|
193
|
+
const files = [];
|
|
194
|
+
for (const f of (rev?.manifest?.files || []).slice(0, 20)) {
|
|
195
|
+
if (f.encoding !== 'utf-8') { files.push({ path: f.path, status: 'added', hunks: [], added: 0, removed: 0, note: 'binary' }); continue; }
|
|
196
|
+
const got = await ctx.api.fetchSkillsRevisionFile(card.revisionId, f.path);
|
|
197
|
+
const d = lineDiff('', got.content ?? '');
|
|
198
|
+
files.push({ ...(textDiffModel(f.path, d).files[0] || { path: f.path, hunks: [], added: 0, removed: 0 }), status: 'added' });
|
|
199
|
+
}
|
|
200
|
+
if (!ctx.alive(gen) || !box.isConnected) return;
|
|
201
|
+
setHTML(box, diffHTML(t, { files }, { open: 2 }));
|
|
202
|
+
}
|
|
203
|
+
} catch (err) { if (box.isConnected) setHTML(box, html`<p class="sx-note sx-note--warn" role="alert">${t('skillsStudio.review.diffFailed', { error: studioErrorText(ctx.t, err) })}</p>`); }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** The changes an update makes, from the installed copy's side (what updating it would change). */
|
|
207
|
+
async function updateDiffModel(ctx, row) {
|
|
208
|
+
const upstream = row.update?.upstreamRevisionId;
|
|
209
|
+
if (!upstream) return { model: null, partial: false };
|
|
210
|
+
const deploy = (row.skill?.installations || []).find((i) => row.update.installations.includes(i.installationId));
|
|
211
|
+
if (deploy) return { model: serviceDiff((await ctx.api.fetchSkillsDiff(deploy.sourceRevisionId, upstream)).diff), partial: false };
|
|
212
|
+
const copy = row.update.legacyCopies[0];
|
|
213
|
+
if (!copy) return { model: null, partial: false };
|
|
214
|
+
if (ctx.caps.has('compare-folder')) return { model: serviceDiff(invertDiff((await ctx.api.fetchSkillsCompare(upstream, copy.artifactId)).diff)), partial: false };
|
|
215
|
+
// An older server: SKILL.md alone, the copy's text against SynaBun's.
|
|
216
|
+
const entry = (await ctx.api.fetchSkillsCatalogEntry(copy.artifactId)).entry;
|
|
217
|
+
const shipped = await ctx.api.fetchSkillsRevisionFile(upstream, 'SKILL.md');
|
|
218
|
+
return { model: textDiffModel('SKILL.md', lineDiff(entry.text || '', shipped.content || '')), partial: true };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function loadUpdateDiff(host, ctx, key) {
|
|
222
|
+
const { t } = ctx;
|
|
223
|
+
const box = host.querySelector(`[data-update-box="${CSS.escape(key)}"]`);
|
|
224
|
+
const row = ctx.rows.find((r) => r.key === key);
|
|
225
|
+
if (!box || !row || box.dataset.loaded) return;
|
|
226
|
+
box.dataset.loaded = '1';
|
|
227
|
+
const gen = ctx.gen();
|
|
228
|
+
try {
|
|
229
|
+
const { model, partial } = await updateDiffModel(ctx, row);
|
|
230
|
+
if (!ctx.alive(gen) || !box.isConnected) return;
|
|
231
|
+
setHTML(box, html`${partial ? html`<p class="sx-note" role="note">${t('skillsStudio.review.partialDiff')}</p>` : ''}${diffHTML(t, model)}`);
|
|
232
|
+
} catch (err) { if (box.isConnected) setHTML(box, html`<p class="sx-note sx-note--warn" role="alert">${t('skillsStudio.review.diffFailed', { error: studioErrorText(ctx.t, err) })}</p>`); }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* An update of a shipped skill, from its workspace banner: read it (`review`), or go install SynaBun's version
|
|
237
|
+
* where the old copy is (`apply`: Publish, with those targets picked; the plan shows what it replaces, backed up).
|
|
238
|
+
*/
|
|
239
|
+
export async function reviewUpdateFlow(ctx, row, mode) {
|
|
240
|
+
const { t } = ctx;
|
|
241
|
+
if (mode === 'review') {
|
|
242
|
+
const box = document.createElement('div');
|
|
243
|
+
box.className = 'sx-diff-box';
|
|
244
|
+
box.textContent = t('skillsStudio.review.loadingDiff');
|
|
245
|
+
const shown = ctx.ask({ title: t('skillsStudio.review.updateDiffTitle', { name: row.folderName }), extra: box, wide: true, buttons: [{ id: 'close', label: t('skillsStudio.dialog.close'), kind: 'primary' }], cancelId: 'close' });
|
|
246
|
+
try {
|
|
247
|
+
const { model, partial } = await updateDiffModel(ctx, row);
|
|
248
|
+
if (box.isConnected) setHTML(box, html`${partial ? html`<p class="sx-note" role="note">${t('skillsStudio.review.partialDiff')}</p>` : ''}${diffHTML(t, model)}`);
|
|
249
|
+
} catch (err) { if (box.isConnected) box.textContent = t('skillsStudio.review.diffFailed', { error: studioErrorText(ctx.t, err) }); }
|
|
250
|
+
await shown;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// What the update replaces is ticked: the folder each old copy is in, the target of each older installation (never
|
|
254
|
+
// a second copy for a runtime that reads the first one). Handed to openRow, which starts Publish over with it before
|
|
255
|
+
// Publish is drawn (set afterwards, it raced the reading Publish had already begun: "Cannot set properties of null").
|
|
256
|
+
const update = row.update;
|
|
257
|
+
const installations = row.skill?.installations || [];
|
|
258
|
+
const preset = (pub, targets) => { pub.checked = new Set(updateTargetKeys(update, installations, targets)); };
|
|
259
|
+
await ctx.openRow(row.key, { tab: 'publish', revisionId: update?.upstreamRevisionId || null, preset });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function askReason(ctx, { title, prompt, required }) {
|
|
263
|
+
const { t } = ctx;
|
|
264
|
+
const wrap = document.createElement('div');
|
|
265
|
+
wrap.className = 'sx-dlg-form';
|
|
266
|
+
const label = document.createElement('label');
|
|
267
|
+
label.className = 'sx-field-label';
|
|
268
|
+
label.htmlFor = 'sx-reason';
|
|
269
|
+
label.textContent = prompt;
|
|
270
|
+
const area = document.createElement('textarea');
|
|
271
|
+
area.id = 'sx-reason';
|
|
272
|
+
area.className = 'sx-input sx-textarea';
|
|
273
|
+
area.rows = 4;
|
|
274
|
+
area.maxLength = 4000;
|
|
275
|
+
const err = document.createElement('p');
|
|
276
|
+
err.className = 'ss-dlg-error';
|
|
277
|
+
err.setAttribute('role', 'alert');
|
|
278
|
+
wrap.append(label, area, err);
|
|
279
|
+
setTimeout(() => area.focus(), 0);
|
|
280
|
+
const answer = await ctx.ask({
|
|
281
|
+
title, extra: wrap,
|
|
282
|
+
buttons: [{ id: 'cancel', label: t('skillsStudio.dialog.cancel') }, { id: 'ok', label: t('skillsStudio.review.send'), kind: 'primary' }],
|
|
283
|
+
focus: 'cancel',
|
|
284
|
+
accept: (id) => { if (id === 'ok' && required && !area.value.trim()) { err.textContent = t('skillsStudio.review.reasonRequired'); return false; } return true; },
|
|
285
|
+
});
|
|
286
|
+
return answer === 'ok' ? area.value.trim() : null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function onClick(e, host, ctx) {
|
|
290
|
+
const { t } = ctx;
|
|
291
|
+
const filterBtn = e.target.closest?.('[data-review-filter]');
|
|
292
|
+
if (filterBtn) { ctx.lib.reviewFilter = filterBtn.dataset.reviewFilter; ctx.render(); host.querySelector(`[data-review-filter="${filterBtn.dataset.reviewFilter}"]`)?.focus(); return; }
|
|
293
|
+
const upd = e.target.closest?.('[data-update]');
|
|
294
|
+
if (upd) {
|
|
295
|
+
const row = ctx.rows.find((r) => r.key === upd.dataset.key);
|
|
296
|
+
if (!row) return;
|
|
297
|
+
if (upd.dataset.update === 'keep') { ctx.keepUpdate(row.folderName, row.update?.upstreamRevisionId); ctx.toast(t('skillsStudio.toast.keptMine', { name: row.folderName })); ctx.render(); }
|
|
298
|
+
else reviewUpdateFlow(ctx, row, 'apply');
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const btn = e.target.closest?.('[data-review]');
|
|
302
|
+
if (!btn || btn.disabled) return;
|
|
303
|
+
const review = (ctx.data?.reviews || []).find((r) => r.reviewId === btn.dataset.id);
|
|
304
|
+
if (!review) return;
|
|
305
|
+
const card = reviewCardModel(review);
|
|
306
|
+
const action = btn.dataset.review;
|
|
307
|
+
const row = ctx.rows.find((r) => r.skill?.skillId === review.skillId);
|
|
308
|
+
if (action === 'open') { if (row) ctx.openRow(row.key, { tab: 'versions' }); return; }
|
|
309
|
+
if (action === 'publish') { if (row) ctx.openRow(row.key, { tab: 'publish', revisionId: card.revisionId, reviewId: card.id }); return; }
|
|
310
|
+
if (action === 'install' || action === 'approve_install') { await installFromCard(ctx, host, card, review, btn, { approve: action === 'approve_install' }); return; }
|
|
311
|
+
if (installBusy(review.skillId) && action !== 'open') { ctx.toast(t('skillsStudio.publish.busyElsewhere'), 'error'); return; }
|
|
312
|
+
let reason = '';
|
|
313
|
+
if (action !== 'approve') {
|
|
314
|
+
reason = await askReason(ctx, {
|
|
315
|
+
title: t(action === 'reject' ? 'skillsStudio.review.rejectTitle' : 'skillsStudio.review.changesTitle', { name: card.name }),
|
|
316
|
+
prompt: t(action === 'reject' ? 'skillsStudio.review.rejectPrompt' : 'skillsStudio.review.changesPrompt'),
|
|
317
|
+
required: action === 'request_changes',
|
|
318
|
+
});
|
|
319
|
+
if (reason === null) return;
|
|
320
|
+
}
|
|
321
|
+
const gen = ctx.gen();
|
|
322
|
+
btn.disabled = true;
|
|
323
|
+
try {
|
|
324
|
+
await ctx.api.decideSkillsReview(card.id, action, reason);
|
|
325
|
+
if (!ctx.alive(gen)) return;
|
|
326
|
+
ctx.toast(t(`skillsStudio.review.decided.${action}`, { name: card.name }));
|
|
327
|
+
await ctx.reload();
|
|
328
|
+
} catch (err) {
|
|
329
|
+
if (!ctx.alive(gen)) return;
|
|
330
|
+
btn.disabled = false;
|
|
331
|
+
ctx.toast(t('skillsStudio.review.decideFailed', { error: studioErrorText(ctx.t, err) }), 'error');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Approve (when asked) and install to the targets the submission named, in one go; then the card says where it went
|
|
337
|
+
* and what a running session needs, or why it did not go. Nothing is asked unless the plan asks it.
|
|
338
|
+
*/
|
|
339
|
+
async function installFromCard(ctx, host, card, review, btn, { approve }) {
|
|
340
|
+
const { t } = ctx;
|
|
341
|
+
const row = ctx.rows.find((r) => r.skill?.skillId === review.skillId) || null;
|
|
342
|
+
// Where it goes is decided before anything is approved: nothing of what it named available here means no install
|
|
343
|
+
// (and no approval from this button), never an install elsewhere.
|
|
344
|
+
const dest = reviewTargets(ctx, { review, skill: row?.skill || null, row });
|
|
345
|
+
if (!dest.targets.length) { ctx.toast(t('skillsStudio.review.noDestination'), 'error'); return; }
|
|
346
|
+
// One install of a skill at a time, from whichever surface (the lock covers the approval too, and is let go
|
|
347
|
+
// however this ends); while it is held every card and banner of the skill shows it as on its way.
|
|
348
|
+
const release = acquireInstall(review.skillId);
|
|
349
|
+
if (!release) { ctx.toast(t('skillsStudio.publish.busyElsewhere'), 'error'); return; }
|
|
350
|
+
const gen = ctx.gen();
|
|
351
|
+
let out = null;
|
|
352
|
+
try {
|
|
353
|
+
if (approve) {
|
|
354
|
+
try { await ctx.api.decideSkillsReview(card.id, 'approve', ''); } catch (err) {
|
|
355
|
+
if (ctx.alive(gen)) ctx.toast(t('skillsStudio.review.decideFailed', { error: studioErrorText(ctx.t, err) }), 'error');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
if (!ctx.alive(gen)) return;
|
|
359
|
+
}
|
|
360
|
+
out = await installFlow(ctx, { revisionId: card.revisionId, targets: dest.targets, reviewId: card.id });
|
|
361
|
+
} finally { release(); }
|
|
362
|
+
if (!ctx.alive(gen) || out.status === 'gone') return;
|
|
363
|
+
const words = flowWords(ctx, out);
|
|
364
|
+
if (words) { _said.set(card.id, words); ctx.toast(words.line, words.kind); ctx.announce(words.line); }
|
|
365
|
+
else if (approve) ctx.toast(t('skillsStudio.review.decided.approve', { name: card.name }));
|
|
366
|
+
// The skill's open workspace reads it again: its header pill, banners and Publish say what the Library row says.
|
|
367
|
+
if (out.status === 'done') await refreshSkillWorkspaces(ctx, review.skillId, { resetPublish: true });
|
|
368
|
+
await ctx.reload();
|
|
369
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Skills Studio 2.0: the Review place, as data
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// Review holds what waits for the owner (owner decision 2026-10-22: nothing the Assistant makes installs by itself):
|
|
6
|
+
// - submissions of the Review queue (lib/skills/reviews.js): a new skill or a change, from the SynaBun Assistant
|
|
7
|
+
// or another agent, with the evidence the service computed for exactly its bytes (the checks per target, the
|
|
8
|
+
// scan, what it can do that the approved revision could not, the files it changes, the name it installs as);
|
|
9
|
+
// - updates from SynaBun: a skill this app version ships in a newer version than the copy that is installed
|
|
10
|
+
// (an installation of an older revision, or a folder of its name whose SKILL.md is not the shipped one).
|
|
11
|
+
// Cards are codes and data; the view words them (`skillsStudio.review.*`). An update the owner chose to keep as it
|
|
12
|
+
// is ("Keep mine") stays out of the list until SynaBun ships yet another version. No DOM and no imports.
|
|
13
|
+
|
|
14
|
+
import { revealHidden } from '../ss-unicode.js';
|
|
15
|
+
|
|
16
|
+
export const OPEN_STATES = Object.freeze(['pending', 'changes_requested', 'approved', 'partial', 'publish_failed']);
|
|
17
|
+
export const DECIDED_STATES = Object.freeze(['published', 'rejected', 'withdrawn']);
|
|
18
|
+
const AUTHORITY_KINDS = ['scripts', 'binaries', 'dynamicContext', 'hooks', 'toolGrants', 'domains', 'sensitive', 'persistentWrites', 'installers'];
|
|
19
|
+
|
|
20
|
+
const list = (v) => (Array.isArray(v) ? v : []);
|
|
21
|
+
const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
|
|
22
|
+
|
|
23
|
+
/** What a revision can do that the approved one could not: one entry per kind, with what was added and removed. */
|
|
24
|
+
export function authorityRows(delta) {
|
|
25
|
+
const out = [];
|
|
26
|
+
for (const [kind, change] of Object.entries(delta?.byKind || {})) {
|
|
27
|
+
if (!AUTHORITY_KINDS.includes(kind)) continue;
|
|
28
|
+
const added = list(change?.added).map(String);
|
|
29
|
+
const removed = list(change?.removed).map(String);
|
|
30
|
+
if (added.length || removed.length) out.push({ kind, added, removed });
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The scan of a submission, as a code and a severity. */
|
|
36
|
+
export function scanState(scan) {
|
|
37
|
+
if (!scan) return { code: 'not-scanned', sev: 'warn', found: 0 };
|
|
38
|
+
const found = Object.values(scan.counts || {}).reduce((n, c) => n + num(c), 0);
|
|
39
|
+
if (scan.status === 'error') return { code: 'error', sev: 'err', found };
|
|
40
|
+
if (scan.gate === 'blocked') return { code: 'blocked', sev: 'err', found };
|
|
41
|
+
if (scan.status === 'no_findings') return { code: 'clean', sev: 'ok', found: 0 };
|
|
42
|
+
if (scan.status === 'incomplete') return { code: 'incomplete', sev: 'warn', found };
|
|
43
|
+
return { code: scan.gate === 'review' ? 'review' : 'findings', sev: scan.gate === 'review' ? 'warn' : 'info', found };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* What the owner can do with a submission now. Approving installs: a pending submission's first action is
|
|
48
|
+
* `approve_install` (approve, then install to the targets it named, in one go); `approve` alone stays. One approved
|
|
49
|
+
* but not installed (or installed in part, or whose install failed) offers `install`.
|
|
50
|
+
*/
|
|
51
|
+
export function reviewActions(review, { guest = false } = {}) {
|
|
52
|
+
if (guest) return [];
|
|
53
|
+
if (review?.state === 'pending') return ['approve_install', 'approve', 'request_changes', 'reject'];
|
|
54
|
+
if (review?.state === 'changes_requested') return ['reject'];
|
|
55
|
+
if (['approved', 'partial', 'publish_failed'].includes(review?.state)) return ['install'];
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* One submission as a card.
|
|
61
|
+
* @returns {object}
|
|
62
|
+
*/
|
|
63
|
+
export function reviewCardModel(review, { guest = false } = {}) {
|
|
64
|
+
const ev = review?.evidence || {};
|
|
65
|
+
const lintErrors = Object.values(ev.lint?.profiles || {}).reduce((n, p) => n + num(p?.errors), 0);
|
|
66
|
+
const lintWarnings = Object.values(ev.lint?.profiles || {}).reduce((n, p) => n + num(p?.warnings), 0);
|
|
67
|
+
const author = review?.author || {};
|
|
68
|
+
return {
|
|
69
|
+
id: review?.reviewId,
|
|
70
|
+
skillId: review?.skillId || null,
|
|
71
|
+
name: revealHidden(String(review?.skillName || review?.skillId || '')),
|
|
72
|
+
kind: review?.baseApprovedRevisionId ? 'change' : 'new',
|
|
73
|
+
state: review?.state || 'pending',
|
|
74
|
+
author: { kind: author.kind === 'owner' ? 'owner' : author.kind === 'assistant' ? 'assistant' : 'agent', provider: author.provider || null, model: author.model || null, runId: author.runId || null },
|
|
75
|
+
createdAt: review?.createdAt || null,
|
|
76
|
+
decidedAt: review?.decidedAt || null,
|
|
77
|
+
notes: String(review?.releaseNotes || ''),
|
|
78
|
+
assumptions: list(review?.assumptions).map(String).slice(0, 20),
|
|
79
|
+
targets: list(review?.targets).map((t) => ({ runtime: t?.runtime, scope: t?.scope || 'user', projectPath: t?.projectPath || null, accountId: t?.accountId || null })),
|
|
80
|
+
installAs: typeof ev.installName === 'string' && ev.installName ? revealHidden(ev.installName) : null,
|
|
81
|
+
lint: { errors: lintErrors, warnings: lintWarnings, resourceProblems: num(ev.lint?.resourceProblems) },
|
|
82
|
+
scan: { ...scanState(ev.scan), findings: list(ev.scan?.findings).slice(0, 10).map((f) => ({ ...f, severity: ['critical', 'high', 'medium', 'low', 'info'].includes(f?.severity) ? f.severity : 'low', message: String(f?.message ?? ''), file: f?.file || null, line: f?.line || null, ruleId: f?.ruleId || null })) },
|
|
83
|
+
authority: authorityRows(ev.authority),
|
|
84
|
+
files: list(ev.diff?.files).map((f) => ({ path: String(f.path ?? ''), status: f.status })),
|
|
85
|
+
fromRevisionId: ev.diff?.from?.revisionId || review?.baseApprovedRevisionId || null,
|
|
86
|
+
revisionId: review?.submittedRevisionId || null,
|
|
87
|
+
reason: review?.decisionReason || '',
|
|
88
|
+
comments: list(review?.comments).slice(-5).map((c) => ({ by: c?.by?.kind === 'owner' ? 'owner' : (c?.by?.kind || 'agent'), body: String(c?.body ?? ''), at: c?.at || null })),
|
|
89
|
+
publication: list(review?.publication?.targetResults).slice(-12).map((r) => ({ targetKey: r.targetKey, status: r.status, error: r.error || null })),
|
|
90
|
+
actions: reviewActions(review, { guest }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Open submissions first (pending, then the rest), newest first in each; decided ones apart. */
|
|
95
|
+
export function sortReviews(reviews) {
|
|
96
|
+
const rank = { pending: 0, changes_requested: 1, approved: 2, partial: 3, publish_failed: 4 };
|
|
97
|
+
const open = list(reviews).filter((r) => OPEN_STATES.includes(r?.state))
|
|
98
|
+
.sort((a, b) => (rank[a.state] - rank[b.state]) || String(b.createdAt || '').localeCompare(String(a.createdAt || '')));
|
|
99
|
+
const decided = list(reviews).filter((r) => DECIDED_STATES.includes(r?.state))
|
|
100
|
+
.sort((a, b) => String(b.updatedAt || b.decidedAt || '').localeCompare(String(a.updatedAt || a.decidedAt || '')));
|
|
101
|
+
return { open, decided };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The updates SynaBun ships, from the Library rows (sx-library.js marks a bundled row's `update`), less the ones the
|
|
106
|
+
* owner keeps as they are for that upstream revision.
|
|
107
|
+
* @param {object[]} rows
|
|
108
|
+
* @param {Record<string, string>} [kept] skill name → the upstream revision id the owner said "Keep mine" to
|
|
109
|
+
*/
|
|
110
|
+
export function updateCards(rows, kept = {}) {
|
|
111
|
+
return list(rows).filter((r) => r?.update && r.source === 'synabun' && r.kind === 'skill')
|
|
112
|
+
.filter((r) => !(r.update.upstreamRevisionId && kept?.[r.folderName] === r.update.upstreamRevisionId))
|
|
113
|
+
.map((r) => ({
|
|
114
|
+
key: r.key,
|
|
115
|
+
name: r.folderName,
|
|
116
|
+
label: r.label || revealHidden(r.folderName || ''),
|
|
117
|
+
skillId: r.skill?.skillId || null,
|
|
118
|
+
upstreamRevisionId: r.update.upstreamRevisionId,
|
|
119
|
+
kind: r.update.kind,
|
|
120
|
+
installations: r.update.installations,
|
|
121
|
+
legacyCopies: r.update.legacyCopies,
|
|
122
|
+
runtimes: [...new Set([...(r.update.legacyCopies || []).flatMap((c) => c.runtimes || []), ...((r.skill?.installations || []).filter((i) => r.update.installations.includes(i.installationId)).map((i) => i.runtime))])],
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The install targets an update of a shipped skill replaces: the folder each old copy is in (the target whose root
|
|
128
|
+
* holds it), and the target of each older installation. Never every runtime that reads a copy: OpenCode reads
|
|
129
|
+
* Claude Code's folder too, and a second copy in its own folder would be listed twice.
|
|
130
|
+
* @param {object} update a row's `update` (sx-library.js)
|
|
131
|
+
* @param {object[]} installations the library skill's installations (GET /skills)
|
|
132
|
+
* @param {{ key: string, path: string|null, runtime: string, scope: string, accountId: string, projectPath: string|null }[]} targets sx-subject.js installTargets()
|
|
133
|
+
* @returns {string[]} target keys
|
|
134
|
+
*/
|
|
135
|
+
export function updateTargetKeys(update, installations, targets) {
|
|
136
|
+
const trim = (p) => String(p || '').replace(/[\\/]+$/, '');
|
|
137
|
+
const parent = (p) => trim(p).replace(/[\\/][^\\/]*$/, '');
|
|
138
|
+
const folders = new Set(list(update?.legacyCopies).map((c) => parent(c?.path)).filter(Boolean));
|
|
139
|
+
const ids = new Set(list(update?.installations));
|
|
140
|
+
const keyOf = (i) => (i?.scope === 'project' ? `${i.runtime}:project:${i.projectPath}` : `${i?.runtime}:user:${i?.accountId || 'default'}`);
|
|
141
|
+
const installed = new Set(list(installations).filter((i) => ids.has(i?.installationId)).map(keyOf));
|
|
142
|
+
return list(targets).filter((tg) => !tg.off && ((tg.path && folders.has(trim(tg.path))) || installed.has(tg.key))).map((tg) => tg.key);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** How many things wait in Review: open submissions that need the owner, and updates. */
|
|
146
|
+
export function reviewCount(reviews, updates = []) {
|
|
147
|
+
return list(reviews).filter((r) => r?.state === 'pending' || ['approved', 'partial', 'publish_failed'].includes(r?.state)).length + list(updates).length;
|
|
148
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Scan evidence belongs to the exact immutable bytes. Missing/stale/partial evidence never becomes a clean badge.
|
|
2
|
+
export const SCAN_SEVERITIES = ['critical', 'high', 'medium', 'low', 'info'];
|
|
3
|
+
export function scanStatus(scan, digest) {
|
|
4
|
+
const counts = Object.fromEntries(SCAN_SEVERITIES.map((s) => [s, Number.isSafeInteger(scan?.counts?.[s]) && scan.counts[s] >= 0 ? scan.counts[s] : (scan?.findings || []).filter((f) => f.severity === s).length]));
|
|
5
|
+
const count = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
6
|
+
let code = 'notScanned';
|
|
7
|
+
if (scan && scan.digest === digest) {
|
|
8
|
+
if (scan.status === 'error') code = 'error';
|
|
9
|
+
else if (scan.coverage?.complete === false || ['incomplete', 'skipped'].includes(scan.status)) code = 'incomplete';
|
|
10
|
+
else if (scan.coverage?.complete === true && ['no_findings', 'findings'].includes(scan.status)) code = count || scan.status === 'findings' ? 'findings' : 'clean';
|
|
11
|
+
}
|
|
12
|
+
return { code, counts, count, passed: ['clean', 'findings'].includes(code) && scan?.gate === 'allowed' && !['critical', 'high', 'medium'].some((s) => counts[s]) };
|
|
13
|
+
}
|
|
14
|
+
export function scanText(t, scan, digest) {
|
|
15
|
+
const status = scanStatus(scan, digest);
|
|
16
|
+
const counts = SCAN_SEVERITIES.filter((s) => status.counts[s]).map((s) => t('skillsStudio.scan.severityCount', { severity: t(`skillsStudio.scan.severity.${s}`), count: status.counts[s] })).join(', ');
|
|
17
|
+
return t('skillsStudio.scan.summary', { status: t(`skillsStudio.scan.status.${status.code}`), count: status.count, detail: counts ? ` · ${counts}` : '' });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function scanReasonText(t, reason) {
|
|
21
|
+
const keys = { 'asset-invalid-structure': 'assetInvalid', 'asset-script-content': 'assetScript', 'asset-too-large': 'assetTooLarge', 'asset-decompression-budget': 'assetBudget', 'shell-analysis-limit': 'shellLimit' };
|
|
22
|
+
return keys[reason] ? t(`skillsStudio.scan.reasons.${keys[reason]}`) : reason;
|
|
23
|
+
}
|
|
24
|
+
export function scanFindingText(t, finding) {
|
|
25
|
+
if (finding.reason) return scanReasonText(t, finding.reason);
|
|
26
|
+
if (finding.ruleId === 'S-09-asset-command') return t('skillsStudio.scan.assetCommand');
|
|
27
|
+
if (finding.ruleId === 'S-08-download-run') {
|
|
28
|
+
const { download, run } = finding.locations || {};
|
|
29
|
+
if (!download?.file || !download?.line || !run?.file || !run?.line) return finding.message;
|
|
30
|
+
return t('skillsStudio.scan.downloadRun', { download: `${download.file}:${download.line}`, run: `${run.file}:${run.line}` });
|
|
31
|
+
}
|
|
32
|
+
if (finding.ruleId === 'S-09-asset-exec') return t('skillsStudio.scan.assetExecution');
|
|
33
|
+
if (finding.ruleId === 'S-12-hidden-chars' && finding.total) return t('skillsStudio.scan.hiddenTotal', { count: finding.total, listed: finding.samples?.length || 0 }) + (finding.samples?.length ? ` · ${finding.samples.map((s) => `${s.file}:${s.line}:${s.column} ${s.label}`).join('; ')}` : finding.label ? `: ${finding.label}` : '');
|
|
34
|
+
return finding.message;
|
|
35
|
+
}
|