@dsh-cc/skill-loader 0.5.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.
Files changed (49) hide show
  1. package/LICENSE +201 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +75 -0
  4. package/README.zh.md +75 -0
  5. package/lib/bundled/batch.d.ts +15 -0
  6. package/lib/bundled/batch.d.ts.map +1 -0
  7. package/lib/bundled/batch.js +97 -0
  8. package/lib/bundled/batch.js.map +1 -0
  9. package/lib/bundled/debug.d.ts +14 -0
  10. package/lib/bundled/debug.d.ts.map +1 -0
  11. package/lib/bundled/debug.js +51 -0
  12. package/lib/bundled/debug.js.map +1 -0
  13. package/lib/bundled/index.d.ts +36 -0
  14. package/lib/bundled/index.d.ts.map +1 -0
  15. package/lib/bundled/index.js +47 -0
  16. package/lib/bundled/index.js.map +1 -0
  17. package/lib/bundled/simplify.d.ts +12 -0
  18. package/lib/bundled/simplify.d.ts.map +1 -0
  19. package/lib/bundled/simplify.js +67 -0
  20. package/lib/bundled/simplify.js.map +1 -0
  21. package/lib/discovery.d.ts +70 -0
  22. package/lib/discovery.d.ts.map +1 -0
  23. package/lib/discovery.js +165 -0
  24. package/lib/discovery.js.map +1 -0
  25. package/lib/frontmatter.d.ts +72 -0
  26. package/lib/frontmatter.d.ts.map +1 -0
  27. package/lib/frontmatter.js +200 -0
  28. package/lib/frontmatter.js.map +1 -0
  29. package/lib/index.d.ts +100 -0
  30. package/lib/index.d.ts.map +1 -0
  31. package/lib/index.js +343 -0
  32. package/lib/index.js.map +1 -0
  33. package/lib/invariant.d.ts +16 -0
  34. package/lib/invariant.d.ts.map +1 -0
  35. package/lib/invariant.js +22 -0
  36. package/lib/invariant.js.map +1 -0
  37. package/lib/render.d.ts +111 -0
  38. package/lib/render.d.ts.map +1 -0
  39. package/lib/render.js +179 -0
  40. package/lib/render.js.map +1 -0
  41. package/lib/translate.d.ts +66 -0
  42. package/lib/translate.d.ts.map +1 -0
  43. package/lib/translate.js +134 -0
  44. package/lib/translate.js.map +1 -0
  45. package/lib/types.d.ts +46 -0
  46. package/lib/types.d.ts.map +1 -0
  47. package/lib/types.js +8 -0
  48. package/lib/types.js.map +1 -0
  49. package/package.json +59 -0
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Claude Code skill frontmatter parsing.
3
+ *
4
+ * This module owns the full Claude Code `SKILL.md` frontmatter spec: it reads
5
+ * every known field, tolerates unknown fields, and throws for known fields with
6
+ * invalid values. Parsing keeps frontmatter independent of body loading so
7
+ * discovery can estimate tokens and build summaries without reading the body.
8
+ *
9
+ * @module
10
+ */
11
+ import { parse as parseYaml } from 'yaml';
12
+ const TRUE_FORMS = /^(?:true|yes|on|1)$/i;
13
+ const FALSE_FORMS = /^(?:false|no|off|0)$/i;
14
+ function booleanValue(key, value) {
15
+ if (typeof value === 'boolean')
16
+ return value;
17
+ if (typeof value !== 'string') {
18
+ throw new TypeError(`frontmatter field "${key}" must be a boolean`);
19
+ }
20
+ if (TRUE_FORMS.test(value))
21
+ return true;
22
+ if (FALSE_FORMS.test(value))
23
+ return false;
24
+ throw new TypeError(`frontmatter field "${key}" must be a boolean`);
25
+ }
26
+ function stringValue(key, value) {
27
+ if (typeof value !== 'string') {
28
+ throw new TypeError(`frontmatter field "${key}" must be a string`);
29
+ }
30
+ return value;
31
+ }
32
+ function optionalString(key, value) {
33
+ if (value === undefined)
34
+ return undefined;
35
+ return stringValue(key, value);
36
+ }
37
+ function stringList(key, value) {
38
+ const names = stringValue(key, value).split(',').map(item => item.trim());
39
+ return names.filter((item, index) => item.length > 0 && names.indexOf(item) === index);
40
+ }
41
+ function parseNamedArguments(value) {
42
+ if (value === undefined)
43
+ return [];
44
+ const raw = Array.isArray(value) ? value : typeof value === 'string' ? value.split(/\s+/) : null;
45
+ if (raw === null) {
46
+ throw new TypeError('frontmatter field "arguments" must be a string or array of strings');
47
+ }
48
+ return raw
49
+ .filter((item) => typeof item === 'string')
50
+ .map(item => item.trim())
51
+ .filter(item => item.length > 0 && !/^\d+$/.test(item));
52
+ }
53
+ function optionalModel(value) {
54
+ if (value === undefined)
55
+ return undefined;
56
+ const model = stringValue('model', value);
57
+ return model === 'inherit' ? undefined : model;
58
+ }
59
+ function optionalEffort(value) {
60
+ if (value === undefined)
61
+ return undefined;
62
+ const effort = stringValue('effort', value);
63
+ const KNOWN = ['low', 'medium', 'high', 'ultrahigh'];
64
+ if (KNOWN.some(level => level === effort))
65
+ return effort;
66
+ if (/^\d+$/.test(effort))
67
+ return effort;
68
+ throw new TypeError('frontmatter field "effort" must be a known level or an integer');
69
+ }
70
+ function optionalShell(value) {
71
+ if (value === undefined)
72
+ return undefined;
73
+ return booleanValue('shell', value);
74
+ }
75
+ function parseContext(value) {
76
+ if (value === undefined)
77
+ return undefined;
78
+ const context = stringValue('context', value);
79
+ if (context === 'fork')
80
+ return 'fork';
81
+ throw new TypeError('frontmatter field "context" only supports "fork"');
82
+ }
83
+ function optionalPaths(value) {
84
+ if (value === undefined)
85
+ return undefined;
86
+ const raw = Array.isArray(value) ? value : [value];
87
+ const patterns = raw
88
+ .filter((item) => typeof item === 'string')
89
+ // `ignore` treats "path" as matching both the path and everything inside it,
90
+ // so a trailing `/**` is redundant and collapses to that directory.
91
+ .map(pattern => (pattern.endsWith('/**') ? pattern.slice(0, -3) : pattern))
92
+ .map(String)
93
+ .filter(pattern => pattern.length > 0);
94
+ if (patterns.length === 0 || patterns.every(pattern => pattern === '**'))
95
+ return undefined;
96
+ return [...new Set(patterns)];
97
+ }
98
+ const KNOWN_KEYS = new Set([
99
+ 'name',
100
+ 'description',
101
+ 'when_to_use',
102
+ 'allowed-tools',
103
+ 'argument-hint',
104
+ 'arguments',
105
+ 'version',
106
+ 'model',
107
+ 'user-invocable',
108
+ 'disable-model-invocation',
109
+ 'context',
110
+ 'agent',
111
+ 'effort',
112
+ 'shell',
113
+ 'hooks',
114
+ 'paths',
115
+ ]);
116
+ /**
117
+ * Parse the complete Claude Code frontmatter from a raw `SKILL.md` document.
118
+ * Unknown keys are preserved in `unknown`; a known key with an invalid value
119
+ * throws so a malformed skill fails loudly instead of silently mis-activating.
120
+ * @param raw - the full `SKILL.md` text.
121
+ * @returns the parsed fields, or `undefined` when the document has no frontmatter.
122
+ */
123
+ export function parseCcFrontmatter(raw) {
124
+ const document = parseCcFrontmatterDocument(raw);
125
+ if (document === undefined)
126
+ return undefined;
127
+ const { data } = document;
128
+ const unknown = {};
129
+ for (const key of Object.keys(data)) {
130
+ if (!KNOWN_KEYS.has(key))
131
+ unknown[key] = data[key];
132
+ }
133
+ const fields = {
134
+ name: optionalString('name', data.name),
135
+ description: stringValue('description', data.description),
136
+ whenToUse: optionalString('when_to_use', data.when_to_use),
137
+ allowedTools: stringList('allowed-tools', data['allowed-tools'] ?? ''),
138
+ argumentHint: optionalString('argument-hint', data['argument-hint']),
139
+ arguments: parseNamedArguments(data.arguments),
140
+ version: optionalString('version', data.version),
141
+ model: optionalModel(data.model),
142
+ userInvocable: data['user-invocable'] === undefined ? true : booleanValue('user-invocable', data['user-invocable']),
143
+ disableModelInvocation: data['disable-model-invocation'] === undefined
144
+ ? false
145
+ : booleanValue('disable-model-invocation', data['disable-model-invocation']),
146
+ executionContext: parseContext(data.context),
147
+ agent: optionalString('agent', data.agent),
148
+ effort: optionalEffort(data.effort),
149
+ shell: optionalShell(data.shell),
150
+ hooks: data.hooks,
151
+ paths: optionalPaths(data.paths),
152
+ unknown,
153
+ };
154
+ return fields;
155
+ }
156
+ /**
157
+ * Split a raw `SKILL.md` into its frontmatter YAML payload and Markdown body.
158
+ * Returns `undefined` when the document does not begin with a `---` fence.
159
+ * @param raw - the full `SKILL.md` text.
160
+ * @returns the parsed YAML object and trailing body, or `undefined`.
161
+ */
162
+ export function parseCcFrontmatterDocument(raw) {
163
+ const firstLineEnd = raw.indexOf('\n');
164
+ if (firstLineEnd < 0)
165
+ return undefined;
166
+ const firstLine = raw.slice(0, firstLineEnd).replace(/\r$/, '');
167
+ if (firstLine !== '---')
168
+ return undefined;
169
+ const start = firstLineEnd + 1;
170
+ const closingStart = findClosingFence(raw, start);
171
+ if (closingStart === undefined)
172
+ return undefined;
173
+ const yaml = raw.slice(start, closingStart);
174
+ const parsed = parseYaml(yaml);
175
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
176
+ return undefined;
177
+ return {
178
+ data: parsed,
179
+ body: raw.slice(closingFenceBodyStart(raw, closingStart)).replace(/^[ \t]*\r?\n/, '').replace(/\r\n$/, '\n'),
180
+ };
181
+ }
182
+ function findClosingFence(raw, start) {
183
+ let lineStart = start;
184
+ while (lineStart <= raw.length) {
185
+ const nextNewline = raw.indexOf('\n', lineStart);
186
+ const lineEnd = nextNewline < 0 ? raw.length : nextNewline;
187
+ const line = raw.slice(lineStart, lineEnd).replace(/\r$/, '');
188
+ if (line === '---')
189
+ return lineStart;
190
+ if (nextNewline < 0)
191
+ return undefined;
192
+ lineStart = nextNewline + 1;
193
+ }
194
+ return undefined;
195
+ }
196
+ function closingFenceBodyStart(raw, closingStart) {
197
+ const nextNewline = raw.indexOf('\n', closingStart);
198
+ return nextNewline < 0 ? raw.length : nextNewline + 1;
199
+ }
200
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAA;AAmDzC,MAAM,UAAU,GAAG,sBAAsB,CAAA;AACzC,MAAM,WAAW,GAAG,uBAAuB,CAAA;AAE3C,SAAS,YAAY,CAAC,GAAW,EAAE,KAAc;IAC/C,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,sBAAsB,GAAG,qBAAqB,CAAC,CAAA;IACrE,CAAC;IACD,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACvC,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACzC,MAAM,IAAI,SAAS,CAAC,sBAAsB,GAAG,qBAAqB,CAAC,CAAA;AACrE,CAAC;AAED,SAAS,WAAW,CAAC,GAAW,EAAE,KAAc;IAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,sBAAsB,GAAG,oBAAoB,CAAC,CAAA;IACpE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAS,cAAc,CAAC,GAAW,EAAE,KAAc;IACjD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,OAAO,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;AAChC,CAAC;AAED,SAAS,UAAU,CAAC,GAAW,EAAE,KAAc;IAC7C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACzE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAA;AACxF,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAClC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAChG,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,SAAS,CAAC,oEAAoE,CAAC,CAAA;IAC3F,CAAC;IACD,OAAO,GAAG;SACP,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;SAC1D,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SACxB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACzC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAA;AAChD,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAU,CAAA;IAC7D,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IACxD,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAA;IACvC,MAAM,IAAI,SAAS,CAAC,gEAAgE,CAAC,CAAA;AACvF,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,OAAO,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;AACrC,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAC7C,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,MAAM,CAAA;IACrC,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAA;AACzE,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAClD,MAAM,QAAQ,GAAG,GAAG;SACjB,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC;QAC3D,6EAA6E;QAC7E,oEAAoE;SACnE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAC1E,GAAG,CAAC,MAAM,CAAC;SACX,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACxC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IAC1F,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,MAAM;IACN,aAAa;IACb,aAAa;IACb,eAAe;IACf,eAAe;IACf,WAAW;IACX,SAAS;IACT,OAAO;IACP,gBAAgB;IAChB,0BAA0B;IAC1B,SAAS;IACT,OAAO;IACP,QAAQ;IACR,OAAO;IACP,OAAO;IACP,OAAO;CACR,CAAC,CAAA;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,MAAM,QAAQ,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAA;IAChD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAA;IACzB,MAAM,OAAO,GAA4B,EAAE,CAAA;IAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;IACpD,CAAC;IACD,MAAM,MAAM,GAAG;QACb,IAAI,EAAE,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;QACvC,WAAW,EAAE,WAAW,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC;QACzD,SAAS,EAAE,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC,WAAW,CAAC;QAC1D,YAAY,EAAE,UAAU,CAAC,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QACtE,YAAY,EAAE,cAAc,CAAC,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACpE,SAAS,EAAE,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;QAC9C,OAAO,EAAE,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC;QAChD,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,aAAa,EAAE,IAAI,CAAC,gBAAgB,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACnH,sBAAsB,EAAE,IAAI,CAAC,0BAA0B,CAAC,KAAK,SAAS;YACpE,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,YAAY,CAAC,0BAA0B,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC9E,gBAAgB,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC;QAC5C,KAAK,EAAE,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC;QAC1C,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;QACnC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,OAAO;KACR,CAAA;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACpD,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,IAAI,YAAY,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IACtC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC/D,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,SAAS,CAAA;IACzC,MAAM,KAAK,GAAG,YAAY,GAAG,CAAC,CAAA;IAC9B,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IACjD,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAChD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;IAC3C,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAY,CAAA;IACzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5F,OAAO;QACL,IAAI,EAAE,MAAiC;QACvC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;KAC7G,CAAA;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW,EAAE,KAAa;IAClD,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,OAAO,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAChD,MAAM,OAAO,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAA;QAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAC7D,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,SAAS,CAAA;QACpC,IAAI,WAAW,GAAG,CAAC;YAAE,OAAO,SAAS,CAAA;QACrC,SAAS,GAAG,WAAW,GAAG,CAAC,CAAA;IAC7B,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,qBAAqB,CAAC,GAAW,EAAE,YAAoB;IAC9D,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IACnD,OAAO,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAA;AACvD,CAAC"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Claude Code skill-format compatible provider for the `ctx.skills` registry.
3
+ *
4
+ * This package discovers `SKILL.md` skills in Claude Code's directory layout
5
+ * (managed, project, user, and additional roots), parses the full Claude Code
6
+ * frontmatter spec, serves a portable subset of Claude Code's bundled skills,
7
+ * and hands everything to `@deepseek-ai/dsh-skill`. It is a compatibility
8
+ * provider: the harness can consume skills written for Claude Code without
9
+ * copying the runtime that executes them.
10
+ *
11
+ * @module @dsh-cc/skill-loader
12
+ */
13
+ import type { Context } from '@deepseek-ai/cordis';
14
+ import type Schema from '@deepseek-ai/schemastery';
15
+ import { type SkillCandidate, type SkillDefinition, type SkillInvocationPolicy, type SkillLookupOptions, type SkillProvider, type SkillProviderControl } from '@deepseek-ai/dsh-skill';
16
+ import { type ParsedCcFrontmatter } from './frontmatter.ts';
17
+ import type { CcInvocationPolicy } from './types.ts';
18
+ export type { CcSkillMetadata, CcInvocationPolicy } from './types.ts';
19
+ export { parseCcFrontmatter, parseCcFrontmatterDocument, type ParsedCcFrontmatter } from './frontmatter.ts';
20
+ export { estimateFrontmatterTokens, renderSkillBody, substituteArguments, extractInlineShell } from './render.ts';
21
+ export { ccRestriction, ccPathMatcher, registerPathActivator } from './translate.ts';
22
+ export { discoverCcRoots, discoverCcSkills, type CcSkillFile, type CcRoot, type CcSkillSource } from './discovery.ts';
23
+ export { discoverBundledSkills, type BundledSkillFile } from './bundled/index.ts';
24
+ export declare const name = "skill-claude-code";
25
+ export declare const inject: string[];
26
+ /**
27
+ * Claude Code skill provider configuration.
28
+ */
29
+ export interface Config {
30
+ /** Unique provider name. Defaults to `claude-code`. */
31
+ providerName?: string;
32
+ /** Harness home that owns the user skill root. */
33
+ dshHome?: string;
34
+ /** Optional managed policy skill root, scanned before all defaults. */
35
+ managedDir?: string;
36
+ /** Additional skill roots appended after project and user roots. */
37
+ additionalDirs?: string[];
38
+ }
39
+ export declare const Config: Schema<Config>;
40
+ /**
41
+ * Register the Claude Code skill provider on `ctx.skills`.
42
+ * @param ctx - active context carrying the skill registry.
43
+ * @param config - provider configuration.
44
+ */
45
+ export declare function apply(ctx: Context, config?: Config): void;
46
+ /** Provider that maps Claude Code skill directories into `ctx.skills`. */
47
+ export declare class ClaudeCodeSkillProvider implements SkillProvider {
48
+ private readonly ctx;
49
+ readonly name: string;
50
+ private readonly dshHome;
51
+ private readonly managedDir;
52
+ private readonly additionalDirs;
53
+ private readonly control;
54
+ /** Installed bundled skills for `source: 'bundled'`, parsed once. */
55
+ private readonly bundled;
56
+ /**
57
+ * Conditional (paths-gated) skills discovered so far, keyed by project root
58
+ * then skill name -> project-relative path matcher.
59
+ */
60
+ private readonly conditional;
61
+ /** Set of skill names already activated per project root (idempotence guard). */
62
+ private readonly activated;
63
+ private disposeFsObserver;
64
+ constructor(ctx: Context, control: SkillProviderControl, config?: Config);
65
+ /**
66
+ * Discover Claude Code skills for a cwd-sensitive workspace plus the bundled
67
+ * subset. Paths-gated skills are excluded until their paths are touched.
68
+ * @param options - lookup options; `cwd` selects the project root to scan.
69
+ * @returns provider candidates, one per discovered `SKILL.md`.
70
+ */
71
+ list(options: SkillLookupOptions): Promise<SkillCandidate[]>;
72
+ /**
73
+ * Load a complete Claude Code skill body from a candidate's file.
74
+ * @param candidate - the winning candidate returned by this provider.
75
+ * @returns the full skill definition, or `undefined` if the file disappeared.
76
+ */
77
+ get(candidate: SkillCandidate, _options: SkillLookupOptions): Promise<SkillDefinition | undefined>;
78
+ private parseFile;
79
+ private toCandidate;
80
+ private toBundledCandidate;
81
+ private toDefinition;
82
+ private toBundledDefinition;
83
+ private toMetadata;
84
+ private sourceOf;
85
+ private rememberConditional;
86
+ private isActivated;
87
+ private activate;
88
+ /**
89
+ * Register the `fs/observed` listener that activates path-conditional skills
90
+ * when a Read/Write/Edit tool touches a matching project-relative path. On a
91
+ * first activation it invalidates the registry so consumers refetch the (now
92
+ * larger) catalog. This mirrors the `registerPathActivator` contract, but the
93
+ * projects/skills are discovered dynamically per cwd, so the provider owns the
94
+ * listener over its live conditional catalog.
95
+ */
96
+ private wireConditionalActivation;
97
+ }
98
+ /** Resolve the registry invocation policy from Claude Code bool frontmatter. */
99
+ export declare function ccInvocation(parsed: Pick<ParsedCcFrontmatter, 'userInvocable' | 'disableModelInvocation'>): CcInvocationPolicy & SkillInvocationPolicy;
100
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/index.js ADDED
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Claude Code skill-format compatible provider for the `ctx.skills` registry.
3
+ *
4
+ * This package discovers `SKILL.md` skills in Claude Code's directory layout
5
+ * (managed, project, user, and additional roots), parses the full Claude Code
6
+ * frontmatter spec, serves a portable subset of Claude Code's bundled skills,
7
+ * and hands everything to `@deepseek-ai/dsh-skill`. It is a compatibility
8
+ * provider: the harness can consume skills written for Claude Code without
9
+ * copying the runtime that executes them.
10
+ *
11
+ * @module @dsh-cc/skill-loader
12
+ */
13
+ import { readFile } from 'node:fs/promises';
14
+ import { relative, resolve } from 'node:path';
15
+ import z from '@deepseek-ai/schemastery';
16
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
17
+ import { BUNDLED_SKILL_RANK, isSkillName, } from '@deepseek-ai/dsh-skill';
18
+ import { discoverBundledSkills } from "./bundled/index.js";
19
+ import { discoverCcSkills, findProjectRoot } from "./discovery.js";
20
+ import { parseCcFrontmatter, parseCcFrontmatterDocument, } from "./frontmatter.js";
21
+ import { ccPathMatcher } from "./translate.js";
22
+ export { parseCcFrontmatter, parseCcFrontmatterDocument } from "./frontmatter.js";
23
+ export { estimateFrontmatterTokens, renderSkillBody, substituteArguments, extractInlineShell } from "./render.js";
24
+ export { ccRestriction, ccPathMatcher, registerPathActivator } from "./translate.js";
25
+ export { discoverCcRoots, discoverCcSkills } from "./discovery.js";
26
+ export { discoverBundledSkills } from "./bundled/index.js";
27
+ export const name = 'skill-claude-code';
28
+ export const inject = ['skills'];
29
+ const DEFAULT_PROVIDER_NAME = 'claude-code';
30
+ const MANAGED_SOURCE = 'managed';
31
+ const PROJECT_SOURCE = 'project-dsh';
32
+ const USER_SOURCE = 'user-dsh';
33
+ const ADDITIONAL_SOURCE = 'custom';
34
+ /** Mutating first-party fs tools that trigger conditional activation. */
35
+ const TOUCH_TOOLS = new Set(['read', 'write', 'edit']);
36
+ export const Config = z.object({
37
+ providerName: z.string().min(1).default(DEFAULT_PROVIDER_NAME),
38
+ dshHome: z.string(),
39
+ managedDir: z.string(),
40
+ additionalDirs: z.array(z.string()).default([]),
41
+ });
42
+ /**
43
+ * Register the Claude Code skill provider on `ctx.skills`.
44
+ * @param ctx - active context carrying the skill registry.
45
+ * @param config - provider configuration.
46
+ */
47
+ export function apply(ctx, config = {}) {
48
+ ctx.skills.registerProvider((control) => {
49
+ return new ClaudeCodeSkillProvider(ctx, control, config);
50
+ });
51
+ }
52
+ /** Provider that maps Claude Code skill directories into `ctx.skills`. */
53
+ export class ClaudeCodeSkillProvider {
54
+ ctx;
55
+ name;
56
+ dshHome;
57
+ managedDir;
58
+ additionalDirs;
59
+ control;
60
+ /** Installed bundled skills for `source: 'bundled'`, parsed once. */
61
+ bundled;
62
+ /**
63
+ * Conditional (paths-gated) skills discovered so far, keyed by project root
64
+ * then skill name -> project-relative path matcher.
65
+ */
66
+ conditional = new Map();
67
+ /** Set of skill names already activated per project root (idempotence guard). */
68
+ activated = new Map();
69
+ disposeFsObserver;
70
+ constructor(ctx, control, config = {}) {
71
+ this.ctx = ctx;
72
+ this.name = config.providerName ?? DEFAULT_PROVIDER_NAME;
73
+ this.dshHome = resolveDshHome(config.dshHome);
74
+ this.managedDir = config.managedDir === undefined ? undefined : resolve(config.managedDir);
75
+ this.additionalDirs = (config.additionalDirs ?? []).map(dir => resolve(dir));
76
+ this.control = control;
77
+ this.bundled = discoverBundledSkills();
78
+ this.wireConditionalActivation();
79
+ control.signal.addEventListener('abort', () => this.disposeFsObserver?.(), { once: true });
80
+ }
81
+ /**
82
+ * Discover Claude Code skills for a cwd-sensitive workspace plus the bundled
83
+ * subset. Paths-gated skills are excluded until their paths are touched.
84
+ * @param options - lookup options; `cwd` selects the project root to scan.
85
+ * @returns provider candidates, one per discovered `SKILL.md`.
86
+ */
87
+ async list(options) {
88
+ const files = await discoverCcSkills({
89
+ dshHome: this.dshHome,
90
+ managedDir: this.managedDir,
91
+ projectCwd: options.cwd,
92
+ additionalDirs: this.additionalDirs,
93
+ });
94
+ const root = options.cwd === undefined ? undefined : await findProjectRoot(resolve(options.cwd));
95
+ const candidates = [];
96
+ for (const file of files) {
97
+ const parsed = await this.parseFile(file);
98
+ if (parsed === undefined)
99
+ continue;
100
+ if (parsed.paths !== undefined) {
101
+ // Conditional skill: gate on project-relative path activation.
102
+ if (root === undefined)
103
+ continue;
104
+ this.rememberConditional(root, parsed);
105
+ if (!this.isActivated(root, parsed.name))
106
+ continue;
107
+ }
108
+ candidates.push(this.toCandidate(file, parsed));
109
+ }
110
+ for (const bundled of this.bundled) {
111
+ // Bundled skills are never paths-gated; defend anyway.
112
+ if (bundled.parsed.paths !== undefined)
113
+ continue;
114
+ candidates.push(this.toBundledCandidate(bundled));
115
+ }
116
+ return candidates;
117
+ }
118
+ /**
119
+ * Load a complete Claude Code skill body from a candidate's file.
120
+ * @param candidate - the winning candidate returned by this provider.
121
+ * @returns the full skill definition, or `undefined` if the file disappeared.
122
+ */
123
+ async get(candidate, _options) {
124
+ if (isBundledLocator(candidate.locator)) {
125
+ return this.toBundledDefinition(candidate.locator);
126
+ }
127
+ const file = candidate.locator;
128
+ const loaded = await loadCcSkillFile(file.path);
129
+ if (loaded === undefined)
130
+ return undefined;
131
+ const parsed = loaded.parsed;
132
+ if (!validCcName(parsed))
133
+ return undefined;
134
+ return this.toDefinition(candidate, file, parsed, loaded.body);
135
+ }
136
+ async parseFile(file) {
137
+ const loaded = await loadCcSkillFile(file.path);
138
+ if (loaded === undefined)
139
+ return undefined;
140
+ const parsed = loaded.parsed;
141
+ if (!validCcName(parsed)) {
142
+ this.ctx.logger.warn(`Claude Code skill ${file.path} ignored: invalid or missing name`);
143
+ return undefined;
144
+ }
145
+ return parsed;
146
+ }
147
+ toCandidate(file, parsed) {
148
+ const source = this.sourceOf(file.source);
149
+ const metadata = metadataRecord(this.toMetadata(file, parsed));
150
+ return {
151
+ name: parsed.name,
152
+ description: parsed.description,
153
+ ...parsed.whenToUse !== undefined ? { whenToUse: parsed.whenToUse } : {},
154
+ invocation: ccInvocation(parsed),
155
+ source,
156
+ provider: this.name,
157
+ rank: file.rank,
158
+ locator: file,
159
+ resourceBase: { kind: 'directory', path: file.directory },
160
+ path: file.path,
161
+ metadata,
162
+ };
163
+ }
164
+ toBundledCandidate(file) {
165
+ const parsed = file.parsed;
166
+ const metadata = metadataRecord(this.toMetadata(file, parsed));
167
+ return {
168
+ name: parsed.name,
169
+ description: parsed.description,
170
+ ...parsed.whenToUse !== undefined ? { whenToUse: parsed.whenToUse } : {},
171
+ invocation: ccInvocation(parsed),
172
+ source: 'bundled',
173
+ provider: this.name,
174
+ rank: BUNDLED_SKILL_RANK,
175
+ locator: file,
176
+ resourceBase: { kind: 'directory', path: file.directory },
177
+ metadata,
178
+ };
179
+ }
180
+ toDefinition(candidate, file, parsed, body) {
181
+ return {
182
+ name: parsed.name,
183
+ description: parsed.description,
184
+ ...parsed.whenToUse !== undefined ? { whenToUse: parsed.whenToUse } : {},
185
+ invocation: ccInvocation(parsed),
186
+ source: candidate.source,
187
+ provider: this.name,
188
+ resourceBase: { kind: 'directory', path: file.directory },
189
+ path: file.path,
190
+ metadata: metadataRecord(this.toMetadata(file, parsed)),
191
+ content: body,
192
+ };
193
+ }
194
+ toBundledDefinition(file) {
195
+ return {
196
+ name: file.parsed.name,
197
+ description: file.parsed.description,
198
+ ...file.parsed.whenToUse !== undefined ? { whenToUse: file.parsed.whenToUse } : {},
199
+ invocation: ccInvocation(file.parsed),
200
+ source: 'bundled',
201
+ provider: this.name,
202
+ resourceBase: { kind: 'directory', path: file.directory },
203
+ path: file.path,
204
+ metadata: metadataRecord(this.toMetadata(file, file.parsed)),
205
+ content: file.body,
206
+ };
207
+ }
208
+ toMetadata(file, parsed) {
209
+ return {
210
+ allowedTools: parsed.allowedTools,
211
+ arguments: parsed.arguments,
212
+ deprecated: 'deprecated' in file ? file.deprecated : false,
213
+ source: 'source' in file ? file.source : 'bundled',
214
+ unknown: parsed.unknown,
215
+ ...parsed.argumentHint !== undefined ? { argumentHint: parsed.argumentHint } : {},
216
+ ...parsed.version !== undefined ? { version: parsed.version } : {},
217
+ ...parsed.model !== undefined ? { model: parsed.model } : {},
218
+ ...parsed.executionContext !== undefined ? { executionContext: parsed.executionContext } : {},
219
+ ...parsed.agent !== undefined ? { agent: parsed.agent } : {},
220
+ ...parsed.effort !== undefined ? { effort: parsed.effort } : {},
221
+ ...parsed.shell !== undefined ? { shell: parsed.shell } : {},
222
+ ...parsed.hooks !== undefined ? { hooks: parsed.hooks } : {},
223
+ ...parsed.paths !== undefined ? { paths: parsed.paths } : {},
224
+ };
225
+ }
226
+ sourceOf(source) {
227
+ switch (source) {
228
+ case 'managed':
229
+ return MANAGED_SOURCE;
230
+ case 'user':
231
+ return USER_SOURCE;
232
+ case 'project':
233
+ return PROJECT_SOURCE;
234
+ case 'additional':
235
+ return ADDITIONAL_SOURCE;
236
+ }
237
+ }
238
+ rememberConditional(root, parsed) {
239
+ if (parsed.paths === undefined || parsed.name === undefined)
240
+ return;
241
+ const normalizedRoot = normalizeRoot(root);
242
+ let skills = this.conditional.get(normalizedRoot);
243
+ if (skills === undefined) {
244
+ skills = new Map();
245
+ this.conditional.set(normalizedRoot, skills);
246
+ }
247
+ if (!skills.has(parsed.name))
248
+ skills.set(parsed.name, ccPathMatcher(parsed.paths));
249
+ }
250
+ isActivated(root, name) {
251
+ return this.activated.get(normalizeRoot(root))?.has(name) ?? false;
252
+ }
253
+ activate(root, name) {
254
+ let set = this.activated.get(normalizeRoot(root));
255
+ if (set === undefined) {
256
+ set = new Set();
257
+ this.activated.set(normalizeRoot(root), set);
258
+ }
259
+ if (set.has(name))
260
+ return false; // idempotent: already active
261
+ set.add(name);
262
+ return true;
263
+ }
264
+ /**
265
+ * Register the `fs/observed` listener that activates path-conditional skills
266
+ * when a Read/Write/Edit tool touches a matching project-relative path. On a
267
+ * first activation it invalidates the registry so consumers refetch the (now
268
+ * larger) catalog. This mirrors the `registerPathActivator` contract, but the
269
+ * projects/skills are discovered dynamically per cwd, so the provider owns the
270
+ * listener over its live conditional catalog.
271
+ */
272
+ wireConditionalActivation() {
273
+ const dispose = this.ctx.on('fs/observed', (target, _observation, actor) => {
274
+ if (!touchActor(actor))
275
+ return;
276
+ const path = target.displayPath.replaceAll('\\', '/');
277
+ for (const [root, skills] of this.conditional) {
278
+ if (!inProject(path, root))
279
+ continue;
280
+ const rel = relative(root, target.displayPath).replaceAll('\\', '/');
281
+ for (const [name, matcher] of skills) {
282
+ if (this.isActivated(root, name))
283
+ continue;
284
+ if (!matcher(rel))
285
+ continue;
286
+ if (this.activate(root, name))
287
+ this.control.invalidate();
288
+ }
289
+ }
290
+ });
291
+ this.disposeFsObserver = dispose;
292
+ }
293
+ }
294
+ /** Whether `path` is under `root` (identity or root/...), both forward-slash normalized. */
295
+ function inProject(path, root) {
296
+ return path === root || path.startsWith(`${root}/`);
297
+ }
298
+ function normalizeRoot(root) {
299
+ return root.replaceAll('\\', '/').replace(/\/+$/, '');
300
+ }
301
+ function touchActor(actor) {
302
+ if (actor === undefined || !('name' in actor))
303
+ return false;
304
+ const value = actor.name;
305
+ return typeof value === 'string' && TOUCH_TOOLS.has(value);
306
+ }
307
+ function isBundledLocator(locator) {
308
+ if (typeof locator !== 'object' || locator === null || Array.isArray(locator))
309
+ return false;
310
+ return locator.kind === 'bundled';
311
+ }
312
+ /** Resolve the registry invocation policy from Claude Code bool frontmatter. */
313
+ export function ccInvocation(parsed) {
314
+ return {
315
+ modelInvocable: !parsed.disableModelInvocation,
316
+ userInvocable: parsed.userInvocable,
317
+ };
318
+ }
319
+ /** Whether a parsed skill carries a valid registry name. */
320
+ function validCcName(parsed) {
321
+ return parsed.name !== undefined && isSkillName(parsed.name);
322
+ }
323
+ /** Widen a typed CC metadata object to the registry's string-keyed metadata map. */
324
+ function metadataRecord(metadata) {
325
+ return metadata;
326
+ }
327
+ async function loadCcSkillFile(path) {
328
+ let raw;
329
+ try {
330
+ raw = await readFile(path, 'utf8');
331
+ }
332
+ catch {
333
+ return undefined;
334
+ }
335
+ const document = parseCcFrontmatterDocument(raw);
336
+ if (document === undefined)
337
+ return undefined;
338
+ const parsed = parseCcFrontmatter(raw);
339
+ if (parsed === undefined)
340
+ return undefined;
341
+ return { parsed, body: document.body };
342
+ }
343
+ //# sourceMappingURL=index.js.map