skill-family-engineering-kit 0.25.0 → 0.26.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.
@@ -0,0 +1,1937 @@
1
+ import { lstat, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parseDocument } from "yaml";
4
+ import { readFileBound } from "skill-family-harness-node";
5
+
6
+ /**
7
+ * Limited seven-host project-skill loading check.
8
+ * Daily run is read-only: existing root binding, no spawn, network, or writes.
9
+ * Exact native versions only; not a host adapter SPI or registry.
10
+ */
11
+
12
+ const MAX_SKILLS_DIRS_PER_ROOT = 2000;
13
+ const MAX_SKILLS_ENTRIES_PER_ROOT = 20_000;
14
+ const CLAUDE_MAX_BYTES = 1_000_000;
15
+ const CURSOR_MAX_DEPTH = 10;
16
+ const KIMI_MAX_DEPTH = 9;
17
+ const WORKBUDDY_MAX_DEPTH = 6;
18
+ const CODEBUDDY_MAX_DEPTH = 5;
19
+ const QODER_MAX_DEPTH = 4;
20
+ const CURSOR_DESC_MAX = 1536;
21
+ const WORKBUDDY_BODY_DESC = 100;
22
+ const CURSOR_SKIP_DIR_NAMES = new Set(["node_modules", "__pycache__", "dist", "build"]);
23
+ const CURSOR_PARSE_FAILURE = "Failed to read skill file";
24
+ const DSH_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
25
+ const WORKBUDDY_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+$/;
26
+ const WORKBUDDY_META_FILES = [
27
+ "_skillhub_meta.json",
28
+ "_marketplace_meta.json",
29
+ "_knot_meta.json",
30
+ "_builtin_market_meta.json",
31
+ "_user_meta.json",
32
+ ];
33
+ const CURSOR_EVIDENCE = "源码核实、未作原生运行校准";
34
+
35
+ export const NATIVE_HOSTS = Object.freeze({
36
+ claude: Object.freeze({
37
+ hostId: "claude",
38
+ hostVersion: "2.1.233",
39
+ pathCategory: "claude-project-skills",
40
+ relPath: ".claude/skills",
41
+ }),
42
+ cursor: Object.freeze({
43
+ hostId: "cursor",
44
+ hostVersion: "2026.09.10-fd3934a",
45
+ pathCategory: "cursor-project-skills",
46
+ relPath: ".cursor/skills",
47
+ }),
48
+ "kimi-code": Object.freeze({
49
+ hostId: "kimi-code",
50
+ hostVersion: "2.2.0",
51
+ pathCategory: "kimi-code-project-skills",
52
+ relPath: ".kimi-code/skills",
53
+ }),
54
+ workbuddy: Object.freeze({
55
+ hostId: "workbuddy",
56
+ hostVersion: "5.8.2",
57
+ pathCategory: "workbuddy-project-skills",
58
+ relPath: ".workbuddy/skills",
59
+ }),
60
+ codebuddy: Object.freeze({
61
+ hostId: "codebuddy",
62
+ hostVersion: "2.158.0",
63
+ pathCategory: "codebuddy-project-skills",
64
+ relPath: ".codebuddy/skills",
65
+ }),
66
+ qoder: Object.freeze({
67
+ hostId: "qoder",
68
+ hostVersion: "1.1.51",
69
+ pathCategory: "qoder-project-skills",
70
+ relPath: ".qoder/skills",
71
+ }),
72
+ "deepseek-harness": Object.freeze({
73
+ hostId: "deepseek-harness",
74
+ hostVersion: "0.1.1-rc.2",
75
+ pathCategory: "deepseek-harness-project-skills",
76
+ relPath: ".dsh/skills",
77
+ }),
78
+ });
79
+
80
+ const RULE_SOURCES = Object.freeze({
81
+ claude: Object.freeze([
82
+ Object.freeze({
83
+ id: "claude-code",
84
+ version: "2.1.233",
85
+ source: "claude",
86
+ sha256: "bc466b6cde63edafc773f471a1fb98787fabb31f52240c8616ce7e1f587b212d",
87
+ }),
88
+ ]),
89
+ cursor: Object.freeze([
90
+ Object.freeze({
91
+ id: "cursor-agent-index",
92
+ version: "2026.09.10-fd3934a",
93
+ source: "index.js",
94
+ sha256: "230df5356d29c26fae77aeed83bd705b2ddd5fd5d1abccd3eb8c8b8c24e23468",
95
+ }),
96
+ Object.freeze({
97
+ id: "cursor-agent-8060",
98
+ version: "2026.09.10-fd3934a",
99
+ source: "8060.index.js",
100
+ sha256: "aed953660e1a5190eef2127d0124dbb39dc3913c4084d1c404aea39b9b9594ae",
101
+ }),
102
+ ]),
103
+ "kimi-code": Object.freeze([
104
+ Object.freeze({
105
+ id: "kimi",
106
+ version: "2.2.0",
107
+ source: "kimi",
108
+ sha256: "dab42ead275f8a49e9ce30a6263f614fce5926455bed103d0d16940655619bf5",
109
+ }),
110
+ ]),
111
+ workbuddy: Object.freeze([
112
+ Object.freeze({
113
+ id: "workbuddy-app-asar",
114
+ version: "5.8.2",
115
+ source: "app.asar",
116
+ sha256: "b7dee8481e5106f6cfcea1b20cf66c07a103be793333ed45b164aede41cdcce0",
117
+ }),
118
+ Object.freeze({
119
+ id: "workbuddy-netdrive-env-route",
120
+ version: "5.8.2",
121
+ source: "main/netdrive-env-route.js",
122
+ sha256: "e37e21bed8d05d6665a32feccf16c8db2891fbfa26ea6570395da0af78a5e0ef",
123
+ }),
124
+ ]),
125
+ codebuddy: Object.freeze([
126
+ Object.freeze({
127
+ id: "codebuddy-headless",
128
+ version: "2.158.0",
129
+ source: "@tencent-ai/codebuddy-code/dist/codebuddy-headless.js",
130
+ sha256: "9249eedc33a1e6406bff37add7fe92787cf1490eb8261b478d43e3c11b02c84f",
131
+ }),
132
+ ]),
133
+ qoder: Object.freeze([
134
+ Object.freeze({
135
+ id: "qodercli",
136
+ version: "1.1.51",
137
+ source: "qodercli",
138
+ sha256: "82fd756e711bcaeb6ae2ebbd0401a45d71ae54545f408d8867bf61f645b46e3c",
139
+ }),
140
+ ]),
141
+ "deepseek-harness": Object.freeze([
142
+ Object.freeze({
143
+ id: "dsh-skill-filesystem",
144
+ version: "0.1.1-rc.2",
145
+ source: "@deepseek-ai/dsh-skill-filesystem",
146
+ sha256: "1aea87781ba5b4d44c7cfd6184d933a1c171a3fd2791456b65ebfc8b255f1221",
147
+ }),
148
+ Object.freeze({
149
+ id: "dsh-skill",
150
+ version: "0.1.1-rc.2",
151
+ source: "@deepseek-ai/dsh-skill",
152
+ sha256: "5402787cbc95b4586e2c3aa4b0129e91094ad368b428aa19bcbbd8553bbfa9d7",
153
+ }),
154
+ ]),
155
+ });
156
+
157
+ const HOST_LIMITATIONS = Object.freeze({
158
+ claude: Object.freeze([
159
+ "Only Claude Code 2.1.233 and pathCategory claude-project-skills are calibrated.",
160
+ "The check covers one explicit project root/.claude/skills; it does not claim default automatic discovery or ordinary-session paths activation.",
161
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
162
+ "User-command visibility is a local scan projection; it does not prove model invocation.",
163
+ ]),
164
+ cursor: Object.freeze([
165
+ "Only Cursor CLI 2026.09.10-fd3934a and pathCategory cursor-project-skills are calibrated.",
166
+ "Menu, completion, and model metadata are independent surfaces.",
167
+ "modelMetadata.* values use source-reviewed-unexecuted evidence and do not prove server filtering or model trigger.",
168
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
169
+ ]),
170
+ "kimi-code": Object.freeze([
171
+ "Only Kimi Code 2.2.0 and pathCategory kimi-code-project-skills are calibrated.",
172
+ "The check covers one project root/.kimi-code/skills workspace collection.",
173
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
174
+ "disable_model_invocation is a file marker; it does not prove user invocability or model execution.",
175
+ ]),
176
+ workbuddy: Object.freeze([
177
+ "Only WorkBuddy desktop 5.8.2 original scan component and pathCategory workbuddy-project-skills are calibrated.",
178
+ "The check does not merge desktop, account, or session overlays, and does not cover embedded CLI 2.161.0.",
179
+ "iconLocalPath asset resolution is uncovered.",
180
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
181
+ ]),
182
+ codebuddy: Object.freeze([
183
+ "Only CodeBuddy 2.158.0 and pathCategory codebuddy-project-skills are calibrated.",
184
+ "The check reproduces the local project provider and menu projection; it does not read skillOverrides.",
185
+ "ACP menu suffixes are display-only and are not reported as local description.",
186
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
187
+ ]),
188
+ qoder: Object.freeze([
189
+ "Only Qoder CLI 1.1.51 and pathCategory qoder-project-skills are calibrated.",
190
+ "Same-basename candidates keep path observations and return uncovered 2; this slice does not guess a winner.",
191
+ "Final enablement is unknown; workspace disable configuration is not read.",
192
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
193
+ ]),
194
+ "deepseek-harness": Object.freeze([
195
+ "Only DeepSeek Harness 0.1.1-rc.2 filesystem and skill registry components and pathCategory deepseek-harness-project-skills are calibrated.",
196
+ "The check does not cover full CLI ctx.fs, multi-root ranking, or runtime invocation.",
197
+ "Native out-of-project symlink follow is not reproduced; containment is preserved as uncovered 2.",
198
+ "Directory and file symlinks are recorded as uncovered because this slice does not follow them.",
199
+ ]),
200
+ });
201
+
202
+ export function matchNativeHost({ hostId, hostVersion, pathCategory, relPath }) {
203
+ const spec = NATIVE_HOSTS[hostId];
204
+ if (!spec) return null;
205
+ if (spec.hostVersion !== hostVersion) return null;
206
+ if (spec.pathCategory !== pathCategory) return null;
207
+ if (path.posix.normalize(String(relPath ?? "")) !== spec.relPath) return null;
208
+ return spec;
209
+ }
210
+
211
+ export function nativeRuleSources(hostId) {
212
+ return RULE_SOURCES[hostId] ? [...RULE_SOURCES[hostId]] : [];
213
+ }
214
+
215
+ export function nativeLimitations(hostId) {
216
+ return HOST_LIMITATIONS[hostId] ? [...HOST_LIMITATIONS[hostId]] : [];
217
+ }
218
+
219
+ function uncovered(kind, message, extra = {}) {
220
+ return { class: "host-loading", kind, message, ...extra };
221
+ }
222
+
223
+ function finding(kind, message, extra = {}) {
224
+ return { class: "host-loading", kind, message, blocking: true, ...extra };
225
+ }
226
+
227
+ function isPlainObject(value) {
228
+ return value !== null && typeof value === "object" && !Array.isArray(value);
229
+ }
230
+
231
+ function posixRelative(rootAbs, absolute) {
232
+ return path.relative(rootAbs, absolute).split(path.sep).join("/");
233
+ }
234
+
235
+ function boundReadKind(error) {
236
+ return error?.details?.kind ?? error?.details?.boundReadDisposition ?? error?.code ?? "unknown";
237
+ }
238
+
239
+ function decodeUtf8PreserveBom(bytes) {
240
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
241
+ }
242
+
243
+ function observation(field, value, sourceId, extra = {}) {
244
+ const item = {
245
+ field,
246
+ value,
247
+ basis: extra.basis ?? "native-observed",
248
+ sourceId,
249
+ };
250
+ if (item.basis === "source-reviewed-unexecuted") {
251
+ item.evidenceLabel = extra.evidenceLabel ?? CURSOR_EVIDENCE;
252
+ }
253
+ return item;
254
+ }
255
+
256
+ function sourceIdFor(hostId) {
257
+ return RULE_SOURCES[hostId][0].id;
258
+ }
259
+
260
+ function nativeSkillBase(pathAbs, extra = {}) {
261
+ return {
262
+ path: pathAbs,
263
+ name: extra.name ?? null,
264
+ description: extra.description ?? null,
265
+ shortDescription: null,
266
+ parse: extra.parse ?? null,
267
+ enabled: null,
268
+ disabled: null,
269
+ ignored: extra.ignored ?? [],
270
+ filtered: extra.filtered ?? null,
271
+ implicitInvocation: null,
272
+ interface: null,
273
+ observations: extra.observations ?? [],
274
+ retained: extra.retained ?? null,
275
+ supersededBy: extra.supersededBy ?? null,
276
+ ...(extra.error !== undefined ? { error: extra.error } : {}),
277
+ };
278
+ }
279
+
280
+ function firstLine(text) {
281
+ const match = String(text ?? "").match(/^[^\r\n]*/);
282
+ return match ? match[0] : "";
283
+ }
284
+
285
+ function stripHeading(line) {
286
+ return line.startsWith("# ") ? line.slice(2) : line.startsWith("#") ? line.slice(1).trimStart() : line;
287
+ }
288
+
289
+ function bodyFirstLine(body) {
290
+ return stripHeading(firstLine(body ?? ""));
291
+ }
292
+
293
+ function stringifyJs(value) {
294
+ if (value === undefined || value === null) return null;
295
+ if (typeof value === "string") return value;
296
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
297
+ if (Array.isArray(value)) return String(value);
298
+ return null;
299
+ }
300
+
301
+ function jsonStringifyFallback(value) {
302
+ if (value === undefined || value === null) return null;
303
+ if (typeof value === "string") return value;
304
+ try {
305
+ return JSON.stringify(value);
306
+ } catch {
307
+ return null;
308
+ }
309
+ }
310
+
311
+ function isAsciiText(value) {
312
+ return typeof value === "string" && /^[\u0000-\u007F]*$/.test(value);
313
+ }
314
+
315
+ function isJsonCompatible(value, seen = new Set()) {
316
+ if (value === null) return true;
317
+ const type = typeof value;
318
+ if (type === "string" || type === "boolean") return true;
319
+ if (type === "number") return Number.isFinite(value);
320
+ if (type !== "object") return false;
321
+ if (seen.has(value)) return false;
322
+ seen.add(value);
323
+ if (Array.isArray(value)) return value.every((item) => isJsonCompatible(item, seen));
324
+ if (!isPlainObject(value)) return false;
325
+ return Object.values(value).every((item) => isJsonCompatible(item, seen));
326
+ }
327
+
328
+ function systemErrorCode(error) {
329
+ return error?.code ?? error?.cause?.code ?? null;
330
+ }
331
+
332
+ function trimBody(text) {
333
+ return String(text ?? "").replace(/^\uFEFF/, "").trim();
334
+ }
335
+
336
+ function cursorSkipDirName(name) {
337
+ return name.startsWith(".") || CURSOR_SKIP_DIR_NAMES.has(name);
338
+ }
339
+
340
+ async function lstatMissingOrUncovered(absPath, ctx) {
341
+ try {
342
+ return { stat: await lstat(absPath) };
343
+ } catch (cause) {
344
+ if (systemErrorCode(cause) === "ENOENT") return { missing: true };
345
+ ctx.uncoveredItems.push(uncovered(
346
+ "unread-entry",
347
+ "a skills file could not be lstat'd",
348
+ { path: absPath, causeKind: boundReadKind(cause) },
349
+ ));
350
+ return { unread: true };
351
+ }
352
+ }
353
+
354
+ function splitLines(text) {
355
+ return String(text).split(/\r?\n/);
356
+ }
357
+
358
+ function splitFrontmatter(text, options = {}) {
359
+ const allowBom = options.allowBom !== false;
360
+ const firstLineExact = options.firstLineExact === true;
361
+ if (text.length === 0) return { kind: "no-fm", body: text, dataText: null };
362
+ let source = text;
363
+ const hasBom = source.charCodeAt(0) === 0xfeff;
364
+ if (hasBom) {
365
+ if (!allowBom) return { kind: "no-fm", body: text, dataText: null, bom: true };
366
+ source = source.slice(1);
367
+ }
368
+ const lines = splitLines(source);
369
+ const opener = lines[0] ?? "";
370
+ if (firstLineExact && opener !== "---") return { kind: "no-fm", body: text, dataText: null };
371
+ if (opener !== "---") return { kind: "no-fm", body: text, dataText: null };
372
+ let close = -1;
373
+ for (let index = 1; index < lines.length; index += 1) {
374
+ if (lines[index] === "---") {
375
+ close = index;
376
+ break;
377
+ }
378
+ }
379
+ if (close < 0) return { kind: "unclosed", body: text, dataText: null, opener: true };
380
+ return {
381
+ kind: "fm",
382
+ dataText: lines.slice(1, close).join("\n"),
383
+ body: lines.slice(close + 1).join("\n"),
384
+ bom: hasBom,
385
+ };
386
+ }
387
+
388
+ function parseYamlMap(text, { uniqueKeys = false } = {}) {
389
+ try {
390
+ const document = parseDocument(text, { uniqueKeys, prettyErrors: false });
391
+ if (document.errors.length > 0) return { ok: false, reason: "yaml-error" };
392
+ const data = document.toJS({ maxAliasCount: 0 });
393
+ if (data === null || data === undefined) return { ok: true, data: {} };
394
+ if (!isPlainObject(data)) return { ok: false, reason: "not-map" };
395
+ return { ok: true, data };
396
+ } catch {
397
+ return { ok: false, reason: "yaml-error" };
398
+ }
399
+ }
400
+
401
+ function parseLineMap(text) {
402
+ const data = {};
403
+ for (const line of splitLines(text)) {
404
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
405
+ if (!match) continue;
406
+ data[match[1]] = match[2];
407
+ }
408
+ return data;
409
+ }
410
+
411
+ function dirDepth(skillsRootAbs, directoryAbs) {
412
+ const relative = posixRelative(skillsRootAbs, directoryAbs);
413
+ if (!relative || relative === "." || relative.startsWith("..")) return 0;
414
+ return relative.split("/").filter(Boolean).length;
415
+ }
416
+
417
+ async function listDir(directoryAbs, ctx) {
418
+ ctx.directoryCount += 1;
419
+ if (ctx.directoryCount > MAX_SKILLS_DIRS_PER_ROOT) {
420
+ ctx.truncated = true;
421
+ return null;
422
+ }
423
+ let names;
424
+ try {
425
+ names = await readdir(directoryAbs);
426
+ } catch (cause) {
427
+ ctx.uncoveredItems.push(uncovered(
428
+ "unread-directory",
429
+ "a skills directory could not be read during finite discovery",
430
+ { path: directoryAbs, causeKind: boundReadKind(cause) },
431
+ ));
432
+ return null;
433
+ }
434
+ const entries = [];
435
+ for (const name of names) {
436
+ ctx.entryCount += 1;
437
+ if (ctx.entryCount > MAX_SKILLS_ENTRIES_PER_ROOT) {
438
+ ctx.truncated = true;
439
+ break;
440
+ }
441
+ const abs = path.join(directoryAbs, name);
442
+ let st;
443
+ try {
444
+ st = await lstat(abs);
445
+ } catch (cause) {
446
+ ctx.uncoveredItems.push(uncovered(
447
+ "unread-entry",
448
+ "a skills entry could not be lstat'd during finite discovery",
449
+ { path: abs, causeKind: boundReadKind(cause) },
450
+ ));
451
+ continue;
452
+ }
453
+ entries.push({ name, abs, st });
454
+ }
455
+ return entries;
456
+ }
457
+
458
+ async function readBoundText(rootAbs, rootBinding, absPath, ctx) {
459
+ let st;
460
+ try {
461
+ st = await lstat(absPath);
462
+ } catch (cause) {
463
+ ctx.uncoveredItems.push(uncovered(
464
+ "unread-entry",
465
+ "a skills file could not be lstat'd",
466
+ { path: absPath, causeKind: boundReadKind(cause) },
467
+ ));
468
+ return null;
469
+ }
470
+ if (st.isSymbolicLink()) {
471
+ ctx.uncoveredItems.push(uncovered(
472
+ "file-symlink-unfollowed",
473
+ "a file symlink was not followed; native loading is not claimed",
474
+ { path: absPath },
475
+ ));
476
+ return null;
477
+ }
478
+ if (!st.isFile()) {
479
+ ctx.uncoveredItems.push(uncovered(
480
+ "unread-entry",
481
+ "a skills path exists but is not a regular file",
482
+ { path: absPath },
483
+ ));
484
+ return null;
485
+ }
486
+ try {
487
+ const receipt = await readFileBound(rootAbs, posixRelative(rootAbs, absPath), { rootBinding });
488
+ return { text: decodeUtf8PreserveBom(receipt.content), bytes: receipt.content.length, st };
489
+ } catch (cause) {
490
+ if (cause instanceof TypeError && /UTF-8|encoding/i.test(String(cause.message))) {
491
+ ctx.uncoveredItems.push(uncovered("undecodable-skill", "file is not valid UTF-8", { path: absPath }));
492
+ return null;
493
+ }
494
+ try {
495
+ const receipt = await readFileBound(rootAbs, posixRelative(rootAbs, absPath), { rootBinding });
496
+ decodeUtf8PreserveBom(receipt.content);
497
+ } catch (inner) {
498
+ if (inner instanceof TypeError) {
499
+ ctx.uncoveredItems.push(uncovered("undecodable-skill", "file is not valid UTF-8", { path: absPath }));
500
+ return null;
501
+ }
502
+ }
503
+ ctx.uncoveredItems.push(uncovered(
504
+ "bound-read-boundary",
505
+ "file could not be read through the existing root binding",
506
+ { path: absPath, causeKind: boundReadKind(cause) },
507
+ ));
508
+ return null;
509
+ }
510
+ }
511
+
512
+ function recordSymlink(entry, ctx, message) {
513
+ ctx.uncoveredItems.push(uncovered(
514
+ "directory-symlink-unfollowed",
515
+ message ?? "a directory or file symlink was not followed; native follow is not reproduced",
516
+ { path: entry.abs },
517
+ ));
518
+ }
519
+
520
+ function utf16Sort(entries) {
521
+ return [...entries].sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
522
+ }
523
+
524
+ function localeSort(entries) {
525
+ return [...entries].sort((left, right) => left.name.localeCompare(right.name));
526
+ }
527
+
528
+ function nameAmbiguitiesFrom(skills, predicate) {
529
+ const groups = new Map();
530
+ for (const skill of skills) {
531
+ if (!predicate(skill) || skill.name == null) continue;
532
+ const list = groups.get(skill.name) ?? [];
533
+ list.push(skill.path);
534
+ groups.set(skill.name, list);
535
+ }
536
+ return [...groups.entries()]
537
+ .filter(([, paths]) => paths.length > 1)
538
+ .map(([name, paths]) => ({ name, paths }));
539
+ }
540
+
541
+ function finalizeNative({
542
+ hostId,
543
+ skills,
544
+ findings,
545
+ uncoveredItems,
546
+ nameAmbiguities,
547
+ }) {
548
+ const retained = skills.filter((item) => item.retained === true);
549
+ if (uncoveredItems.length === 0 && findings.length === 0 && retained.length === 0) {
550
+ const scanned = skills.length > 0;
551
+ findings.push(finding(
552
+ scanned ? "no-discoverable-target" : "no-skill-entry",
553
+ scanned
554
+ ? "local filtering left no retained skill target"
555
+ : "no skill entries under the calibrated project skill root",
556
+ ));
557
+ }
558
+ return {
559
+ skills,
560
+ findings,
561
+ uncoveredItems,
562
+ nameAmbiguities,
563
+ ruleSources: nativeRuleSources(hostId),
564
+ limitations: nativeLimitations(hostId),
565
+ };
566
+ }
567
+
568
+ async function runClaude(ctx) {
569
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
570
+ const sourceId = sourceIdFor("claude");
571
+ const skills = [];
572
+ const findings = [];
573
+ const entries = await listDir(skillsRootAbs, ctx);
574
+ if (!entries) return finalizeNative({ hostId: "claude", skills, findings, uncoveredItems: ctx.uncoveredItems, nameAmbiguities: [] });
575
+ for (const entry of entries) {
576
+ if (ctx.truncated) break;
577
+ if (entry.st.isSymbolicLink()) {
578
+ recordSymlink(entry, ctx);
579
+ continue;
580
+ }
581
+ if (!entry.st.isDirectory()) continue;
582
+ if (entry.name === "synced") {
583
+ const skillPath = path.join(entry.abs, "SKILL.md");
584
+ const looked = await lstatMissingOrUncovered(skillPath, ctx);
585
+ if (!looked.stat) continue;
586
+ const skillStat = looked.stat;
587
+ if (skillStat.isFile() || skillStat.isSymbolicLink()) {
588
+ skills.push(nativeSkillBase(skillPath, {
589
+ name: entry.name,
590
+ parse: "ok",
591
+ filtered: true,
592
+ retained: false,
593
+ observations: [
594
+ observation("discovered", false, sourceId),
595
+ observation("menuVisible", false, sourceId),
596
+ ],
597
+ }));
598
+ }
599
+ continue;
600
+ }
601
+ const skillPath = path.join(entry.abs, "SKILL.md");
602
+ const looked = await lstatMissingOrUncovered(skillPath, ctx);
603
+ if (!looked.stat) continue;
604
+ const skillStat = looked.stat;
605
+ if (skillStat.isSymbolicLink()) {
606
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "SKILL.md is a symlink and is not followed", { path: skillPath }));
607
+ continue;
608
+ }
609
+ if (!skillStat.isFile()) continue;
610
+ if (skillStat.size > CLAUDE_MAX_BYTES) {
611
+ skills.push(nativeSkillBase(skillPath, {
612
+ name: entry.name,
613
+ parse: "ok",
614
+ filtered: true,
615
+ retained: false,
616
+ observations: [
617
+ observation("discovered", false, sourceId),
618
+ observation("menuVisible", false, sourceId),
619
+ ],
620
+ }));
621
+ continue;
622
+ }
623
+ const read = await readBoundText(rootAbs, rootBinding, skillPath, ctx);
624
+ if (!read) continue;
625
+ const split = splitFrontmatter(read.text);
626
+ let name = entry.name;
627
+ let description = bodyFirstLine(split.kind === "fm" ? split.body : read.text);
628
+ let userInvocable = null;
629
+ if (split.kind === "unclosed" || split.kind === "no-fm") {
630
+ name = entry.name;
631
+ description = split.kind === "unclosed" ? "---" : bodyFirstLine(read.text);
632
+ } else {
633
+ const parsed = parseYamlMap(split.dataText, { uniqueKeys: false });
634
+ const data = parsed.ok ? parsed.data : parseLineMap(split.dataText);
635
+ if (Object.hasOwn(data, "name") && data.name !== undefined && data.name !== null && data.name !== "") {
636
+ const asString = stringifyJs(data.name);
637
+ if (asString !== null) name = asString;
638
+ }
639
+ if (typeof data.description === "string") {
640
+ description = data.description;
641
+ } else if (Object.hasOwn(data, "description")) {
642
+ const asString = stringifyJs(data.description);
643
+ description = typeof data.description === "boolean" || typeof data.description === "number"
644
+ ? asString
645
+ : bodyFirstLine(split.body);
646
+ } else {
647
+ description = bodyFirstLine(split.body);
648
+ }
649
+ if (data["user-invocable"] === false) userInvocable = false;
650
+ }
651
+ const menuVisible = userInvocable !== false;
652
+ skills.push(nativeSkillBase(skillPath, {
653
+ name,
654
+ description,
655
+ parse: "ok",
656
+ filtered: menuVisible ? false : true,
657
+ retained: menuVisible,
658
+ observations: [
659
+ observation("discovered", true, sourceId),
660
+ observation("menuVisible", menuVisible, sourceId),
661
+ ],
662
+ }));
663
+ }
664
+ return finalizeNative({
665
+ hostId: "claude",
666
+ skills,
667
+ findings,
668
+ uncoveredItems: ctx.uncoveredItems,
669
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.retained === true),
670
+ });
671
+ }
672
+
673
+ function cursorParseFrontmatter(text) {
674
+ const split = splitFrontmatter(text);
675
+ if (split.kind !== "fm") return { kind: split.kind, data: null, body: split.kind === "fm" ? split.body : text };
676
+ const parsed = parseYamlMap(split.dataText, { uniqueKeys: false });
677
+ if (!parsed.ok) return { kind: "broken", data: null, body: split.body };
678
+ return { kind: "fm", data: parsed.data, body: split.body };
679
+ }
680
+
681
+ function cursorCompletionDescription(data, body) {
682
+ if (data && typeof data.description === "string") return data.description;
683
+ return bodyFirstLine(body);
684
+ }
685
+
686
+ function splitSurfaceString(raw) {
687
+ return raw.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
688
+ }
689
+
690
+ function cursorModelSurfaces(data) {
691
+ const metadata = data?.metadata;
692
+ if (!isPlainObject(metadata) || !Object.hasOwn(metadata, "surfaces")) {
693
+ return { ok: true, present: false, value: [] };
694
+ }
695
+ const raw = metadata.surfaces;
696
+ if (typeof raw === "string") {
697
+ return { ok: true, present: true, value: splitSurfaceString(raw) };
698
+ }
699
+ if (Array.isArray(raw)) {
700
+ if (!isJsonCompatible(raw)) return { ok: false, present: true, value: null };
701
+ return { ok: true, present: true, value: raw, isArray: true };
702
+ }
703
+ return { ok: true, present: true, value: [] };
704
+ }
705
+
706
+ function cursorCompletionSurfacesAllowed(data) {
707
+ const raw = data?.metadata?.surfaces;
708
+ if (Array.isArray(raw)) {
709
+ const strings = raw.filter((item) => typeof item === "string");
710
+ if (strings.length > 0 && !strings.includes("cli")) return false;
711
+ return true;
712
+ }
713
+ if (typeof raw === "string") {
714
+ const strings = splitSurfaceString(raw);
715
+ if (strings.length > 0 && !strings.includes("cli")) return false;
716
+ }
717
+ return true;
718
+ }
719
+
720
+ function cursorCompletionUserInvocable(data) {
721
+ if (data?.["user-invocable"] === false) return { allowed: false };
722
+ if (typeof data?.["user-invocable"] === "string" && data["user-invocable"].trim().toLowerCase() === "false") {
723
+ return { unknown: true };
724
+ }
725
+ return { allowed: true };
726
+ }
727
+
728
+ async function runCursor(ctx) {
729
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
730
+ const nativeSource = sourceIdFor("cursor");
731
+ const modelSource = "cursor-agent-index";
732
+ const skills = [];
733
+ const findings = [];
734
+ const candidates = [];
735
+
736
+ async function walk(directoryAbs, depth, skipModelCompletion) {
737
+ if (ctx.truncated) return;
738
+ const entries = await listDir(directoryAbs, ctx);
739
+ if (!entries) return;
740
+ const skillFile = entries.find((item) => item.name === "SKILL.md" && (item.st.isFile() || item.st.isSymbolicLink()));
741
+ if (skillFile) {
742
+ candidates.push({
743
+ skillPath: skillFile.abs,
744
+ dirAbs: directoryAbs,
745
+ depth,
746
+ skipModelCompletion: skipModelCompletion === true || (depth > 0 && cursorSkipDirName(path.basename(directoryAbs))),
747
+ directChild: depth === 1,
748
+ containerRoot: depth === 0,
749
+ symlink: skillFile.st.isSymbolicLink(),
750
+ });
751
+ }
752
+ for (const entry of entries) {
753
+ if (ctx.truncated) break;
754
+ if (entry.st.isSymbolicLink()) {
755
+ recordSymlink(entry, ctx);
756
+ continue;
757
+ }
758
+ if (!entry.st.isDirectory()) continue;
759
+ const nextDepth = depth + 1;
760
+ if (nextDepth > CURSOR_MAX_DEPTH) continue;
761
+ const childSkip = skipModelCompletion || cursorSkipDirName(entry.name);
762
+ if (childSkip && nextDepth > 1) continue;
763
+ await walk(entry.abs, nextDepth, childSkip);
764
+ }
765
+ }
766
+
767
+ await walk(skillsRootAbs, 0, false);
768
+
769
+ const completionNames = [];
770
+ for (const candidate of candidates) {
771
+ if (candidate.symlink) {
772
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "SKILL.md is a symlink and is not followed", { path: candidate.skillPath }));
773
+ continue;
774
+ }
775
+ const read = await readBoundText(rootAbs, rootBinding, candidate.skillPath, ctx);
776
+ if (!read) continue;
777
+ const parsed = cursorParseFrontmatter(read.text);
778
+ const basename = candidate.containerRoot ? "skills" : path.basename(candidate.dirAbs);
779
+ const menuVisible = candidate.directChild === true;
780
+ const broken = parsed.kind === "broken" || parsed.kind === "unclosed";
781
+ const data = parsed.data;
782
+
783
+ let completionVisible = false;
784
+ let completionUnknown = false;
785
+ if (!candidate.skipModelCompletion && !broken) {
786
+ const invocability = cursorCompletionUserInvocable(data);
787
+ if (invocability.unknown) {
788
+ completionUnknown = true;
789
+ ctx.uncoveredItems.push(uncovered(
790
+ "cursor-completion-unproven-user-invocable",
791
+ "Cursor completion string user-invocable false is outside the calibrated native boolean sample",
792
+ { path: candidate.skillPath },
793
+ ));
794
+ } else if (invocability.allowed !== false && cursorCompletionSurfacesAllowed(data)) {
795
+ completionVisible = true;
796
+ }
797
+ }
798
+
799
+ const completionOk = completionVisible === true;
800
+ const completionDescription = completionOk
801
+ ? (parsed.kind === "fm" ? cursorCompletionDescription(data, parsed.body) : bodyFirstLine(parsed.body ?? read.text))
802
+ : null;
803
+ const completionTitle = completionOk ? (typeof data?.name === "string" && data.name !== "" ? data.name : basename) : null;
804
+
805
+ const modelObs = [];
806
+ const pushModel = (field, value) => {
807
+ modelObs.push(observation(field, value, modelSource, { basis: "source-reviewed-unexecuted", evidenceLabel: CURSOR_EVIDENCE }));
808
+ };
809
+
810
+ let parse = null;
811
+ let filtered = null;
812
+ let retained = null;
813
+ let error;
814
+ let modelDescription = null;
815
+
816
+ if (candidate.skipModelCompletion) {
817
+ parse = null;
818
+ filtered = true;
819
+ retained = false;
820
+ pushModel("modelMetadata.surfaces", null);
821
+ pushModel("modelMetadata.kind", null);
822
+ pushModel("modelMetadata.description", null);
823
+ pushModel("modelMetadata.parseError", null);
824
+ pushModel("modelMetadata.disableModelInvocation", null);
825
+ } else if (broken) {
826
+ parse = "error";
827
+ filtered = false;
828
+ retained = true;
829
+ error = CURSOR_PARSE_FAILURE;
830
+ findings.push(finding("skill-parse-error", CURSOR_PARSE_FAILURE, { path: candidate.skillPath }));
831
+ pushModel("modelMetadata.surfaces", null);
832
+ pushModel("modelMetadata.kind", "AgentSkill");
833
+ pushModel("modelMetadata.description", null);
834
+ pushModel("modelMetadata.parseError", CURSOR_PARSE_FAILURE);
835
+ pushModel("modelMetadata.disableModelInvocation", null);
836
+ } else if (parsed.kind === "fm" && data) {
837
+ const surfacesInfo = cursorModelSurfaces(data);
838
+ if (!surfacesInfo.ok) {
839
+ ctx.uncoveredItems.push(uncovered(
840
+ "unproven-yaml-shape",
841
+ "Cursor metadata.surfaces is not JSON-compatible",
842
+ { path: candidate.skillPath },
843
+ ));
844
+ parse = null;
845
+ filtered = null;
846
+ retained = null;
847
+ pushModel("modelMetadata.surfaces", null);
848
+ pushModel("modelMetadata.kind", null);
849
+ pushModel("modelMetadata.description", null);
850
+ pushModel("modelMetadata.parseError", null);
851
+ pushModel("modelMetadata.disableModelInvocation", null);
852
+ } else {
853
+ const surfaces = surfacesInfo.value;
854
+ const hasCli = Array.isArray(surfaces) && surfaces.includes("cli");
855
+ const nonemptyWithoutCli = surfaces.length > 0 && !hasCli;
856
+ if (nonemptyWithoutCli) {
857
+ parse = null;
858
+ filtered = true;
859
+ retained = false;
860
+ pushModel("modelMetadata.surfaces", surfaces);
861
+ pushModel("modelMetadata.kind", null);
862
+ pushModel("modelMetadata.description", null);
863
+ pushModel("modelMetadata.parseError", null);
864
+ pushModel("modelMetadata.disableModelInvocation", null);
865
+ } else if (data.alwaysApply === true) {
866
+ parse = "ok";
867
+ filtered = true;
868
+ retained = false;
869
+ pushModel("modelMetadata.surfaces", surfaces);
870
+ pushModel("modelMetadata.kind", "globalCursorRule");
871
+ pushModel("modelMetadata.description", null);
872
+ pushModel("modelMetadata.parseError", null);
873
+ pushModel("modelMetadata.disableModelInvocation", null);
874
+ } else {
875
+ const desc = data.description;
876
+ const valid = typeof desc === "string" && desc.length > 0;
877
+ const sliced = valid ? desc.slice(0, CURSOR_DESC_MAX) : null;
878
+ modelDescription = sliced;
879
+ parse = valid ? "ok" : "error";
880
+ filtered = false;
881
+ retained = true;
882
+ if (!valid) {
883
+ error = "Description is required";
884
+ findings.push(finding("skill-parse-error", "Description is required", { path: candidate.skillPath }));
885
+ }
886
+ pushModel("modelMetadata.surfaces", surfaces);
887
+ pushModel("modelMetadata.kind", "AgentSkill");
888
+ pushModel("modelMetadata.description", sliced);
889
+ pushModel("modelMetadata.parseError", valid ? null : "Description is required");
890
+ pushModel("modelMetadata.disableModelInvocation", data["disable-model-invocation"] === true);
891
+ }
892
+ }
893
+ } else {
894
+ parse = "error";
895
+ filtered = false;
896
+ retained = true;
897
+ error = "Description is required";
898
+ findings.push(finding("skill-parse-error", "Description is required", { path: candidate.skillPath }));
899
+ pushModel("modelMetadata.surfaces", []);
900
+ pushModel("modelMetadata.kind", "AgentSkill");
901
+ pushModel("modelMetadata.description", null);
902
+ pushModel("modelMetadata.parseError", "Description is required");
903
+ pushModel("modelMetadata.disableModelInvocation", false);
904
+ }
905
+
906
+ if (completionOk) completionNames.push({ basename, path: candidate.skillPath });
907
+
908
+ const observations = [
909
+ observation("discovered", menuVisible || completionOk || retained === true, nativeSource),
910
+ observation("menuVisible", menuVisible, nativeSource),
911
+ observation("completion.visible", completionUnknown ? null : completionOk, nativeSource),
912
+ observation("completion.id", completionOk ? basename : null, nativeSource),
913
+ observation("completion.title", completionOk ? completionTitle : null, nativeSource),
914
+ observation("completion.description", completionOk ? completionDescription : null, nativeSource),
915
+ ...modelObs,
916
+ ];
917
+
918
+ skills.push(nativeSkillBase(candidate.skillPath, {
919
+ name: basename,
920
+ description: modelDescription,
921
+ parse,
922
+ filtered,
923
+ retained,
924
+ error,
925
+ observations,
926
+ }));
927
+ }
928
+
929
+ const completionCounts = new Map();
930
+ for (const item of completionNames) {
931
+ completionCounts.set(item.basename, (completionCounts.get(item.basename) ?? 0) + 1);
932
+ }
933
+ const collisions = [...completionCounts.entries()].filter(([, count]) => count > 1).map(([name]) => name);
934
+ if (collisions.length > 0) {
935
+ for (const skill of skills) {
936
+ const idObs = skill.observations.find((item) => item.field === "completion.id");
937
+ const visible = skill.observations.find((item) => item.field === "completion.visible")?.value === true;
938
+ if (visible && collisions.includes(skill.name)) {
939
+ idObs.value = null;
940
+ ctx.uncoveredItems.push(uncovered(
941
+ "cursor-completion-identity-collision",
942
+ "completion command identity collides on directory basename; this slice does not guess a suffix",
943
+ { path: skill.path, name: skill.name },
944
+ ));
945
+ }
946
+ }
947
+ }
948
+
949
+ return finalizeNative({
950
+ hostId: "cursor",
951
+ skills,
952
+ findings,
953
+ uncoveredItems: ctx.uncoveredItems,
954
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.retained === true),
955
+ });
956
+ }
957
+
958
+ function kimiDirectoryAccepted(data) {
959
+ if (!isPlainObject(data)) return false;
960
+ const name = typeof data.name === "string" ? data.name.trim() : "";
961
+ const description = typeof data.description === "string" ? data.description : "";
962
+ if (name.length === 0 || description.length === 0) return false;
963
+ if (Object.hasOwn(data, "type") && data.type !== "inline") return false;
964
+ return true;
965
+ }
966
+
967
+ function kimiHasSubSkill(data) {
968
+ return data?.["has-sub-skill"] === true || data?.hasSubSkill === true;
969
+ }
970
+
971
+ function kimiNestedSubSkillMarker(data) {
972
+ if (!isPlainObject(data?.metadata)) return false;
973
+ return Object.hasOwn(data.metadata, "has-sub-skill") || Object.hasOwn(data.metadata, "hasSubSkill");
974
+ }
975
+
976
+ async function kimiDirectoryHasChildSkill(directoryAbs, ctx) {
977
+ const entries = await listDir(directoryAbs, ctx);
978
+ if (!entries) return false;
979
+ for (const entry of entries) {
980
+ if (entry.st.isSymbolicLink() || !entry.st.isDirectory()) continue;
981
+ const childSkill = path.join(entry.abs, "SKILL.md");
982
+ try {
983
+ const stat = await lstat(childSkill);
984
+ if (stat.isFile() || stat.isSymbolicLink()) return true;
985
+ } catch (cause) {
986
+ if (systemErrorCode(cause) !== "ENOENT") return true;
987
+ }
988
+ }
989
+ return false;
990
+ }
991
+
992
+ async function runKimi(ctx) {
993
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
994
+ const sourceId = sourceIdFor("kimi-code");
995
+ const skills = [];
996
+ const findings = [];
997
+ const seenKeys = new Map();
998
+
999
+ async function processSkillFile({ skillPath, fallbackName, isFlat, parentName, depth, kind }) {
1000
+ const read = await readBoundText(rootAbs, rootBinding, skillPath, ctx);
1001
+ if (!read) return { discovered: false };
1002
+ const split = splitFrontmatter(read.text);
1003
+ if (split.kind !== "fm") {
1004
+ if (isFlat) {
1005
+ const name = fallbackName;
1006
+ const description = trimBody(split.body ?? read.text);
1007
+ return registerKimi({
1008
+ skillPath,
1009
+ name,
1010
+ description,
1011
+ data: {},
1012
+ discovered: true,
1013
+ depth,
1014
+ kind,
1015
+ });
1016
+ }
1017
+ skills.push(nativeSkillBase(skillPath, {
1018
+ name: fallbackName,
1019
+ parse: "ok",
1020
+ filtered: true,
1021
+ retained: false,
1022
+ observations: [
1023
+ observation("discovered", false, sourceId),
1024
+ observation("disableModelInvocation", null, sourceId),
1025
+ ],
1026
+ }));
1027
+ return { discovered: false };
1028
+ }
1029
+ const parsed = parseYamlMap(split.dataText, { uniqueKeys: false });
1030
+ if (!parsed.ok) {
1031
+ skills.push(nativeSkillBase(skillPath, {
1032
+ name: fallbackName,
1033
+ parse: "ok",
1034
+ filtered: true,
1035
+ retained: false,
1036
+ observations: [
1037
+ observation("discovered", false, sourceId),
1038
+ observation("disableModelInvocation", null, sourceId),
1039
+ ],
1040
+ }));
1041
+ return { discovered: false };
1042
+ }
1043
+ const data = parsed.data;
1044
+ if (isFlat) {
1045
+ const rawName = typeof data.name === "string" && data.name.trim() !== "" ? data.name.trim() : fallbackName;
1046
+ const description = typeof data.description === "string" && data.description !== ""
1047
+ ? data.description
1048
+ : trimBody(split.body);
1049
+ return registerKimi({ skillPath, name: rawName, description, data, discovered: true, depth, kind });
1050
+ }
1051
+ if (!kimiDirectoryAccepted(data)) {
1052
+ skills.push(nativeSkillBase(skillPath, {
1053
+ name: fallbackName,
1054
+ parse: "ok",
1055
+ filtered: true,
1056
+ retained: false,
1057
+ observations: [
1058
+ observation("discovered", false, sourceId),
1059
+ observation("disableModelInvocation", null, sourceId),
1060
+ ],
1061
+ }));
1062
+ return { discovered: false, data };
1063
+ }
1064
+ const childName = data.name.trim();
1065
+ const name = parentName ? `${parentName}.${childName}` : childName;
1066
+ return registerKimi({
1067
+ skillPath,
1068
+ name,
1069
+ description: data.description,
1070
+ data,
1071
+ discovered: true,
1072
+ depth,
1073
+ kind,
1074
+ });
1075
+ }
1076
+
1077
+ function registerKimi({ skillPath, name, description, data, discovered, depth, kind }) {
1078
+ const key = name.toLowerCase();
1079
+ const disable = data?.["disable-model-invocation"] === true;
1080
+ const previous = seenKeys.get(key);
1081
+ if (previous) {
1082
+ const calibratedFirstWins = !previous.ambiguous
1083
+ && previous.kind === "dir"
1084
+ && kind === "dir"
1085
+ && previous.depth === depth
1086
+ && isAsciiText(name)
1087
+ && isAsciiText(previous.name);
1088
+ if (!calibratedFirstWins) {
1089
+ previous.ambiguous = true;
1090
+ for (const candidate of skills) {
1091
+ if (candidate.name?.toLowerCase() !== key || !candidate.observations.some((item) => item.field === "discovered" && item.value === true)) continue;
1092
+ candidate.retained = null;
1093
+ candidate.supersededBy = null;
1094
+ candidate.filtered = false;
1095
+ }
1096
+ ctx.uncoveredItems.push(uncovered(
1097
+ "kimi-name-collision-uncovered",
1098
+ "Kimi final-name competition is outside the calibrated same-layer ASCII first-wins sample",
1099
+ { path: skillPath, name },
1100
+ ));
1101
+ skills.push(nativeSkillBase(skillPath, {
1102
+ name,
1103
+ description,
1104
+ parse: "ok",
1105
+ filtered: false,
1106
+ retained: null,
1107
+ supersededBy: null,
1108
+ observations: [
1109
+ observation("discovered", discovered, sourceId),
1110
+ observation("disableModelInvocation", disable, sourceId),
1111
+ ],
1112
+ }));
1113
+ return { discovered, data, retained: null };
1114
+ }
1115
+ skills.push(nativeSkillBase(skillPath, {
1116
+ name,
1117
+ description,
1118
+ parse: "ok",
1119
+ filtered: true,
1120
+ retained: false,
1121
+ supersededBy: previous.skill.path,
1122
+ observations: [
1123
+ observation("discovered", discovered, sourceId),
1124
+ observation("disableModelInvocation", disable, sourceId),
1125
+ ],
1126
+ }));
1127
+ return { discovered, data, retained: false };
1128
+ }
1129
+ const skill = nativeSkillBase(skillPath, {
1130
+ name,
1131
+ description,
1132
+ parse: "ok",
1133
+ filtered: false,
1134
+ retained: true,
1135
+ observations: [
1136
+ observation("discovered", true, sourceId),
1137
+ observation("disableModelInvocation", disable, sourceId),
1138
+ ],
1139
+ });
1140
+ seenKeys.set(key, { name, skill, depth, kind });
1141
+ skills.push(skill);
1142
+ return { discovered: true, data, retained: true, name };
1143
+ }
1144
+
1145
+ async function walk(directoryAbs, depth, parentName) {
1146
+ if (ctx.truncated) return;
1147
+ const entries = await listDir(directoryAbs, ctx);
1148
+ if (!entries) return;
1149
+ const sorted = utf16Sort(entries);
1150
+ const dirs = sorted.filter((item) => item.st.isDirectory() || item.st.isSymbolicLink());
1151
+ const files = sorted.filter((item) => item.st.isFile() || (item.st.isSymbolicLink() && !item.st.isDirectory()));
1152
+ const skillDirs = [];
1153
+ for (const entry of dirs) {
1154
+ if (entry.st.isSymbolicLink()) {
1155
+ recordSymlink(entry, ctx);
1156
+ continue;
1157
+ }
1158
+ const skillPath = path.join(entry.abs, "SKILL.md");
1159
+ let skillStat;
1160
+ try {
1161
+ skillStat = await lstat(skillPath);
1162
+ } catch {
1163
+ skillStat = null;
1164
+ }
1165
+ if (skillStat?.isSymbolicLink()) {
1166
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "SKILL.md is a symlink and is not followed", { path: skillPath }));
1167
+ continue;
1168
+ }
1169
+ if (skillStat?.isFile()) skillDirs.push(entry);
1170
+ }
1171
+
1172
+ const produced = [];
1173
+ for (const entry of skillDirs) {
1174
+ const nextDepth = depth + 1;
1175
+ if (nextDepth > KIMI_MAX_DEPTH) continue;
1176
+ const skillPath = path.join(entry.abs, "SKILL.md");
1177
+ const result = await processSkillFile({
1178
+ skillPath,
1179
+ fallbackName: entry.name,
1180
+ isFlat: false,
1181
+ parentName,
1182
+ depth: nextDepth,
1183
+ kind: "dir",
1184
+ });
1185
+ produced.push({ entry, result, nextDepth });
1186
+ }
1187
+
1188
+ if (depth === 0) {
1189
+ const skillDirNames = new Set(skillDirs.map((item) => item.name));
1190
+ for (const entry of files) {
1191
+ if (entry.st.isSymbolicLink()) {
1192
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "a file symlink was not followed", { path: entry.abs }));
1193
+ continue;
1194
+ }
1195
+ if (!entry.name.endsWith(".md") || entry.name === "SKILL.md") continue;
1196
+ const base = entry.name.slice(0, -3);
1197
+ if (skillDirNames.has(base)) continue;
1198
+ await processSkillFile({
1199
+ skillPath: entry.abs,
1200
+ fallbackName: base,
1201
+ isFlat: true,
1202
+ parentName: null,
1203
+ depth: 0,
1204
+ kind: "flat",
1205
+ });
1206
+ }
1207
+ }
1208
+
1209
+ for (const { entry, result, nextDepth } of produced) {
1210
+ const hidden = entry.name.startsWith(".") || entry.name === "node_modules";
1211
+ if (hidden) continue;
1212
+ if (!result.discovered) {
1213
+ const nested = await kimiDirectoryHasChildSkill(entry.abs, ctx);
1214
+ if (nested) {
1215
+ ctx.uncoveredItems.push(uncovered(
1216
+ "kimi-invalid-parent-child-uncovered",
1217
+ "child skills under a failed Kimi parent directory skill are outside the calibrated set",
1218
+ { path: path.join(entry.abs, "SKILL.md") },
1219
+ ));
1220
+ }
1221
+ continue;
1222
+ }
1223
+ if (kimiNestedSubSkillMarker(result.data)) {
1224
+ ctx.uncoveredItems.push(uncovered(
1225
+ "kimi-nested-subskill-marker-uncovered",
1226
+ "nested metadata has-sub-skill markers are outside the calibrated top-level flag set",
1227
+ { path: path.join(entry.abs, "SKILL.md") },
1228
+ ));
1229
+ continue;
1230
+ }
1231
+ if (kimiHasSubSkill(result.data)) await walk(entry.abs, nextDepth, result.name);
1232
+ }
1233
+
1234
+ for (const entry of dirs) {
1235
+ if (entry.st.isSymbolicLink() || !entry.st.isDirectory()) continue;
1236
+ if (skillDirs.some((item) => item.abs === entry.abs)) continue;
1237
+ const hidden = entry.name.startsWith(".") || entry.name === "node_modules";
1238
+ if (hidden) continue;
1239
+ const nextDepth = depth + 1;
1240
+ if (nextDepth > KIMI_MAX_DEPTH) continue;
1241
+ await walk(entry.abs, nextDepth, parentName);
1242
+ }
1243
+ }
1244
+
1245
+ await walk(skillsRootAbs, 0, null);
1246
+ return finalizeNative({
1247
+ hostId: "kimi-code",
1248
+ skills,
1249
+ findings,
1250
+ uncoveredItems: ctx.uncoveredItems,
1251
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.retained === true || item.retained === false),
1252
+ });
1253
+ }
1254
+
1255
+ function workbuddyCoerceDisable(value) {
1256
+ return value === true;
1257
+ }
1258
+
1259
+ async function readOptionalBound(rootAbs, rootBinding, absPath, ctx) {
1260
+ let st;
1261
+ try {
1262
+ st = await lstat(absPath);
1263
+ } catch {
1264
+ return { exists: false };
1265
+ }
1266
+ if (st.isSymbolicLink()) {
1267
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "optional metadata is a symlink and is not followed", { path: absPath }));
1268
+ return { exists: true, unreadable: true };
1269
+ }
1270
+ if (!st.isFile()) return { exists: false };
1271
+ const read = await readBoundText(rootAbs, rootBinding, absPath, ctx);
1272
+ if (!read) return { exists: true, unreadable: true };
1273
+ return { exists: true, text: read.text };
1274
+ }
1275
+
1276
+ function workbuddyBodyDescription(text, dirName) {
1277
+ const sliced = trimBody(text).slice(0, WORKBUDDY_BODY_DESC);
1278
+ return sliced || dirName;
1279
+ }
1280
+
1281
+ function workbuddyParseSkill(text, dirName) {
1282
+ if (text.charCodeAt(0) === 0xfeff) {
1283
+ return { name: dirName, description: workbuddyBodyDescription(text, dirName), data: {}, bom: true };
1284
+ }
1285
+ if (text.length === 0) {
1286
+ return { name: dirName, description: dirName, data: {}, empty: true };
1287
+ }
1288
+ const split = splitFrontmatter(text);
1289
+ if (split.kind !== "fm") {
1290
+ return { name: dirName, description: workbuddyBodyDescription(text, dirName), data: {} };
1291
+ }
1292
+ const parsed = parseYamlMap(split.dataText, { uniqueKeys: false });
1293
+ if (!parsed.ok) {
1294
+ return { name: dirName, description: dirName, data: {}, fallback: true };
1295
+ }
1296
+ const data = parsed.data;
1297
+ if (isPlainObject(data["allowed-tools"])) {
1298
+ return { name: dirName, description: dirName, data: {}, fallback: true };
1299
+ }
1300
+ let name;
1301
+ if (!Object.hasOwn(data, "name") || data.name === undefined || data.name === null || data.name === "") {
1302
+ name = dirName;
1303
+ } else if (Array.isArray(data.name) || isPlainObject(data.name)) {
1304
+ name = jsonStringifyFallback(data.name);
1305
+ } else {
1306
+ name = stringifyJs(data.name) ?? dirName;
1307
+ }
1308
+ let description;
1309
+ if (!Object.hasOwn(data, "description") || data.description === undefined || data.description === null || data.description === "") {
1310
+ description = workbuddyBodyDescription(split.body ?? "", dirName);
1311
+ } else if (Array.isArray(data.description) || isPlainObject(data.description)) {
1312
+ description = jsonStringifyFallback(data.description);
1313
+ } else {
1314
+ description = stringifyJs(data.description);
1315
+ }
1316
+ return { name, description, data };
1317
+ }
1318
+
1319
+ async function runWorkbuddy(ctx) {
1320
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
1321
+ const sourceId = sourceIdFor("workbuddy");
1322
+ const skills = [];
1323
+ const findings = [];
1324
+
1325
+ async function walk(directoryAbs, depth, parentHasSkill) {
1326
+ if (ctx.truncated) return;
1327
+ if (depth > WORKBUDDY_MAX_DEPTH) return;
1328
+ const entries = await listDir(directoryAbs, ctx);
1329
+ if (!entries) return;
1330
+ const skillEntry = entries.find((item) => item.name === "SKILL.md");
1331
+ const hasSkill = Boolean(skillEntry && (skillEntry.st.isFile() || skillEntry.st.isSymbolicLink()));
1332
+ if (hasSkill && depth > 0) {
1333
+ if (skillEntry.st.isSymbolicLink()) {
1334
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "SKILL.md is a symlink and is not followed", { path: skillEntry.abs }));
1335
+ } else {
1336
+ const dirName = path.basename(directoryAbs);
1337
+ const read = await readBoundText(rootAbs, rootBinding, skillEntry.abs, ctx);
1338
+ if (read) {
1339
+ const parsed = workbuddyParseSkill(read.text, dirName);
1340
+ let selectedMeta = null;
1341
+ let metaStopped = false;
1342
+ for (const fileName of WORKBUDDY_META_FILES) {
1343
+ const metaPath = path.join(directoryAbs, fileName);
1344
+ const meta = await readOptionalBound(rootAbs, rootBinding, metaPath, ctx);
1345
+ if (!meta.exists) continue;
1346
+ if (meta.unreadable) {
1347
+ metaStopped = true;
1348
+ break;
1349
+ }
1350
+ try {
1351
+ const json = JSON.parse(meta.text);
1352
+ if (!isPlainObject(json)) {
1353
+ metaStopped = true;
1354
+ selectedMeta = null;
1355
+ break;
1356
+ }
1357
+ selectedMeta = { fileName, json };
1358
+ } catch {
1359
+ metaStopped = true;
1360
+ selectedMeta = null;
1361
+ }
1362
+ break;
1363
+ }
1364
+ let name = parsed.name;
1365
+ let retained = true;
1366
+ if (!metaStopped && selectedMeta?.fileName === "_user_meta.json") {
1367
+ if (Object.hasOwn(selectedMeta.json, "name")) {
1368
+ if (typeof selectedMeta.json.name === "string" && selectedMeta.json.name !== "") {
1369
+ name = selectedMeta.json.name;
1370
+ } else {
1371
+ ctx.uncoveredItems.push(uncovered(
1372
+ "workbuddy-uncalibrated-name-type",
1373
+ "install metadata name type is outside the calibrated _user_meta.json non-empty string sample",
1374
+ { path: skillEntry.abs, source: selectedMeta.fileName },
1375
+ ));
1376
+ name = null;
1377
+ retained = null;
1378
+ }
1379
+ }
1380
+ } else if (!metaStopped && selectedMeta) {
1381
+ ctx.uncoveredItems.push(uncovered(
1382
+ "workbuddy-uncalibrated-name-source",
1383
+ "install metadata source is outside the calibrated _user_meta.json string sample",
1384
+ { path: skillEntry.abs, source: selectedMeta.fileName },
1385
+ ));
1386
+ name = null;
1387
+ retained = null;
1388
+ }
1389
+
1390
+ let version = null;
1391
+ const skillJsonPath = path.join(directoryAbs, "skill.json");
1392
+ const skillJson = await readOptionalBound(rootAbs, rootBinding, skillJsonPath, ctx);
1393
+ let versionChosen = false;
1394
+ if (skillJson.exists && skillJson.text) {
1395
+ try {
1396
+ const json = JSON.parse(skillJson.text);
1397
+ if (isPlainObject(json) && Object.hasOwn(json, "version")) {
1398
+ versionChosen = true;
1399
+ if (typeof json.version === "string" && WORKBUDDY_VERSION.test(json.version)) {
1400
+ version = json.version;
1401
+ } else {
1402
+ ctx.uncoveredItems.push(uncovered(
1403
+ "workbuddy-uncalibrated-version-source",
1404
+ "skill.json version is outside the calibrated x.y.z string set",
1405
+ { path: skillJsonPath },
1406
+ ));
1407
+ version = null;
1408
+ }
1409
+ }
1410
+ } catch {
1411
+ ctx.uncoveredItems.push(uncovered(
1412
+ "workbuddy-uncalibrated-version-source",
1413
+ "skill.json could not be parsed for version selection",
1414
+ { path: skillJsonPath },
1415
+ ));
1416
+ }
1417
+ }
1418
+ if (!versionChosen) {
1419
+ for (const fileName of ["manifest.yaml", "manifest.yml"]) {
1420
+ const manifest = await readOptionalBound(rootAbs, rootBinding, path.join(directoryAbs, fileName), ctx);
1421
+ if (!manifest.exists) continue;
1422
+ ctx.uncoveredItems.push(uncovered(
1423
+ "workbuddy-uncalibrated-version-source",
1424
+ `${fileName} version source is not in the calibrated native set`,
1425
+ { path: path.join(directoryAbs, fileName) },
1426
+ ));
1427
+ versionChosen = true;
1428
+ break;
1429
+ }
1430
+ }
1431
+ if (!versionChosen && parsed.data && Object.hasOwn(parsed.data, "version")) {
1432
+ ctx.uncoveredItems.push(uncovered(
1433
+ "workbuddy-uncalibrated-version-source",
1434
+ "SKILL.md frontmatter version is not in the calibrated native set",
1435
+ { path: skillEntry.abs },
1436
+ ));
1437
+ versionChosen = true;
1438
+ }
1439
+ if (!versionChosen && selectedMeta && Object.hasOwn(selectedMeta.json, "version")) {
1440
+ ctx.uncoveredItems.push(uncovered(
1441
+ "workbuddy-uncalibrated-version-source",
1442
+ "install metadata version is not in the calibrated native set",
1443
+ { path: path.join(directoryAbs, selectedMeta.fileName) },
1444
+ ));
1445
+ }
1446
+
1447
+ const iconFields = [parsed.data?.icon, parsed.data?.iconLocalPath, selectedMeta?.json?.icon, selectedMeta?.json?.iconLocalPath];
1448
+ if (iconFields.some((item) => typeof item === "string" && item !== "")) {
1449
+ ctx.uncoveredItems.push(uncovered(
1450
+ "workbuddy-icon-uncovered",
1451
+ "iconLocalPath asset resolution is not calibrated",
1452
+ { path: skillEntry.abs },
1453
+ ));
1454
+ }
1455
+
1456
+ const disable = workbuddyCoerceDisable(parsed.data?.disable);
1457
+ const disableModel = parsed.data?.["disable-model-invocation"] === true;
1458
+ const userInvocable = parsed.data?.["user-invocable"] === false ? false : null;
1459
+ const nested = parentHasSkill && path.basename(directoryAbs) !== path.basename(skillsRootAbs);
1460
+ void nested;
1461
+ skills.push(nativeSkillBase(skillEntry.abs, {
1462
+ name,
1463
+ description: parsed.description,
1464
+ parse: "ok",
1465
+ filtered: false,
1466
+ retained,
1467
+ observations: [
1468
+ observation("discovered", true, sourceId),
1469
+ observation("userInvocable", userInvocable, sourceId),
1470
+ observation("disableModelInvocation", disableModel, sourceId),
1471
+ observation("disabled", disable, sourceId),
1472
+ observation("version", version, sourceId),
1473
+ ],
1474
+ }));
1475
+ }
1476
+ }
1477
+ }
1478
+
1479
+ for (const entry of entries) {
1480
+ if (ctx.truncated) break;
1481
+ if (entry.st.isSymbolicLink()) {
1482
+ recordSymlink(entry, ctx);
1483
+ continue;
1484
+ }
1485
+ if (!entry.st.isDirectory()) continue;
1486
+ if (hasSkill && depth > 0 && entry.name !== "skills") continue;
1487
+ await walk(entry.abs, depth + 1, depth > 0 && hasSkill);
1488
+ }
1489
+ }
1490
+
1491
+ await walk(skillsRootAbs, 0, false);
1492
+ return finalizeNative({
1493
+ hostId: "workbuddy",
1494
+ skills,
1495
+ findings,
1496
+ uncoveredItems: ctx.uncoveredItems,
1497
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.retained === true),
1498
+ });
1499
+ }
1500
+
1501
+ function isBlockScalarIndicator(value) {
1502
+ return typeof value === "string" && /^[|>](?:[+-])?(?:\d+)?$/.test(value.trim());
1503
+ }
1504
+
1505
+ function unquoteLineValue(raw) {
1506
+ if (typeof raw !== "string" || raw.length < 2) return raw;
1507
+ const start = raw[0];
1508
+ const end = raw[raw.length - 1];
1509
+ if ((start === "\"" && end === "\"") || (start === "'" && end === "'")) return raw.slice(1, -1);
1510
+ return raw;
1511
+ }
1512
+
1513
+ function coerceLineValue(raw) {
1514
+ if (raw === "true") return true;
1515
+ if (raw === "false") return false;
1516
+ if (raw === "null") return null;
1517
+ return unquoteLineValue(raw);
1518
+ }
1519
+
1520
+ function codebuddyLineParse(text) {
1521
+ const split = splitFrontmatter(text);
1522
+ if (split.kind !== "fm") return { data: {}, noFm: true, unprovenKeys: [] };
1523
+ const raw = parseLineMap(split.dataText);
1524
+ const data = {};
1525
+ const unprovenKeys = [];
1526
+ for (const [key, value] of Object.entries(raw)) {
1527
+ if (isBlockScalarIndicator(value)) {
1528
+ unprovenKeys.push(key);
1529
+ data[key] = value;
1530
+ continue;
1531
+ }
1532
+ data[key] = coerceLineValue(value);
1533
+ }
1534
+ return { data, body: split.body, unprovenKeys };
1535
+ }
1536
+
1537
+ async function runCodebuddy(ctx) {
1538
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
1539
+ const sourceId = sourceIdFor("codebuddy");
1540
+ const skills = [];
1541
+ const findings = [];
1542
+
1543
+ async function walk(directoryAbs, depth) {
1544
+ if (ctx.truncated) return;
1545
+ const entries = await listDir(directoryAbs, ctx);
1546
+ if (!entries) return;
1547
+ const skillEntry = entries.find((item) => item.name === "SKILL.md");
1548
+ if (skillEntry && (skillEntry.st.isFile() || skillEntry.st.isSymbolicLink())) {
1549
+ if (skillEntry.st.isSymbolicLink()) {
1550
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "SKILL.md is a symlink and is not followed", { path: skillEntry.abs }));
1551
+ } else {
1552
+ const relativeDir = posixRelative(skillsRootAbs, directoryAbs);
1553
+ const pathName = !relativeDir || relativeDir === "." ? path.basename(skillsRootAbs) : relativeDir.split("/").join(":");
1554
+ const read = await readBoundText(rootAbs, rootBinding, skillEntry.abs, ctx);
1555
+ if (read) {
1556
+ const parsed = codebuddyLineParse(read.text);
1557
+ const data = parsed.data;
1558
+ let name;
1559
+ let description;
1560
+ const rawName = data.name;
1561
+ const rawDesc = data.description;
1562
+ const numericName = typeof rawName === "string" && /^-?\d+(\.\d+)?$/.test(rawName.trim());
1563
+ const typeFallback = numericName || typeof rawName === "number" || Array.isArray(rawName) || Array.isArray(rawDesc);
1564
+ const nameUnproven = parsed.unprovenKeys.includes("name");
1565
+ const descriptionUnproven = parsed.unprovenKeys.includes("description");
1566
+ if (parsed.unprovenKeys.length > 0) {
1567
+ ctx.uncoveredItems.push(uncovered(
1568
+ "codebuddy-unproven-parser-shape",
1569
+ "CodeBuddy simplified parser shape is outside the calibrated single-line field set",
1570
+ { path: skillEntry.abs, fields: parsed.unprovenKeys },
1571
+ ));
1572
+ }
1573
+ if (parsed.noFm) {
1574
+ name = pathName;
1575
+ description = pathName;
1576
+ } else if (typeFallback) {
1577
+ name = pathName;
1578
+ description = pathName;
1579
+ } else {
1580
+ name = nameUnproven
1581
+ ? null
1582
+ : typeof rawName === "string" && rawName.trim() !== "" ? rawName : pathName;
1583
+ description = descriptionUnproven
1584
+ ? null
1585
+ : typeof rawDesc === "string" && rawDesc.trim() !== "" ? rawDesc : name;
1586
+ }
1587
+ const visible = data["user-invocable"] !== false;
1588
+ skills.push(nativeSkillBase(skillEntry.abs, {
1589
+ name,
1590
+ description,
1591
+ parse: "ok",
1592
+ filtered: visible ? false : true,
1593
+ retained: visible,
1594
+ observations: [
1595
+ observation("discovered", true, sourceId),
1596
+ observation("menuVisible", visible, sourceId),
1597
+ ],
1598
+ }));
1599
+ }
1600
+ }
1601
+ }
1602
+ for (const entry of entries) {
1603
+ if (ctx.truncated) break;
1604
+ if (entry.st.isSymbolicLink()) {
1605
+ recordSymlink(entry, ctx);
1606
+ continue;
1607
+ }
1608
+ if (!entry.st.isDirectory()) continue;
1609
+ if (depth + 1 > CODEBUDDY_MAX_DEPTH) continue;
1610
+ await walk(entry.abs, depth + 1);
1611
+ }
1612
+ }
1613
+
1614
+ await walk(skillsRootAbs, 0);
1615
+ return finalizeNative({
1616
+ hostId: "codebuddy",
1617
+ skills,
1618
+ findings,
1619
+ uncoveredItems: ctx.uncoveredItems,
1620
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.retained === true),
1621
+ });
1622
+ }
1623
+
1624
+ async function runQoder(ctx) {
1625
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
1626
+ const sourceId = sourceIdFor("qoder");
1627
+ const skills = [];
1628
+ const findings = [];
1629
+ const byName = new Map();
1630
+
1631
+ async function walk(directoryAbs, depth, stop) {
1632
+ if (ctx.truncated || stop) return;
1633
+ const entries = await listDir(directoryAbs, ctx);
1634
+ if (!entries) return;
1635
+ const skillEntry = entries.find((item) => item.name === "SKILL.md");
1636
+ let hasSkill = false;
1637
+ if (skillEntry && depth > 0) {
1638
+ if (skillEntry.st.isSymbolicLink()) {
1639
+ ctx.uncoveredItems.push(uncovered("file-symlink-unfollowed", "SKILL.md is a symlink and is not followed", { path: skillEntry.abs }));
1640
+ } else if (skillEntry.st.isFile()) {
1641
+ hasSkill = true;
1642
+ const basename = path.basename(directoryAbs);
1643
+ const read = await readBoundText(rootAbs, rootBinding, skillEntry.abs, ctx);
1644
+ if (read) {
1645
+ const split = splitFrontmatter(read.text);
1646
+ let description;
1647
+ if (split.kind === "no-fm") description = "No frontmatter";
1648
+ else if (split.kind === "unclosed") description = "Invalid yaml";
1649
+ else {
1650
+ const parsed = parseYamlMap(split.dataText, { uniqueKeys: false });
1651
+ if (!parsed.ok) description = "Invalid yaml";
1652
+ else if (typeof parsed.data.description === "string") description = parsed.data.description;
1653
+ else description = bodyFirstLine(split.body) || "Body";
1654
+ }
1655
+ const previous = byName.get(basename);
1656
+ const skill = nativeSkillBase(skillEntry.abs, {
1657
+ name: basename,
1658
+ description,
1659
+ parse: "ok",
1660
+ filtered: false,
1661
+ retained: previous ? null : true,
1662
+ supersededBy: null,
1663
+ observations: [observation("discovered", true, sourceId)],
1664
+ });
1665
+ if (previous) {
1666
+ previous.retained = null;
1667
+ previous.supersededBy = null;
1668
+ skill.retained = null;
1669
+ ctx.uncoveredItems.push(uncovered(
1670
+ "qoder-same-basename-uncovered",
1671
+ "Qoder same-basename candidates are outside the calibrated winner order",
1672
+ { path: skillEntry.abs, name: basename },
1673
+ ));
1674
+ } else {
1675
+ byName.set(basename, skill);
1676
+ }
1677
+ skills.push(skill);
1678
+ }
1679
+ }
1680
+ }
1681
+ if (hasSkill) return;
1682
+ for (const entry of entries) {
1683
+ if (ctx.truncated) break;
1684
+ if (entry.st.isSymbolicLink()) {
1685
+ recordSymlink(entry, ctx);
1686
+ continue;
1687
+ }
1688
+ if (!entry.st.isDirectory()) continue;
1689
+ if (depth + 1 > QODER_MAX_DEPTH) continue;
1690
+ await walk(entry.abs, depth + 1, false);
1691
+ }
1692
+ }
1693
+
1694
+ await walk(skillsRootAbs, 0, false);
1695
+ return finalizeNative({
1696
+ hostId: "qoder",
1697
+ skills,
1698
+ findings,
1699
+ uncoveredItems: ctx.uncoveredItems,
1700
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.name != null),
1701
+ });
1702
+ }
1703
+
1704
+ function dshCoerceBool(value) {
1705
+ if (value === true || value === 1) return { ok: true, value: true };
1706
+ if (value === false || value === 0) return { ok: true, value: false };
1707
+ if (value === "YES" || value === "true") return { ok: true, value: true };
1708
+ if (value === "OFF" || value === "false") return { ok: true, value: false };
1709
+ if (value === null || value === undefined) return { ok: false, nullish: value === null };
1710
+ return { ok: false };
1711
+ }
1712
+
1713
+ function dshParseSkill(text) {
1714
+ if (text.charCodeAt(0) === 0xfeff) return { discovered: false };
1715
+ if (text.startsWith("\n") || text.startsWith("\r\n")) return { discovered: false };
1716
+ const split = splitFrontmatter(text, { allowBom: false, firstLineExact: true });
1717
+ if (split.kind !== "fm") return { discovered: false };
1718
+ const parsed = parseYamlMap(split.dataText, { uniqueKeys: true });
1719
+ if (!parsed.ok) return { discovered: false };
1720
+ const data = parsed.data;
1721
+ if (Object.hasOwn(data, "userInvocable") || Object.hasOwn(data, "modelInvocable") || Object.hasOwn(data, "disableModelInvocation")) {
1722
+ return { discovered: false };
1723
+ }
1724
+ if (typeof data.name !== "string" || !DSH_NAME.test(data.name)) return { discovered: false };
1725
+ if (typeof data.description !== "string" || data.description === "") return { discovered: false };
1726
+ let userInvocable = true;
1727
+ let modelInvocable = true;
1728
+ if (Object.hasOwn(data, "user-invocable")) {
1729
+ const coerced = dshCoerceBool(data["user-invocable"]);
1730
+ if (!coerced.ok) return { discovered: false };
1731
+ userInvocable = coerced.value;
1732
+ }
1733
+ if (Object.hasOwn(data, "disable-model-invocation")) {
1734
+ const coerced = dshCoerceBool(data["disable-model-invocation"]);
1735
+ if (!coerced.ok) return { discovered: false };
1736
+ modelInvocable = coerced.value !== true;
1737
+ }
1738
+ return {
1739
+ discovered: true,
1740
+ name: data.name,
1741
+ description: data.description,
1742
+ userInvocable,
1743
+ modelInvocable,
1744
+ };
1745
+ }
1746
+
1747
+ async function runDsh(ctx) {
1748
+ const { skillsRootAbs, rootAbs, rootBinding } = ctx;
1749
+ const sourceId = sourceIdFor("deepseek-harness");
1750
+ const skills = [];
1751
+ const findings = [];
1752
+ const entries = await listDir(skillsRootAbs, ctx);
1753
+ if (!entries) {
1754
+ return finalizeNative({ hostId: "deepseek-harness", skills, findings, uncoveredItems: ctx.uncoveredItems, nameAmbiguities: [] });
1755
+ }
1756
+ const sorted = localeSort(entries);
1757
+ const seen = new Map();
1758
+ let localOrder = 0;
1759
+ for (const entry of sorted) {
1760
+ if (ctx.truncated) break;
1761
+ if (entry.st.isSymbolicLink()) {
1762
+ ctx.uncoveredItems.push(uncovered(
1763
+ "dsh-external-symlink-unfollowed",
1764
+ "native filesystem component may follow this symlink; Foundation containment does not expand it",
1765
+ { path: entry.abs },
1766
+ ));
1767
+ continue;
1768
+ }
1769
+ if (entry.st.isFile()) {
1770
+ if (entry.name === "SKILL.md" || (entry.name.endsWith(".md") && !entry.name.endsWith(".MD") && entry.name.toLowerCase().endsWith(".md") && entry.name.slice(-3) === ".md")) {
1771
+ localOrder += 1;
1772
+ const read = await readBoundText(rootAbs, rootBinding, entry.abs, ctx);
1773
+ if (!read) continue;
1774
+ const parsed = dshParseSkill(read.text);
1775
+ if (!parsed.discovered) {
1776
+ skills.push(nativeSkillBase(entry.abs, {
1777
+ parse: "ok",
1778
+ filtered: true,
1779
+ retained: false,
1780
+ observations: [
1781
+ observation("discovered", false, sourceId),
1782
+ observation("userInvocable", null, sourceId),
1783
+ observation("modelInvocable", null, sourceId),
1784
+ ],
1785
+ }));
1786
+ continue;
1787
+ }
1788
+ registerDsh(entry.abs, parsed, localOrder, { entryName: entry.name, kind: "flat" });
1789
+ }
1790
+ continue;
1791
+ }
1792
+ if (!entry.st.isDirectory()) continue;
1793
+ const skillPath = path.join(entry.abs, "SKILL.md");
1794
+ const looked = await lstatMissingOrUncovered(skillPath, ctx);
1795
+ if (!looked.stat) continue;
1796
+ const skillStat = looked.stat;
1797
+ if (skillStat.isSymbolicLink()) {
1798
+ ctx.uncoveredItems.push(uncovered(
1799
+ "dsh-external-symlink-unfollowed",
1800
+ "native filesystem component may follow this symlink; Foundation containment does not expand it",
1801
+ { path: skillPath },
1802
+ ));
1803
+ continue;
1804
+ }
1805
+ if (!skillStat.isFile()) continue;
1806
+ localOrder += 1;
1807
+ const read = await readBoundText(rootAbs, rootBinding, skillPath, ctx);
1808
+ if (!read) continue;
1809
+ const parsed = dshParseSkill(read.text);
1810
+ if (!parsed.discovered) {
1811
+ skills.push(nativeSkillBase(skillPath, {
1812
+ name: parsed.name ?? null,
1813
+ parse: "ok",
1814
+ filtered: true,
1815
+ retained: false,
1816
+ observations: [
1817
+ observation("discovered", false, sourceId),
1818
+ observation("userInvocable", null, sourceId),
1819
+ observation("modelInvocable", null, sourceId),
1820
+ ],
1821
+ }));
1822
+ continue;
1823
+ }
1824
+ registerDsh(skillPath, parsed, localOrder, { entryName: entry.name, kind: "dir" });
1825
+ }
1826
+
1827
+ function registerDsh(skillPath, parsed, order, identity) {
1828
+ const previous = seen.get(parsed.name);
1829
+ if (previous) {
1830
+ const uncalibrated = previous.ambiguous
1831
+ || identity.kind !== previous.kind
1832
+ || !isAsciiText(identity.entryName)
1833
+ || !isAsciiText(previous.entryName)
1834
+ || identity.entryName.localeCompare(previous.entryName) === 0;
1835
+ if (uncalibrated) {
1836
+ previous.ambiguous = true;
1837
+ for (const candidate of skills) {
1838
+ if (candidate.name !== parsed.name || !candidate.observations.some((item) => item.field === "discovered" && item.value === true)) continue;
1839
+ candidate.retained = null;
1840
+ candidate.supersededBy = null;
1841
+ candidate.filtered = false;
1842
+ }
1843
+ ctx.uncoveredItems.push(uncovered(
1844
+ "dsh-name-collision-uncovered",
1845
+ "DeepSeek Harness name competition is outside the calibrated ASCII first-wins sample",
1846
+ { path: skillPath, name: parsed.name },
1847
+ ));
1848
+ skills.push(nativeSkillBase(skillPath, {
1849
+ name: parsed.name,
1850
+ description: parsed.description,
1851
+ parse: "ok",
1852
+ filtered: false,
1853
+ retained: null,
1854
+ supersededBy: null,
1855
+ observations: [
1856
+ observation("discovered", true, sourceId),
1857
+ observation("userInvocable", parsed.userInvocable, sourceId),
1858
+ observation("modelInvocable", parsed.modelInvocable, sourceId),
1859
+ ],
1860
+ }));
1861
+ return;
1862
+ }
1863
+ skills.push(nativeSkillBase(skillPath, {
1864
+ name: parsed.name,
1865
+ description: parsed.description,
1866
+ parse: "ok",
1867
+ filtered: true,
1868
+ retained: false,
1869
+ supersededBy: previous.path,
1870
+ observations: [
1871
+ observation("discovered", true, sourceId),
1872
+ observation("userInvocable", parsed.userInvocable, sourceId),
1873
+ observation("modelInvocable", parsed.modelInvocable, sourceId),
1874
+ ],
1875
+ }));
1876
+ return;
1877
+ }
1878
+ const skill = nativeSkillBase(skillPath, {
1879
+ name: parsed.name,
1880
+ description: parsed.description,
1881
+ parse: "ok",
1882
+ filtered: false,
1883
+ retained: true,
1884
+ observations: [
1885
+ observation("discovered", true, sourceId),
1886
+ observation("userInvocable", parsed.userInvocable, sourceId),
1887
+ observation("modelInvocable", parsed.modelInvocable, sourceId),
1888
+ ],
1889
+ });
1890
+ seen.set(parsed.name, { path: skillPath, order, skill, entryName: identity.entryName, kind: identity.kind });
1891
+ skills.push(skill);
1892
+ }
1893
+
1894
+ return finalizeNative({
1895
+ hostId: "deepseek-harness",
1896
+ skills,
1897
+ findings,
1898
+ uncoveredItems: ctx.uncoveredItems,
1899
+ nameAmbiguities: nameAmbiguitiesFrom(skills, (item) => item.name != null),
1900
+ });
1901
+ }
1902
+
1903
+ const RUNNERS = {
1904
+ claude: runClaude,
1905
+ cursor: runCursor,
1906
+ "kimi-code": runKimi,
1907
+ workbuddy: runWorkbuddy,
1908
+ codebuddy: runCodebuddy,
1909
+ qoder: runQoder,
1910
+ "deepseek-harness": runDsh,
1911
+ };
1912
+
1913
+ export async function runNativeHostLoadingCheck({
1914
+ rootAbs,
1915
+ skillsRootAbs,
1916
+ hostId,
1917
+ rootBinding,
1918
+ }) {
1919
+ const ctx = {
1920
+ rootAbs,
1921
+ skillsRootAbs,
1922
+ rootBinding,
1923
+ uncoveredItems: [],
1924
+ directoryCount: 0,
1925
+ entryCount: 0,
1926
+ truncated: false,
1927
+ };
1928
+ const runner = RUNNERS[hostId];
1929
+ const result = await runner(ctx);
1930
+ if (ctx.truncated) {
1931
+ result.uncoveredItems.push(uncovered(
1932
+ "scan-limit",
1933
+ "native skill discovery hit the calibrated directory or entry capacity",
1934
+ ));
1935
+ }
1936
+ return result;
1937
+ }