fix(builtin-skills): resolve shared templates after bundling
This commit is contained in:
@@ -40,6 +40,30 @@ describe("shared builtin skill file loader", () => {
|
|||||||
expect(reads).toHaveLength(1)
|
expect(reads).toHaveLength(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("#given source and bundled layouts #when loading shared skill templates #then both package-relative paths resolve", () => {
|
||||||
|
// given
|
||||||
|
const expectedContent = "---\nname: layout\n---\nLayout body"
|
||||||
|
const createMissingFileError = (): Error => {
|
||||||
|
const error = new Error("ENOENT missing SKILL.md")
|
||||||
|
Object.defineProperty(error, "code", { value: "ENOENT" })
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
const readFile = (path: string): string => {
|
||||||
|
if (path.endsWith("/packages/shared-skills/skills/layout/SKILL.md")) {
|
||||||
|
return expectedContent
|
||||||
|
}
|
||||||
|
throw createMissingFileError()
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const bundledLoader = createSharedSkillTemplateLoader(readFile, "/workspace/dist")
|
||||||
|
const sourceLoader = createSharedSkillTemplateLoader(readFile, "/workspace/src/features/builtin-skills")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(bundledLoader("layout")).toBe("Layout body")
|
||||||
|
expect(sourceLoader("layout")).toBe("Layout body")
|
||||||
|
})
|
||||||
|
|
||||||
test("#given a missing shared skill file #when loading the template #then the loader fails fast", () => {
|
test("#given a missing shared skill file #when loading the template #then the loader fails fast", () => {
|
||||||
// given
|
// given
|
||||||
const loader = createSharedSkillTemplateLoader(() => {
|
const loader = createSharedSkillTemplateLoader(() => {
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
import { readFileSync } from "node:fs"
|
import { readFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { dirname, join } from "node:path"
|
||||||
|
import { fileURLToPath } from "node:url"
|
||||||
import { parseFrontmatter } from "../../shared/frontmatter"
|
import { parseFrontmatter } from "../../shared/frontmatter"
|
||||||
|
|
||||||
type SkillFileReader = (path: string, encoding: "utf8") => string
|
type SkillFileReader = (path: string, encoding: "utf8") => string
|
||||||
|
const SHARED_SKILL_PATHS = [
|
||||||
export function createSharedSkillTemplateLoader(readFile: SkillFileReader = readFileSync): (skillName: string) => string {
|
["..", "packages", "shared-skills", "skills"],
|
||||||
|
["..", "..", "..", "packages", "shared-skills", "skills"],
|
||||||
|
] as const
|
||||||
|
const moduleDir = typeof import.meta.dir === "string" ? import.meta.dir : dirname(fileURLToPath(import.meta.url))
|
||||||
|
export function createSharedSkillTemplateLoader(
|
||||||
|
readFile: SkillFileReader = readFileSync,
|
||||||
|
baseDir: string = moduleDir,
|
||||||
|
): (skillName: string) => string {
|
||||||
const cache = new Map<string, string>()
|
const cache = new Map<string, string>()
|
||||||
|
|
||||||
return (skillName) => {
|
return (skillName) => {
|
||||||
const cached = cache.get(skillName)
|
const cached = cache.get(skillName)
|
||||||
if (cached !== undefined) {
|
if (cached !== undefined) return cached
|
||||||
return cached
|
let missingFileError: unknown
|
||||||
|
for (const segments of SHARED_SKILL_PATHS) {
|
||||||
|
try {
|
||||||
|
const { body } = parseFrontmatter(readFile(join(baseDir, ...segments, skillName, "SKILL.md"), "utf8"))
|
||||||
|
cache.set(skillName, body)
|
||||||
|
return body
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof Error && Reflect.get(error, "code") === "ENOENT")) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
missingFileError ??= error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
throw missingFileError ?? new Error(`missing shared skill template: ${skillName}`)
|
||||||
const skillPath = join(import.meta.dir, "..", "..", "..", "packages", "shared-skills", "skills", skillName, "SKILL.md")
|
|
||||||
const content = readFile(skillPath, "utf8")
|
|
||||||
const { body } = parseFrontmatter(content)
|
|
||||||
cache.set(skillName, body)
|
|
||||||
return body
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadSharedSkillTemplateFromDisk = createSharedSkillTemplateLoader()
|
const loadSharedSkillTemplateFromDisk = createSharedSkillTemplateLoader()
|
||||||
|
|
||||||
export function loadSharedSkillTemplate(skillName: string): string {
|
export function loadSharedSkillTemplate(skillName: string): string {
|
||||||
return loadSharedSkillTemplateFromDisk(skillName)
|
return loadSharedSkillTemplateFromDisk(skillName)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user