Skip to content

Commit 5aadf57

Browse files
committed
fix(openclaw): serve bundled resource paths in skill commands; clear stale generated dirs
Two follow-ups for multi-file skill support: - commands/skills.ts served only SKILL.md text, so bundled commands for multi-file skills directed the assistant to references/ files it was never given. Each command now appends a 'Bundled resource files' section mapping every relative resource path to its absolute installed location, so the assistant can read them with its own file tools. Single-file skills are unchanged (no section emitted); evals/ is excluded as test fixtures. - fetch-skills.ts merged into existing output dirs on rerun, so files deleted or renamed in a source skill survived as stale artifacts in the published package. The generated skill dir is now removed before copying. Verified: fetcher rerun clears planted stale files; mock-API command registration shows the /onboarding text mapping all four references with evals excluded, and single-file skills emitting no section. Signed-off-by: Cody Faust <cody@luminarysol.com>
1 parent c2544d3 commit 5aadf57

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

integrations/openclaw/commands/skills.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { existsSync, readFileSync } from "node:fs"
1+
import { existsSync, readFileSync, readdirSync } from "node:fs"
22
import { dirname, resolve } from "node:path"
33
import { fileURLToPath } from "node:url"
44
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry"
@@ -39,13 +39,62 @@ function loadSkill(skillsDir: string, dir: string): string {
3939
return readFileSync(resolve(skillsDir, dir, "SKILL.md"), "utf-8")
4040
}
4141

42+
/**
43+
* List a skill's bundled resource files (references/, assets/, ...) so the
44+
* command can tell the assistant where they live on disk. SKILL.md workflows
45+
* reference these by relative path; without the mapping, bundled commands
46+
* would direct the assistant to files it was never given. evals/ is excluded
47+
* as test fixtures.
48+
*/
49+
function listResourceFiles(skillsDir: string, dir: string): string[] {
50+
const root = resolve(skillsDir, dir)
51+
const files: string[] = []
52+
const walk = (rel: string) => {
53+
for (const entry of readdirSync(resolve(root, rel), {
54+
withFileTypes: true,
55+
})) {
56+
const relPath = rel ? `${rel}/${entry.name}` : entry.name
57+
if (entry.isDirectory()) {
58+
if (relPath !== "evals") walk(relPath)
59+
} else if (relPath !== "SKILL.md") {
60+
files.push(relPath)
61+
}
62+
}
63+
}
64+
walk("")
65+
return files.sort()
66+
}
67+
68+
function resourceSection(
69+
skillsDir: string,
70+
dir: string,
71+
files: string[],
72+
): string {
73+
if (files.length === 0) return ""
74+
const lines = files
75+
.map((f) => `- \`${f}\` → ${resolve(skillsDir, dir, f)}`)
76+
.join("\n")
77+
return (
78+
"\n\n## Bundled resource files\n\n" +
79+
"This skill references companion files by relative path. They are installed at:\n\n" +
80+
`${lines}\n\n` +
81+
"When the workflow directs you to read one of these files, read it from the absolute path listed above."
82+
)
83+
}
84+
4285
export function registerSkillCommands(api: OpenClawPluginApi): void {
4386
const skillsDir = resolveSkillsDir(api)
4487
const manifest = loadManifest(skillsDir)
4588

4689
for (const entry of manifest) {
4790
const commandName = entry.dir.replace(/^memory-/, "")
48-
const content = loadSkill(skillsDir, entry.dir)
91+
const content =
92+
loadSkill(skillsDir, entry.dir) +
93+
resourceSection(
94+
skillsDir,
95+
entry.dir,
96+
listResourceFiles(skillsDir, entry.dir),
97+
)
4998

5099
api.registerCommand({
51100
name: commandName,

integrations/openclaw/scripts/fetch-skills.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
mkdirSync,
1313
readdirSync,
1414
readFileSync,
15+
rmSync,
1516
writeFileSync,
1617
} from "node:fs"
1718
import { dirname, resolve } from "node:path"
@@ -82,6 +83,9 @@ function main() {
8283
const meta = parseFrontmatter(content)
8384

8485
const outDir = resolve(SKILLS_DIR, dir)
86+
// Clear any previously generated copy so files deleted or renamed in
87+
// the source skill don't survive as stale artifacts in the package.
88+
rmSync(outDir, { recursive: true, force: true })
8589
mkdirSync(outDir, { recursive: true })
8690
writeFileSync(resolve(outDir, "SKILL.md"), content)
8791

0 commit comments

Comments
 (0)