@sayknow-cli/coding-agent 0.3.9 → 0.3.10
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 +13 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/notify-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/config/model-profile-activation.d.ts +7 -7
- package/dist/types/config/model-registry.d.ts +11 -3
- package/dist/types/config/model-resolver.d.ts +2 -2
- package/dist/types/config/settings-schema.d.ts +1 -1
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +20 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -2
- package/dist/types/modes/components/thinking-selector.d.ts +6 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/notifications/config-commands.d.ts +29 -0
- package/dist/types/notifications/index.d.ts +12 -1
- package/dist/types/notifications/threaded-render.d.ts +2 -0
- package/dist/types/notifications/topic-registry.d.ts +4 -5
- package/dist/types/runtime-credential-selector.d.ts +7 -0
- package/dist/types/sdk.d.ts +7 -1
- package/dist/types/session/agent-session.d.ts +6 -3
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/skill-discovery.d.ts +40 -0
- package/package.json +7 -7
- package/src/cli/args.ts +8 -0
- package/src/cli/notify-cli.ts +27 -1
- package/src/cli/update-cli.ts +24 -63
- package/src/cli.ts +5 -0
- package/src/commands/launch.ts +5 -0
- package/src/commands/notify.ts +2 -2
- package/src/config/model-profile-activation.ts +23 -7
- package/src/config/model-registry.ts +107 -11
- package/src/config/model-resolver.ts +5 -16
- package/src/config/settings-schema.ts +3 -2
- package/src/config/settings.ts +24 -1
- package/src/extensibility/runtime-skill-discovery.ts +229 -0
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/main.ts +26 -3
- package/src/modes/components/settings-selector.ts +3 -3
- package/src/modes/components/thinking-selector.ts +79 -18
- package/src/modes/controllers/input-controller.ts +50 -9
- package/src/modes/controllers/selector-controller.ts +7 -5
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +48 -15
- package/src/notifications/config-commands.ts +70 -0
- package/src/notifications/index.ts +156 -9
- package/src/notifications/rich-render.ts +1 -0
- package/src/notifications/telegram-daemon.ts +87 -4
- package/src/notifications/threaded-render.ts +14 -0
- package/src/notifications/topic-registry.ts +9 -8
- package/src/prompts/tools/skill-discovery.md +13 -0
- package/src/runtime-credential-selector.ts +35 -0
- package/src/sdk.ts +68 -5
- package/src/session/agent-session.ts +42 -18
- package/src/skc-runtime/psmux-detect.ts +17 -2
- package/src/system-prompt.ts +4 -6
- package/src/tools/index.ts +4 -0
- package/src/tools/skill-discovery.ts +73 -0
- package/src/tools/skill.ts +15 -2
|
@@ -473,6 +473,8 @@ export interface PromptOptions {
|
|
|
473
473
|
images?: ImageContent[];
|
|
474
474
|
/** When streaming, how to queue the message: "steer" (interrupt) or "followUp" (wait). */
|
|
475
475
|
streamingBehavior?: "steer" | "followUp";
|
|
476
|
+
/** When set to "sequential", this follow-up is delivered one prompt at a time even if followUpMode is "all". */
|
|
477
|
+
followUpQueuePolicy?: "respect-mode" | "sequential";
|
|
476
478
|
/** Optional tool choice override for the next LLM call. */
|
|
477
479
|
toolChoice?: ToolChoice;
|
|
478
480
|
/** Send as developer/system message instead of user. Providers that support it use the developer role; others fall back to user. */
|
|
@@ -5304,7 +5306,9 @@ export class AgentSession {
|
|
|
5304
5306
|
throw new AgentBusyError();
|
|
5305
5307
|
}
|
|
5306
5308
|
if (options.streamingBehavior === "followUp") {
|
|
5307
|
-
await this.#queueFollowUp(expandedText, options?.images
|
|
5309
|
+
await this.#queueFollowUp(expandedText, options?.images, {
|
|
5310
|
+
forceOneAtATime: options.followUpQueuePolicy === "sequential",
|
|
5311
|
+
});
|
|
5308
5312
|
} else {
|
|
5309
5313
|
await this.#queueSteer(expandedText, options?.images);
|
|
5310
5314
|
}
|
|
@@ -5422,7 +5426,7 @@ export class AgentSession {
|
|
|
5422
5426
|
|
|
5423
5427
|
async promptCustomMessage<T = unknown>(
|
|
5424
5428
|
message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
|
|
5425
|
-
options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice">,
|
|
5429
|
+
options?: Pick<PromptOptions, "streamingBehavior" | "toolChoice" | "followUpQueuePolicy">,
|
|
5426
5430
|
): Promise<void> {
|
|
5427
5431
|
const textContent =
|
|
5428
5432
|
typeof message.content === "string"
|
|
@@ -5436,7 +5440,10 @@ export class AgentSession {
|
|
|
5436
5440
|
if (!options?.streamingBehavior) {
|
|
5437
5441
|
throw new AgentBusyError();
|
|
5438
5442
|
}
|
|
5439
|
-
await this.sendCustomMessage(message, {
|
|
5443
|
+
await this.sendCustomMessage(message, {
|
|
5444
|
+
deliverAs: options.streamingBehavior,
|
|
5445
|
+
followUpQueuePolicy: options.followUpQueuePolicy,
|
|
5446
|
+
});
|
|
5440
5447
|
return;
|
|
5441
5448
|
}
|
|
5442
5449
|
|
|
@@ -5783,13 +5790,19 @@ export class AgentSession {
|
|
|
5783
5790
|
/**
|
|
5784
5791
|
* Queue a follow-up message to process after the agent would otherwise stop.
|
|
5785
5792
|
*/
|
|
5786
|
-
async followUp(
|
|
5793
|
+
async followUp(
|
|
5794
|
+
text: string,
|
|
5795
|
+
images?: ImageContent[],
|
|
5796
|
+
options?: Pick<PromptOptions, "followUpQueuePolicy">,
|
|
5797
|
+
): Promise<void> {
|
|
5787
5798
|
if (text.startsWith("/")) {
|
|
5788
5799
|
this.#throwIfExtensionCommand(text);
|
|
5789
5800
|
}
|
|
5790
5801
|
|
|
5791
5802
|
const expandedText = expandPromptTemplate(text, [...this.#promptTemplates]);
|
|
5792
|
-
await this.#queueFollowUp(expandedText, images
|
|
5803
|
+
await this.#queueFollowUp(expandedText, images, {
|
|
5804
|
+
forceOneAtATime: options?.followUpQueuePolicy === "sequential",
|
|
5805
|
+
});
|
|
5793
5806
|
}
|
|
5794
5807
|
|
|
5795
5808
|
/**
|
|
@@ -5826,19 +5839,22 @@ export class AgentSession {
|
|
|
5826
5839
|
/**
|
|
5827
5840
|
* Internal: Queue a follow-up message (already expanded, no extension command check).
|
|
5828
5841
|
*/
|
|
5829
|
-
async #queueFollowUp(text: string, images?: ImageContent[]): Promise<void> {
|
|
5842
|
+
async #queueFollowUp(text: string, images?: ImageContent[], options?: { forceOneAtATime?: boolean }): Promise<void> {
|
|
5830
5843
|
const displayText = text || (images && images.length > 0 ? "[Image]" : "");
|
|
5831
5844
|
this.#followUpMessages.push(this.#createQueuedDisplayEntry(displayText));
|
|
5832
5845
|
const content: (TextContent | ImageContent)[] = [{ type: "text", text }];
|
|
5833
5846
|
if (images && images.length > 0) {
|
|
5834
5847
|
content.push(...images);
|
|
5835
5848
|
}
|
|
5836
|
-
this.agent.followUp(
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5849
|
+
this.agent.followUp(
|
|
5850
|
+
{
|
|
5851
|
+
role: "user",
|
|
5852
|
+
content,
|
|
5853
|
+
attribution: "user",
|
|
5854
|
+
timestamp: Date.now(),
|
|
5855
|
+
},
|
|
5856
|
+
options?.forceOneAtATime ? { forceOneAtATime: true } : undefined,
|
|
5857
|
+
);
|
|
5842
5858
|
// When fully idle AND the session is in a resumable assistant-ended state,
|
|
5843
5859
|
// schedule an immediate continue so the queued follow-up is delivered
|
|
5844
5860
|
// without waiting for the next user turn. We gate on isStreaming (model
|
|
@@ -5986,7 +6002,11 @@ export class AgentSession {
|
|
|
5986
6002
|
*/
|
|
5987
6003
|
async sendCustomMessage<T = unknown>(
|
|
5988
6004
|
message: Pick<CustomMessage<T>, "customType" | "content" | "display" | "details" | "attribution">,
|
|
5989
|
-
options?: {
|
|
6005
|
+
options?: {
|
|
6006
|
+
triggerTurn?: boolean;
|
|
6007
|
+
deliverAs?: "steer" | "followUp" | "nextTurn";
|
|
6008
|
+
followUpQueuePolicy?: "respect-mode" | "sequential";
|
|
6009
|
+
},
|
|
5990
6010
|
): Promise<void> {
|
|
5991
6011
|
const appMessage: CustomMessage<T> = {
|
|
5992
6012
|
role: "custom",
|
|
@@ -6004,7 +6024,10 @@ export class AgentSession {
|
|
|
6004
6024
|
}
|
|
6005
6025
|
|
|
6006
6026
|
if (options?.deliverAs === "followUp") {
|
|
6007
|
-
this.agent.followUp(
|
|
6027
|
+
this.agent.followUp(
|
|
6028
|
+
appMessage,
|
|
6029
|
+
options.followUpQueuePolicy === "sequential" ? { forceOneAtATime: true } : undefined,
|
|
6030
|
+
);
|
|
6008
6031
|
} else {
|
|
6009
6032
|
this.agent.steer(appMessage);
|
|
6010
6033
|
}
|
|
@@ -6930,7 +6953,7 @@ export class AgentSession {
|
|
|
6930
6953
|
|
|
6931
6954
|
/**
|
|
6932
6955
|
* Set thinking level.
|
|
6933
|
-
* Saves the effective metadata-clamped level to session and settings
|
|
6956
|
+
* Saves the effective metadata-clamped level to the session, and to settings when requested.
|
|
6934
6957
|
*/
|
|
6935
6958
|
setThinkingLevel(level: ThinkingLevel | undefined, persist: boolean = false): void {
|
|
6936
6959
|
const effectiveLevel = resolveThinkingLevelForModel(this.model, level);
|
|
@@ -6939,11 +6962,12 @@ export class AgentSession {
|
|
|
6939
6962
|
this.#thinkingLevel = effectiveLevel;
|
|
6940
6963
|
this.agent.setThinkingLevel(toReasoningEffort(effectiveLevel));
|
|
6941
6964
|
|
|
6965
|
+
if (persist && effectiveLevel !== undefined) {
|
|
6966
|
+
this.settings.set("defaultThinkingLevel", effectiveLevel);
|
|
6967
|
+
}
|
|
6968
|
+
|
|
6942
6969
|
if (isChanging) {
|
|
6943
6970
|
this.sessionManager.appendThinkingLevelChange(effectiveLevel);
|
|
6944
|
-
if (persist && effectiveLevel !== undefined && effectiveLevel !== ThinkingLevel.Off) {
|
|
6945
|
-
this.settings.set("defaultThinkingLevel", effectiveLevel);
|
|
6946
|
-
}
|
|
6947
6971
|
this.#emit({ type: "thinking_level_changed", thinkingLevel: effectiveLevel });
|
|
6948
6972
|
}
|
|
6949
6973
|
}
|
|
@@ -115,6 +115,20 @@ function outputMentionsPsmux(output: string): boolean {
|
|
|
115
115
|
return PSMUX_VERSION_MARKERS.some(marker => output.includes(marker));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
function normalizedCommandBaseName(command: string): string {
|
|
119
|
+
const normalized = command
|
|
120
|
+
.trim()
|
|
121
|
+
.replace(/^['"]|['"]$/g, "")
|
|
122
|
+
.replace(/\\/g, "/");
|
|
123
|
+
const basename = normalized.slice(normalized.lastIndexOf("/") + 1).toLowerCase();
|
|
124
|
+
return basename.endsWith(".exe") ? basename.slice(0, -4) : basename;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isNamedPsmuxCommand(command: string): boolean {
|
|
128
|
+
const basename = normalizedCommandBaseName(command);
|
|
129
|
+
return basename === "psmux" || basename === "pmux";
|
|
130
|
+
}
|
|
131
|
+
|
|
118
132
|
function resolveBinaryPath(candidate: string): string | null {
|
|
119
133
|
return activeBinaryResolver(candidate);
|
|
120
134
|
}
|
|
@@ -194,13 +208,14 @@ export function resolveSkcTmuxBinary(options: ResolveSkcTmuxBinaryOptions = {}):
|
|
|
194
208
|
const runner = options.runner ?? readSpawnRunner();
|
|
195
209
|
const explicit = env.SKC_TMUX_COMMAND?.trim() || env.SKC_TEAM_TMUX_COMMAND?.trim();
|
|
196
210
|
if (explicit) {
|
|
197
|
-
const isPsmux =
|
|
211
|
+
const isPsmux =
|
|
212
|
+
platform === "win32" && isNamedPsmuxCommand(explicit) ? true : detectPsmux(explicit, { env, runner });
|
|
198
213
|
return { command: explicit, isPsmux, viaExplicitOverride: true };
|
|
199
214
|
}
|
|
200
215
|
if (platform === "win32") {
|
|
201
216
|
for (const candidate of PSMUX_BINARY_NAMES) {
|
|
202
217
|
if (resolveBinaryPath(candidate)) {
|
|
203
|
-
const isPsmux = detectPsmux(candidate, { env, runner });
|
|
218
|
+
const isPsmux = isNamedPsmuxCommand(candidate) ? true : detectPsmux(candidate, { env, runner });
|
|
204
219
|
return { command: candidate, isPsmux, viaExplicitOverride: false };
|
|
205
220
|
}
|
|
206
221
|
}
|
package/src/system-prompt.ts
CHANGED
|
@@ -507,7 +507,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
507
507
|
? loadSkills({ ...skillsSettings, cwd: resolvedCwd }).then(result => result.skills)
|
|
508
508
|
: Promise.resolve([]);
|
|
509
509
|
|
|
510
|
-
const [resolvedCustomPrompt, resolvedAppendPrompt, systemPromptCustomization, contextFiles,
|
|
510
|
+
const [resolvedCustomPrompt, resolvedAppendPrompt, systemPromptCustomization, contextFiles, , workspaceTree] =
|
|
511
511
|
await Promise.all([
|
|
512
512
|
withDeadline(
|
|
513
513
|
"customPrompt",
|
|
@@ -581,11 +581,9 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
581
581
|
description: tools?.get(name)?.description ?? "",
|
|
582
582
|
}));
|
|
583
583
|
|
|
584
|
-
//
|
|
585
|
-
//
|
|
586
|
-
|
|
587
|
-
const hasRead = tools?.has("read");
|
|
588
|
-
const filteredSkills = hasRead ? skills.filter(skill => skill.hide !== true) : [];
|
|
584
|
+
// Runtime skills are discovered by `skill_discovery` and loaded narrowly through
|
|
585
|
+
// `skill`; do not dump the full custom skill catalog into the core prompt.
|
|
586
|
+
const filteredSkills: Skill[] = [];
|
|
589
587
|
|
|
590
588
|
const effectiveSystemPromptCustomization = dedupePromptSource(systemPromptCustomization, [
|
|
591
589
|
resolvedCustomPrompt,
|
package/src/tools/index.ts
CHANGED
|
@@ -56,6 +56,7 @@ import { reportFindingTool } from "./review";
|
|
|
56
56
|
import { SearchTool } from "./search";
|
|
57
57
|
import { SearchToolBm25Tool } from "./search-tool-bm25";
|
|
58
58
|
import { SkillTool } from "./skill";
|
|
59
|
+
import { SkillDiscoveryTool } from "./skill-discovery";
|
|
59
60
|
import { loadSshTool } from "./ssh";
|
|
60
61
|
import { SubagentTool } from "./subagent";
|
|
61
62
|
import { TelegramSendTool } from "./telegram-send";
|
|
@@ -95,6 +96,7 @@ export * from "./review";
|
|
|
95
96
|
export * from "./search";
|
|
96
97
|
export * from "./search-tool-bm25";
|
|
97
98
|
export * from "./skill";
|
|
99
|
+
export * from "./skill-discovery";
|
|
98
100
|
export * from "./ssh";
|
|
99
101
|
export * from "./subagent";
|
|
100
102
|
export * from "./telegram-send";
|
|
@@ -411,6 +413,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
411
413
|
todo_write: s => new TodoWriteTool(s),
|
|
412
414
|
web_search: s => new WebSearchTool(s),
|
|
413
415
|
search_tool_bm25: SearchToolBm25Tool.createIf,
|
|
416
|
+
skill_discovery: SkillDiscoveryTool.createIf,
|
|
414
417
|
telegram_send: TelegramSendTool.createIf,
|
|
415
418
|
write: s => new WriteTool(s),
|
|
416
419
|
skill: SkillTool.createIf,
|
|
@@ -570,6 +573,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
570
573
|
if (name === "search_tool_bm25") return discoveryActive;
|
|
571
574
|
if (name === "calc") return session.settings.get("calc.enabled");
|
|
572
575
|
if (name === "skill") return session.settings.get("skill.enabled");
|
|
576
|
+
if (name === "skill_discovery") return session.settings.get("skill.enabled");
|
|
573
577
|
if (name === "browser") return session.settings.get("browser.enabled");
|
|
574
578
|
if (name === "computer") return isComputerCallable(session);
|
|
575
579
|
if (name === "checkpoint" || name === "rewind") return session.settings.get("checkpoint.enabled");
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@sayknow-cli/agent-core";
|
|
2
|
+
import { prompt, untilAborted } from "@sayknow-cli/utils";
|
|
3
|
+
import * as z from "zod/v4";
|
|
4
|
+
import { discoverRuntimeSkills, type RuntimeSkillDiscoveryCandidate } from "../extensibility/runtime-skill-discovery";
|
|
5
|
+
import skillDiscoveryDescription from "../prompts/tools/skill-discovery.md" with { type: "text" };
|
|
6
|
+
import type { ToolSession } from ".";
|
|
7
|
+
|
|
8
|
+
const skillDiscoverySchema = z
|
|
9
|
+
.object({
|
|
10
|
+
query: z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("words to match against skill name, description, source, or use conditions"),
|
|
14
|
+
source: z.enum(["all", "project", "user"]).default("all").optional().describe("skill source scope to search"),
|
|
15
|
+
limit: z.number().min(1).max(50).default(20).optional().describe("maximum results"),
|
|
16
|
+
})
|
|
17
|
+
.strict();
|
|
18
|
+
|
|
19
|
+
export type SkillDiscoveryToolInput = z.infer<typeof skillDiscoverySchema>;
|
|
20
|
+
|
|
21
|
+
export interface SkillDiscoveryToolDetails {
|
|
22
|
+
candidates: RuntimeSkillDiscoveryCandidate[];
|
|
23
|
+
count: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class SkillDiscoveryTool implements AgentTool<typeof skillDiscoverySchema, SkillDiscoveryToolDetails> {
|
|
27
|
+
readonly name = "skill_discovery";
|
|
28
|
+
readonly label = "SkillDiscovery";
|
|
29
|
+
readonly summary = "Discover project and user runtime skills by thin metadata";
|
|
30
|
+
readonly loadMode = "essential";
|
|
31
|
+
readonly description: string;
|
|
32
|
+
readonly parameters = skillDiscoverySchema;
|
|
33
|
+
readonly strict = true;
|
|
34
|
+
|
|
35
|
+
readonly #session: ToolSession;
|
|
36
|
+
|
|
37
|
+
constructor(session: ToolSession) {
|
|
38
|
+
this.#session = session;
|
|
39
|
+
this.description = prompt.render(skillDiscoveryDescription);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#getRuntimeSkillPolicy() {
|
|
43
|
+
return {
|
|
44
|
+
...this.#session.settings.getGroup("skills"),
|
|
45
|
+
disabledExtensions: this.#session.settings.get("disabledExtensions"),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static createIf(session: ToolSession): SkillDiscoveryTool | null {
|
|
50
|
+
if (session.settings.get("skill.enabled") === false) return null;
|
|
51
|
+
return new SkillDiscoveryTool(session);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async execute(
|
|
55
|
+
_toolCallId: string,
|
|
56
|
+
input: SkillDiscoveryToolInput,
|
|
57
|
+
signal?: AbortSignal,
|
|
58
|
+
): Promise<AgentToolResult<SkillDiscoveryToolDetails>> {
|
|
59
|
+
return untilAborted(signal, async () => {
|
|
60
|
+
const candidates = await discoverRuntimeSkills({
|
|
61
|
+
cwd: this.#session.cwd,
|
|
62
|
+
query: input.query,
|
|
63
|
+
source: input.source ?? "all",
|
|
64
|
+
limit: input.limit,
|
|
65
|
+
policy: this.#getRuntimeSkillPolicy(),
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: JSON.stringify({ candidates, count: candidates.length }, null, 2) }],
|
|
69
|
+
details: { candidates, count: candidates.length },
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
package/src/tools/skill.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { AgentTool, AgentToolResult } from "@sayknow-cli/agent-core";
|
|
19
19
|
import { prompt, untilAborted } from "@sayknow-cli/utils";
|
|
20
20
|
import * as z from "zod/v4";
|
|
21
|
+
import { findRuntimeSkillByName } from "../extensibility/runtime-skill-discovery";
|
|
21
22
|
import { resolveSubskillActivationForSkillInvocation } from "../extensibility/skc-plugins";
|
|
22
23
|
import { buildSkillPromptMessage } from "../extensibility/skills";
|
|
23
24
|
import skillDescription from "../prompts/tools/skill.md" with { type: "text" };
|
|
@@ -64,6 +65,13 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
|
|
|
64
65
|
this.description = prompt.render(skillDescription);
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
#getRuntimeSkillPolicy() {
|
|
69
|
+
return {
|
|
70
|
+
...this.#session.settings.getGroup("skills"),
|
|
71
|
+
disabledExtensions: this.#session.settings.get("disabledExtensions"),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
static createIf(session: ToolSession): SkillTool | null {
|
|
68
76
|
// The tool can only chain when the session can deliver the same-turn
|
|
69
77
|
// custom message. Without `sendCustomMessage` (e.g. minimal tool
|
|
@@ -110,10 +118,15 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
|
|
|
110
118
|
);
|
|
111
119
|
}
|
|
112
120
|
|
|
113
|
-
const skill =
|
|
121
|
+
const skill =
|
|
122
|
+
skills.find(s => s.name === requestedName) ??
|
|
123
|
+
(await findRuntimeSkillByName(this.#session.cwd, requestedName, this.#getRuntimeSkillPolicy()));
|
|
114
124
|
if (!skill) {
|
|
115
125
|
const available = skills.map(s => s.name).sort();
|
|
116
|
-
const hint =
|
|
126
|
+
const hint =
|
|
127
|
+
available.length > 0
|
|
128
|
+
? ` Available: ${available.join(", ")}. Use skill_discovery to find project/user runtime skills.`
|
|
129
|
+
: " Use skill_discovery to find project/user runtime skills.";
|
|
117
130
|
throw new ToolError(`skill tool: unknown skill "${requestedName}".${hint}`);
|
|
118
131
|
}
|
|
119
132
|
|