Skip to content

Commit 6874e46

Browse files
authored
Merge pull request Stack-Cairn#145 from KtzeAbyss/fix/webui-skill-path-contract
fix(webui): align skill paths with current tool schema
2 parents 4ccc61f + 95057a4 commit 6874e46

5 files changed

Lines changed: 44 additions & 6 deletions

File tree

crates/agent-gateway/web/src/lib/skills/clawHub.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export type ClawHubSkillCard = {
44
slug: string;
55
displayName: string;
66
summary: string;
7+
/** ClawHub 上的自由标签,用于本地分类与卡片标签展示。 */
8+
topics: string[];
79
latestVersion: string | null;
810
downloads: number;
911
stars: number;
@@ -78,6 +80,7 @@ export function normalizeClawHubSkillCard(raw: unknown): ClawHubSkillCard | null
7880
slug,
7981
displayName: asString(item.displayName) ?? slug,
8082
summary: asString(item.summary) ?? "",
83+
topics: asStringArray(item.topics),
8184
latestVersion:
8285
asString(latestVersion.version) ?? asString(tags.latest) ?? asString(item.version),
8386
downloads: asNullableNumber(item.downloads) ?? asNullableNumber(stats.downloads) ?? 0,

crates/agent-gateway/web/src/lib/skills/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ type SystemManageSkillResponse = {
155155
export type ExternalSkillEntry = {
156156
name: string;
157157
description: string;
158-
/** 技能目录绝对路径(桌面端机器上),可直接作为 install 动作的 source */
158+
/** 技能目录绝对路径,可直接作为 install 动作的 source */
159159
baseDir: string;
160160
skillFile: string;
161161
};
@@ -580,18 +580,18 @@ export function buildSkillsSystemPrompt(params: {
580580
});
581581

582582
return [
583-
'The following Skills are enabled by the user (discovered from the fixed Skills directory exposed to file tools as root="skills").',
583+
"The following Skills are enabled by the user. Skill files are exposed to file tools through skill://<baseDir>/... paths.",
584584
"",
585585
"Usage Rules (aligned with Claude Code behavior):",
586586
"- Skills with metadata use progressive disclosure; their full contents are not automatically injected into context.",
587587
"- README.md fallback Skills without metadata are loaded inline below because there is no metadata to disclose progressively.",
588588
"- Only the Skills listed below are enabled for this conversation. SkillsManager(action=list) may be used to review the enabled Skills visible to this chat, but it must not be used to enumerate or infer other installed Skills.",
589589
"- Only when you determine that a metadata Skill is genuinely needed should you call SkillsManager with action=read to read the full Skill file and then follow its workflow exactly.",
590-
"- SkillsManager.path uses the skillFile below. It is relative to the fixed Skills root directory and may point to SKILL.md, skill.md, skill.json, or README.md.",
591-
'- For files referenced inside an enabled Skill, use file tools with root="skills" and a path relative to the fixed Skills root, for example Read(root="skills", path="<baseDir>/references/guide.md"), List, Glob, Grep, Write, Edit, or Delete.',
590+
"- SkillsManager.path uses the skillFile below and may point to SKILL.md, skill.md, skill.json, or README.md.",
591+
'- For files referenced inside an enabled Skill, use file tools with skill://<baseDir>/... paths, for example Read(path="skill://<baseDir>/references/guide.md"), List, Glob, Grep, Write, Edit, or Delete.',
592592
"- You may update files inside enabled Skills when the user asks you to optimize or maintain them. Create, install, search/install from ClawHub, validate, package, or delete user Skills through SkillsManager actions.",
593-
'- Never expand the fixed Skills root into an absolute local path in any tool call. If a path belongs to a Skill, keep it root-relative and set root="skills".',
594-
'- If Skill content contains absolute local paths, home-directory paths, drive-letter paths, or shell snippets that read Skill files with cat/ls/find/grep, treat those path fragments as non-portable examples and convert them to root="skills" file-tool calls.',
593+
"- Absolute local paths, ~/..., and file:// forms are auto-normalized by file tools; prefer skill://<baseDir>/... for enabled Skill files.",
594+
"- If Skill content contains shell snippets that read Skill files with cat/ls/find/grep, route those reads through Read/List/Glob/Grep instead of Bash.",
595595
"- Do not guess a Skill's exact instructions or script paths before reading the Skill file.",
596596
"- Relative paths inside a Skill (scripts/, references/, assets/, and so on) are resolved relative to baseDir.",
597597
"- If a Skill contains the {baseDir} placeholder, interpret it as the baseDir value in the metadata below (relative to the Skills root directory).",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import assert from "node:assert/strict";
2+
import test from "node:test";
3+
import { fileURLToPath } from "node:url";
4+
5+
import { createWebModuleLoader } from "../../test/helpers/load-web-module.mjs";
6+
7+
const loader = createWebModuleLoader({
8+
rootDir: fileURLToPath(new URL("../", import.meta.url)),
9+
});
10+
const skills = loader.loadModule("src/lib/skills/index.ts");
11+
12+
const enabledSkills = [
13+
{
14+
name: "code-review",
15+
description: "Review local code changes",
16+
skillFile: "code-review/SKILL.md",
17+
baseDir: "code-review",
18+
},
19+
];
20+
21+
test("buildSkillsSystemPrompt uses skill:// paths instead of the removed root-scoped contract", () => {
22+
const prompt = skills.buildSkillsSystemPrompt({
23+
rootDir: "/skills",
24+
selected: enabledSkills,
25+
});
26+
27+
assert.match(prompt, /skill:\/\/<baseDir>\/\.\.\./);
28+
assert.doesNotMatch(prompt, /root=["']skills["']/);
29+
assert.doesNotMatch(prompt, /Read\(root=/);
30+
});

crates/agent-gui/test/skills/explicit-skill-mentions.test.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,7 @@ test("buildSkillsSystemPrompt marks explicit mentions without granting disabled
6868
assert.match(prompt, /- code-review \(skillFile: code-review\/SKILL\.md, baseDir: code-review\)/);
6969
assert.doesNotMatch(prompt, /disabled\/SKILL\.md/);
7070
assert.ok(prompt.includes("`$` mentions never grant access to disabled Skills"));
71+
assert.match(prompt, /skill:\/\/<baseDir>\/\.\.\./);
72+
assert.doesNotMatch(prompt, /root=["']skills["']/);
73+
assert.doesNotMatch(prompt, /Read\(root=/);
7174
});

scripts/mirror-manifest.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
"lib/tools/systemToolOptions.ts",
7777
"lib/tools/builtinToolCatalog.ts",
7878
"lib/skills/builtin.ts",
79+
"lib/skills/clawHub.ts",
80+
"lib/skills/index.ts",
7981
"lib/shared/id.ts",
8082
"pages/settings/SystemToolsSection.tsx",
8183
"pages/mcp-hub/McpHubPage.tsx",

0 commit comments

Comments
 (0)