@senior-skills/core 1.0.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 (177) hide show
  1. package/dist/application/agent-selection.d.ts +42 -0
  2. package/dist/application/agent-selection.d.ts.map +1 -0
  3. package/dist/application/agent-selection.js +143 -0
  4. package/dist/application/agent-selection.js.map +1 -0
  5. package/dist/application/apply-skill-updates.d.ts +10 -0
  6. package/dist/application/apply-skill-updates.d.ts.map +1 -0
  7. package/dist/application/apply-skill-updates.js +75 -0
  8. package/dist/application/apply-skill-updates.js.map +1 -0
  9. package/dist/application/check-updates.d.ts +40 -0
  10. package/dist/application/check-updates.d.ts.map +1 -0
  11. package/dist/application/check-updates.js +130 -0
  12. package/dist/application/check-updates.js.map +1 -0
  13. package/dist/application/context.d.ts +28 -0
  14. package/dist/application/context.d.ts.map +1 -0
  15. package/dist/application/context.js +2 -0
  16. package/dist/application/context.js.map +1 -0
  17. package/dist/application/create-skill.d.ts +33 -0
  18. package/dist/application/create-skill.d.ts.map +1 -0
  19. package/dist/application/create-skill.js +58 -0
  20. package/dist/application/create-skill.js.map +1 -0
  21. package/dist/application/diagnose.d.ts +44 -0
  22. package/dist/application/diagnose.d.ts.map +1 -0
  23. package/dist/application/diagnose.js +228 -0
  24. package/dist/application/diagnose.js.map +1 -0
  25. package/dist/application/install-skills.d.ts +59 -0
  26. package/dist/application/install-skills.d.ts.map +1 -0
  27. package/dist/application/install-skills.js +191 -0
  28. package/dist/application/install-skills.js.map +1 -0
  29. package/dist/application/list-installed.d.ts +39 -0
  30. package/dist/application/list-installed.d.ts.map +1 -0
  31. package/dist/application/list-installed.js +68 -0
  32. package/dist/application/list-installed.js.map +1 -0
  33. package/dist/application/package-loader.d.ts +42 -0
  34. package/dist/application/package-loader.d.ts.map +1 -0
  35. package/dist/application/package-loader.js +320 -0
  36. package/dist/application/package-loader.js.map +1 -0
  37. package/dist/application/publish-skill.d.ts +41 -0
  38. package/dist/application/publish-skill.d.ts.map +1 -0
  39. package/dist/application/publish-skill.js +112 -0
  40. package/dist/application/publish-skill.js.map +1 -0
  41. package/dist/application/remove-skills.d.ts +34 -0
  42. package/dist/application/remove-skills.d.ts.map +1 -0
  43. package/dist/application/remove-skills.js +95 -0
  44. package/dist/application/remove-skills.js.map +1 -0
  45. package/dist/application/resolution-source.d.ts +25 -0
  46. package/dist/application/resolution-source.d.ts.map +1 -0
  47. package/dist/application/resolution-source.js +60 -0
  48. package/dist/application/resolution-source.js.map +1 -0
  49. package/dist/application/resolve-suggestions.d.ts +17 -0
  50. package/dist/application/resolve-suggestions.d.ts.map +1 -0
  51. package/dist/application/resolve-suggestions.js +77 -0
  52. package/dist/application/resolve-suggestions.js.map +1 -0
  53. package/dist/application/search-skills.d.ts +36 -0
  54. package/dist/application/search-skills.d.ts.map +1 -0
  55. package/dist/application/search-skills.js +78 -0
  56. package/dist/application/search-skills.js.map +1 -0
  57. package/dist/application/update-skills.d.ts +55 -0
  58. package/dist/application/update-skills.d.ts.map +1 -0
  59. package/dist/application/update-skills.js +115 -0
  60. package/dist/application/update-skills.js.map +1 -0
  61. package/dist/application/validate-package.d.ts +27 -0
  62. package/dist/application/validate-package.d.ts.map +1 -0
  63. package/dist/application/validate-package.js +165 -0
  64. package/dist/application/validate-package.js.map +1 -0
  65. package/dist/application/workspace.d.ts +22 -0
  66. package/dist/application/workspace.d.ts.map +1 -0
  67. package/dist/application/workspace.js +66 -0
  68. package/dist/application/workspace.js.map +1 -0
  69. package/dist/domain/agent.d.ts +89 -0
  70. package/dist/domain/agent.d.ts.map +1 -0
  71. package/dist/domain/agent.js +20 -0
  72. package/dist/domain/agent.js.map +1 -0
  73. package/dist/domain/errors.d.ts +91 -0
  74. package/dist/domain/errors.d.ts.map +1 -0
  75. package/dist/domain/errors.js +121 -0
  76. package/dist/domain/errors.js.map +1 -0
  77. package/dist/domain/integrity.d.ts +19 -0
  78. package/dist/domain/integrity.d.ts.map +1 -0
  79. package/dist/domain/integrity.js +0 -0
  80. package/dist/domain/integrity.js.map +1 -0
  81. package/dist/domain/js-literal.d.ts +22 -0
  82. package/dist/domain/js-literal.d.ts.map +1 -0
  83. package/dist/domain/js-literal.js +221 -0
  84. package/dist/domain/js-literal.js.map +1 -0
  85. package/dist/domain/lockfile.d.ts +36 -0
  86. package/dist/domain/lockfile.d.ts.map +1 -0
  87. package/dist/domain/lockfile.js +148 -0
  88. package/dist/domain/lockfile.js.map +1 -0
  89. package/dist/domain/manifest.d.ts +96 -0
  90. package/dist/domain/manifest.d.ts.map +1 -0
  91. package/dist/domain/manifest.js +448 -0
  92. package/dist/domain/manifest.js.map +1 -0
  93. package/dist/domain/path-safety.d.ts +19 -0
  94. package/dist/domain/path-safety.d.ts.map +1 -0
  95. package/dist/domain/path-safety.js +125 -0
  96. package/dist/domain/path-safety.js.map +1 -0
  97. package/dist/domain/posix-path.d.ts +28 -0
  98. package/dist/domain/posix-path.d.ts.map +1 -0
  99. package/dist/domain/posix-path.js +84 -0
  100. package/dist/domain/posix-path.js.map +1 -0
  101. package/dist/domain/receipt.d.ts +41 -0
  102. package/dist/domain/receipt.d.ts.map +1 -0
  103. package/dist/domain/receipt.js +93 -0
  104. package/dist/domain/receipt.js.map +1 -0
  105. package/dist/domain/registry-index.d.ts +41 -0
  106. package/dist/domain/registry-index.d.ts.map +1 -0
  107. package/dist/domain/registry-index.js +150 -0
  108. package/dist/domain/registry-index.js.map +1 -0
  109. package/dist/domain/resolver.d.ts +54 -0
  110. package/dist/domain/resolver.d.ts.map +1 -0
  111. package/dist/domain/resolver.js +238 -0
  112. package/dist/domain/resolver.js.map +1 -0
  113. package/dist/domain/skill-document.d.ts +16 -0
  114. package/dist/domain/skill-document.d.ts.map +1 -0
  115. package/dist/domain/skill-document.js +60 -0
  116. package/dist/domain/skill-document.js.map +1 -0
  117. package/dist/domain/skill-package.d.ts +43 -0
  118. package/dist/domain/skill-package.d.ts.map +1 -0
  119. package/dist/domain/skill-package.js +63 -0
  120. package/dist/domain/skill-package.js.map +1 -0
  121. package/dist/domain/skill-ref.d.ts +30 -0
  122. package/dist/domain/skill-ref.d.ts.map +1 -0
  123. package/dist/domain/skill-ref.js +119 -0
  124. package/dist/domain/skill-ref.js.map +1 -0
  125. package/dist/domain/validation.d.ts +30 -0
  126. package/dist/domain/validation.d.ts.map +1 -0
  127. package/dist/domain/validation.js +32 -0
  128. package/dist/domain/validation.js.map +1 -0
  129. package/dist/domain/version.d.ts +23 -0
  130. package/dist/domain/version.d.ts.map +1 -0
  131. package/dist/domain/version.js +81 -0
  132. package/dist/domain/version.js.map +1 -0
  133. package/dist/domain/workflow-document.d.ts +23 -0
  134. package/dist/domain/workflow-document.d.ts.map +1 -0
  135. package/dist/domain/workflow-document.js +56 -0
  136. package/dist/domain/workflow-document.js.map +1 -0
  137. package/dist/index.d.ts +47 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +45 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/ports/agent-adapter.d.ts +65 -0
  142. package/dist/ports/agent-adapter.d.ts.map +1 -0
  143. package/dist/ports/agent-adapter.js +28 -0
  144. package/dist/ports/agent-adapter.js.map +1 -0
  145. package/dist/ports/config-store.d.ts +27 -0
  146. package/dist/ports/config-store.d.ts.map +1 -0
  147. package/dist/ports/config-store.js +2 -0
  148. package/dist/ports/config-store.js.map +1 -0
  149. package/dist/ports/infrastructure.d.ts +123 -0
  150. package/dist/ports/infrastructure.d.ts.map +1 -0
  151. package/dist/ports/infrastructure.js +9 -0
  152. package/dist/ports/infrastructure.js.map +1 -0
  153. package/dist/ports/installation.d.ts +86 -0
  154. package/dist/ports/installation.d.ts.map +1 -0
  155. package/dist/ports/installation.js +2 -0
  156. package/dist/ports/installation.js.map +1 -0
  157. package/dist/ports/skill-registry.d.ts +74 -0
  158. package/dist/ports/skill-registry.d.ts.map +1 -0
  159. package/dist/ports/skill-registry.js +2 -0
  160. package/dist/ports/skill-registry.js.map +1 -0
  161. package/dist/ports/updates.d.ts +31 -0
  162. package/dist/ports/updates.d.ts.map +1 -0
  163. package/dist/ports/updates.js +4 -0
  164. package/dist/ports/updates.js.map +1 -0
  165. package/dist/testing/doubles.d.ts +127 -0
  166. package/dist/testing/doubles.d.ts.map +1 -0
  167. package/dist/testing/doubles.js +299 -0
  168. package/dist/testing/doubles.js.map +1 -0
  169. package/dist/testing/in-memory-file-system.d.ts +61 -0
  170. package/dist/testing/in-memory-file-system.d.ts.map +1 -0
  171. package/dist/testing/in-memory-file-system.js +297 -0
  172. package/dist/testing/in-memory-file-system.js.map +1 -0
  173. package/dist/testing/index.d.ts +10 -0
  174. package/dist/testing/index.d.ts.map +1 -0
  175. package/dist/testing/index.js +10 -0
  176. package/dist/testing/index.js.map +1 -0
  177. package/package.json +37 -0
