@batonfx/skills 0.3.7 → 0.4.0
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/.turbo/turbo-build.log +2 -2
- package/dist/index.js +2780 -609
- package/package.json +2 -2
- package/src/github-catalog.ts +80 -0
- package/src/hosted-catalog.ts +270 -0
- package/src/http-catalog.ts +29 -0
- package/src/index.ts +4 -0
- package/src/s3-catalog.ts +49 -0
- package/src/skill-document.ts +174 -0
- package/src/skill-loader.ts +30 -200
- package/test/hosted-catalog.test.ts +398 -0
- package/test/skill-loader.test.ts +67 -6
|
@@ -67,9 +67,7 @@ describe("SkillLoader", () => {
|
|
|
67
67
|
name: review
|
|
68
68
|
description: Review code carefully
|
|
69
69
|
when-to-use: before merging
|
|
70
|
-
|
|
71
|
-
- read
|
|
72
|
-
- grep
|
|
70
|
+
allowed-tools: read grep
|
|
73
71
|
disableModelInvocation: false
|
|
74
72
|
userInvocable: true
|
|
75
73
|
contextFork: true
|
|
@@ -112,9 +110,10 @@ Use the checklist.
|
|
|
112
110
|
}).pipe(Effect.provide(loaderTestLayer({ cwd: "/repo", roots: [".agents/skills"] }, files, directories, reads)))
|
|
113
111
|
})
|
|
114
112
|
|
|
115
|
-
it.effect("
|
|
113
|
+
it.effect("uses standard names for nested skills and lets later roots win collisions", () => {
|
|
116
114
|
const files = {
|
|
117
115
|
"/repo/a/frontend/lint/SKILL.md": `---
|
|
116
|
+
name: lint
|
|
118
117
|
description: Lint frontend
|
|
119
118
|
---
|
|
120
119
|
body a`,
|
|
@@ -136,10 +135,10 @@ body second`,
|
|
|
136
135
|
return Effect.gen(function* () {
|
|
137
136
|
const source = yield* SkillSource.SkillSource
|
|
138
137
|
const all = yield* source.all
|
|
139
|
-
const nested = yield* source.get("
|
|
138
|
+
const nested = yield* source.get("lint")
|
|
140
139
|
const duplicate = yield* source.get("dup")
|
|
141
140
|
|
|
142
|
-
expect(all.map((skill) => skill.frontmatter.name)).toEqual(["dup", "
|
|
141
|
+
expect(all.map((skill) => skill.frontmatter.name)).toEqual(["dup", "lint"])
|
|
143
142
|
expect(nested?.frontmatter.description).toBe("Lint frontend")
|
|
144
143
|
expect(duplicate?.frontmatter.description).toBe("Second duplicate")
|
|
145
144
|
}).pipe(Effect.provide(loaderTestLayer({ cwd: "/repo", roots: ["a", "b"] }, files, directories)))
|
|
@@ -148,6 +147,7 @@ body second`,
|
|
|
148
147
|
it.effect("fails typed for invalid frontmatter and keeps user-only skills addressable", () => {
|
|
149
148
|
const files = {
|
|
150
149
|
"/repo/skills/user-only/SKILL.md": `---
|
|
150
|
+
name: user-only
|
|
151
151
|
description: User only
|
|
152
152
|
disableModelInvocation: true
|
|
153
153
|
---
|
|
@@ -184,4 +184,65 @@ body`,
|
|
|
184
184
|
expect(SkillSource.selectListings(userOnly === undefined ? [] : [userOnly], 1_000, [])).toEqual([])
|
|
185
185
|
})
|
|
186
186
|
})
|
|
187
|
+
|
|
188
|
+
it.effect("enforces required standard name, directory equality, and description length", () => {
|
|
189
|
+
const cases = [
|
|
190
|
+
{
|
|
191
|
+
file: "/repo/skills/missing/SKILL.md",
|
|
192
|
+
content: `---
|
|
193
|
+
description: Missing name
|
|
194
|
+
---
|
|
195
|
+
body`,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
file: "/repo/skills/directory/SKILL.md",
|
|
199
|
+
content: `---
|
|
200
|
+
name: different
|
|
201
|
+
description: Mismatched directory
|
|
202
|
+
---
|
|
203
|
+
body`,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
file: "/repo/skills/invalid/SKILL.md",
|
|
207
|
+
content: `---
|
|
208
|
+
name: Invalid_Name
|
|
209
|
+
description: Invalid name
|
|
210
|
+
---
|
|
211
|
+
body`,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
file: "/repo/skills/double--dash/SKILL.md",
|
|
215
|
+
content: `---
|
|
216
|
+
name: double--dash
|
|
217
|
+
description: Consecutive hyphens are invalid
|
|
218
|
+
---
|
|
219
|
+
body`,
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
file: "/repo/skills/long/SKILL.md",
|
|
223
|
+
content: `---
|
|
224
|
+
name: long
|
|
225
|
+
description: ${"x".repeat(1025)}
|
|
226
|
+
---
|
|
227
|
+
body`,
|
|
228
|
+
},
|
|
229
|
+
]
|
|
230
|
+
return Effect.gen(function* () {
|
|
231
|
+
for (const testCase of cases) {
|
|
232
|
+
const relative = testCase.file.slice("/repo/skills/".length)
|
|
233
|
+
const failure = yield* Effect.flip(
|
|
234
|
+
SkillSource.SkillSource.pipe(
|
|
235
|
+
Effect.provide(
|
|
236
|
+
loaderTestLayer(
|
|
237
|
+
{ cwd: "/repo", roots: ["skills"] },
|
|
238
|
+
{ [testCase.file]: testCase.content },
|
|
239
|
+
{ "/repo/skills": [relative] },
|
|
240
|
+
),
|
|
241
|
+
),
|
|
242
|
+
),
|
|
243
|
+
)
|
|
244
|
+
expect(failure._tag).toBe("@batonfx/core/SkillSourceError")
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
})
|
|
187
248
|
})
|