@scitrera/memorylayer-sdk 0.0.5 → 0.2.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/LICENSE +177 -0
- package/README.md +22 -0
- package/dist/client.d.ts +316 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +896 -67
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +14 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledgebase.d.ts +41 -0
- package/dist/knowledgebase.d.ts.map +1 -0
- package/dist/knowledgebase.js +86 -0
- package/dist/knowledgebase.js.map +1 -0
- package/dist/mcp_servers.d.ts +94 -0
- package/dist/mcp_servers.d.ts.map +1 -0
- package/dist/mcp_servers.js +132 -0
- package/dist/mcp_servers.js.map +1 -0
- package/dist/rpg.d.ts +178 -0
- package/dist/rpg.d.ts.map +1 -0
- package/dist/rpg.js +126 -0
- package/dist/rpg.js.map +1 -0
- package/dist/skills.d.ts +194 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +465 -0
- package/dist/skills.js.map +1 -0
- package/dist/types.d.ts +630 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +27 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +43 -0
- package/dist/utils.js.map +1 -1
- package/package.json +11 -3
- package/vitest.config.ts +0 -12
package/dist/skills.js
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { MemoryLayerError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* UTF-8 encode a string and base64 it for the skill-file upsert endpoint.
|
|
4
|
+
* Uses Node's Buffer when present (the SDK's skill helpers are Node-oriented:
|
|
5
|
+
* parseSkillFolder/materialize use fs), falling back to btoa in the browser.
|
|
6
|
+
*/
|
|
7
|
+
function _toBase64(content) {
|
|
8
|
+
const g = globalThis;
|
|
9
|
+
if (g.Buffer) {
|
|
10
|
+
return g.Buffer.from(content, "utf-8").toString("base64");
|
|
11
|
+
}
|
|
12
|
+
if (g.btoa && g.TextEncoder) {
|
|
13
|
+
const bytes = new g.TextEncoder().encode(content);
|
|
14
|
+
let binary = "";
|
|
15
|
+
for (const b of bytes)
|
|
16
|
+
binary += String.fromCharCode(b);
|
|
17
|
+
return g.btoa(binary);
|
|
18
|
+
}
|
|
19
|
+
throw new MemoryLayerError("No base64 encoder available (need Node Buffer or browser btoa/TextEncoder)");
|
|
20
|
+
}
|
|
21
|
+
// ------------------------------------------------------------------ //
|
|
22
|
+
// SkillsNamespace — attached as client.skills
|
|
23
|
+
// ------------------------------------------------------------------ //
|
|
24
|
+
export class SkillsNamespace {
|
|
25
|
+
_client;
|
|
26
|
+
_authority;
|
|
27
|
+
_workspaceId;
|
|
28
|
+
constructor(client, authority, workspaceId) {
|
|
29
|
+
this._client = client;
|
|
30
|
+
this._authority = authority;
|
|
31
|
+
this._workspaceId = workspaceId;
|
|
32
|
+
}
|
|
33
|
+
/** Return a scoped proxy that injects OBO headers and workspace on every call. */
|
|
34
|
+
withAuthority(authority, workspaceId) {
|
|
35
|
+
return new SkillsNamespace(this._client, authority, workspaceId ?? this._workspaceId);
|
|
36
|
+
}
|
|
37
|
+
_req(method, path, body, authority, responseMode = "json") {
|
|
38
|
+
return this._client._skillsRequest(method, path, body, authority ?? this._authority, responseMode);
|
|
39
|
+
}
|
|
40
|
+
async list(options = {}) {
|
|
41
|
+
const params = new URLSearchParams();
|
|
42
|
+
const wsId = options.workspaceId ?? this._workspaceId;
|
|
43
|
+
if (wsId)
|
|
44
|
+
params.set("workspace_id", wsId);
|
|
45
|
+
if (options.scope)
|
|
46
|
+
params.set("scope", options.scope);
|
|
47
|
+
if (options.name)
|
|
48
|
+
params.set("name", options.name);
|
|
49
|
+
if (options.enabled !== undefined)
|
|
50
|
+
params.set("enabled", String(options.enabled));
|
|
51
|
+
if (options.includeShadowed)
|
|
52
|
+
params.set("include_shadowed", "true");
|
|
53
|
+
if (options.tags?.length)
|
|
54
|
+
params.set("tags", options.tags.join(","));
|
|
55
|
+
const query = params.toString();
|
|
56
|
+
const response = await this._req("GET", `/v1/skills${query ? `?${query}` : ""}`, undefined, options.authority);
|
|
57
|
+
return response.skills;
|
|
58
|
+
}
|
|
59
|
+
async get(skillId) {
|
|
60
|
+
const response = await this._req("GET", `/v1/skills/${skillId}`);
|
|
61
|
+
return response.skill;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Render the full `SKILL.md` for a skill.
|
|
65
|
+
*
|
|
66
|
+
* The server returns raw markdown (`Content-Type: text/markdown`), not JSON,
|
|
67
|
+
* so this reads the body as text rather than parsing it.
|
|
68
|
+
*/
|
|
69
|
+
async getManifest(skillId) {
|
|
70
|
+
return this._req("GET", `/v1/skills/${skillId}/manifest`, undefined, undefined, "text");
|
|
71
|
+
}
|
|
72
|
+
async listFiles(skillId) {
|
|
73
|
+
const response = await this._req("GET", `/v1/skills/${skillId}/files`);
|
|
74
|
+
return response.files;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Fetch only the raw content of a skill file (no metadata lookup).
|
|
78
|
+
*
|
|
79
|
+
* Used internally by `materialize()` to avoid the extra `listFiles()` call
|
|
80
|
+
* that `getFile()` performs. The caller already has the full `SkillFile`
|
|
81
|
+
* metadata from the single `listFiles()` done by `materialize()`, so we only
|
|
82
|
+
* need the raw bytes here.
|
|
83
|
+
*/
|
|
84
|
+
async _fetchFileContent(skillId, filePath) {
|
|
85
|
+
const encoded = filePath
|
|
86
|
+
.split("/")
|
|
87
|
+
.map((seg) => encodeURIComponent(seg))
|
|
88
|
+
.join("/");
|
|
89
|
+
return this._req("GET", `/v1/skills/${skillId}/files/${encoded}`, undefined, undefined, "text");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Fetch a single skill file with its content populated.
|
|
93
|
+
*
|
|
94
|
+
* `GET /v1/skills/{id}/files/{path}` streams the raw file bytes (e.g.
|
|
95
|
+
* `text/x-python`), NOT a JSON envelope — so we read the body as text and
|
|
96
|
+
* merge it onto the file's metadata from `listFiles()`. The metadata lookup
|
|
97
|
+
* is best-effort: if the path isn't in the listing we still return the
|
|
98
|
+
* fetched content with the path filled in.
|
|
99
|
+
*/
|
|
100
|
+
async getFile(skillId, filePath) {
|
|
101
|
+
const encoded = filePath
|
|
102
|
+
.split("/")
|
|
103
|
+
.map((seg) => encodeURIComponent(seg))
|
|
104
|
+
.join("/");
|
|
105
|
+
const [content, files] = await Promise.all([
|
|
106
|
+
this._req("GET", `/v1/skills/${skillId}/files/${encoded}`, undefined, undefined, "text"),
|
|
107
|
+
this.listFiles(skillId),
|
|
108
|
+
]);
|
|
109
|
+
const info = files.find((f) => f.path === filePath);
|
|
110
|
+
return {
|
|
111
|
+
...(info ?? { path: filePath }),
|
|
112
|
+
skill_id: skillId,
|
|
113
|
+
path: filePath,
|
|
114
|
+
content,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Delete a single file from a skill bundle.
|
|
119
|
+
*
|
|
120
|
+
* Calls `DELETE /v1/skills/{id}/files/{path}` (per-segment encodeURIComponent
|
|
121
|
+
* so embedded `/` separators are preserved, matching `getFile`). The server
|
|
122
|
+
* responds 204 with an empty body and is idempotent — deleting an
|
|
123
|
+
* already-absent path still succeeds — so the request helper's 204 handling
|
|
124
|
+
* means no JSON parsing happens here.
|
|
125
|
+
*/
|
|
126
|
+
async deleteFile(skillId, filePath) {
|
|
127
|
+
const encoded = filePath
|
|
128
|
+
.split("/")
|
|
129
|
+
.map((seg) => encodeURIComponent(seg))
|
|
130
|
+
.join("/");
|
|
131
|
+
const wsQuery = this._workspaceId
|
|
132
|
+
? `?workspace_id=${encodeURIComponent(this._workspaceId)}`
|
|
133
|
+
: "";
|
|
134
|
+
await this._req("DELETE", `/v1/skills/${skillId}/files/${encoded}${wsQuery}`);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create or update a skill AND persist its bundle files.
|
|
138
|
+
*
|
|
139
|
+
* The server's create/update inputs (`POST /v1/skills`, `PUT /v1/skills/{id}`)
|
|
140
|
+
* carry NO `files` field — files are persisted out-of-band via
|
|
141
|
+
* `PUT /v1/skills/{id}/files/{path}` with base64 `content_b64`. A previous
|
|
142
|
+
* version sent `manifest.files` on the create body, where the server silently
|
|
143
|
+
* ignored it, so a skill saved with files lost them. This method now:
|
|
144
|
+
*
|
|
145
|
+
* 1. Detects create-vs-update by resolving the skill by name (the unique key
|
|
146
|
+
* within a scope). Found → UPDATE (`PUT /v1/skills/{id}`); else CREATE
|
|
147
|
+
* (`POST /v1/skills`). Neither create nor update bodies include `files`.
|
|
148
|
+
* 2. Upserts every manifest file via the dedicated file endpoint.
|
|
149
|
+
* 3. On UPDATE, performs a TRUE file reconcile: it lists the skill's current
|
|
150
|
+
* server files and DELETEs (`DELETE /v1/skills/{id}/files/{path}`) any whose
|
|
151
|
+
* path is no longer in the manifest, so the saved skill's bundle is an exact
|
|
152
|
+
* mirror of the manifest. (CREATE has no pre-existing files to prune.)
|
|
153
|
+
* 4. If the skill create/update succeeds but ANY file PUT fails, this RAISES a
|
|
154
|
+
* clear error rather than resolving — never silently dropping files. File
|
|
155
|
+
* PUTs are idempotent upserts, so the failed save is safe to retry.
|
|
156
|
+
*
|
|
157
|
+
* Returns the fully-persisted skill (re-fetched for fresh manifest/bundle
|
|
158
|
+
* hashes). The public signature and return type are unchanged.
|
|
159
|
+
*/
|
|
160
|
+
async save(manifest) {
|
|
161
|
+
const files = manifest.files ?? [];
|
|
162
|
+
// Detect an existing skill by name (unique within scope). resolve() honors
|
|
163
|
+
// precedence/shadowing, matching how the rest of the SDK identifies skills.
|
|
164
|
+
let existing = null;
|
|
165
|
+
try {
|
|
166
|
+
existing = await this.resolve({
|
|
167
|
+
name: manifest.name,
|
|
168
|
+
authority: this._authority,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// resolve failures (e.g. 404-style empties) are treated as "not found";
|
|
173
|
+
// a hard create attempt below surfaces any real error.
|
|
174
|
+
existing = null;
|
|
175
|
+
}
|
|
176
|
+
let skillId;
|
|
177
|
+
if (existing) {
|
|
178
|
+
// ── UPDATE path: PUT manifest fields (no files), then reconcile files ──
|
|
179
|
+
const updateBody = {
|
|
180
|
+
description: manifest.description,
|
|
181
|
+
body: manifest.body,
|
|
182
|
+
metadata: manifest.metadata ?? {},
|
|
183
|
+
};
|
|
184
|
+
if (manifest.version !== undefined)
|
|
185
|
+
updateBody.version = manifest.version;
|
|
186
|
+
if (manifest.license !== undefined)
|
|
187
|
+
updateBody.license = manifest.license;
|
|
188
|
+
if (manifest.compatibility !== undefined)
|
|
189
|
+
updateBody.compatibility = manifest.compatibility;
|
|
190
|
+
if (manifest.allowed_tools !== undefined)
|
|
191
|
+
updateBody.allowed_tools = manifest.allowed_tools;
|
|
192
|
+
if (manifest.source_mode !== undefined)
|
|
193
|
+
updateBody.source_mode = manifest.source_mode;
|
|
194
|
+
const wsQuery = this._workspaceId
|
|
195
|
+
? `?workspace_id=${encodeURIComponent(this._workspaceId)}`
|
|
196
|
+
: "";
|
|
197
|
+
await this._req("PUT", `/v1/skills/${existing.id}${wsQuery}`, updateBody);
|
|
198
|
+
skillId = existing.id;
|
|
199
|
+
// Reconcile deletions: list the skill's current server files and DELETE
|
|
200
|
+
// any whose path is no longer in the manifest, so the saved bundle is an
|
|
201
|
+
// exact mirror of the manifest. A delete failure must surface (don't
|
|
202
|
+
// silently diverge), so this is NOT wrapped in a swallow-all catch.
|
|
203
|
+
const serverFiles = await this.listFiles(skillId);
|
|
204
|
+
const manifestPaths = new Set(files.map((f) => f.path));
|
|
205
|
+
const stale = serverFiles
|
|
206
|
+
.map((f) => f.path)
|
|
207
|
+
.filter((p) => !manifestPaths.has(p));
|
|
208
|
+
for (const path of stale) {
|
|
209
|
+
await this.deleteFile(skillId, path);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// ── CREATE path: POST manifest MINUS files, capture new id ──
|
|
214
|
+
const createBody = {
|
|
215
|
+
name: manifest.name,
|
|
216
|
+
description: manifest.description,
|
|
217
|
+
version: manifest.version ?? "0.1.0",
|
|
218
|
+
body: manifest.body,
|
|
219
|
+
metadata: manifest.metadata ?? {},
|
|
220
|
+
source_mode: manifest.source_mode ?? "server",
|
|
221
|
+
};
|
|
222
|
+
if (manifest.license !== undefined)
|
|
223
|
+
createBody.license = manifest.license;
|
|
224
|
+
if (manifest.compatibility !== undefined)
|
|
225
|
+
createBody.compatibility = manifest.compatibility;
|
|
226
|
+
if (manifest.allowed_tools !== undefined)
|
|
227
|
+
createBody.allowed_tools = manifest.allowed_tools;
|
|
228
|
+
if (this._workspaceId)
|
|
229
|
+
createBody.workspace_id = this._workspaceId;
|
|
230
|
+
const created = await this._req("POST", "/v1/skills", createBody);
|
|
231
|
+
skillId = created.skill.id;
|
|
232
|
+
}
|
|
233
|
+
// ── Persist each manifest file via the dedicated upsert endpoint ──
|
|
234
|
+
// A failure here, AFTER the skill create/update succeeded, must RAISE (never
|
|
235
|
+
// resolve) so files are never silently dropped. PUTs are idempotent upserts,
|
|
236
|
+
// so a failed save is retry-safe; we do NOT auto-delete the skill.
|
|
237
|
+
const wsQuery = this._workspaceId
|
|
238
|
+
? `?workspace_id=${encodeURIComponent(this._workspaceId)}`
|
|
239
|
+
: "";
|
|
240
|
+
for (const file of files) {
|
|
241
|
+
const encoded = file.path
|
|
242
|
+
.split("/")
|
|
243
|
+
.map((seg) => encodeURIComponent(seg))
|
|
244
|
+
.join("/");
|
|
245
|
+
try {
|
|
246
|
+
await this._req("PUT", `/v1/skills/${skillId}/files/${encoded}${wsQuery}`, { content_b64: _toBase64(file.content) });
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
throw new MemoryLayerError(`skill ${skillId} (${manifest.name}) saved but file ${file.path} ` +
|
|
250
|
+
`failed to persist: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
251
|
+
`File upserts are idempotent — retry save() to complete persistence.`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Re-fetch so callers get fresh manifest/bundle hashes reflecting files.
|
|
255
|
+
return this.get(skillId);
|
|
256
|
+
}
|
|
257
|
+
async delete(skillId) {
|
|
258
|
+
await this._req("DELETE", `/v1/skills/${skillId}`);
|
|
259
|
+
}
|
|
260
|
+
async resolve(options = {}) {
|
|
261
|
+
const body = {};
|
|
262
|
+
if (options.name)
|
|
263
|
+
body.name = options.name;
|
|
264
|
+
if (options.query)
|
|
265
|
+
body.query = options.query;
|
|
266
|
+
if (options.scopeHint)
|
|
267
|
+
body.scope_hint = options.scopeHint;
|
|
268
|
+
const response = await this._req("POST", "/v1/skills/resolve", body, options.authority);
|
|
269
|
+
return response.skill;
|
|
270
|
+
}
|
|
271
|
+
async pull(skillId) {
|
|
272
|
+
const [skill, files] = await Promise.all([
|
|
273
|
+
this.get(skillId),
|
|
274
|
+
this.listFiles(skillId),
|
|
275
|
+
]);
|
|
276
|
+
return { skill, files };
|
|
277
|
+
}
|
|
278
|
+
async push(manifest) {
|
|
279
|
+
return this.save(manifest);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Write every matching skill's `SKILL.md` + files into `targetDir`.
|
|
283
|
+
*
|
|
284
|
+
* The set of skills written is whatever the internal `list(...)` returns for
|
|
285
|
+
* the given `workspaceId`/`scope`. By default `enabled` is left unset, so
|
|
286
|
+
* both enabled and disabled server-visible skills are materialized (backwards
|
|
287
|
+
* compatible). Pass `enabled: true` to restrict the on-disk cache to the
|
|
288
|
+
* enabled subset (e.g. the OpenClaw skills-sync hook does this so disabled
|
|
289
|
+
* skills are never written into the agent's watched skills dir).
|
|
290
|
+
*
|
|
291
|
+
* By default materialize is purely additive: it writes/overwrites the desired
|
|
292
|
+
* skills but never removes anything already on disk. Pass `reconcile: true` to
|
|
293
|
+
* turn the sync into a true reconcile — after successfully listing AND writing
|
|
294
|
+
* the desired skills, any top-level entry in `targetDir` whose name is NOT in
|
|
295
|
+
* the desired skill-name set is removed. This is how a skill that was disabled,
|
|
296
|
+
* deleted, or renamed in MemoryLayer gets its stale on-disk copy pruned.
|
|
297
|
+
*
|
|
298
|
+
* CONTRACT (reconcile only): `targetDir` MUST be an exclusively
|
|
299
|
+
* MemoryLayer-managed directory (e.g. the OpenClaw plugin's dedicated
|
|
300
|
+
* `extraDir`). Reconcile will delete subdirectories it did not write. As a
|
|
301
|
+
* defensive guard it only removes a directory that *looks* like a materialized
|
|
302
|
+
* skill (i.e. contains a `SKILL.md`); unrelated files and non-skill directories
|
|
303
|
+
* are left untouched. Pruning runs ONLY after the list+materialize succeeded,
|
|
304
|
+
* so a transient server/list failure can never wipe the directory.
|
|
305
|
+
*/
|
|
306
|
+
async materialize(targetDir, options = {}) {
|
|
307
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
308
|
+
throw new MemoryLayerError("Skill folder operations require Node.js");
|
|
309
|
+
}
|
|
310
|
+
const fs = await import("fs/promises");
|
|
311
|
+
const path = await import("path");
|
|
312
|
+
// If list() throws, this rejects before any prune — a transient server
|
|
313
|
+
// outage can never trigger a wipe of targetDir.
|
|
314
|
+
const skills = await this.list({
|
|
315
|
+
workspaceId: options.workspaceId ?? this._workspaceId,
|
|
316
|
+
scope: options.scope,
|
|
317
|
+
enabled: options.enabled,
|
|
318
|
+
});
|
|
319
|
+
// Desired set: the exact directory names materialize writes below.
|
|
320
|
+
const desiredNames = new Set(skills.map((skill) => skill.name));
|
|
321
|
+
for (const skill of skills) {
|
|
322
|
+
const skillDir = path.join(targetDir, skill.name);
|
|
323
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
324
|
+
// SKILL.md comes back as raw markdown text (not JSON) from getManifest.
|
|
325
|
+
const manifest = await this.getManifest(skill.id);
|
|
326
|
+
await fs.writeFile(path.join(skillDir, "SKILL.md"), manifest, "utf8");
|
|
327
|
+
// listFiles() returns metadata for all files in one call. We then fetch
|
|
328
|
+
// each file's raw bytes via _fetchFileContent() — which does a single
|
|
329
|
+
// content GET with no extra listFiles() — instead of calling getFile(),
|
|
330
|
+
// which would re-issue listFiles() per file. Net: 1 listFiles + N content
|
|
331
|
+
// GETs rather than 1 + N listFiles calls.
|
|
332
|
+
const files = await this.listFiles(skill.id);
|
|
333
|
+
for (const f of files) {
|
|
334
|
+
const filePath = path.join(skillDir, f.path);
|
|
335
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
336
|
+
const content = await this._fetchFileContent(skill.id, f.path);
|
|
337
|
+
await fs.writeFile(filePath, content, "utf8");
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Reconcile/prune: only after list+materialize succeeded above.
|
|
341
|
+
if (options.reconcile) {
|
|
342
|
+
let entries;
|
|
343
|
+
try {
|
|
344
|
+
entries = await fs.readdir(targetDir, { withFileTypes: true });
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
// targetDir doesn't exist (no skills were written, e.g. empty set);
|
|
348
|
+
// nothing to prune.
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
for (const entry of entries) {
|
|
352
|
+
if (!entry.isDirectory())
|
|
353
|
+
continue; // never touch loose files
|
|
354
|
+
if (desiredNames.has(entry.name))
|
|
355
|
+
continue; // still wanted
|
|
356
|
+
const dir = path.join(targetDir, entry.name);
|
|
357
|
+
// Defensive: only remove dirs that look like a materialized skill.
|
|
358
|
+
try {
|
|
359
|
+
await fs.access(path.join(dir, "SKILL.md"));
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
continue; // no SKILL.md → not ours, leave it alone
|
|
363
|
+
}
|
|
364
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// ------------------------------------------------------------------ //
|
|
370
|
+
// parseSkillFolder — Node.js helper
|
|
371
|
+
// ------------------------------------------------------------------ //
|
|
372
|
+
/**
|
|
373
|
+
* Parse a skill folder from the filesystem.
|
|
374
|
+
* Reads SKILL.md (frontmatter + body) and walks scripts/, references/, assets/.
|
|
375
|
+
* Requires Node.js (uses fs/promises). Not available in browser environments.
|
|
376
|
+
*/
|
|
377
|
+
export async function parseSkillFolder(dir) {
|
|
378
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
379
|
+
throw new MemoryLayerError("Skill folder operations require Node.js");
|
|
380
|
+
}
|
|
381
|
+
const fs = await import("fs/promises");
|
|
382
|
+
const path = await import("path");
|
|
383
|
+
const manifestPath = path.join(dir, "SKILL.md");
|
|
384
|
+
const raw = await fs.readFile(manifestPath, "utf8");
|
|
385
|
+
const { frontmatter, body } = _parseMarkdownFrontmatter(raw);
|
|
386
|
+
const manifest = {
|
|
387
|
+
name: String(frontmatter.name ?? ""),
|
|
388
|
+
description: String(frontmatter.description ?? ""),
|
|
389
|
+
version: frontmatter.version ? String(frontmatter.version) : "0.1.0",
|
|
390
|
+
body,
|
|
391
|
+
};
|
|
392
|
+
if (frontmatter.license)
|
|
393
|
+
manifest.license = String(frontmatter.license);
|
|
394
|
+
if (frontmatter.compatibility)
|
|
395
|
+
manifest.compatibility = String(frontmatter.compatibility);
|
|
396
|
+
if (frontmatter.allowed_tools)
|
|
397
|
+
manifest.allowed_tools = String(frontmatter.allowed_tools);
|
|
398
|
+
const extraKeys = new Set([
|
|
399
|
+
"name",
|
|
400
|
+
"description",
|
|
401
|
+
"version",
|
|
402
|
+
"license",
|
|
403
|
+
"compatibility",
|
|
404
|
+
"allowed_tools",
|
|
405
|
+
]);
|
|
406
|
+
const metadata = {};
|
|
407
|
+
for (const [k, v] of Object.entries(frontmatter)) {
|
|
408
|
+
if (!extraKeys.has(k))
|
|
409
|
+
metadata[k] = v;
|
|
410
|
+
}
|
|
411
|
+
manifest.metadata = metadata;
|
|
412
|
+
const files = [];
|
|
413
|
+
const knownDirs = ["scripts", "references", "assets"];
|
|
414
|
+
for (const subdir of knownDirs) {
|
|
415
|
+
const subdirPath = path.join(dir, subdir);
|
|
416
|
+
let entries;
|
|
417
|
+
try {
|
|
418
|
+
entries = await fs.readdir(subdirPath, { withFileTypes: true });
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
for (const entry of entries) {
|
|
424
|
+
if (!entry.isFile())
|
|
425
|
+
continue;
|
|
426
|
+
const filePath = path.join(subdirPath, entry.name);
|
|
427
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
428
|
+
files.push({ path: `${subdir}/${entry.name}`, content });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return { manifest: { ...manifest, files }, files };
|
|
432
|
+
}
|
|
433
|
+
/** Minimal YAML-ish frontmatter parser — handles string/number/bool values only. */
|
|
434
|
+
function _parseMarkdownFrontmatter(raw) {
|
|
435
|
+
const trimmed = raw.trimStart();
|
|
436
|
+
if (!trimmed.startsWith("---")) {
|
|
437
|
+
return { frontmatter: {}, body: raw };
|
|
438
|
+
}
|
|
439
|
+
const end = trimmed.indexOf("\n---", 3);
|
|
440
|
+
if (end === -1) {
|
|
441
|
+
return { frontmatter: {}, body: raw };
|
|
442
|
+
}
|
|
443
|
+
const yamlBlock = trimmed.slice(4, end);
|
|
444
|
+
const body = trimmed.slice(end + 4).trimStart();
|
|
445
|
+
const frontmatter = {};
|
|
446
|
+
for (const line of yamlBlock.split("\n")) {
|
|
447
|
+
const colonIdx = line.indexOf(":");
|
|
448
|
+
if (colonIdx === -1)
|
|
449
|
+
continue;
|
|
450
|
+
const key = line.slice(0, colonIdx).trim();
|
|
451
|
+
const val = line.slice(colonIdx + 1).trim();
|
|
452
|
+
if (!key)
|
|
453
|
+
continue;
|
|
454
|
+
if (val === "true")
|
|
455
|
+
frontmatter[key] = true;
|
|
456
|
+
else if (val === "false")
|
|
457
|
+
frontmatter[key] = false;
|
|
458
|
+
else if (val !== "" && !isNaN(Number(val)))
|
|
459
|
+
frontmatter[key] = Number(val);
|
|
460
|
+
else
|
|
461
|
+
frontmatter[key] = val.replace(/^["']|["']$/g, "");
|
|
462
|
+
}
|
|
463
|
+
return { frontmatter, body };
|
|
464
|
+
}
|
|
465
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
|