vellum 0.0.16 → 0.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/.dockerignore +27 -0
- package/.env.example +22 -0
- package/Dockerfile +99 -0
- package/Dockerfile.sandbox +5 -0
- package/README.md +150 -3
- package/bun.lock +1768 -0
- package/bunfig.toml +2 -0
- package/docs/skills.md +158 -0
- package/drizzle/0000_dizzy_maggott.sql +301 -0
- package/drizzle/meta/0000_snapshot.json +1999 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/eslint.config.mjs +17 -0
- package/hook-templates/debug-prompt-logger/hook.json +7 -0
- package/hook-templates/debug-prompt-logger/run.sh +68 -0
- package/knip.json +9 -0
- package/package.json +60 -10
- package/scripts/ipc/check-contract-inventory.ts +104 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +163 -0
- package/scripts/ipc/generate-swift.ts +492 -0
- package/scripts/test-filesystem-tools.sh +48 -0
- package/scripts/test.sh +122 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2079 -0
- package/src/__tests__/account-registry.test.ts +244 -0
- package/src/__tests__/active-skill-tools.test.ts +378 -0
- package/src/__tests__/agent-loop-thinking.test.ts +81 -0
- package/src/__tests__/agent-loop.test.ts +1135 -0
- package/src/__tests__/anthropic-provider.test.ts +778 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +290 -0
- package/src/__tests__/app-bundler.test.ts +313 -0
- package/src/__tests__/app-executors.test.ts +613 -0
- package/src/__tests__/app-open-proxy.test.ts +62 -0
- package/src/__tests__/asset-materialize-tool.test.ts +451 -0
- package/src/__tests__/asset-search-tool.test.ts +476 -0
- package/src/__tests__/assistant-attachment-directive.test.ts +401 -0
- package/src/__tests__/assistant-attachments.test.ts +437 -0
- package/src/__tests__/assistant-event-hub.test.ts +226 -0
- package/src/__tests__/assistant-event.test.ts +123 -0
- package/src/__tests__/attachments-store.test.ts +547 -0
- package/src/__tests__/attachments.test.ts +134 -0
- package/src/__tests__/audit-log-rotation.test.ts +154 -0
- package/src/__tests__/browser-fill-credential.test.ts +309 -0
- package/src/__tests__/browser-manager.test.ts +203 -0
- package/src/__tests__/browser-runtime-check.test.ts +55 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +67 -0
- package/src/__tests__/browser-skill-endstate.test.ts +198 -0
- package/src/__tests__/bundle-scanner.test.ts +313 -0
- package/src/__tests__/checker.test.ts +3856 -0
- package/src/__tests__/clarification-resolver.test.ts +159 -0
- package/src/__tests__/classifier.test.ts +67 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +127 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +88 -0
- package/src/__tests__/cli-discover.test.ts +85 -0
- package/src/__tests__/cli.test.ts +81 -0
- package/src/__tests__/clipboard.test.ts +80 -0
- package/src/__tests__/commit-guarantee.test.ts +335 -0
- package/src/__tests__/computer-use-session-compaction.test.ts +132 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +293 -0
- package/src/__tests__/computer-use-session-working-dir.test.ts +117 -0
- package/src/__tests__/computer-use-skill-baseline.test.ts +74 -0
- package/src/__tests__/computer-use-skill-endstate.test.ts +89 -0
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +217 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +107 -0
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +54 -0
- package/src/__tests__/config-schema.test.ts +720 -0
- package/src/__tests__/conflict-store.test.ts +329 -0
- package/src/__tests__/connection-policy.test.ts +102 -0
- package/src/__tests__/context-memory-e2e.test.ts +434 -0
- package/src/__tests__/context-token-estimator.test.ts +135 -0
- package/src/__tests__/context-window-manager.test.ts +376 -0
- package/src/__tests__/contradiction-checker.test.ts +216 -0
- package/src/__tests__/conversation-store.test.ts +614 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +517 -0
- package/src/__tests__/credential-broker-server-use.test.ts +554 -0
- package/src/__tests__/credential-broker.test.ts +167 -0
- package/src/__tests__/credential-host-pattern-match.test.ts +104 -0
- package/src/__tests__/credential-metadata-store.test.ts +779 -0
- package/src/__tests__/credential-policy-validate.test.ts +121 -0
- package/src/__tests__/credential-resolve.test.ts +328 -0
- package/src/__tests__/credential-security-e2e.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +563 -0
- package/src/__tests__/credential-selection.test.ts +354 -0
- package/src/__tests__/credential-vault.test.ts +852 -0
- package/src/__tests__/daemon-assistant-events.test.ts +164 -0
- package/src/__tests__/daemon-server-session-init.test.ts +522 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +97 -0
- package/src/__tests__/diff.test.ts +121 -0
- package/src/__tests__/domain-normalize.test.ts +112 -0
- package/src/__tests__/domain-policy.test.ts +124 -0
- package/src/__tests__/doordash-client.test.ts +186 -0
- package/src/__tests__/doordash-session.test.ts +143 -0
- package/src/__tests__/dynamic-page-surface.test.ts +91 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +132 -0
- package/src/__tests__/edit-engine.test.ts +180 -0
- package/src/__tests__/email-cli.test.ts +283 -0
- package/src/__tests__/encrypted-store.test.ts +332 -0
- package/src/__tests__/entity-extractor.test.ts +190 -0
- package/src/__tests__/ephemeral-permissions.test.ts +312 -0
- package/src/__tests__/evaluate-typescript-tool.test.ts +286 -0
- package/src/__tests__/event-bus.test.ts +222 -0
- package/src/__tests__/file-edit-tool.test.ts +122 -0
- package/src/__tests__/file-ops-service.test.ts +330 -0
- package/src/__tests__/file-read-tool.test.ts +75 -0
- package/src/__tests__/file-write-tool.test.ts +113 -0
- package/src/__tests__/fixtures/credential-security-fixtures.ts +181 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +126 -0
- package/src/__tests__/fixtures/mock-signup-server.ts +387 -0
- package/src/__tests__/fixtures/proxy-fixtures.ts +147 -0
- package/src/__tests__/fuzzy-match-property.test.ts +216 -0
- package/src/__tests__/fuzzy-match.test.ts +138 -0
- package/src/__tests__/gemini-image-service.test.ts +261 -0
- package/src/__tests__/gemini-provider.test.ts +651 -0
- package/src/__tests__/get-weather.test.ts +318 -0
- package/src/__tests__/gmail-integration.test.ts +73 -0
- package/src/__tests__/handlers-cu-observation-blob.test.ts +351 -0
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +190 -0
- package/src/__tests__/handlers-slack-config.test.ts +199 -0
- package/src/__tests__/handlers-task-submit-slash.test.ts +38 -0
- package/src/__tests__/headless-browser-interactions.test.ts +536 -0
- package/src/__tests__/headless-browser-navigate.test.ts +211 -0
- package/src/__tests__/headless-browser-read-tools.test.ts +261 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +185 -0
- package/src/__tests__/history-repair-observability.test.ts +56 -0
- package/src/__tests__/history-repair.test.ts +510 -0
- package/src/__tests__/home-base-bootstrap.test.ts +77 -0
- package/src/__tests__/hooks-blocking.test.ts +128 -0
- package/src/__tests__/hooks-cli.test.ts +144 -0
- package/src/__tests__/hooks-config.test.ts +93 -0
- package/src/__tests__/hooks-discovery.test.ts +199 -0
- package/src/__tests__/hooks-integration.test.ts +189 -0
- package/src/__tests__/hooks-manager.test.ts +187 -0
- package/src/__tests__/hooks-runner.test.ts +178 -0
- package/src/__tests__/hooks-settings.test.ts +154 -0
- package/src/__tests__/hooks-templates.test.ts +137 -0
- package/src/__tests__/hooks-ts-runner.test.ts +125 -0
- package/src/__tests__/hooks-watch.test.ts +100 -0
- package/src/__tests__/host-file-edit-tool.test.ts +104 -0
- package/src/__tests__/host-file-read-tool.test.ts +61 -0
- package/src/__tests__/host-file-write-tool.test.ts +77 -0
- package/src/__tests__/host-shell-tool.test.ts +311 -0
- package/src/__tests__/intent-routing.test.ts +255 -0
- package/src/__tests__/ipc-blob-store.test.ts +315 -0
- package/src/__tests__/ipc-contract-inventory.test.ts +54 -0
- package/src/__tests__/ipc-contract.test.ts +74 -0
- package/src/__tests__/ipc-protocol.test.ts +113 -0
- package/src/__tests__/ipc-snapshot.test.ts +1560 -0
- package/src/__tests__/ipc-validate.test.ts +357 -0
- package/src/__tests__/key-migration.test.ts +183 -0
- package/src/__tests__/keychain.test.ts +258 -0
- package/src/__tests__/llm-usage-store.test.ts +226 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +257 -0
- package/src/__tests__/managed-store.test.ts +608 -0
- package/src/__tests__/media-generate-image.test.ts +238 -0
- package/src/__tests__/media-reuse-story.e2e.test.ts +676 -0
- package/src/__tests__/media-visibility-policy.test.ts +141 -0
- package/src/__tests__/memory-context-benchmark.test.ts +235 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +481 -0
- package/src/__tests__/memory-query-builder.test.ts +59 -0
- package/src/__tests__/memory-recall-quality.test.ts +846 -0
- package/src/__tests__/memory-regressions.experimental.test.ts +538 -0
- package/src/__tests__/memory-regressions.test.ts +4238 -0
- package/src/__tests__/memory-retrieval-budget.test.ts +49 -0
- package/src/__tests__/migration-cli-flows.test.ts +169 -0
- package/src/__tests__/migration-ordering.test.ts +249 -0
- package/src/__tests__/mock-signup-server.test.ts +528 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +166 -0
- package/src/__tests__/onboarding-template-contract.test.ts +58 -0
- package/src/__tests__/openai-provider.test.ts +753 -0
- package/src/__tests__/parser.test.ts +472 -0
- package/src/__tests__/path-classifier.test.ts +73 -0
- package/src/__tests__/path-policy.test.ts +435 -0
- package/src/__tests__/platform-move-helper.test.ts +99 -0
- package/src/__tests__/platform-socket-path.test.ts +52 -0
- package/src/__tests__/platform-workspace-migration.test.ts +1000 -0
- package/src/__tests__/platform.test.ts +131 -0
- package/src/__tests__/prebuilt-home-base-seed.test.ts +71 -0
- package/src/__tests__/pricing.test.ts +256 -0
- package/src/__tests__/profile-compiler.test.ts +373 -0
- package/src/__tests__/provider-registry-ollama.test.ts +16 -0
- package/src/__tests__/proxy-approval-callback.test.ts +601 -0
- package/src/__tests__/ratelimit.test.ts +297 -0
- package/src/__tests__/registry.test.ts +487 -0
- package/src/__tests__/reminder-store.test.ts +220 -0
- package/src/__tests__/reminder.test.ts +263 -0
- package/src/__tests__/request-file-tool.test.ts +158 -0
- package/src/__tests__/run-orchestrator.test.ts +200 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +190 -0
- package/src/__tests__/runtime-runs-http.test.ts +451 -0
- package/src/__tests__/runtime-runs.test.ts +273 -0
- package/src/__tests__/sandbox-diagnostics.test.ts +408 -0
- package/src/__tests__/sandbox-host-parity.test.ts +950 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +253 -0
- package/src/__tests__/script-proxy-certs.test.ts +90 -0
- package/src/__tests__/script-proxy-connect-tunnel.test.ts +177 -0
- package/src/__tests__/script-proxy-decision-trace.test.ts +156 -0
- package/src/__tests__/script-proxy-http-forwarder.test.ts +281 -0
- package/src/__tests__/script-proxy-injection-runtime.test.ts +401 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +407 -0
- package/src/__tests__/script-proxy-policy-runtime.test.ts +287 -0
- package/src/__tests__/script-proxy-policy.test.ts +310 -0
- package/src/__tests__/script-proxy-rewrite-specificity.test.ts +135 -0
- package/src/__tests__/script-proxy-router.test.ts +180 -0
- package/src/__tests__/script-proxy-session-manager.test.ts +382 -0
- package/src/__tests__/script-proxy-session-runtime.test.ts +113 -0
- package/src/__tests__/secret-allowlist.test.ts +229 -0
- package/src/__tests__/secret-ingress-handler.test.ts +99 -0
- package/src/__tests__/secret-onetime-send.test.ts +130 -0
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +106 -0
- package/src/__tests__/secret-response-routing.test.ts +93 -0
- package/src/__tests__/secret-scanner-executor.test.ts +348 -0
- package/src/__tests__/secret-scanner.test.ts +857 -0
- package/src/__tests__/secure-keys.test.ts +323 -0
- package/src/__tests__/server-history-render.test.ts +430 -0
- package/src/__tests__/session-abort-tool-results.test.ts +240 -0
- package/src/__tests__/session-conflict-gate.test.ts +697 -0
- package/src/__tests__/session-error.test.ts +341 -0
- package/src/__tests__/session-evictor.test.ts +188 -0
- package/src/__tests__/session-load-history-repair.test.ts +222 -0
- package/src/__tests__/session-pre-run-repair.test.ts +213 -0
- package/src/__tests__/session-profile-injection.test.ts +444 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +306 -0
- package/src/__tests__/session-queue.test.ts +1462 -0
- package/src/__tests__/session-runtime-assembly.test.ts +315 -0
- package/src/__tests__/session-runtime-workspace.test.ts +183 -0
- package/src/__tests__/session-skill-tools.test.ts +2431 -0
- package/src/__tests__/session-slash-known.test.ts +368 -0
- package/src/__tests__/session-slash-queue.test.ts +288 -0
- package/src/__tests__/session-slash-unknown.test.ts +271 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +473 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +140 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +140 -0
- package/src/__tests__/session-undo.test.ts +75 -0
- package/src/__tests__/session-workspace-cache-state.test.ts +246 -0
- package/src/__tests__/session-workspace-injection.test.ts +327 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +240 -0
- package/src/__tests__/shared-filesystem-errors.test.ts +78 -0
- package/src/__tests__/shell-credential-ref.test.ts +187 -0
- package/src/__tests__/shell-parser-fuzz.test.ts +544 -0
- package/src/__tests__/shell-parser-property.test.ts +433 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +272 -0
- package/src/__tests__/signup-e2e.test.ts +352 -0
- package/src/__tests__/size-guard.test.ts +117 -0
- package/src/__tests__/skill-include-graph.test.ts +303 -0
- package/src/__tests__/skill-load-tool.test.ts +409 -0
- package/src/__tests__/skill-script-runner-host.test.ts +489 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +349 -0
- package/src/__tests__/skill-tool-factory.test.ts +252 -0
- package/src/__tests__/skill-tool-manifest.test.ts +658 -0
- package/src/__tests__/skill-version-hash.test.ts +182 -0
- package/src/__tests__/skills.test.ts +597 -0
- package/src/__tests__/slash-commands-catalog.test.ts +86 -0
- package/src/__tests__/slash-commands-parser.test.ts +119 -0
- package/src/__tests__/slash-commands-resolver.test.ts +193 -0
- package/src/__tests__/slash-commands-rewrite.test.ts +39 -0
- package/src/__tests__/starter-bundle.test.ts +136 -0
- package/src/__tests__/starter-task-flow.test.ts +143 -0
- package/src/__tests__/subagent-manager-notify.test.ts +372 -0
- package/src/__tests__/subagent-tools.test.ts +118 -0
- package/src/__tests__/subagent-types.test.ts +78 -0
- package/src/__tests__/swarm-orchestrator.test.ts +428 -0
- package/src/__tests__/swarm-plan-validator.test.ts +330 -0
- package/src/__tests__/swarm-recursion.test.ts +165 -0
- package/src/__tests__/swarm-router-planner.test.ts +208 -0
- package/src/__tests__/swarm-session-integration.test.ts +274 -0
- package/src/__tests__/swarm-tool.test.ts +145 -0
- package/src/__tests__/swarm-worker-backend.test.ts +129 -0
- package/src/__tests__/swarm-worker-runner.test.ts +272 -0
- package/src/__tests__/system-prompt.test.ts +461 -0
- package/src/__tests__/task-compiler.test.ts +283 -0
- package/src/__tests__/task-runner.test.ts +215 -0
- package/src/__tests__/task-scheduler.test.ts +216 -0
- package/src/__tests__/task-tools.test.ts +602 -0
- package/src/__tests__/terminal-sandbox-docker.test.ts +1064 -0
- package/src/__tests__/terminal-sandbox.integration.test.ts +178 -0
- package/src/__tests__/terminal-sandbox.test.ts +202 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +90 -0
- package/src/__tests__/test-support/computer-use-skill-harness.ts +45 -0
- package/src/__tests__/tool-audit-listener.test.ts +112 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +251 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +516 -0
- package/src/__tests__/tool-executor-redaction.test.ts +289 -0
- package/src/__tests__/tool-executor.test.ts +1971 -0
- package/src/__tests__/tool-metrics-listener.test.ts +225 -0
- package/src/__tests__/tool-notification-listener.test.ts +49 -0
- package/src/__tests__/tool-policy.test.ts +54 -0
- package/src/__tests__/tool-profiling-listener.test.ts +268 -0
- package/src/__tests__/tool-result-truncation.test.ts +217 -0
- package/src/__tests__/tool-trace-listener.test.ts +226 -0
- package/src/__tests__/top-level-renderer.test.ts +121 -0
- package/src/__tests__/top-level-scanner.test.ts +141 -0
- package/src/__tests__/trace-emitter.test.ts +173 -0
- package/src/__tests__/trust-store.test.ts +2030 -0
- package/src/__tests__/turn-commit.test.ts +219 -0
- package/src/__tests__/url-safety.test.ts +418 -0
- package/src/__tests__/weather-skill-regression.test.ts +225 -0
- package/src/__tests__/web-fetch.test.ts +869 -0
- package/src/__tests__/web-search.test.ts +584 -0
- package/src/__tests__/workspace-git-service.test.ts +750 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +347 -0
- package/src/__tests__/workspace-lifecycle.test.ts +292 -0
- package/src/agent/attachments.ts +35 -0
- package/src/agent/loop.ts +500 -0
- package/src/agent/message-types.ts +17 -0
- package/src/autonomy/autonomy-resolver.ts +60 -0
- package/src/autonomy/autonomy-store.ts +122 -0
- package/src/autonomy/disposition-mapper.ts +31 -0
- package/src/autonomy/index.ts +11 -0
- package/src/autonomy/types.ts +39 -0
- package/src/bundler/app-bundler.ts +274 -0
- package/src/bundler/bundle-scanner.ts +535 -0
- package/src/bundler/bundle-signer.ts +124 -0
- package/src/bundler/manifest.ts +21 -0
- package/src/bundler/signature-verifier.ts +184 -0
- package/src/cli/autonomy.ts +188 -0
- package/src/cli/contacts.ts +149 -0
- package/src/cli/doordash.ts +824 -0
- package/src/cli/email-guardrails.ts +200 -0
- package/src/cli/email.ts +405 -0
- package/src/cli/main-screen.tsx +155 -0
- package/src/cli.ts +935 -0
- package/src/config/bundled-skills/.gitkeep +0 -0
- package/src/config/bundled-skills/agentmail/SKILL.md +128 -0
- package/src/config/bundled-skills/agentmail/icon.svg +21 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1348 -0
- package/src/config/bundled-skills/app-builder/TOOLS.json +279 -0
- package/src/config/bundled-skills/app-builder/icon.svg +9 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +15 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +11 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +18 -0
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +11 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-query.ts +10 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +20 -0
- package/src/config/bundled-skills/browser/SKILL.md +28 -0
- package/src/config/bundled-skills/browser/TOOLS.json +234 -0
- package/src/config/bundled-skills/browser/tools/browser-click.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-close.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-type.ts +9 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +9 -0
- package/src/config/bundled-skills/claude-code/SKILL.md +50 -0
- package/src/config/bundled-skills/claude-code/TOOLS.json +40 -0
- package/src/config/bundled-skills/claude-code/tools/claude-code.ts +9 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +17 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +326 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-done.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-double-click.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-drag.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-key.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-open-app.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-request-control.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-respond.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-right-click.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-run-applescript.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-scroll.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-type-text.ts +9 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-wait.ts +9 -0
- package/src/config/bundled-skills/google-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/google-calendar/TOOLS.json +108 -0
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +165 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +21 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +42 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +13 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +30 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +41 -0
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +18 -0
- package/src/config/bundled-skills/google-calendar/types.ts +97 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +32 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +42 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +137 -0
- package/src/config/bundled-skills/messaging/SKILL.md +126 -0
- package/src/config/bundled-skills/messaging/TOOLS.json +357 -0
- package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +23 -0
- package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +23 -0
- package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/gmail-draft.ts +26 -0
- package/src/config/bundled-skills/messaging/tools/gmail-label.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/gmail-trash.ts +23 -0
- package/src/config/bundled-skills/messaging/tools/gmail-unsubscribe.ts +84 -0
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +18 -0
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +124 -0
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +16 -0
- package/src/config/bundled-skills/messaging/tools/messaging-draft.ts +49 -0
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +21 -0
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +28 -0
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +29 -0
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +22 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +27 -0
- package/src/config/bundled-skills/messaging/tools/shared.ts +71 -0
- package/src/config/bundled-skills/messaging/tools/slack-add-reaction.ts +25 -0
- package/src/config/bundled-skills/messaging/tools/slack-leave-channel.ts +23 -0
- package/src/config/bundled-skills/self-upgrade/SKILL.md +74 -0
- package/src/config/bundled-skills/start-the-day/SKILL.md +70 -0
- package/src/config/bundled-skills/start-the-day/icon.svg +13 -0
- package/src/config/bundled-skills/weather/SKILL.md +37 -0
- package/src/config/bundled-skills/weather/TOOLS.json +32 -0
- package/src/config/bundled-skills/weather/icon.svg +24 -0
- package/src/config/bundled-skills/weather/tools/get-weather.ts +9 -0
- package/src/config/computer-use-prompt.ts +97 -0
- package/src/config/defaults.ts +186 -0
- package/src/config/loader.ts +336 -0
- package/src/config/schema.ts +1004 -0
- package/src/config/skill-state.ts +95 -0
- package/src/config/skills.ts +972 -0
- package/src/config/system-prompt.ts +927 -0
- package/src/config/templates/BOOTSTRAP.md +70 -0
- package/src/config/templates/IDENTITY.md +18 -0
- package/src/config/templates/LOOKS.md +25 -0
- package/src/config/templates/SOUL.md +37 -0
- package/src/config/templates/USER.md +19 -0
- package/src/config/types.ts +32 -0
- package/src/config/vellum-skills/deploy-fullstack-vercel/SKILL.md +179 -0
- package/src/config/vellum-skills/document-writer/SKILL.md +195 -0
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +194 -0
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +147 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +105 -0
- package/src/contacts/contact-store.ts +410 -0
- package/src/contacts/index.ts +11 -0
- package/src/contacts/types.ts +28 -0
- package/src/context/token-estimator.ts +108 -0
- package/src/context/tool-result-truncation.ts +128 -0
- package/src/context/window-manager.ts +531 -0
- package/src/daemon/assistant-attachments.ts +679 -0
- package/src/daemon/classifier.ts +108 -0
- package/src/daemon/computer-use-session.ts +900 -0
- package/src/daemon/connection-policy.ts +41 -0
- package/src/daemon/handlers/apps.ts +446 -0
- package/src/daemon/handlers/computer-use.ts +181 -0
- package/src/daemon/handlers/config.ts +434 -0
- package/src/daemon/handlers/diagnostics.ts +334 -0
- package/src/daemon/handlers/documents.ts +184 -0
- package/src/daemon/handlers/home-base.ts +73 -0
- package/src/daemon/handlers/index.ts +355 -0
- package/src/daemon/handlers/misc.ts +323 -0
- package/src/daemon/handlers/open-bundle-handler.ts +80 -0
- package/src/daemon/handlers/publish.ts +182 -0
- package/src/daemon/handlers/sessions.ts +486 -0
- package/src/daemon/handlers/shared.ts +533 -0
- package/src/daemon/handlers/skills.ts +487 -0
- package/src/daemon/handlers/subagents.ts +122 -0
- package/src/daemon/handlers/work-items.ts +176 -0
- package/src/daemon/handlers.ts +17 -0
- package/src/daemon/history-repair.ts +214 -0
- package/src/daemon/ipc-blob-store.ts +231 -0
- package/src/daemon/ipc-contract-inventory.json +407 -0
- package/src/daemon/ipc-contract-inventory.ts +126 -0
- package/src/daemon/ipc-contract.ts +2102 -0
- package/src/daemon/ipc-protocol.ts +70 -0
- package/src/daemon/ipc-validate.ts +171 -0
- package/src/daemon/lifecycle.ts +503 -0
- package/src/daemon/main.ts +15 -0
- package/src/daemon/media-visibility-policy.ts +57 -0
- package/src/daemon/ride-shotgun-handler.ts +244 -0
- package/src/daemon/server.ts +1085 -0
- package/src/daemon/session-attachments.ts +173 -0
- package/src/daemon/session-conflict-gate.ts +219 -0
- package/src/daemon/session-dynamic-profile.ts +63 -0
- package/src/daemon/session-error.ts +269 -0
- package/src/daemon/session-evictor.ts +196 -0
- package/src/daemon/session-history.ts +437 -0
- package/src/daemon/session-memory.ts +212 -0
- package/src/daemon/session-process.ts +264 -0
- package/src/daemon/session-queue-manager.ts +81 -0
- package/src/daemon/session-runtime-assembly.ts +395 -0
- package/src/daemon/session-skill-tools.ts +237 -0
- package/src/daemon/session-slash.ts +302 -0
- package/src/daemon/session-surfaces.ts +624 -0
- package/src/daemon/session-tool-setup.ts +286 -0
- package/src/daemon/session-usage.ts +74 -0
- package/src/daemon/session-workspace.ts +19 -0
- package/src/daemon/session.ts +1651 -0
- package/src/daemon/trace-emitter.ts +82 -0
- package/src/daemon/watch-handler.ts +274 -0
- package/src/doordash/client.ts +905 -0
- package/src/doordash/queries.ts +1312 -0
- package/src/doordash/query-extractor.ts +93 -0
- package/src/doordash/session.ts +82 -0
- package/src/email/provider.ts +117 -0
- package/src/email/providers/agentmail.ts +317 -0
- package/src/email/providers/index.ts +58 -0
- package/src/email/service.ts +303 -0
- package/src/email/types.ts +126 -0
- package/src/events/bus.ts +157 -0
- package/src/events/domain-events.ts +83 -0
- package/src/events/index.ts +18 -0
- package/src/events/tool-audit-listener.ts +80 -0
- package/src/events/tool-domain-event-publisher.ts +111 -0
- package/src/events/tool-metrics-listener.ts +159 -0
- package/src/events/tool-notification-listener.ts +17 -0
- package/src/events/tool-profiling-listener.ts +158 -0
- package/src/events/tool-trace-listener.ts +75 -0
- package/src/export/formatter.ts +96 -0
- package/src/followups/followup-store.ts +166 -0
- package/src/followups/index.ts +10 -0
- package/src/followups/types.ts +23 -0
- package/src/gallery/default-gallery.ts +795 -0
- package/src/gallery/gallery-manifest.ts +24 -0
- package/src/home-base/app-link-store.ts +82 -0
- package/src/home-base/bootstrap.ts +66 -0
- package/src/home-base/prebuilt/index.html +662 -0
- package/src/home-base/prebuilt/seed-metadata.json +21 -0
- package/src/home-base/prebuilt/seed.ts +101 -0
- package/src/home-base/prebuilt-home-base-updater.ts +30 -0
- package/src/hooks/cli.ts +163 -0
- package/src/hooks/config.ts +88 -0
- package/src/hooks/discovery.ts +110 -0
- package/src/hooks/manager.ts +128 -0
- package/src/hooks/runner.ts +123 -0
- package/src/hooks/templates.ts +52 -0
- package/src/hooks/types.ts +72 -0
- package/src/index.ts +1194 -0
- package/src/instrument.ts +60 -0
- package/src/logfire.ts +99 -0
- package/src/media/gemini-image-service.ts +136 -0
- package/src/memory/account-store.ts +108 -0
- package/src/memory/admin.ts +211 -0
- package/src/memory/app-store.ts +556 -0
- package/src/memory/attachments-store.ts +453 -0
- package/src/memory/channel-delivery-store.ts +368 -0
- package/src/memory/checkpoints.ts +52 -0
- package/src/memory/clarification-resolver.ts +297 -0
- package/src/memory/conflict-store.ts +342 -0
- package/src/memory/contradiction-checker.ts +329 -0
- package/src/memory/conversation-key-store.ts +127 -0
- package/src/memory/conversation-store.ts +469 -0
- package/src/memory/db.ts +1105 -0
- package/src/memory/embedding-backend.ts +229 -0
- package/src/memory/embedding-gemini.ts +52 -0
- package/src/memory/embedding-local.ts +75 -0
- package/src/memory/embedding-ollama.ts +55 -0
- package/src/memory/embedding-openai.ts +25 -0
- package/src/memory/entity-extractor.ts +471 -0
- package/src/memory/fingerprint.ts +20 -0
- package/src/memory/indexer.ts +156 -0
- package/src/memory/items-extractor.ts +460 -0
- package/src/memory/job-handlers/backfill.ts +139 -0
- package/src/memory/job-handlers/cleanup.ts +58 -0
- package/src/memory/job-handlers/conflict.ts +99 -0
- package/src/memory/job-handlers/embedding.ts +61 -0
- package/src/memory/job-handlers/extraction.ts +123 -0
- package/src/memory/job-handlers/index-maintenance.ts +54 -0
- package/src/memory/job-handlers/summarization.ts +286 -0
- package/src/memory/job-utils.ts +170 -0
- package/src/memory/jobs-store.ts +400 -0
- package/src/memory/jobs-worker.ts +274 -0
- package/src/memory/llm-request-log-store.ts +45 -0
- package/src/memory/llm-usage-store.ts +62 -0
- package/src/memory/message-content.ts +54 -0
- package/src/memory/profile-compiler.ts +160 -0
- package/src/memory/published-pages-store.ts +137 -0
- package/src/memory/qdrant-client.ts +366 -0
- package/src/memory/qdrant-manager.ts +242 -0
- package/src/memory/query-builder.ts +45 -0
- package/src/memory/retrieval-budget.ts +30 -0
- package/src/memory/retriever.ts +653 -0
- package/src/memory/runs-store.ts +211 -0
- package/src/memory/schema.ts +529 -0
- package/src/memory/search/entity.ts +298 -0
- package/src/memory/search/formatting.ts +207 -0
- package/src/memory/search/lexical.ts +227 -0
- package/src/memory/search/ranking.ts +401 -0
- package/src/memory/search/semantic.ts +121 -0
- package/src/memory/search/types.ts +137 -0
- package/src/memory/segmenter.ts +68 -0
- package/src/memory/shared-app-links-store.ts +138 -0
- package/src/memory/tool-usage-store.ts +62 -0
- package/src/messaging/activity-analyzer.ts +76 -0
- package/src/messaging/draft-store.ts +88 -0
- package/src/messaging/index.ts +3 -0
- package/src/messaging/provider-types.ts +80 -0
- package/src/messaging/provider.ts +43 -0
- package/src/messaging/providers/gmail/adapter.ts +193 -0
- package/src/messaging/providers/gmail/client.ts +204 -0
- package/src/messaging/providers/gmail/types.ts +90 -0
- package/src/messaging/providers/slack/adapter.ts +202 -0
- package/src/messaging/providers/slack/client.ts +198 -0
- package/src/messaging/providers/slack/types.ts +119 -0
- package/src/messaging/registry.ts +34 -0
- package/src/messaging/style-analyzer.ts +158 -0
- package/src/messaging/thread-summarizer.ts +310 -0
- package/src/messaging/triage-engine.ts +321 -0
- package/src/messaging/types.ts +55 -0
- package/src/permissions/checker.ts +636 -0
- package/src/permissions/defaults.ts +243 -0
- package/src/permissions/prompter.ts +102 -0
- package/src/permissions/secret-prompter.ts +114 -0
- package/src/permissions/trust-store.ts +584 -0
- package/src/permissions/types.ts +62 -0
- package/src/playbooks/index.ts +2 -0
- package/src/playbooks/playbook-compiler.ts +90 -0
- package/src/playbooks/types.ts +55 -0
- package/src/providers/anthropic/client.ts +751 -0
- package/src/providers/failover.ts +129 -0
- package/src/providers/fireworks/client.ts +20 -0
- package/src/providers/gemini/client.ts +285 -0
- package/src/providers/ollama/client.ts +30 -0
- package/src/providers/openai/client.ts +337 -0
- package/src/providers/ratelimit.ts +93 -0
- package/src/providers/registry.ts +138 -0
- package/src/providers/retry.ts +106 -0
- package/src/providers/stream-timeout.ts +38 -0
- package/src/providers/types.ts +109 -0
- package/src/runtime/assistant-event-hub.ts +120 -0
- package/src/runtime/assistant-event.ts +82 -0
- package/src/runtime/http-server.ts +478 -0
- package/src/runtime/http-types.ts +68 -0
- package/src/runtime/routes/app-routes.ts +174 -0
- package/src/runtime/routes/attachment-routes.ts +134 -0
- package/src/runtime/routes/channel-routes.ts +342 -0
- package/src/runtime/routes/conversation-routes.ts +349 -0
- package/src/runtime/routes/run-routes.ts +223 -0
- package/src/runtime/routes/secret-routes.ts +76 -0
- package/src/runtime/run-orchestrator.ts +206 -0
- package/src/schedule/schedule-store.ts +452 -0
- package/src/schedule/scheduler.ts +168 -0
- package/src/security/encrypted-store.ts +238 -0
- package/src/security/keychain.ts +252 -0
- package/src/security/oauth2.ts +241 -0
- package/src/security/redaction.ts +89 -0
- package/src/security/secret-allowlist.ts +118 -0
- package/src/security/secret-ingress.ts +57 -0
- package/src/security/secret-scanner.ts +543 -0
- package/src/security/secure-keys.ts +180 -0
- package/src/security/token-manager.ts +141 -0
- package/src/services/published-app-updater.ts +69 -0
- package/src/services/vercel-deploy.ts +73 -0
- package/src/skills/active-skill-tools.ts +81 -0
- package/src/skills/clawhub.ts +414 -0
- package/src/skills/include-graph.ts +146 -0
- package/src/skills/managed-store.ts +233 -0
- package/src/skills/path-classifier.ts +128 -0
- package/src/skills/slash-commands.ts +174 -0
- package/src/skills/tool-manifest.ts +165 -0
- package/src/skills/version-hash.ts +110 -0
- package/src/slack/slack-webhook.ts +61 -0
- package/src/subagent/index.ts +19 -0
- package/src/subagent/manager.ts +477 -0
- package/src/subagent/types.ts +69 -0
- package/src/swarm/backend-claude-code.ts +90 -0
- package/src/swarm/index.ts +44 -0
- package/src/swarm/limits.ts +37 -0
- package/src/swarm/orchestrator.ts +279 -0
- package/src/swarm/plan-validator.ts +151 -0
- package/src/swarm/router-planner.ts +100 -0
- package/src/swarm/router-prompts.ts +36 -0
- package/src/swarm/synthesizer.ts +62 -0
- package/src/swarm/types.ts +62 -0
- package/src/swarm/worker-backend.ts +121 -0
- package/src/swarm/worker-prompts.ts +78 -0
- package/src/swarm/worker-runner.ts +164 -0
- package/src/tasks/SPEC.md +133 -0
- package/src/tasks/candidate-store.ts +86 -0
- package/src/tasks/ephemeral-permissions.ts +41 -0
- package/src/tasks/task-compiler.ts +198 -0
- package/src/tasks/task-runner.ts +85 -0
- package/src/tasks/task-scheduler.ts +20 -0
- package/src/tasks/task-store.ts +127 -0
- package/src/tools/apps/definitions.ts +59 -0
- package/src/tools/apps/executors.ts +313 -0
- package/src/tools/apps/open-proxy.ts +43 -0
- package/src/tools/apps/registry.ts +16 -0
- package/src/tools/assets/materialize.ts +218 -0
- package/src/tools/assets/search.ts +396 -0
- package/src/tools/browser/__tests__/auth-cache.test.ts +219 -0
- package/src/tools/browser/__tests__/auth-detector.test.ts +362 -0
- package/src/tools/browser/__tests__/jit-auth.test.ts +189 -0
- package/src/tools/browser/auth-cache.ts +149 -0
- package/src/tools/browser/auth-detector.ts +347 -0
- package/src/tools/browser/browser-execution.ts +979 -0
- package/src/tools/browser/browser-handoff.ts +79 -0
- package/src/tools/browser/browser-manager.ts +715 -0
- package/src/tools/browser/browser-screencast.ts +217 -0
- package/src/tools/browser/headless-browser.ts +450 -0
- package/src/tools/browser/jit-auth.ts +51 -0
- package/src/tools/browser/network-recorder.ts +348 -0
- package/src/tools/browser/network-recording-types.ts +49 -0
- package/src/tools/browser/recording-store.ts +49 -0
- package/src/tools/browser/runtime-check.ts +43 -0
- package/src/tools/claude-code/claude-code.ts +232 -0
- package/src/tools/computer-use/definitions.ts +443 -0
- package/src/tools/computer-use/registry.ts +22 -0
- package/src/tools/computer-use/request-computer-control.ts +53 -0
- package/src/tools/computer-use/skill-proxy-bridge.ts +28 -0
- package/src/tools/contacts/contact-merge.ts +87 -0
- package/src/tools/contacts/contact-search.ts +102 -0
- package/src/tools/contacts/contact-upsert.ts +137 -0
- package/src/tools/contacts/index.ts +4 -0
- package/src/tools/credentials/account-registry.ts +127 -0
- package/src/tools/credentials/broker-types.ts +107 -0
- package/src/tools/credentials/broker.ts +372 -0
- package/src/tools/credentials/domain-policy.ts +51 -0
- package/src/tools/credentials/host-pattern-match.ts +60 -0
- package/src/tools/credentials/metadata-store.ts +335 -0
- package/src/tools/credentials/policy-types.ts +52 -0
- package/src/tools/credentials/policy-validate.ts +80 -0
- package/src/tools/credentials/resolve.ts +122 -0
- package/src/tools/credentials/selection.ts +159 -0
- package/src/tools/credentials/tool-policy.ts +25 -0
- package/src/tools/credentials/vault.ts +641 -0
- package/src/tools/document/document-tool.ts +165 -0
- package/src/tools/document/editor-template.ts +237 -0
- package/src/tools/document/index.ts +5 -0
- package/src/tools/executor.ts +825 -0
- package/src/tools/filesystem/edit.ts +127 -0
- package/src/tools/filesystem/fuzzy-match.ts +202 -0
- package/src/tools/filesystem/read.ts +71 -0
- package/src/tools/filesystem/view-image.ts +199 -0
- package/src/tools/filesystem/write.ts +79 -0
- package/src/tools/followups/followup_create.ts +118 -0
- package/src/tools/followups/followup_list.ts +100 -0
- package/src/tools/followups/followup_resolve.ts +91 -0
- package/src/tools/followups/index.ts +3 -0
- package/src/tools/host-filesystem/edit.ts +125 -0
- package/src/tools/host-filesystem/read.ts +80 -0
- package/src/tools/host-filesystem/write.ts +76 -0
- package/src/tools/host-terminal/cli-discover.ts +179 -0
- package/src/tools/host-terminal/host-shell.ts +181 -0
- package/src/tools/memory/definitions.ts +69 -0
- package/src/tools/memory/handlers.ts +245 -0
- package/src/tools/memory/register.ts +66 -0
- package/src/tools/network/domain-normalize.ts +85 -0
- package/src/tools/network/script-proxy/certs.ts +237 -0
- package/src/tools/network/script-proxy/connect-tunnel.ts +82 -0
- package/src/tools/network/script-proxy/http-forwarder.ts +151 -0
- package/src/tools/network/script-proxy/index.ts +28 -0
- package/src/tools/network/script-proxy/logging.ts +196 -0
- package/src/tools/network/script-proxy/mitm-handler.ts +269 -0
- package/src/tools/network/script-proxy/policy.ts +152 -0
- package/src/tools/network/script-proxy/router.ts +60 -0
- package/src/tools/network/script-proxy/server.ts +136 -0
- package/src/tools/network/script-proxy/session-manager.ts +534 -0
- package/src/tools/network/script-proxy/types.ts +125 -0
- package/src/tools/network/url-safety.ts +227 -0
- package/src/tools/network/web-fetch.ts +701 -0
- package/src/tools/network/web-search.ts +319 -0
- package/src/tools/playbooks/index.ts +5 -0
- package/src/tools/playbooks/playbook-create.ts +140 -0
- package/src/tools/playbooks/playbook-delete.ts +76 -0
- package/src/tools/playbooks/playbook-list.ts +101 -0
- package/src/tools/playbooks/playbook-update.ts +159 -0
- package/src/tools/registry.ts +297 -0
- package/src/tools/reminder/reminder-store.ts +148 -0
- package/src/tools/reminder/reminder.ts +153 -0
- package/src/tools/schedule/create.ts +86 -0
- package/src/tools/schedule/delete.ts +54 -0
- package/src/tools/schedule/list.ts +88 -0
- package/src/tools/schedule/update.ts +97 -0
- package/src/tools/shared/filesystem/edit-engine.ts +56 -0
- package/src/tools/shared/filesystem/errors.ts +85 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +215 -0
- package/src/tools/shared/filesystem/format-diff.ts +35 -0
- package/src/tools/shared/filesystem/path-policy.ts +125 -0
- package/src/tools/shared/filesystem/size-guard.ts +41 -0
- package/src/tools/shared/filesystem/types.ts +80 -0
- package/src/tools/shared/shell-output.ts +52 -0
- package/src/tools/skills/delete-managed.ts +60 -0
- package/src/tools/skills/load.ts +139 -0
- package/src/tools/skills/sandbox-runner.ts +279 -0
- package/src/tools/skills/scaffold-managed.ts +150 -0
- package/src/tools/skills/script-contract.ts +6 -0
- package/src/tools/skills/skill-script-runner.ts +86 -0
- package/src/tools/skills/skill-tool-factory.ts +64 -0
- package/src/tools/skills/vellum-catalog.ts +217 -0
- package/src/tools/subagent/abort.ts +62 -0
- package/src/tools/subagent/index.ts +5 -0
- package/src/tools/subagent/message.ts +72 -0
- package/src/tools/subagent/read.ts +98 -0
- package/src/tools/subagent/spawn.ts +85 -0
- package/src/tools/subagent/status.ts +74 -0
- package/src/tools/swarm/delegate.ts +182 -0
- package/src/tools/system/request-permission.ts +98 -0
- package/src/tools/tasks/index.ts +25 -0
- package/src/tools/tasks/task-delete.ts +69 -0
- package/src/tools/tasks/task-list.ts +65 -0
- package/src/tools/tasks/task-run.ts +125 -0
- package/src/tools/tasks/task-save.ts +79 -0
- package/src/tools/tasks/work-item-enqueue.ts +176 -0
- package/src/tools/tasks/work-item-list.ts +86 -0
- package/src/tools/terminal/backends/docker.ts +372 -0
- package/src/tools/terminal/backends/native.ts +188 -0
- package/src/tools/terminal/backends/types.ts +26 -0
- package/src/tools/terminal/evaluate-typescript.ts +275 -0
- package/src/tools/terminal/parser.ts +393 -0
- package/src/tools/terminal/safe-env.ts +37 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +149 -0
- package/src/tools/terminal/sandbox.ts +44 -0
- package/src/tools/terminal/shell.ts +257 -0
- package/src/tools/tool-manifest.ts +250 -0
- package/src/tools/types.ts +177 -0
- package/src/tools/ui-surface/definitions.ts +232 -0
- package/src/tools/ui-surface/registry.ts +14 -0
- package/src/tools/watch/screen-watch.ts +128 -0
- package/src/tools/watch/watch-state.ts +119 -0
- package/src/tools/watcher/create.ts +110 -0
- package/src/tools/watcher/delete.ts +53 -0
- package/src/tools/watcher/digest.ts +84 -0
- package/src/tools/watcher/list.ts +90 -0
- package/src/tools/watcher/update.ts +102 -0
- package/src/tools/weather/service.ts +551 -0
- package/src/usage/actors.ts +24 -0
- package/src/usage/types.ts +38 -0
- package/src/util/clipboard.ts +33 -0
- package/src/util/content-id.ts +16 -0
- package/src/util/diff.ts +181 -0
- package/src/util/errors.ts +129 -0
- package/src/util/logger.ts +243 -0
- package/src/util/platform.ts +607 -0
- package/src/util/pricing.ts +150 -0
- package/src/util/spinner.ts +51 -0
- package/src/util/time.ts +16 -0
- package/src/util/xml.ts +4 -0
- package/src/version.ts +3 -0
- package/src/watcher/constants.ts +11 -0
- package/src/watcher/engine.ts +199 -0
- package/src/watcher/provider-registry.ts +15 -0
- package/src/watcher/provider-types.ts +48 -0
- package/src/watcher/providers/gmail.ts +198 -0
- package/src/watcher/providers/google-calendar.ts +228 -0
- package/src/watcher/providers/slack.ts +128 -0
- package/src/watcher/watcher-store.ts +418 -0
- package/src/work-items/work-item-store.ts +91 -0
- package/src/workspace/git-service.ts +620 -0
- package/src/workspace/heartbeat-service.ts +288 -0
- package/src/workspace/top-level-renderer.ts +19 -0
- package/src/workspace/top-level-scanner.ts +41 -0
- package/src/workspace/turn-commit.ts +122 -0
- package/tsconfig.json +21 -0
- package/LICENSE +0 -674
- package/dist/cli.js +0 -569
|
@@ -0,0 +1,972 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
4
|
+
import { getConfig } from './loader.js';
|
|
5
|
+
import { getWorkspaceSkillsDir } from '../util/platform.js';
|
|
6
|
+
import { getLogger } from '../util/logger.js';
|
|
7
|
+
import { stripCommentLines } from './system-prompt.js';
|
|
8
|
+
import { parseToolManifestFile } from '../skills/tool-manifest.js';
|
|
9
|
+
import { computeSkillVersionHash } from '../skills/version-hash.js';
|
|
10
|
+
|
|
11
|
+
const log = getLogger('skills');
|
|
12
|
+
|
|
13
|
+
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
14
|
+
|
|
15
|
+
// ─── New interfaces for extended skill metadata ──────────────────────────────
|
|
16
|
+
|
|
17
|
+
export interface VellumMetadata {
|
|
18
|
+
emoji?: string;
|
|
19
|
+
os?: string[];
|
|
20
|
+
requires?: SkillRequirements;
|
|
21
|
+
primaryEnv?: string;
|
|
22
|
+
install?: InstallerSpec[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SkillRequirements {
|
|
26
|
+
bins?: string[];
|
|
27
|
+
anyBins?: string[];
|
|
28
|
+
env?: string[];
|
|
29
|
+
config?: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface InstallerSpec {
|
|
33
|
+
id: string;
|
|
34
|
+
kind: 'brew' | 'node' | 'go' | 'uv' | 'download';
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type SkillSource = 'bundled' | 'managed' | 'workspace' | 'extra';
|
|
39
|
+
|
|
40
|
+
// ─── Core interfaces ─────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export interface SkillSummary {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
directoryPath: string;
|
|
47
|
+
skillFilePath: string;
|
|
48
|
+
bundled?: boolean;
|
|
49
|
+
icon?: string;
|
|
50
|
+
emoji?: string;
|
|
51
|
+
homepage?: string;
|
|
52
|
+
userInvocable: boolean;
|
|
53
|
+
disableModelInvocation: boolean;
|
|
54
|
+
source: SkillSource;
|
|
55
|
+
metadata?: VellumMetadata;
|
|
56
|
+
/** Parsed tool manifest metadata, if the skill has a valid TOOLS.json. */
|
|
57
|
+
toolManifest?: SkillToolManifestMeta;
|
|
58
|
+
/** IDs of child skills that this skill includes (metadata-only, not auto-activated). */
|
|
59
|
+
includes?: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface SkillDefinition extends SkillSummary {
|
|
63
|
+
body: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface SkillLookupResult {
|
|
67
|
+
skill?: SkillDefinition;
|
|
68
|
+
error?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SkillSelectorResult {
|
|
72
|
+
skill?: SkillSummary;
|
|
73
|
+
error?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ─── Skill Tool Manifest Types ────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Schema for a skill's TOOLS.json manifest file.
|
|
80
|
+
* Declares which tools a skill provides and how they should be executed.
|
|
81
|
+
*/
|
|
82
|
+
export interface SkillToolManifest {
|
|
83
|
+
version: 1;
|
|
84
|
+
tools: SkillToolEntry[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A single tool entry within a skill's TOOLS.json manifest.
|
|
89
|
+
*/
|
|
90
|
+
export interface SkillToolEntry {
|
|
91
|
+
/** Unique tool name (must not collide with core tool names). */
|
|
92
|
+
name: string;
|
|
93
|
+
/** Human-readable description shown to the model. */
|
|
94
|
+
description: string;
|
|
95
|
+
/** Tool category for grouping/display. */
|
|
96
|
+
category: string;
|
|
97
|
+
/** Default risk level for permission checks. */
|
|
98
|
+
risk: 'low' | 'medium' | 'high';
|
|
99
|
+
/** JSON Schema for the tool's input parameters. */
|
|
100
|
+
input_schema: Record<string, unknown>;
|
|
101
|
+
/** Relative path to the executor script within the skill directory. */
|
|
102
|
+
executor: string;
|
|
103
|
+
/** Where the tool script runs. */
|
|
104
|
+
execution_target: 'host' | 'sandbox';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Lightweight metadata about a skill's tool manifest, attached to SkillSummary
|
|
109
|
+
* without loading the full manifest at catalog time.
|
|
110
|
+
*/
|
|
111
|
+
export interface SkillToolManifestMeta {
|
|
112
|
+
/** Whether the skill has a TOOLS.json file. */
|
|
113
|
+
present: boolean;
|
|
114
|
+
/** Whether the manifest parsed successfully. */
|
|
115
|
+
valid: boolean;
|
|
116
|
+
/** Number of tools declared in the manifest (0 if invalid). */
|
|
117
|
+
toolCount: number;
|
|
118
|
+
/** Tool names declared in the manifest (empty if invalid). */
|
|
119
|
+
toolNames: string[];
|
|
120
|
+
/**
|
|
121
|
+
* Deterministic content hash of the skill directory (`v1:<hex-sha256>`).
|
|
122
|
+
* Lazily computed on first access to avoid hashing every skill directory
|
|
123
|
+
* during catalog load.
|
|
124
|
+
*/
|
|
125
|
+
versionHash?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Requirements check ──────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
export interface RequirementsCheckResult {
|
|
131
|
+
eligible: boolean;
|
|
132
|
+
missing: {
|
|
133
|
+
bins?: string[];
|
|
134
|
+
env?: string[];
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function checkSkillRequirements(
|
|
139
|
+
skill: SkillSummary,
|
|
140
|
+
envOverrides?: Record<string, string>,
|
|
141
|
+
): RequirementsCheckResult {
|
|
142
|
+
const vellum = skill.metadata;
|
|
143
|
+
if (!vellum) {
|
|
144
|
+
return { eligible: true, missing: {} };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const missingBins: string[] = [];
|
|
148
|
+
const missingEnv: string[] = [];
|
|
149
|
+
|
|
150
|
+
// OS check
|
|
151
|
+
if (vellum.os && vellum.os.length > 0) {
|
|
152
|
+
if (!vellum.os.includes(process.platform)) {
|
|
153
|
+
return {
|
|
154
|
+
eligible: false,
|
|
155
|
+
missing: {
|
|
156
|
+
bins: [`(unsupported platform: ${process.platform}, requires: ${vellum.os.join(', ')})`],
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const requires = vellum.requires;
|
|
163
|
+
if (!requires) {
|
|
164
|
+
return { eligible: true, missing: {} };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// bins: all must exist
|
|
168
|
+
if (requires.bins) {
|
|
169
|
+
for (const bin of requires.bins) {
|
|
170
|
+
if (!Bun.which(bin)) {
|
|
171
|
+
missingBins.push(bin);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// anyBins: at least one must exist
|
|
177
|
+
if (requires.anyBins && requires.anyBins.length > 0) {
|
|
178
|
+
const hasAny = requires.anyBins.some((bin) => Bun.which(bin) !== null);
|
|
179
|
+
if (!hasAny) {
|
|
180
|
+
missingBins.push(`(one of: ${requires.anyBins.join(', ')})`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// env: check process.env or envOverrides
|
|
185
|
+
if (requires.env) {
|
|
186
|
+
const env = envOverrides ? { ...process.env, ...envOverrides } : process.env;
|
|
187
|
+
for (const key of requires.env) {
|
|
188
|
+
if (!env[key]) {
|
|
189
|
+
missingEnv.push(key);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// config: skip for now (needs config integration from M2)
|
|
195
|
+
|
|
196
|
+
const missing: RequirementsCheckResult['missing'] = {};
|
|
197
|
+
if (missingBins.length > 0) missing.bins = missingBins;
|
|
198
|
+
if (missingEnv.length > 0) missing.env = missingEnv;
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
eligible: missingBins.length === 0 && missingEnv.length === 0,
|
|
202
|
+
missing,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
export function getSkillsDir(): string {
|
|
209
|
+
return getWorkspaceSkillsDir();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function getBundledSkillsDir(): string {
|
|
213
|
+
return join(import.meta.dir, 'bundled-skills');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function getSkillsIndexPath(skillsDir: string): string {
|
|
217
|
+
return join(skillsDir, 'SKILLS.md');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── Frontmatter parsing ─────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
interface ParsedFrontmatter {
|
|
223
|
+
name: string;
|
|
224
|
+
description: string;
|
|
225
|
+
body: string;
|
|
226
|
+
homepage?: string;
|
|
227
|
+
userInvocable: boolean;
|
|
228
|
+
disableModelInvocation: boolean;
|
|
229
|
+
metadata?: VellumMetadata;
|
|
230
|
+
includes?: string[];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function parseIncludes(raw: string | undefined, skillFilePath: string): string[] | undefined {
|
|
234
|
+
if (!raw) return undefined;
|
|
235
|
+
|
|
236
|
+
let parsed: unknown;
|
|
237
|
+
try {
|
|
238
|
+
parsed = JSON.parse(raw);
|
|
239
|
+
} catch (err) {
|
|
240
|
+
log.warn({ err, skillFilePath }, 'Failed to parse includes JSON in frontmatter');
|
|
241
|
+
return undefined;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (!Array.isArray(parsed)) {
|
|
245
|
+
log.warn({ skillFilePath }, 'includes must be a JSON array');
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!parsed.every((item: unknown) => typeof item === 'string')) {
|
|
250
|
+
log.warn({ skillFilePath }, 'includes must be an array of strings');
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Normalize: trim, remove empty strings, deduplicate preserving first-seen order
|
|
255
|
+
const seen = new Set<string>();
|
|
256
|
+
const result: string[] = [];
|
|
257
|
+
for (const item of parsed as string[]) {
|
|
258
|
+
const trimmed = item.trim();
|
|
259
|
+
if (trimmed.length === 0) continue;
|
|
260
|
+
if (seen.has(trimmed)) continue;
|
|
261
|
+
seen.add(trimmed);
|
|
262
|
+
result.push(trimmed);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return result.length > 0 ? result : undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function parseFrontmatter(content: string, skillFilePath: string): ParsedFrontmatter | null {
|
|
269
|
+
const match = content.match(FRONTMATTER_REGEX);
|
|
270
|
+
if (!match) {
|
|
271
|
+
log.warn({ skillFilePath }, 'Skipping skill without YAML frontmatter');
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const frontmatter = match[1];
|
|
276
|
+
const fields: Record<string, string> = {};
|
|
277
|
+
for (const line of frontmatter.split(/\r?\n/)) {
|
|
278
|
+
const trimmed = line.trim();
|
|
279
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
280
|
+
const separatorIndex = trimmed.indexOf(':');
|
|
281
|
+
if (separatorIndex === -1) continue;
|
|
282
|
+
|
|
283
|
+
const key = trimmed.slice(0, separatorIndex).trim();
|
|
284
|
+
let value = trimmed.slice(separatorIndex + 1).trim();
|
|
285
|
+
const isDoubleQuoted = value.startsWith('"') && value.endsWith('"');
|
|
286
|
+
const isSingleQuoted = value.startsWith('\'') && value.endsWith('\'');
|
|
287
|
+
if (isDoubleQuoted || isSingleQuoted) {
|
|
288
|
+
value = value.slice(1, -1);
|
|
289
|
+
if (isDoubleQuoted) {
|
|
290
|
+
// Unescape sequences produced by buildSkillMarkdown's esc().
|
|
291
|
+
// Only for double-quoted values — single-quoted YAML treats backslashes literally.
|
|
292
|
+
// Single-pass to avoid misinterpreting \\n (escaped backslash + n) as a newline.
|
|
293
|
+
value = value.replace(/\\(["\\nr])/g, (_, ch) => {
|
|
294
|
+
if (ch === 'n') return '\n';
|
|
295
|
+
if (ch === 'r') return '\r';
|
|
296
|
+
return ch; // handles \\ → \ and \" → "
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
fields[key] = value;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const name = fields.name?.trim();
|
|
304
|
+
const description = fields.description?.trim();
|
|
305
|
+
if (!name || !description) {
|
|
306
|
+
log.warn({ skillFilePath }, 'Skipping skill missing required frontmatter keys "name" and/or "description"');
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Parse new optional fields
|
|
311
|
+
const homepage = fields.homepage?.trim() || undefined;
|
|
312
|
+
|
|
313
|
+
const userInvocableRaw = fields['user-invocable']?.trim().toLowerCase();
|
|
314
|
+
const userInvocable = userInvocableRaw !== 'false';
|
|
315
|
+
|
|
316
|
+
const disableModelInvocationRaw = fields['disable-model-invocation']?.trim().toLowerCase();
|
|
317
|
+
const disableModelInvocation = disableModelInvocationRaw === 'true';
|
|
318
|
+
|
|
319
|
+
// Parse metadata as single-line JSON string, extract .vellum namespace
|
|
320
|
+
let metadata: VellumMetadata | undefined;
|
|
321
|
+
const metadataRaw = fields.metadata?.trim();
|
|
322
|
+
if (metadataRaw) {
|
|
323
|
+
try {
|
|
324
|
+
const parsed = JSON.parse(metadataRaw);
|
|
325
|
+
if (parsed && typeof parsed === 'object' && parsed.vellum) {
|
|
326
|
+
metadata = parsed.vellum as VellumMetadata;
|
|
327
|
+
}
|
|
328
|
+
} catch (err) {
|
|
329
|
+
log.warn({ err, skillFilePath }, 'Failed to parse metadata JSON in frontmatter');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const includes = parseIncludes(fields.includes, skillFilePath);
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
name,
|
|
337
|
+
description,
|
|
338
|
+
body: stripCommentLines(content.slice(match[0].length)),
|
|
339
|
+
homepage,
|
|
340
|
+
userInvocable,
|
|
341
|
+
disableModelInvocation,
|
|
342
|
+
metadata,
|
|
343
|
+
includes,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ─── Path utilities ──────────────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
function getCanonicalPath(path: string): string {
|
|
350
|
+
return existsSync(path) ? realpathSync(path) : resolve(path);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function getRelativeToSkillsRoot(skillsDir: string, candidatePath: string): string {
|
|
354
|
+
return relative(getCanonicalPath(skillsDir), getCanonicalPath(candidatePath));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function isOutsideSkillsRoot(skillsDir: string, candidatePath: string): boolean {
|
|
358
|
+
const relativePath = getRelativeToSkillsRoot(skillsDir, candidatePath);
|
|
359
|
+
return relativePath.startsWith('..') || isAbsolute(relativePath);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ─── Tool manifest detection ─────────────────────────────────────────────────
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Create a SkillToolManifestMeta with a lazily-computed versionHash.
|
|
366
|
+
* The hash is only computed when first accessed, avoiding the cost of
|
|
367
|
+
* recursively hashing the skill directory during catalog load.
|
|
368
|
+
*/
|
|
369
|
+
function createManifestMeta(
|
|
370
|
+
base: Omit<SkillToolManifestMeta, 'versionHash'>,
|
|
371
|
+
directoryPath: string,
|
|
372
|
+
): SkillToolManifestMeta {
|
|
373
|
+
let cached: string | undefined;
|
|
374
|
+
let computed = false;
|
|
375
|
+
return Object.defineProperty({ ...base }, 'versionHash', {
|
|
376
|
+
get() {
|
|
377
|
+
if (!computed) {
|
|
378
|
+
computed = true;
|
|
379
|
+
try {
|
|
380
|
+
cached = computeSkillVersionHash(directoryPath);
|
|
381
|
+
} catch (err) {
|
|
382
|
+
log.warn({ err, directoryPath }, 'Failed to compute skill version hash');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return cached;
|
|
386
|
+
},
|
|
387
|
+
enumerable: true,
|
|
388
|
+
configurable: true,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Detect and parse a TOOLS.json manifest in a skill directory.
|
|
394
|
+
* Returns the manifest metadata if the file exists, or undefined if it doesn't.
|
|
395
|
+
* On parse failure, returns a degraded metadata object (present but invalid).
|
|
396
|
+
*/
|
|
397
|
+
function detectToolManifest(directoryPath: string): SkillToolManifestMeta | undefined {
|
|
398
|
+
const manifestPath = join(directoryPath, 'TOOLS.json');
|
|
399
|
+
if (!existsSync(manifestPath)) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
const manifest = parseToolManifestFile(manifestPath);
|
|
405
|
+
return createManifestMeta({
|
|
406
|
+
present: true,
|
|
407
|
+
valid: true,
|
|
408
|
+
toolCount: manifest.tools.length,
|
|
409
|
+
toolNames: manifest.tools.map((t) => t.name),
|
|
410
|
+
}, directoryPath);
|
|
411
|
+
} catch (err) {
|
|
412
|
+
log.warn({ err, manifestPath }, 'Failed to parse TOOLS.json manifest');
|
|
413
|
+
return createManifestMeta({
|
|
414
|
+
present: true,
|
|
415
|
+
valid: false,
|
|
416
|
+
toolCount: 0,
|
|
417
|
+
toolNames: [],
|
|
418
|
+
}, directoryPath);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ─── Skill reading ───────────────────────────────────────────────────────────
|
|
423
|
+
|
|
424
|
+
function readSkillFromDirectory(directoryPath: string, skillsDir: string, source: SkillSource): SkillDefinition | null {
|
|
425
|
+
const skillFilePath = join(directoryPath, 'SKILL.md');
|
|
426
|
+
if (!existsSync(skillFilePath)) {
|
|
427
|
+
log.warn({ directoryPath }, 'Skipping skill directory without SKILL.md');
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
try {
|
|
432
|
+
if (isOutsideSkillsRoot(skillsDir, directoryPath)) {
|
|
433
|
+
log.warn({ directoryPath }, 'Skipping skill directory that resolves outside ~/.vellum/workspace/skills');
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const stat = statSync(skillFilePath);
|
|
438
|
+
if (!stat.isFile()) {
|
|
439
|
+
log.warn({ skillFilePath }, 'Skipping skill path because SKILL.md is not a file');
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (isOutsideSkillsRoot(skillsDir, skillFilePath)) {
|
|
444
|
+
log.warn({ skillFilePath }, 'Skipping SKILL.md that resolves outside ~/.vellum/workspace/skills');
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const content = readFileSync(skillFilePath, 'utf-8');
|
|
449
|
+
const parsed = parseFrontmatter(content, skillFilePath);
|
|
450
|
+
if (!parsed) return null;
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
id: basename(directoryPath),
|
|
454
|
+
name: parsed.name,
|
|
455
|
+
description: parsed.description,
|
|
456
|
+
directoryPath,
|
|
457
|
+
skillFilePath,
|
|
458
|
+
body: parsed.body,
|
|
459
|
+
emoji: parsed.metadata?.emoji,
|
|
460
|
+
homepage: parsed.homepage,
|
|
461
|
+
userInvocable: parsed.userInvocable,
|
|
462
|
+
disableModelInvocation: parsed.disableModelInvocation,
|
|
463
|
+
source,
|
|
464
|
+
metadata: parsed.metadata,
|
|
465
|
+
toolManifest: detectToolManifest(directoryPath),
|
|
466
|
+
includes: parsed.includes,
|
|
467
|
+
};
|
|
468
|
+
} catch (err) {
|
|
469
|
+
log.warn({ err, skillFilePath }, 'Failed to read skill file');
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function readBundledSkillFromDirectory(directoryPath: string): SkillDefinition | null {
|
|
475
|
+
const skillFilePath = join(directoryPath, 'SKILL.md');
|
|
476
|
+
if (!existsSync(skillFilePath)) {
|
|
477
|
+
log.warn({ directoryPath }, 'Skipping bundled skill directory without SKILL.md');
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
try {
|
|
482
|
+
const stat = statSync(skillFilePath);
|
|
483
|
+
if (!stat.isFile()) {
|
|
484
|
+
log.warn({ skillFilePath }, 'Skipping bundled skill path because SKILL.md is not a file');
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const content = readFileSync(skillFilePath, 'utf-8');
|
|
489
|
+
const parsed = parseFrontmatter(content, skillFilePath);
|
|
490
|
+
if (!parsed) return null;
|
|
491
|
+
|
|
492
|
+
return {
|
|
493
|
+
id: basename(directoryPath),
|
|
494
|
+
name: parsed.name,
|
|
495
|
+
description: parsed.description,
|
|
496
|
+
directoryPath,
|
|
497
|
+
skillFilePath,
|
|
498
|
+
body: parsed.body,
|
|
499
|
+
bundled: true,
|
|
500
|
+
emoji: parsed.metadata?.emoji,
|
|
501
|
+
homepage: parsed.homepage,
|
|
502
|
+
userInvocable: parsed.userInvocable,
|
|
503
|
+
disableModelInvocation: parsed.disableModelInvocation,
|
|
504
|
+
source: 'bundled',
|
|
505
|
+
metadata: parsed.metadata,
|
|
506
|
+
toolManifest: detectToolManifest(directoryPath),
|
|
507
|
+
includes: parsed.includes,
|
|
508
|
+
};
|
|
509
|
+
} catch (err) {
|
|
510
|
+
log.warn({ err, skillFilePath }, 'Failed to read bundled skill file');
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ─── Skill discovery ─────────────────────────────────────────────────────────
|
|
516
|
+
|
|
517
|
+
function discoverBundledSkillDirectories(): string[] {
|
|
518
|
+
const bundledDir = getBundledSkillsDir();
|
|
519
|
+
if (!existsSync(bundledDir)) return [];
|
|
520
|
+
|
|
521
|
+
const dirs: string[] = [];
|
|
522
|
+
try {
|
|
523
|
+
const entries = readdirSync(bundledDir, { withFileTypes: true });
|
|
524
|
+
for (const entry of entries) {
|
|
525
|
+
if (!entry.isDirectory()) continue;
|
|
526
|
+
const directoryPath = join(bundledDir, entry.name);
|
|
527
|
+
if (existsSync(join(directoryPath, 'SKILL.md'))) {
|
|
528
|
+
dirs.push(directoryPath);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
} catch (err) {
|
|
532
|
+
log.warn({ err, bundledDir }, 'Failed to discover bundled skill directories');
|
|
533
|
+
return [];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return dirs.sort((a, b) => a.localeCompare(b));
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function loadBundledSkills(): SkillSummary[] {
|
|
540
|
+
const directories = discoverBundledSkillDirectories();
|
|
541
|
+
const skills: SkillSummary[] = [];
|
|
542
|
+
|
|
543
|
+
for (const directory of directories) {
|
|
544
|
+
const skill = readBundledSkillFromDirectory(directory);
|
|
545
|
+
if (!skill) continue;
|
|
546
|
+
|
|
547
|
+
skills.push({
|
|
548
|
+
id: skill.id,
|
|
549
|
+
name: skill.name,
|
|
550
|
+
description: skill.description,
|
|
551
|
+
directoryPath: skill.directoryPath,
|
|
552
|
+
skillFilePath: skill.skillFilePath,
|
|
553
|
+
bundled: true,
|
|
554
|
+
emoji: skill.emoji,
|
|
555
|
+
homepage: skill.homepage,
|
|
556
|
+
userInvocable: skill.userInvocable,
|
|
557
|
+
disableModelInvocation: skill.disableModelInvocation,
|
|
558
|
+
source: 'bundled',
|
|
559
|
+
metadata: skill.metadata,
|
|
560
|
+
toolManifest: skill.toolManifest,
|
|
561
|
+
includes: skill.includes,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return skills;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// ─── Index parsing ───────────────────────────────────────────────────────────
|
|
569
|
+
|
|
570
|
+
function parseIndexEntry(line: string): string | null {
|
|
571
|
+
const bulletMatch = line.trim().match(/^[-*]\s+(.+)$/);
|
|
572
|
+
if (!bulletMatch) return null;
|
|
573
|
+
|
|
574
|
+
let entry = bulletMatch[1].trim();
|
|
575
|
+
const markdownLinkMatch = entry.match(/^\[[^\]]+\]\(([^)]+)\)$/);
|
|
576
|
+
if (markdownLinkMatch) {
|
|
577
|
+
entry = markdownLinkMatch[1].trim();
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (entry.startsWith('`') && entry.endsWith('`')) {
|
|
581
|
+
entry = entry.slice(1, -1).trim();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return entry.length > 0 ? entry : null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function resolveIndexEntryToDirectory(skillsDir: string, entry: string): string | null {
|
|
588
|
+
if (isAbsolute(entry)) {
|
|
589
|
+
log.warn({ entry }, 'Skipping SKILLS.md entry because absolute paths are not allowed');
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const resolvedEntryPath = resolve(skillsDir, entry);
|
|
594
|
+
const resolvedDirectory = basename(resolvedEntryPath).toLowerCase() === 'skill.md'
|
|
595
|
+
? dirname(resolvedEntryPath)
|
|
596
|
+
: resolvedEntryPath;
|
|
597
|
+
|
|
598
|
+
const relativePath = getRelativeToSkillsRoot(skillsDir, resolvedDirectory);
|
|
599
|
+
if (relativePath.length === 0) {
|
|
600
|
+
log.warn({ entry }, 'Skipping SKILLS.md entry that resolves to the skills root');
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
if (isOutsideSkillsRoot(skillsDir, resolvedDirectory)) {
|
|
604
|
+
log.warn(
|
|
605
|
+
{ entry, resolvedDirectory: getCanonicalPath(resolvedDirectory) },
|
|
606
|
+
'Skipping SKILLS.md entry that resolves outside ~/.vellum/workspace/skills',
|
|
607
|
+
);
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return resolvedDirectory;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function getIndexedSkillDirectories(skillsDir: string): string[] | null {
|
|
615
|
+
const indexPath = getSkillsIndexPath(skillsDir);
|
|
616
|
+
if (!existsSync(indexPath)) return null;
|
|
617
|
+
|
|
618
|
+
let rawIndex = '';
|
|
619
|
+
try {
|
|
620
|
+
rawIndex = readFileSync(indexPath, 'utf-8');
|
|
621
|
+
} catch (err) {
|
|
622
|
+
log.warn({ err, indexPath }, 'Failed to read SKILLS.md; treating as empty catalog');
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const directories: string[] = [];
|
|
627
|
+
const seen = new Set<string>();
|
|
628
|
+
|
|
629
|
+
for (const line of rawIndex.split(/\r?\n/)) {
|
|
630
|
+
const parsedEntry = parseIndexEntry(line);
|
|
631
|
+
if (!parsedEntry) continue;
|
|
632
|
+
|
|
633
|
+
const directory = resolveIndexEntryToDirectory(skillsDir, parsedEntry);
|
|
634
|
+
if (!directory || seen.has(directory)) continue;
|
|
635
|
+
|
|
636
|
+
seen.add(directory);
|
|
637
|
+
directories.push(directory);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return directories;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function discoverSkillDirectories(skillsDir: string): string[] {
|
|
644
|
+
if (!existsSync(skillsDir)) return [];
|
|
645
|
+
|
|
646
|
+
const dirs: string[] = [];
|
|
647
|
+
try {
|
|
648
|
+
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
649
|
+
for (const entry of entries) {
|
|
650
|
+
if (!entry.isDirectory()) continue;
|
|
651
|
+
const directoryPath = join(skillsDir, entry.name);
|
|
652
|
+
if (existsSync(join(directoryPath, 'SKILL.md'))) {
|
|
653
|
+
dirs.push(directoryPath);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
} catch (err) {
|
|
657
|
+
log.warn({ err, skillsDir }, 'Failed to discover skill directories');
|
|
658
|
+
return [];
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return dirs.sort((a, b) => a.localeCompare(b));
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// ─── Catalog loading ─────────────────────────────────────────────────────────
|
|
665
|
+
|
|
666
|
+
function skillSummaryFromDefinition(skill: SkillDefinition, source: SkillSource): SkillSummary {
|
|
667
|
+
return {
|
|
668
|
+
id: skill.id,
|
|
669
|
+
name: skill.name,
|
|
670
|
+
description: skill.description,
|
|
671
|
+
directoryPath: skill.directoryPath,
|
|
672
|
+
skillFilePath: skill.skillFilePath,
|
|
673
|
+
bundled: skill.bundled,
|
|
674
|
+
emoji: skill.emoji,
|
|
675
|
+
homepage: skill.homepage,
|
|
676
|
+
userInvocable: skill.userInvocable,
|
|
677
|
+
disableModelInvocation: skill.disableModelInvocation,
|
|
678
|
+
source,
|
|
679
|
+
metadata: skill.metadata,
|
|
680
|
+
toolManifest: skill.toolManifest,
|
|
681
|
+
includes: skill.includes,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export function loadSkillCatalog(workspaceSkillsDir?: string, extraDirs?: string[]): SkillSummary[] {
|
|
686
|
+
const catalog: SkillSummary[] = [];
|
|
687
|
+
const seenIds = new Set<string>();
|
|
688
|
+
|
|
689
|
+
// Load extra directories first (lowest precedence, before bundled)
|
|
690
|
+
if (extraDirs) {
|
|
691
|
+
for (const dir of extraDirs) {
|
|
692
|
+
if (!existsSync(dir)) continue;
|
|
693
|
+
const dirs = discoverSkillDirectories(dir);
|
|
694
|
+
for (const directory of dirs) {
|
|
695
|
+
const skillFilePath = join(directory, 'SKILL.md');
|
|
696
|
+
if (!existsSync(skillFilePath)) continue;
|
|
697
|
+
|
|
698
|
+
try {
|
|
699
|
+
const stat = statSync(skillFilePath);
|
|
700
|
+
if (!stat.isFile()) continue;
|
|
701
|
+
|
|
702
|
+
const content = readFileSync(skillFilePath, 'utf-8');
|
|
703
|
+
const parsed = parseFrontmatter(content, skillFilePath);
|
|
704
|
+
if (!parsed) continue;
|
|
705
|
+
|
|
706
|
+
const id = basename(directory);
|
|
707
|
+
if (seenIds.has(id)) {
|
|
708
|
+
log.warn({ id, directory }, 'Skipping duplicate skill id from extraDirs');
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
seenIds.add(id);
|
|
713
|
+
catalog.push({
|
|
714
|
+
id,
|
|
715
|
+
name: parsed.name,
|
|
716
|
+
description: parsed.description,
|
|
717
|
+
directoryPath: directory,
|
|
718
|
+
skillFilePath,
|
|
719
|
+
emoji: parsed.metadata?.emoji,
|
|
720
|
+
homepage: parsed.homepage,
|
|
721
|
+
userInvocable: parsed.userInvocable,
|
|
722
|
+
disableModelInvocation: parsed.disableModelInvocation,
|
|
723
|
+
source: 'extra',
|
|
724
|
+
metadata: parsed.metadata,
|
|
725
|
+
toolManifest: detectToolManifest(directory),
|
|
726
|
+
includes: parsed.includes,
|
|
727
|
+
});
|
|
728
|
+
} catch (err) {
|
|
729
|
+
log.warn({ err, directory }, 'Failed to read skill from extraDirs');
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// Load bundled skills (override extraDirs skills with same ID)
|
|
736
|
+
const bundledSkills = loadBundledSkills();
|
|
737
|
+
for (const skill of bundledSkills) {
|
|
738
|
+
if (seenIds.has(skill.id)) {
|
|
739
|
+
// Bundled wins over extraDirs
|
|
740
|
+
const existingIndex = catalog.findIndex((s) => s.id === skill.id);
|
|
741
|
+
if (existingIndex !== -1 && catalog[existingIndex].source === 'extra') {
|
|
742
|
+
log.info({ id: skill.id, directory: skill.directoryPath }, 'Bundled skill overrides extraDirs skill');
|
|
743
|
+
catalog[existingIndex] = skill;
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
log.warn({ id: skill.id, directory: skill.directoryPath }, 'Skipping duplicate bundled skill id');
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
seenIds.add(skill.id);
|
|
750
|
+
catalog.push(skill);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Load managed (user) skills, which take precedence over bundled skills with the same ID
|
|
754
|
+
const skillsDir = getSkillsDir();
|
|
755
|
+
const indexedDirectories = getIndexedSkillDirectories(skillsDir);
|
|
756
|
+
const directories = indexedDirectories ?? discoverSkillDirectories(skillsDir);
|
|
757
|
+
|
|
758
|
+
for (const directory of directories) {
|
|
759
|
+
const skill = readSkillFromDirectory(directory, skillsDir, 'managed');
|
|
760
|
+
if (!skill) continue;
|
|
761
|
+
|
|
762
|
+
if (seenIds.has(skill.id)) {
|
|
763
|
+
// If the existing entry is bundled, the user skill overrides it
|
|
764
|
+
const existingIndex = catalog.findIndex((s) => s.id === skill.id);
|
|
765
|
+
if (existingIndex !== -1 && (catalog[existingIndex].bundled || catalog[existingIndex].source === 'extra')) {
|
|
766
|
+
log.info({ id: skill.id, directory }, 'User skill overrides bundled skill');
|
|
767
|
+
catalog[existingIndex] = skillSummaryFromDefinition(skill, 'managed');
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
log.warn({ id: skill.id, directory }, 'Skipping duplicate skill id');
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
seenIds.add(skill.id);
|
|
775
|
+
catalog.push(skillSummaryFromDefinition(skill, 'managed'));
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Load workspace skills with highest precedence
|
|
779
|
+
if (workspaceSkillsDir && existsSync(workspaceSkillsDir)) {
|
|
780
|
+
const workspaceDirs = discoverSkillDirectories(workspaceSkillsDir);
|
|
781
|
+
|
|
782
|
+
for (const directory of workspaceDirs) {
|
|
783
|
+
const skillFilePath = join(directory, 'SKILL.md');
|
|
784
|
+
if (!existsSync(skillFilePath)) continue;
|
|
785
|
+
|
|
786
|
+
try {
|
|
787
|
+
const stat = statSync(skillFilePath);
|
|
788
|
+
if (!stat.isFile()) continue;
|
|
789
|
+
|
|
790
|
+
const content = readFileSync(skillFilePath, 'utf-8');
|
|
791
|
+
const parsed = parseFrontmatter(content, skillFilePath);
|
|
792
|
+
if (!parsed) continue;
|
|
793
|
+
|
|
794
|
+
const id = basename(directory);
|
|
795
|
+
const workspaceSkill: SkillSummary = {
|
|
796
|
+
id,
|
|
797
|
+
name: parsed.name,
|
|
798
|
+
description: parsed.description,
|
|
799
|
+
directoryPath: directory,
|
|
800
|
+
skillFilePath,
|
|
801
|
+
emoji: parsed.metadata?.emoji,
|
|
802
|
+
homepage: parsed.homepage,
|
|
803
|
+
userInvocable: parsed.userInvocable,
|
|
804
|
+
disableModelInvocation: parsed.disableModelInvocation,
|
|
805
|
+
source: 'workspace',
|
|
806
|
+
metadata: parsed.metadata,
|
|
807
|
+
toolManifest: detectToolManifest(directory),
|
|
808
|
+
includes: parsed.includes,
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
if (seenIds.has(id)) {
|
|
812
|
+
// Workspace skills override any existing skill
|
|
813
|
+
const existingIndex = catalog.findIndex((s) => s.id === id);
|
|
814
|
+
if (existingIndex !== -1) {
|
|
815
|
+
log.info({ id, directory }, 'Workspace skill overrides existing skill');
|
|
816
|
+
catalog[existingIndex] = workspaceSkill;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
seenIds.add(id);
|
|
822
|
+
catalog.push(workspaceSkill);
|
|
823
|
+
} catch (err) {
|
|
824
|
+
log.warn({ err, directory }, 'Failed to read workspace skill');
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return catalog;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function loadSkillDefinition(skill: SkillSummary): SkillLookupResult {
|
|
833
|
+
let loaded: SkillDefinition | null;
|
|
834
|
+
if (skill.bundled) {
|
|
835
|
+
loaded = readBundledSkillFromDirectory(skill.directoryPath);
|
|
836
|
+
} else if (skill.source === 'workspace') {
|
|
837
|
+
// Workspace skills live outside ~/.vellum/workspace/skills, so use their parent
|
|
838
|
+
// directory as the root to avoid the isOutsideSkillsRoot rejection.
|
|
839
|
+
loaded = readSkillFromDirectory(skill.directoryPath, dirname(skill.directoryPath), skill.source);
|
|
840
|
+
} else {
|
|
841
|
+
loaded = readSkillFromDirectory(skill.directoryPath, getSkillsDir(), skill.source);
|
|
842
|
+
}
|
|
843
|
+
if (!loaded) {
|
|
844
|
+
return { error: `Failed to load SKILL.md for "${skill.id}"` };
|
|
845
|
+
}
|
|
846
|
+
// Replace {baseDir} placeholders with the actual skill directory path
|
|
847
|
+
loaded.body = loaded.body.replaceAll('{baseDir}', loaded.directoryPath);
|
|
848
|
+
return { skill: loaded };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
export function resolveSkillSelector(selector: string, workspaceSkillsDir?: string): SkillSelectorResult {
|
|
852
|
+
const needle = selector.trim();
|
|
853
|
+
if (!needle) {
|
|
854
|
+
return { error: 'Skill selector is required and must be a non-empty string.' };
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const catalog = loadSkillCatalog(workspaceSkillsDir);
|
|
858
|
+
if (catalog.length === 0) {
|
|
859
|
+
return { error: 'No skills are available. Configure ~/.vellum/workspace/skills/SKILLS.md or add skill directories.' };
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const exactIdMatch = catalog.find((skill) => skill.id === needle);
|
|
863
|
+
if (exactIdMatch) {
|
|
864
|
+
return { skill: exactIdMatch };
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const exactNameMatches = catalog.filter(
|
|
868
|
+
(skill) => skill.name.toLowerCase() === needle.toLowerCase(),
|
|
869
|
+
);
|
|
870
|
+
if (exactNameMatches.length === 1) {
|
|
871
|
+
return { skill: exactNameMatches[0] };
|
|
872
|
+
}
|
|
873
|
+
if (exactNameMatches.length > 1) {
|
|
874
|
+
const ids = exactNameMatches.map((skill) => skill.id).join(', ');
|
|
875
|
+
return { error: `Ambiguous skill name "${needle}". Matching IDs: ${ids}` };
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const idPrefixMatches = catalog.filter((skill) => skill.id.startsWith(needle));
|
|
879
|
+
if (idPrefixMatches.length === 1) {
|
|
880
|
+
return { skill: idPrefixMatches[0] };
|
|
881
|
+
}
|
|
882
|
+
if (idPrefixMatches.length > 1) {
|
|
883
|
+
const ids = idPrefixMatches.map((skill) => skill.id).join(', ');
|
|
884
|
+
return { error: `Ambiguous skill id prefix "${needle}". Matching IDs: ${ids}` };
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const knownSkills = catalog.map((skill) => skill.id).join(', ');
|
|
888
|
+
return { error: `No skill matched "${needle}". Available skills: ${knownSkills}` };
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
export function loadSkillBySelector(selector: string, workspaceSkillsDir?: string): SkillLookupResult {
|
|
892
|
+
const resolved = resolveSkillSelector(selector, workspaceSkillsDir);
|
|
893
|
+
if (!resolved.skill) {
|
|
894
|
+
return { error: resolved.error ?? 'Failed to resolve skill selector.' };
|
|
895
|
+
}
|
|
896
|
+
return loadSkillDefinition(resolved.skill);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// ─── Icon generation ─────────────────────────────────────────────────────────
|
|
900
|
+
|
|
901
|
+
async function generateSkillIcon(name: string, description: string): Promise<string> {
|
|
902
|
+
const config = getConfig();
|
|
903
|
+
const apiKey = config.apiKeys.anthropic ?? process.env.ANTHROPIC_API_KEY;
|
|
904
|
+
if (!apiKey) {
|
|
905
|
+
throw new Error('No Anthropic API key available for icon generation');
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const client = new Anthropic({ apiKey });
|
|
909
|
+
const response = await client.messages.create({
|
|
910
|
+
model: 'claude-haiku-4-5-20251001',
|
|
911
|
+
max_tokens: 1024,
|
|
912
|
+
system: 'You are a pixel art icon designer. When asked, return ONLY a single <svg> element — no explanation, no markdown, no code fences. The SVG must be a 16x16 grid pixel art icon using <rect> elements. Use a limited palette (3-5 colors). Keep it under 2KB. The viewBox should be "0 0 16 16" with each pixel being a 1x1 rect.',
|
|
913
|
+
messages: [{
|
|
914
|
+
role: 'user',
|
|
915
|
+
content: `Create a 16x16 pixel art SVG icon representing this skill:\nName: ${name}\nDescription: ${description}`,
|
|
916
|
+
}],
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
const text = response.content
|
|
920
|
+
.filter((block): block is Anthropic.TextBlock => block.type === 'text')
|
|
921
|
+
.map((block) => block.text)
|
|
922
|
+
.join('');
|
|
923
|
+
|
|
924
|
+
const svgMatch = text.match(/<svg[\s\S]*<\/svg>/i);
|
|
925
|
+
if (!svgMatch) {
|
|
926
|
+
throw new Error('No <svg> element found in response');
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
return svgMatch[0];
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Synchronously read a cached icon if it exists on disk. Returns undefined if not cached yet.
|
|
934
|
+
*/
|
|
935
|
+
export function readCachedSkillIcon(directoryPath: string): string | undefined {
|
|
936
|
+
const iconPath = join(directoryPath, 'icon.svg');
|
|
937
|
+
if (existsSync(iconPath)) {
|
|
938
|
+
try {
|
|
939
|
+
return readFileSync(iconPath, 'utf-8');
|
|
940
|
+
} catch {
|
|
941
|
+
return undefined;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return undefined;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
export async function ensureSkillIcon(directoryPath: string, name: string, description: string): Promise<string | undefined> {
|
|
948
|
+
const iconPath = join(directoryPath, 'icon.svg');
|
|
949
|
+
|
|
950
|
+
if (existsSync(iconPath)) {
|
|
951
|
+
try {
|
|
952
|
+
return readFileSync(iconPath, 'utf-8');
|
|
953
|
+
} catch {
|
|
954
|
+
log.warn({ iconPath }, 'Failed to read existing icon.svg');
|
|
955
|
+
return undefined;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
try {
|
|
960
|
+
const svg = await generateSkillIcon(name, description);
|
|
961
|
+
try {
|
|
962
|
+
writeFileSync(iconPath, svg, 'utf-8');
|
|
963
|
+
log.info({ iconPath }, 'Generated skill icon');
|
|
964
|
+
} catch (writeErr) {
|
|
965
|
+
log.warn({ err: writeErr, iconPath }, 'Failed to cache icon.svg (returning generated icon anyway)');
|
|
966
|
+
}
|
|
967
|
+
return svg;
|
|
968
|
+
} catch (err) {
|
|
969
|
+
log.warn({ err, iconPath }, 'Failed to generate skill icon');
|
|
970
|
+
return undefined;
|
|
971
|
+
}
|
|
972
|
+
}
|