@poolzin/pool-bot 2026.2.21 → 2026.2.22
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 +17 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
|
@@ -1,19 +1,43 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { formatSkillsForPrompt, loadSkillsFromDir, } from "@mariozechner/pi-coding-agent";
|
|
4
5
|
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
|
5
6
|
import { CONFIG_DIR, resolveUserPath } from "../../utils.js";
|
|
7
|
+
import { resolveSandboxPath } from "../sandbox-paths.js";
|
|
6
8
|
import { resolveBundledSkillsDir } from "./bundled-dir.js";
|
|
7
9
|
import { shouldIncludeSkill } from "./config.js";
|
|
10
|
+
import { normalizeSkillFilter } from "./filter.js";
|
|
8
11
|
import { parseFrontmatter, resolvePoolbotMetadata, resolveSkillInvocationPolicy, } from "./frontmatter.js";
|
|
9
12
|
import { resolvePluginSkillDirs } from "./plugin-skills.js";
|
|
10
13
|
import { serializeByKey } from "./serialize.js";
|
|
11
14
|
const fsp = fs.promises;
|
|
12
15
|
const skillsLogger = createSubsystemLogger("skills");
|
|
13
16
|
const skillCommandDebugOnce = new Set();
|
|
17
|
+
/**
|
|
18
|
+
* Replace the user's home directory prefix with `~` in skill file paths
|
|
19
|
+
* to reduce system prompt token usage. Models understand `~` expansion,
|
|
20
|
+
* and the read tool resolves `~` to the home directory.
|
|
21
|
+
*
|
|
22
|
+
* Example: `/Users/alice/.bun/.../skills/github/SKILL.md`
|
|
23
|
+
* → `~/.bun/.../skills/github/SKILL.md`
|
|
24
|
+
*
|
|
25
|
+
* Saves ~5–6 tokens per skill path × N skills ≈ 400–600 tokens total.
|
|
26
|
+
*/
|
|
27
|
+
function compactSkillPaths(skills) {
|
|
28
|
+
const home = os.homedir();
|
|
29
|
+
if (!home)
|
|
30
|
+
return skills;
|
|
31
|
+
const prefix = home.endsWith(path.sep) ? home : home + path.sep;
|
|
32
|
+
return skills.map((s) => ({
|
|
33
|
+
...s,
|
|
34
|
+
filePath: s.filePath.startsWith(prefix) ? "~/" + s.filePath.slice(prefix.length) : s.filePath,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
14
37
|
function debugSkillCommandOnce(messageKey, message, meta) {
|
|
15
|
-
if (skillCommandDebugOnce.has(messageKey))
|
|
38
|
+
if (skillCommandDebugOnce.has(messageKey)) {
|
|
16
39
|
return;
|
|
40
|
+
}
|
|
17
41
|
skillCommandDebugOnce.add(messageKey);
|
|
18
42
|
skillsLogger.debug(message, meta);
|
|
19
43
|
}
|
|
@@ -21,14 +45,14 @@ function filterSkillEntries(entries, config, skillFilter, eligibility) {
|
|
|
21
45
|
let filtered = entries.filter((entry) => shouldIncludeSkill({ entry, config, eligibility }));
|
|
22
46
|
// If skillFilter is provided, only include skills in the filter list.
|
|
23
47
|
if (skillFilter !== undefined) {
|
|
24
|
-
const normalized = skillFilter
|
|
48
|
+
const normalized = normalizeSkillFilter(skillFilter) ?? [];
|
|
25
49
|
const label = normalized.length > 0 ? normalized.join(", ") : "(none)";
|
|
26
|
-
|
|
50
|
+
skillsLogger.debug(`Applying skill filter: ${label}`);
|
|
27
51
|
filtered =
|
|
28
52
|
normalized.length > 0
|
|
29
53
|
? filtered.filter((entry) => normalized.includes(entry.skill.name))
|
|
30
54
|
: [];
|
|
31
|
-
|
|
55
|
+
skillsLogger.debug(`After skill filter: ${filtered.map((entry) => entry.skill.name).join(", ") || "(none)"}`);
|
|
32
56
|
}
|
|
33
57
|
return filtered;
|
|
34
58
|
}
|
|
@@ -36,6 +60,11 @@ const SKILL_COMMAND_MAX_LENGTH = 32;
|
|
|
36
60
|
const SKILL_COMMAND_FALLBACK = "skill";
|
|
37
61
|
// Discord command descriptions must be ≤100 characters
|
|
38
62
|
const SKILL_COMMAND_DESCRIPTION_MAX_LENGTH = 100;
|
|
63
|
+
const DEFAULT_MAX_CANDIDATES_PER_ROOT = 300;
|
|
64
|
+
const DEFAULT_MAX_SKILLS_LOADED_PER_SOURCE = 200;
|
|
65
|
+
const DEFAULT_MAX_SKILLS_IN_PROMPT = 150;
|
|
66
|
+
const DEFAULT_MAX_SKILLS_PROMPT_CHARS = 30_000;
|
|
67
|
+
const DEFAULT_MAX_SKILL_FILE_BYTES = 256_000;
|
|
39
68
|
function sanitizeSkillCommandName(raw) {
|
|
40
69
|
const normalized = raw
|
|
41
70
|
.toLowerCase()
|
|
@@ -47,35 +76,186 @@ function sanitizeSkillCommandName(raw) {
|
|
|
47
76
|
}
|
|
48
77
|
function resolveUniqueSkillCommandName(base, used) {
|
|
49
78
|
const normalizedBase = base.toLowerCase();
|
|
50
|
-
if (!used.has(normalizedBase))
|
|
79
|
+
if (!used.has(normalizedBase)) {
|
|
51
80
|
return base;
|
|
81
|
+
}
|
|
52
82
|
for (let index = 2; index < 1000; index += 1) {
|
|
53
83
|
const suffix = `_${index}`;
|
|
54
84
|
const maxBaseLength = Math.max(1, SKILL_COMMAND_MAX_LENGTH - suffix.length);
|
|
55
85
|
const trimmedBase = base.slice(0, maxBaseLength);
|
|
56
86
|
const candidate = `${trimmedBase}${suffix}`;
|
|
57
87
|
const candidateKey = candidate.toLowerCase();
|
|
58
|
-
if (!used.has(candidateKey))
|
|
88
|
+
if (!used.has(candidateKey)) {
|
|
59
89
|
return candidate;
|
|
90
|
+
}
|
|
60
91
|
}
|
|
61
92
|
const fallback = `${base.slice(0, Math.max(1, SKILL_COMMAND_MAX_LENGTH - 2))}_x`;
|
|
62
93
|
return fallback;
|
|
63
94
|
}
|
|
95
|
+
function resolveSkillsLimits(config) {
|
|
96
|
+
const limits = config?.skills?.limits;
|
|
97
|
+
return {
|
|
98
|
+
maxCandidatesPerRoot: limits?.maxCandidatesPerRoot ?? DEFAULT_MAX_CANDIDATES_PER_ROOT,
|
|
99
|
+
maxSkillsLoadedPerSource: limits?.maxSkillsLoadedPerSource ?? DEFAULT_MAX_SKILLS_LOADED_PER_SOURCE,
|
|
100
|
+
maxSkillsInPrompt: limits?.maxSkillsInPrompt ?? DEFAULT_MAX_SKILLS_IN_PROMPT,
|
|
101
|
+
maxSkillsPromptChars: limits?.maxSkillsPromptChars ?? DEFAULT_MAX_SKILLS_PROMPT_CHARS,
|
|
102
|
+
maxSkillFileBytes: limits?.maxSkillFileBytes ?? DEFAULT_MAX_SKILL_FILE_BYTES,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function listChildDirectories(dir) {
|
|
106
|
+
try {
|
|
107
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
108
|
+
const dirs = [];
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
if (entry.name.startsWith("."))
|
|
111
|
+
continue;
|
|
112
|
+
if (entry.name === "node_modules")
|
|
113
|
+
continue;
|
|
114
|
+
const fullPath = path.join(dir, entry.name);
|
|
115
|
+
if (entry.isDirectory()) {
|
|
116
|
+
dirs.push(entry.name);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (entry.isSymbolicLink()) {
|
|
120
|
+
try {
|
|
121
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
122
|
+
dirs.push(entry.name);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// ignore broken symlinks
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return dirs;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function resolveNestedSkillsRoot(dir, opts) {
|
|
137
|
+
const nested = path.join(dir, "skills");
|
|
138
|
+
try {
|
|
139
|
+
if (!fs.existsSync(nested) || !fs.statSync(nested).isDirectory()) {
|
|
140
|
+
return { baseDir: dir };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return { baseDir: dir };
|
|
145
|
+
}
|
|
146
|
+
// Heuristic: if `dir/skills/*/SKILL.md` exists for any entry, treat `dir/skills` as the real root.
|
|
147
|
+
// Note: don't stop at 25, but keep a cap to avoid pathological scans.
|
|
148
|
+
const nestedDirs = listChildDirectories(nested);
|
|
149
|
+
const scanLimit = Math.max(0, opts?.maxEntriesToScan ?? 100);
|
|
150
|
+
const toScan = scanLimit === 0 ? [] : nestedDirs.slice(0, Math.min(nestedDirs.length, scanLimit));
|
|
151
|
+
for (const name of toScan) {
|
|
152
|
+
const skillMd = path.join(nested, name, "SKILL.md");
|
|
153
|
+
if (fs.existsSync(skillMd)) {
|
|
154
|
+
return { baseDir: nested, note: `Detected nested skills root at ${nested}` };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { baseDir: dir };
|
|
158
|
+
}
|
|
159
|
+
function unwrapLoadedSkills(loaded) {
|
|
160
|
+
if (Array.isArray(loaded)) {
|
|
161
|
+
return loaded;
|
|
162
|
+
}
|
|
163
|
+
if (loaded && typeof loaded === "object" && "skills" in loaded) {
|
|
164
|
+
const skills = loaded.skills;
|
|
165
|
+
if (Array.isArray(skills)) {
|
|
166
|
+
return skills;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
64
171
|
function loadSkillEntries(workspaceDir, opts) {
|
|
172
|
+
const limits = resolveSkillsLimits(opts?.config);
|
|
65
173
|
const loadSkills = (params) => {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
174
|
+
const resolved = resolveNestedSkillsRoot(params.dir, {
|
|
175
|
+
maxEntriesToScan: limits.maxCandidatesPerRoot,
|
|
176
|
+
});
|
|
177
|
+
const baseDir = resolved.baseDir;
|
|
178
|
+
// If the root itself is a skill directory, just load it directly (but enforce size cap).
|
|
179
|
+
const rootSkillMd = path.join(baseDir, "SKILL.md");
|
|
180
|
+
if (fs.existsSync(rootSkillMd)) {
|
|
181
|
+
try {
|
|
182
|
+
const size = fs.statSync(rootSkillMd).size;
|
|
183
|
+
if (size > limits.maxSkillFileBytes) {
|
|
184
|
+
skillsLogger.warn("Skipping skills root due to oversized SKILL.md.", {
|
|
185
|
+
dir: baseDir,
|
|
186
|
+
filePath: rootSkillMd,
|
|
187
|
+
size,
|
|
188
|
+
maxSkillFileBytes: limits.maxSkillFileBytes,
|
|
189
|
+
});
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
const loaded = loadSkillsFromDir({ dir: baseDir, source: params.source });
|
|
197
|
+
return unwrapLoadedSkills(loaded);
|
|
74
198
|
}
|
|
75
|
-
|
|
199
|
+
const childDirs = listChildDirectories(baseDir);
|
|
200
|
+
const suspicious = childDirs.length > limits.maxCandidatesPerRoot;
|
|
201
|
+
const maxCandidates = Math.max(0, limits.maxSkillsLoadedPerSource);
|
|
202
|
+
const limitedChildren = childDirs.slice().sort().slice(0, maxCandidates);
|
|
203
|
+
if (suspicious) {
|
|
204
|
+
skillsLogger.warn("Skills root looks suspiciously large, truncating discovery.", {
|
|
205
|
+
dir: params.dir,
|
|
206
|
+
baseDir,
|
|
207
|
+
childDirCount: childDirs.length,
|
|
208
|
+
maxCandidatesPerRoot: limits.maxCandidatesPerRoot,
|
|
209
|
+
maxSkillsLoadedPerSource: limits.maxSkillsLoadedPerSource,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else if (childDirs.length > maxCandidates) {
|
|
213
|
+
skillsLogger.warn("Skills root has many entries, truncating discovery.", {
|
|
214
|
+
dir: params.dir,
|
|
215
|
+
baseDir,
|
|
216
|
+
childDirCount: childDirs.length,
|
|
217
|
+
maxSkillsLoadedPerSource: limits.maxSkillsLoadedPerSource,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const loadedSkills = [];
|
|
221
|
+
// Only consider immediate subfolders that look like skills (have SKILL.md) and are under size cap.
|
|
222
|
+
for (const name of limitedChildren) {
|
|
223
|
+
const skillDir = path.join(baseDir, name);
|
|
224
|
+
const skillMd = path.join(skillDir, "SKILL.md");
|
|
225
|
+
if (!fs.existsSync(skillMd)) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const size = fs.statSync(skillMd).size;
|
|
230
|
+
if (size > limits.maxSkillFileBytes) {
|
|
231
|
+
skillsLogger.warn("Skipping skill due to oversized SKILL.md.", {
|
|
232
|
+
skill: name,
|
|
233
|
+
filePath: skillMd,
|
|
234
|
+
size,
|
|
235
|
+
maxSkillFileBytes: limits.maxSkillFileBytes,
|
|
236
|
+
});
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const loaded = loadSkillsFromDir({ dir: skillDir, source: params.source });
|
|
244
|
+
loadedSkills.push(...unwrapLoadedSkills(loaded));
|
|
245
|
+
if (loadedSkills.length >= limits.maxSkillsLoadedPerSource) {
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (loadedSkills.length > limits.maxSkillsLoadedPerSource) {
|
|
250
|
+
return loadedSkills
|
|
251
|
+
.slice()
|
|
252
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
253
|
+
.slice(0, limits.maxSkillsLoadedPerSource);
|
|
254
|
+
}
|
|
255
|
+
return loadedSkills;
|
|
76
256
|
};
|
|
77
257
|
const managedSkillsDir = opts?.managedSkillsDir ?? path.join(CONFIG_DIR, "skills");
|
|
78
|
-
const workspaceSkillsDir = path.
|
|
258
|
+
const workspaceSkillsDir = path.resolve(workspaceDir, "skills");
|
|
79
259
|
const bundledSkillsDir = opts?.bundledSkillsDir ?? resolveBundledSkillsDir();
|
|
80
260
|
const extraDirsRaw = opts?.config?.skills?.load?.extraDirs ?? [];
|
|
81
261
|
const extraDirs = extraDirsRaw
|
|
@@ -103,20 +283,40 @@ function loadSkillEntries(workspaceDir, opts) {
|
|
|
103
283
|
dir: managedSkillsDir,
|
|
104
284
|
source: "poolbot-managed",
|
|
105
285
|
});
|
|
286
|
+
const personalAgentsSkillsDir = path.resolve(os.homedir(), ".agents", "skills");
|
|
287
|
+
const personalAgentsSkills = loadSkills({
|
|
288
|
+
dir: personalAgentsSkillsDir,
|
|
289
|
+
source: "agents-skills-personal",
|
|
290
|
+
});
|
|
291
|
+
const projectAgentsSkillsDir = path.resolve(workspaceDir, ".agents", "skills");
|
|
292
|
+
const projectAgentsSkills = loadSkills({
|
|
293
|
+
dir: projectAgentsSkillsDir,
|
|
294
|
+
source: "agents-skills-project",
|
|
295
|
+
});
|
|
106
296
|
const workspaceSkills = loadSkills({
|
|
107
297
|
dir: workspaceSkillsDir,
|
|
108
298
|
source: "poolbot-workspace",
|
|
109
299
|
});
|
|
110
300
|
const merged = new Map();
|
|
111
|
-
// Precedence: extra < bundled < managed < workspace
|
|
112
|
-
for (const skill of extraSkills)
|
|
301
|
+
// Precedence: extra < bundled < managed < agents-skills-personal < agents-skills-project < workspace
|
|
302
|
+
for (const skill of extraSkills) {
|
|
113
303
|
merged.set(skill.name, skill);
|
|
114
|
-
|
|
304
|
+
}
|
|
305
|
+
for (const skill of bundledSkills) {
|
|
115
306
|
merged.set(skill.name, skill);
|
|
116
|
-
|
|
307
|
+
}
|
|
308
|
+
for (const skill of managedSkills) {
|
|
309
|
+
merged.set(skill.name, skill);
|
|
310
|
+
}
|
|
311
|
+
for (const skill of personalAgentsSkills) {
|
|
117
312
|
merged.set(skill.name, skill);
|
|
118
|
-
|
|
313
|
+
}
|
|
314
|
+
for (const skill of projectAgentsSkills) {
|
|
315
|
+
merged.set(skill.name, skill);
|
|
316
|
+
}
|
|
317
|
+
for (const skill of workspaceSkills) {
|
|
119
318
|
merged.set(skill.name, skill);
|
|
319
|
+
}
|
|
120
320
|
const skillEntries = Array.from(merged.values()).map((skill) => {
|
|
121
321
|
let frontmatter = {};
|
|
122
322
|
try {
|
|
@@ -135,19 +335,64 @@ function loadSkillEntries(workspaceDir, opts) {
|
|
|
135
335
|
});
|
|
136
336
|
return skillEntries;
|
|
137
337
|
}
|
|
338
|
+
function applySkillsPromptLimits(params) {
|
|
339
|
+
const limits = resolveSkillsLimits(params.config);
|
|
340
|
+
const total = params.skills.length;
|
|
341
|
+
const byCount = params.skills.slice(0, Math.max(0, limits.maxSkillsInPrompt));
|
|
342
|
+
let skillsForPrompt = byCount;
|
|
343
|
+
let truncated = total > byCount.length;
|
|
344
|
+
let truncatedReason = truncated ? "count" : null;
|
|
345
|
+
const fits = (skills) => {
|
|
346
|
+
const block = formatSkillsForPrompt(skills);
|
|
347
|
+
return block.length <= limits.maxSkillsPromptChars;
|
|
348
|
+
};
|
|
349
|
+
if (!fits(skillsForPrompt)) {
|
|
350
|
+
// Binary search the largest prefix that fits in the char budget.
|
|
351
|
+
let lo = 0;
|
|
352
|
+
let hi = skillsForPrompt.length;
|
|
353
|
+
while (lo < hi) {
|
|
354
|
+
const mid = Math.ceil((lo + hi) / 2);
|
|
355
|
+
if (fits(skillsForPrompt.slice(0, mid))) {
|
|
356
|
+
lo = mid;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
hi = mid - 1;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
skillsForPrompt = skillsForPrompt.slice(0, lo);
|
|
363
|
+
truncated = true;
|
|
364
|
+
truncatedReason = "chars";
|
|
365
|
+
}
|
|
366
|
+
return { skillsForPrompt, truncated, truncatedReason };
|
|
367
|
+
}
|
|
138
368
|
export function buildWorkspaceSkillSnapshot(workspaceDir, opts) {
|
|
139
369
|
const skillEntries = opts?.entries ?? loadSkillEntries(workspaceDir, opts);
|
|
140
370
|
const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility);
|
|
141
371
|
const promptEntries = eligible.filter((entry) => entry.invocation?.disableModelInvocation !== true);
|
|
142
372
|
const resolvedSkills = promptEntries.map((entry) => entry.skill);
|
|
143
373
|
const remoteNote = opts?.eligibility?.remote?.note?.trim();
|
|
144
|
-
const
|
|
374
|
+
const { skillsForPrompt, truncated } = applySkillsPromptLimits({
|
|
375
|
+
skills: resolvedSkills,
|
|
376
|
+
config: opts?.config,
|
|
377
|
+
});
|
|
378
|
+
const truncationNote = truncated
|
|
379
|
+
? `⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}. Run \`poolbot skills check\` to audit.`
|
|
380
|
+
: "";
|
|
381
|
+
const prompt = [
|
|
382
|
+
remoteNote,
|
|
383
|
+
truncationNote,
|
|
384
|
+
formatSkillsForPrompt(compactSkillPaths(skillsForPrompt)),
|
|
385
|
+
]
|
|
386
|
+
.filter(Boolean)
|
|
387
|
+
.join("\n");
|
|
388
|
+
const skillFilter = normalizeSkillFilter(opts?.skillFilter);
|
|
145
389
|
return {
|
|
146
390
|
prompt,
|
|
147
391
|
skills: eligible.map((entry) => ({
|
|
148
392
|
name: entry.skill.name,
|
|
149
393
|
primaryEnv: entry.metadata?.primaryEnv,
|
|
150
394
|
})),
|
|
395
|
+
...(skillFilter === undefined ? {} : { skillFilter }),
|
|
151
396
|
resolvedSkills,
|
|
152
397
|
version: opts?.snapshotVersion,
|
|
153
398
|
};
|
|
@@ -157,14 +402,23 @@ export function buildWorkspaceSkillsPrompt(workspaceDir, opts) {
|
|
|
157
402
|
const eligible = filterSkillEntries(skillEntries, opts?.config, opts?.skillFilter, opts?.eligibility);
|
|
158
403
|
const promptEntries = eligible.filter((entry) => entry.invocation?.disableModelInvocation !== true);
|
|
159
404
|
const remoteNote = opts?.eligibility?.remote?.note?.trim();
|
|
160
|
-
|
|
405
|
+
const resolvedSkills = promptEntries.map((entry) => entry.skill);
|
|
406
|
+
const { skillsForPrompt, truncated } = applySkillsPromptLimits({
|
|
407
|
+
skills: resolvedSkills,
|
|
408
|
+
config: opts?.config,
|
|
409
|
+
});
|
|
410
|
+
const truncationNote = truncated
|
|
411
|
+
? `⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}. Run \`poolbot skills check\` to audit.`
|
|
412
|
+
: "";
|
|
413
|
+
return [remoteNote, truncationNote, formatSkillsForPrompt(compactSkillPaths(skillsForPrompt))]
|
|
161
414
|
.filter(Boolean)
|
|
162
415
|
.join("\n");
|
|
163
416
|
}
|
|
164
417
|
export function resolveSkillsPromptForRun(params) {
|
|
165
418
|
const snapshotPrompt = params.skillsSnapshot?.prompt?.trim();
|
|
166
|
-
if (snapshotPrompt)
|
|
419
|
+
if (snapshotPrompt) {
|
|
167
420
|
return snapshotPrompt;
|
|
421
|
+
}
|
|
168
422
|
if (params.entries && params.entries.length > 0) {
|
|
169
423
|
const prompt = buildWorkspaceSkillsPrompt(params.workspaceDir, {
|
|
170
424
|
entries: params.entries,
|
|
@@ -177,11 +431,45 @@ export function resolveSkillsPromptForRun(params) {
|
|
|
177
431
|
export function loadWorkspaceSkillEntries(workspaceDir, opts) {
|
|
178
432
|
return loadSkillEntries(workspaceDir, opts);
|
|
179
433
|
}
|
|
434
|
+
function resolveUniqueSyncedSkillDirName(base, used) {
|
|
435
|
+
if (!used.has(base)) {
|
|
436
|
+
used.add(base);
|
|
437
|
+
return base;
|
|
438
|
+
}
|
|
439
|
+
for (let index = 2; index < 10_000; index += 1) {
|
|
440
|
+
const candidate = `${base}-${index}`;
|
|
441
|
+
if (!used.has(candidate)) {
|
|
442
|
+
used.add(candidate);
|
|
443
|
+
return candidate;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
let fallbackIndex = 10_000;
|
|
447
|
+
let fallback = `${base}-${fallbackIndex}`;
|
|
448
|
+
while (used.has(fallback)) {
|
|
449
|
+
fallbackIndex += 1;
|
|
450
|
+
fallback = `${base}-${fallbackIndex}`;
|
|
451
|
+
}
|
|
452
|
+
used.add(fallback);
|
|
453
|
+
return fallback;
|
|
454
|
+
}
|
|
455
|
+
function resolveSyncedSkillDestinationPath(params) {
|
|
456
|
+
const sourceDirName = path.basename(params.entry.skill.baseDir).trim();
|
|
457
|
+
if (!sourceDirName || sourceDirName === "." || sourceDirName === "..") {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
const uniqueDirName = resolveUniqueSyncedSkillDirName(sourceDirName, params.usedDirNames);
|
|
461
|
+
return resolveSandboxPath({
|
|
462
|
+
filePath: uniqueDirName,
|
|
463
|
+
cwd: params.targetSkillsDir,
|
|
464
|
+
root: params.targetSkillsDir,
|
|
465
|
+
}).resolved;
|
|
466
|
+
}
|
|
180
467
|
export async function syncSkillsToWorkspace(params) {
|
|
181
468
|
const sourceDir = resolveUserPath(params.sourceWorkspaceDir);
|
|
182
469
|
const targetDir = resolveUserPath(params.targetWorkspaceDir);
|
|
183
|
-
if (sourceDir === targetDir)
|
|
470
|
+
if (sourceDir === targetDir) {
|
|
184
471
|
return;
|
|
472
|
+
}
|
|
185
473
|
await serializeByKey(`syncSkills:${targetDir}`, async () => {
|
|
186
474
|
const targetSkillsDir = path.join(targetDir, "skills");
|
|
187
475
|
const entries = loadSkillEntries(sourceDir, {
|
|
@@ -191,8 +479,25 @@ export async function syncSkillsToWorkspace(params) {
|
|
|
191
479
|
});
|
|
192
480
|
await fsp.rm(targetSkillsDir, { recursive: true, force: true });
|
|
193
481
|
await fsp.mkdir(targetSkillsDir, { recursive: true });
|
|
482
|
+
const usedDirNames = new Set();
|
|
194
483
|
for (const entry of entries) {
|
|
195
|
-
|
|
484
|
+
let dest = null;
|
|
485
|
+
try {
|
|
486
|
+
dest = resolveSyncedSkillDestinationPath({
|
|
487
|
+
targetSkillsDir,
|
|
488
|
+
entry,
|
|
489
|
+
usedDirNames,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
const message = error instanceof Error ? error.message : JSON.stringify(error);
|
|
494
|
+
console.warn(`[skills] Failed to resolve safe destination for ${entry.skill.name}: ${message}`);
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (!dest) {
|
|
498
|
+
console.warn(`[skills] Failed to resolve safe destination for ${entry.skill.name}: invalid source directory name`);
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
196
501
|
try {
|
|
197
502
|
await fsp.cp(entry.skill.baseDir, dest, {
|
|
198
503
|
recursive: true,
|
|
@@ -239,10 +544,12 @@ export function buildWorkspaceSkillCommandSpecs(workspaceDir, opts) {
|
|
|
239
544
|
"")
|
|
240
545
|
.trim()
|
|
241
546
|
.toLowerCase();
|
|
242
|
-
if (!kindRaw)
|
|
547
|
+
if (!kindRaw) {
|
|
243
548
|
return undefined;
|
|
244
|
-
|
|
549
|
+
}
|
|
550
|
+
if (kindRaw !== "tool") {
|
|
245
551
|
return undefined;
|
|
552
|
+
}
|
|
246
553
|
const toolName = (entry.frontmatter?.["command-tool"] ??
|
|
247
554
|
entry.frontmatter?.["command_tool"] ??
|
|
248
555
|
"").trim();
|