@@ -0,0 +1,448 @@
1
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
2
+ import { AgentSkillsError, ErrorCode } from "./errors.js";
3
+ import { validateSkillName } from "./skill-ref.js";
4
+ import { parseRange, parseVersion } from "./version.js";
5
+ import { escapesRoot, normalize } from "./posix-path.js";
6
+ import { IssueCollector } from "./validation.js";
7
+ /**
8
+ * The package format version. Bumped only for genuinely breaking changes; additive fields
9
+ * ship as optional under the current number.
10
+ */
11
+ export const CURRENT_SCHEMA_VERSION = 1;
12
+ export const MANIFEST_FILENAME = 'skill.yaml';
13
+ export const SKILL_ENTRYPOINT = 'SKILL.md';
14
+ export const COMMAND_ENTRYPOINT = 'COMMAND.md';
15
+ export const WORKFLOW_ENTRYPOINT = 'WORKFLOW.js';
16
+ /**
17
+ * What the package *is*, which decides where an agent stores it and how it is invoked: a
18
+ * skill is model-selected, a command is user-invoked, a workflow is a script the agent runs
19
+ * by name. Everything else about the format — manifest, integrity, dependencies, receipts —
20
+ * is identical across kinds.
21
+ */
22
+ export const PACKAGE_KINDS = ['skill', 'command', 'workflow'];
23
+ /** Packages published before `kind` existed are skills. */
24
+ export const DEFAULT_PACKAGE_KIND = 'skill';
25
+ /** The agent-facing entrypoint document for a kind. */
26
+ export function entrypointFor(kind) {
27
+ if (kind === 'command')
28
+ return COMMAND_ENTRYPOINT;
29
+ if (kind === 'workflow')
30
+ return WORKFLOW_ENTRYPOINT;
31
+ return SKILL_ENTRYPOINT;
32
+ }
33
+ const KNOWN_TOP_LEVEL_KEYS = new Set([
34
+ 'schemaVersion',
35
+ 'name',
36
+ 'kind',
37
+ 'version',
38
+ 'description',
39
+ 'license',
40
+ 'keywords',
41
+ 'authors',
42
+ 'homepage',
43
+ 'repository',
44
+ 'compatibility',
45
+ 'files',
46
+ 'dependencies',
47
+ 'optionalDependencies',
48
+ 'suggests',
49
+ 'capabilities',
50
+ 'integrity',
51
+ 'signatures',
52
+ 'agentOverrides',
53
+ ]);
54
+ function defaultFiles(kind) {
55
+ return [entrypointFor(kind), MANIFEST_FILENAME];
56
+ }
57
+ /**
58
+ * Parses and normalises a `skill.yaml`.
59
+ *
60
+ * Throws {@link AgentSkillsError} for anything that makes the document unusable, and
61
+ * returns non-fatal findings as issues so `validate` can show several problems at once
62
+ * instead of one per run.
63
+ */
64
+ export function parseManifest(text, options = {}) {
65
+ const source = options.source ?? MANIFEST_FILENAME;
66
+ const issues = new IssueCollector();
67
+ let raw;
68
+ try {
69
+ raw = parseYaml(text);
70
+ }
71
+ catch (cause) {
72
+ throw new AgentSkillsError(ErrorCode.INVALID_MANIFEST, `Could not parse ${source}`, {
73
+ details: [cause instanceof Error ? cause.message : String(cause)],
74
+ hints: ['skill.yaml must be valid YAML'],
75
+ cause,
76
+ data: { source },
77
+ });
78
+ }
79
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
80
+ throw new AgentSkillsError(ErrorCode.INVALID_MANIFEST, `${source} must be a YAML mapping`, {
81
+ hints: ['Run `senior-skills create <name>` to generate a valid starting point'],
82
+ data: { source },
83
+ });
84
+ }
85
+ const doc = raw;
86
+ const schemaVersion = readSchemaVersion(doc, source);
87
+ const kind = readKind(doc, issues);
88
+ const name = readRequiredString(doc, 'name', source);
89
+ const nameProblems = validateSkillName(name);
90
+ for (const problem of nameProblems) {
91
+ issues.error('manifest.name.invalid', 'name', `Invalid name "${name}": ${problem}`);
92
+ }
93
+ const version = parseVersion(readRequiredString(doc, 'version', source), source);
94
+ const description = readRequiredString(doc, 'description', source).trim();
95
+ if (description.length < 10) {
96
+ issues.warn('manifest.description.short', 'description', 'Description is very short; agents route on it, so say what the skill covers and when to use it');
97
+ }
98
+ const files = readFiles(doc, kind, issues);
99
+ const dependencies = readDependencies(doc, 'dependencies', name, source, issues);
100
+ const optionalDependencies = readDependencies(doc, 'optionalDependencies', name, source, issues);
101
+ const suggests = readSuggests(doc, name, issues);
102
+ for (const key of Object.keys(doc)) {
103
+ if (KNOWN_TOP_LEVEL_KEYS.has(key))
104
+ continue;
105
+ const message = `Unknown field "${key}"`;
106
+ const hint = 'Remove it, or upgrade the CLI if it belongs to a newer package format';
107
+ if (options.strict) {
108
+ issues.error('manifest.unknownField', key, message, hint);
109
+ }
110
+ else {
111
+ issues.warn('manifest.unknownField', key, message, hint);
112
+ }
113
+ }
114
+ const manifest = {
115
+ schemaVersion,
116
+ name,
117
+ kind,
118
+ version,
119
+ description,
120
+ ...optionalString(doc, 'license'),
121
+ keywords: readStringArray(doc, 'keywords', issues),
122
+ authors: readAuthors(doc, issues),
123
+ ...optionalString(doc, 'homepage'),
124
+ ...readRepository(doc, issues),
125
+ compatibility: readCompatibility(doc, source, issues),
126
+ files,
127
+ dependencies,
128
+ optionalDependencies,
129
+ suggests,
130
+ capabilities: readStringArray(doc, 'capabilities', issues),
131
+ ...optionalString(doc, 'integrity'),
132
+ signatures: Array.isArray(doc['signatures']) ? doc['signatures'] : [],
133
+ agentOverrides: readAgentOverrides(doc, issues),
134
+ };
135
+ if (manifest.license === undefined) {
136
+ issues.warn('manifest.license.missing', 'license', 'No license declared', 'Add an SPDX identifier, e.g. license: Apache-2.0');
137
+ }
138
+ return { manifest, issues: issues.all() };
139
+ }
140
+ function readSchemaVersion(doc, source) {
141
+ const value = doc['schemaVersion'];
142
+ if (value === undefined)
143
+ return CURRENT_SCHEMA_VERSION;
144
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < 1) {
145
+ throw new AgentSkillsError(ErrorCode.INVALID_MANIFEST, `${source}: schemaVersion must be a positive integer`, { data: { source, schemaVersion: value } });
146
+ }
147
+ if (value > CURRENT_SCHEMA_VERSION) {
148
+ throw new AgentSkillsError(ErrorCode.UNSUPPORTED_SCHEMA, `This package uses skill format v${value}, but this CLI understands up to v${CURRENT_SCHEMA_VERSION}`, {
149
+ hints: ['Upgrade the CLI: npm install -g @senior-skills/cli@latest'],
150
+ data: { source, schemaVersion: value, supported: CURRENT_SCHEMA_VERSION },
151
+ });
152
+ }
153
+ return value;
154
+ }
155
+ /**
156
+ * An unknown kind is reported rather than thrown, so `validate` lists it alongside the
157
+ * package's other problems; treating it as a skill keeps the rest of the checks meaningful.
158
+ */
159
+ function readKind(doc, issues) {
160
+ const value = doc['kind'];
161
+ if (value === undefined)
162
+ return DEFAULT_PACKAGE_KIND;
163
+ if (typeof value === 'string' && PACKAGE_KINDS.includes(value)) {
164
+ return value;
165
+ }
166
+ issues.error('manifest.kind.invalid', 'kind', `Unknown kind "${String(value)}"`, `Accepted kinds: ${PACKAGE_KINDS.join(', ')}`);
167
+ return DEFAULT_PACKAGE_KIND;
168
+ }
169
+ function readRequiredString(doc, key, source) {
170
+ const value = doc[key];
171
+ if (typeof value !== 'string' || value.trim() === '') {
172
+ throw new AgentSkillsError(ErrorCode.INVALID_MANIFEST, `${source}: missing required field "${key}"`, {
173
+ hints: [`Add a non-empty ${key}: to ${source}`],
174
+ data: { source, field: key },
175
+ });
176
+ }
177
+ return value.trim();
178
+ }
179
+ function optionalString(doc, key) {
180
+ const value = doc[key];
181
+ return typeof value === 'string' && value.trim() !== '' ? { [key]: value.trim() } : {};
182
+ }
183
+ function readStringArray(doc, key, issues) {
184
+ const value = doc[key];
185
+ if (value === undefined)
186
+ return [];
187
+ if (!Array.isArray(value)) {
188
+ issues.error(`manifest.${key}.type`, key, `"${key}" must be a list of strings`);
189
+ return [];
190
+ }
191
+ const result = [];
192
+ for (const entry of value) {
193
+ if (typeof entry !== 'string') {
194
+ issues.error(`manifest.${key}.type`, key, `"${key}" entries must be strings`);
195
+ continue;
196
+ }
197
+ result.push(entry.trim());
198
+ }
199
+ return result;
200
+ }
201
+ function readFiles(doc, kind, issues) {
202
+ const declared = readStringArray(doc, 'files', issues);
203
+ const files = declared.length > 0 ? declared : defaultFiles(kind);
204
+ const safe = [];
205
+ for (const entry of files) {
206
+ // Path safety is checked at authoring time, not only at extraction time: a manifest
207
+ // must never be able to *describe* a path that escapes the package.
208
+ if (entry.startsWith('/') || /^[a-zA-Z]:/.test(entry) || entry.startsWith('\\\\')) {
209
+ issues.error('manifest.files.absolute', 'files', `"${entry}" is an absolute path; files entries must be relative to the package root`);
210
+ continue;
211
+ }
212
+ if (escapesRoot(entry)) {
213
+ issues.error('manifest.files.traversal', 'files', `"${entry}" escapes the package root`, 'Remove the ".." segments');
214
+ continue;
215
+ }
216
+ safe.push(normalize(entry.replace(/\/+$/, '')) + (entry.endsWith('/') ? '/' : ''));
217
+ }
218
+ const entrypoint = entrypointFor(kind);
219
+ if (!safe.some((entry) => entry === entrypoint)) {
220
+ issues.error('manifest.files.missingEntrypoint', 'files', `"files" must include ${entrypoint}`, `Add ${entrypoint} to the files list`);
221
+ }
222
+ return safe;
223
+ }
224
+ function readSuggests(doc, selfName, issues) {
225
+ const value = doc['suggests'];
226
+ if (value === undefined)
227
+ return [];
228
+ if (!Array.isArray(value)) {
229
+ issues.error('manifest.suggests.type', 'suggests', '"suggests" must be a list of skill names');
230
+ return [];
231
+ }
232
+ const seen = new Set();
233
+ const result = [];
234
+ for (const entry of value) {
235
+ if (typeof entry !== 'string') {
236
+ issues.error('manifest.suggests.type', 'suggests', '"suggests" entries must be skill names, not mappings — a suggestion carries no range');
237
+ continue;
238
+ }
239
+ for (const problem of validateSkillName(entry)) {
240
+ issues.error('manifest.suggests.name', `suggests.${entry}`, `Invalid skill name: ${problem}`);
241
+ }
242
+ if (entry === selfName) {
243
+ issues.error('manifest.suggests.self', `suggests.${entry}`, 'A skill cannot suggest itself');
244
+ continue;
245
+ }
246
+ if (seen.has(entry)) {
247
+ issues.error('manifest.suggests.duplicate', `suggests.${entry}`, `Duplicate suggestion "${entry}"`);
248
+ continue;
249
+ }
250
+ seen.add(entry);
251
+ result.push(entry);
252
+ }
253
+ return result;
254
+ }
255
+ function readDependencies(doc, key, selfName, source, issues) {
256
+ const value = doc[key];
257
+ if (value === undefined)
258
+ return [];
259
+ if (!Array.isArray(value)) {
260
+ issues.error(`manifest.${key}.type`, key, `"${key}" must be a list`);
261
+ return [];
262
+ }
263
+ const seen = new Set();
264
+ const result = [];
265
+ for (const entry of value) {
266
+ if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
267
+ issues.error(`manifest.${key}.type`, key, `"${key}" entries must be mappings with name and version`);
268
+ continue;
269
+ }
270
+ const record = entry;
271
+ const name = record['name'];
272
+ const version = record['version'];
273
+ if (typeof name !== 'string') {
274
+ issues.error(`manifest.${key}.name`, key, `A ${key} entry is missing "name"`);
275
+ continue;
276
+ }
277
+ for (const problem of validateSkillName(name)) {
278
+ issues.error(`manifest.${key}.name`, `${key}.${name}`, `Invalid dependency name: ${problem}`);
279
+ }
280
+ if (name === selfName) {
281
+ issues.error(`manifest.${key}.self`, `${key}.${name}`, 'A skill cannot depend on itself');
282
+ continue;
283
+ }
284
+ if (seen.has(name)) {
285
+ issues.error(`manifest.${key}.duplicate`, `${key}.${name}`, `Duplicate dependency "${name}"`);
286
+ continue;
287
+ }
288
+ seen.add(name);
289
+ const rangeText = typeof version === 'string' && version.trim() !== '' ? version.trim() : '*';
290
+ result.push({ name, version: parseRange(rangeText, `${source} ${key}.${name}`) });
291
+ }
292
+ return result;
293
+ }
294
+ function readAuthors(doc, issues) {
295
+ const value = doc['authors'];
296
+ if (value === undefined)
297
+ return [];
298
+ if (!Array.isArray(value)) {
299
+ issues.error('manifest.authors.type', 'authors', '"authors" must be a list');
300
+ return [];
301
+ }
302
+ const authors = [];
303
+ for (const entry of value) {
304
+ if (typeof entry === 'string') {
305
+ authors.push({ name: entry });
306
+ continue;
307
+ }
308
+ if (entry === null || typeof entry !== 'object') {
309
+ issues.error('manifest.authors.type', 'authors', 'Author entries must be strings or mappings');
310
+ continue;
311
+ }
312
+ const record = entry;
313
+ if (typeof record['name'] !== 'string') {
314
+ issues.error('manifest.authors.name', 'authors', 'Author entries need a "name"');
315
+ continue;
316
+ }
317
+ authors.push({
318
+ name: record['name'],
319
+ ...optionalString(record, 'email'),
320
+ ...optionalString(record, 'url'),
321
+ });
322
+ }
323
+ return authors;
324
+ }
325
+ function readRepository(doc, issues) {
326
+ const value = doc['repository'];
327
+ if (value === undefined)
328
+ return {};
329
+ if (typeof value === 'string')
330
+ return { repository: { type: 'git', url: value } };
331
+ if (typeof value !== 'object' || Array.isArray(value)) {
332
+ issues.error('manifest.repository.type', 'repository', '"repository" must be a mapping or a URL');
333
+ return {};
334
+ }
335
+ const record = value;
336
+ const url = record['url'];
337
+ if (typeof url !== 'string') {
338
+ issues.error('manifest.repository.url', 'repository', '"repository" needs a "url"');
339
+ return {};
340
+ }
341
+ return {
342
+ repository: {
343
+ type: typeof record['type'] === 'string' ? record['type'] : 'git',
344
+ url,
345
+ ...optionalString(record, 'directory'),
346
+ },
347
+ };
348
+ }
349
+ function readCompatibility(doc, source, issues) {
350
+ const value = doc['compatibility'];
351
+ if (value === undefined)
352
+ return [];
353
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
354
+ issues.error('manifest.compatibility.type', 'compatibility', '"compatibility" must be a mapping');
355
+ return [];
356
+ }
357
+ const agents = value['agents'];
358
+ if (agents === undefined)
359
+ return [];
360
+ if (!Array.isArray(agents)) {
361
+ issues.error('manifest.compatibility.agents', 'compatibility.agents', '"compatibility.agents" must be a list');
362
+ return [];
363
+ }
364
+ const result = [];
365
+ for (const entry of agents) {
366
+ if (typeof entry === 'string') {
367
+ result.push({ id: entry });
368
+ continue;
369
+ }
370
+ if (entry === null || typeof entry !== 'object') {
371
+ issues.error('manifest.compatibility.agents', 'compatibility.agents', 'Agent entries must be an id string or a mapping with "id"');
372
+ continue;
373
+ }
374
+ const record = entry;
375
+ const id = record['id'];
376
+ if (typeof id !== 'string') {
377
+ issues.error('manifest.compatibility.agents', 'compatibility.agents', 'Agent entries need an "id"');
378
+ continue;
379
+ }
380
+ const minVersion = record['minVersion'];
381
+ result.push({
382
+ id,
383
+ ...(typeof minVersion === 'string'
384
+ ? { minVersion: parseRange(minVersion, `${source} compatibility.agents.${id}`) }
385
+ : {}),
386
+ });
387
+ }
388
+ return result;
389
+ }
390
+ function readAgentOverrides(doc, issues) {
391
+ const value = doc['agentOverrides'];
392
+ if (value === undefined)
393
+ return {};
394
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
395
+ issues.error('manifest.agentOverrides.type', 'agentOverrides', '"agentOverrides" must be a mapping');
396
+ return {};
397
+ }
398
+ const result = {};
399
+ for (const [agentId, override] of Object.entries(value)) {
400
+ if (typeof override !== 'object' || override === null || Array.isArray(override)) {
401
+ issues.error('manifest.agentOverrides.type', `agentOverrides.${agentId}`, 'Each agent override must be a mapping');
402
+ continue;
403
+ }
404
+ result[agentId] = override;
405
+ }
406
+ return result;
407
+ }
408
+ /** Serialises a manifest back to YAML, omitting empty optional fields to keep files tidy. */
409
+ export function stringifyManifest(manifest) {
410
+ const doc = {
411
+ schemaVersion: manifest.schemaVersion,
412
+ name: manifest.name,
413
+ version: manifest.version,
414
+ description: manifest.description,
415
+ };
416
+ if (manifest.kind !== DEFAULT_PACKAGE_KIND)
417
+ doc['kind'] = manifest.kind;
418
+ if (manifest.license !== undefined)
419
+ doc['license'] = manifest.license;
420
+ if (manifest.keywords.length > 0)
421
+ doc['keywords'] = [...manifest.keywords];
422
+ if (manifest.authors.length > 0)
423
+ doc['authors'] = manifest.authors.map((author) => ({ ...author }));
424
+ if (manifest.homepage !== undefined)
425
+ doc['homepage'] = manifest.homepage;
426
+ if (manifest.repository !== undefined)
427
+ doc['repository'] = { ...manifest.repository };
428
+ if (manifest.compatibility.length > 0) {
429
+ doc['compatibility'] = { agents: manifest.compatibility.map((agent) => ({ ...agent })) };
430
+ }
431
+ doc['files'] = [...manifest.files];
432
+ if (manifest.dependencies.length > 0) {
433
+ doc['dependencies'] = manifest.dependencies.map((dep) => ({ ...dep }));
434
+ }
435
+ if (manifest.optionalDependencies.length > 0) {
436
+ doc['optionalDependencies'] = manifest.optionalDependencies.map((dep) => ({ ...dep }));
437
+ }
438
+ if (manifest.suggests.length > 0)
439
+ doc['suggests'] = [...manifest.suggests];
440
+ if (manifest.capabilities.length > 0)
441
+ doc['capabilities'] = [...manifest.capabilities];
442
+ if (manifest.integrity !== undefined)
443
+ doc['integrity'] = manifest.integrity;
444
+ if (Object.keys(manifest.agentOverrides).length > 0)
445
+ doc['agentOverrides'] = manifest.agentOverrides;
446
+ return stringifyYaml(doc, { lineWidth: 100, singleQuote: false });
447
+ }
448
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/domain/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export declare const MAX_PATH_SEGMENTS = 32;
2
+ export declare const MAX_SEGMENT_LENGTH = 255;
3
+ export type PathSafetyRule = 'empty' | 'controlChars' | 'uncPath' | 'absolute' | 'driveLetter' | 'traversal' | 'tooDeep' | 'segmentTooLong' | 'trailingDotOrSpace' | 'alternateDataStream' | 'illegalCharacter' | 'reservedName';
4
+ export interface PathSafetyViolation {
5
+ readonly rule: PathSafetyRule;
6
+ readonly message: string;
7
+ }
8
+ /**
9
+ * Returns the reason `raw` is unsafe, or undefined when it is acceptable.
10
+ *
11
+ * Order matters: UNC is checked before "absolute" because `\\server\share` normalises to
12
+ * `//server/share`, and the drive-letter check runs only after the colon has been ruled out
13
+ * as an alternate-data-stream marker on a non-leading segment.
14
+ */
15
+ export declare function inspectPath(raw: string): PathSafetyViolation | undefined;
16
+ export declare function isSafePath(raw: string): boolean;
17
+ /** Normalised POSIX form. Only meaningful for a path {@link inspectPath} accepted. */
18
+ export declare function normalizeSafePath(raw: string): string;
19
+ //# sourceMappingURL=path-safety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-safety.d.ts","sourceRoot":"","sources":["../../src/domain/path-safety.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,cAAc,GACd,SAAS,GACT,UAAU,GACV,aAAa,GACb,WAAW,GACX,SAAS,GACT,gBAAgB,GAChB,oBAAoB,GACpB,qBAAqB,GACrB,kBAAkB,GAClB,cAAc,CAAC;AAEnB,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAWD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CA8DxE;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD"}
@@ -0,0 +1,125 @@
1
+ import { escapesRoot, normalize, segments } from "./posix-path.js";
2
+ /**
3
+ * The single definition of "is this path safe to turn into a filename".
4
+ *
5
+ * It lives in the domain because two very different callers need exactly the same answer:
6
+ * `validate` (is this package well-formed?) and the installer's extractor (is this archive
7
+ * hostile?). Keeping two copies meant they drifted — a path like `a:stream` was reported as
8
+ * "absolute" by one and "alternate data stream" by the other — so there is one copy now and
9
+ * both callers wrap it.
10
+ *
11
+ * Rules are enforced on every platform, not just the one in use: a package that installs
12
+ * cleanly on Linux and corrupts a Windows machine is a supply-chain bug, not a quirk.
13
+ */
14
+ const RESERVED_BASENAMES = new Set([
15
+ 'con',
16
+ 'prn',
17
+ 'aux',
18
+ 'nul',
19
+ 'com1',
20
+ 'com2',
21
+ 'com3',
22
+ 'com4',
23
+ 'com5',
24
+ 'com6',
25
+ 'com7',
26
+ 'com8',
27
+ 'com9',
28
+ 'lpt1',
29
+ 'lpt2',
30
+ 'lpt3',
31
+ 'lpt4',
32
+ 'lpt5',
33
+ 'lpt6',
34
+ 'lpt7',
35
+ 'lpt8',
36
+ 'lpt9',
37
+ ]);
38
+ const ILLEGAL_CHARACTERS = ['<', '>', '"', '|', '?', '*'];
39
+ export const MAX_PATH_SEGMENTS = 32;
40
+ export const MAX_SEGMENT_LENGTH = 255;
41
+ /** Tested by code point rather than with a regex literal holding raw C0 bytes. */
42
+ function hasControlCharacters(value) {
43
+ for (let index = 0; index < value.length; index += 1) {
44
+ const code = value.charCodeAt(index);
45
+ if (code < 0x20 || code === 0x7f)
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ /**
51
+ * Returns the reason `raw` is unsafe, or undefined when it is acceptable.
52
+ *
53
+ * Order matters: UNC is checked before "absolute" because `\\server\share` normalises to
54
+ * `//server/share`, and the drive-letter check runs only after the colon has been ruled out
55
+ * as an alternate-data-stream marker on a non-leading segment.
56
+ */
57
+ export function inspectPath(raw) {
58
+ if (raw.trim() === '') {
59
+ return { rule: 'empty', message: 'Path is empty' };
60
+ }
61
+ if (hasControlCharacters(raw)) {
62
+ return { rule: 'controlChars', message: 'Path contains control characters' };
63
+ }
64
+ // Normalise separators before any structural check: an archive can record `..\\..\\x` and
65
+ // rely on a POSIX-only check treating backslashes as ordinary characters.
66
+ const unified = raw.replace(/\\/g, '/');
67
+ if (raw.startsWith('\\\\') || unified.startsWith('//')) {
68
+ return { rule: 'uncPath', message: 'Path is a UNC network path' };
69
+ }
70
+ if (unified.startsWith('/')) {
71
+ return { rule: 'absolute', message: 'Path is absolute' };
72
+ }
73
+ if (/^[a-zA-Z]:[/\\]/.test(raw)) {
74
+ return { rule: 'driveLetter', message: 'Path starts with a drive letter' };
75
+ }
76
+ if (escapesRoot(unified)) {
77
+ return { rule: 'traversal', message: 'Path escapes the destination directory' };
78
+ }
79
+ const parts = segments(unified);
80
+ if (parts.length === 0) {
81
+ return { rule: 'empty', message: 'Path resolves to nothing' };
82
+ }
83
+ if (parts.length > MAX_PATH_SEGMENTS) {
84
+ return { rule: 'tooDeep', message: `Path is more than ${MAX_PATH_SEGMENTS} levels deep` };
85
+ }
86
+ for (const segment of parts) {
87
+ if (segment.length > MAX_SEGMENT_LENGTH) {
88
+ return {
89
+ rule: 'segmentTooLong',
90
+ message: `"${truncate(segment)}" exceeds ${MAX_SEGMENT_LENGTH} characters`,
91
+ };
92
+ }
93
+ if (segment.endsWith('.') || segment.endsWith(' ')) {
94
+ // Windows silently strips these, so `evil.txt.` and `evil.txt` become the same file —
95
+ // a way to reach a path that was already checked under a different name.
96
+ return { rule: 'trailingDotOrSpace', message: `"${segment}" ends with a dot or space` };
97
+ }
98
+ if (segment.includes(':')) {
99
+ return {
100
+ rule: 'alternateDataStream',
101
+ message: `"${segment}" addresses an NTFS alternate data stream`,
102
+ };
103
+ }
104
+ for (const character of ILLEGAL_CHARACTERS) {
105
+ if (segment.includes(character)) {
106
+ return { rule: 'illegalCharacter', message: `"${segment}" contains "${character}"` };
107
+ }
108
+ }
109
+ if (RESERVED_BASENAMES.has(segment.split('.')[0].toLowerCase())) {
110
+ return { rule: 'reservedName', message: `"${segment}" is a reserved device name on Windows` };
111
+ }
112
+ }
113
+ return undefined;
114
+ }
115
+ export function isSafePath(raw) {
116
+ return inspectPath(raw) === undefined;
117
+ }
118
+ /** Normalised POSIX form. Only meaningful for a path {@link inspectPath} accepted. */
119
+ export function normalizeSafePath(raw) {
120
+ return normalize(raw.replace(/\\/g, '/'));
121
+ }
122
+ function truncate(value) {
123
+ return value.length > 120 ? `${value.slice(0, 117)}...` : value;
124
+ }
125
+ //# sourceMappingURL=path-safety.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-safety.js","sourceRoot":"","sources":["../../src/domain/path-safety.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A minimal POSIX-style path helper.
3
+ *
4
+ * The domain keeps its own copy rather than importing `node:path` so that `@senior-skills/core`
5
+ * stays runtime-free: package-relative paths are always POSIX regardless of the host OS, and
6
+ * a domain that imported `node:path` would silently behave differently on Windows.
7
+ *
8
+ * Filesystem-absolute paths are the concern of the `FileSystem` port, not of this module.
9
+ */
10
+ /** Normalise separators and collapse `.` / `..` segments without touching the filesystem. */
11
+ export declare function normalize(input: string): string;
12
+ export declare function join(...parts: readonly string[]): string;
13
+ export declare function dirname(input: string): string;
14
+ export declare function basename(input: string): string;
15
+ export declare function extname(input: string): string;
16
+ /** Split into non-empty segments. `''` and `'.'` yield an empty list. */
17
+ export declare function segments(input: string): readonly string[];
18
+ /**
19
+ * True when `candidate` stays inside `root`. Both are treated as POSIX-relative paths;
20
+ * this is a *lexical* check and is intentionally conservative — the filesystem-level
21
+ * check (symlinks, real paths) lives in the installer.
22
+ */
23
+ export declare function isInside(root: string, candidate: string): boolean;
24
+ /** True when the path escapes its own root, i.e. normalising leaves a leading `..`. */
25
+ export declare function escapesRoot(input: string): boolean;
26
+ /** Convert an OS-native path to the POSIX form used inside packages. */
27
+ export declare function toPosix(input: string): string;
28
+ //# sourceMappingURL=posix-path.d.ts.map