@bradygaster/squad-sdk 0.9.6-insider.2 → 0.10.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 (127) hide show
  1. package/dist/adapter/client.d.ts.map +1 -1
  2. package/dist/adapter/client.js +16 -3
  3. package/dist/adapter/client.js.map +1 -1
  4. package/dist/adapter/types.d.ts +6 -1
  5. package/dist/adapter/types.d.ts.map +1 -1
  6. package/dist/agents/charter-compiler.d.ts +2 -0
  7. package/dist/agents/charter-compiler.d.ts.map +1 -1
  8. package/dist/agents/charter-compiler.js +6 -1
  9. package/dist/agents/charter-compiler.js.map +1 -1
  10. package/dist/agents/index.d.ts.map +1 -1
  11. package/dist/agents/index.js +24 -25
  12. package/dist/agents/index.js.map +1 -1
  13. package/dist/agents/lifecycle.d.ts.map +1 -1
  14. package/dist/agents/lifecycle.js +11 -2
  15. package/dist/agents/lifecycle.js.map +1 -1
  16. package/dist/agents/onboarding.d.ts.map +1 -1
  17. package/dist/agents/onboarding.js +24 -0
  18. package/dist/agents/onboarding.js.map +1 -1
  19. package/dist/config/agent-source.d.ts.map +1 -1
  20. package/dist/config/agent-source.js +60 -33
  21. package/dist/config/agent-source.js.map +1 -1
  22. package/dist/config/feature-audit.js +1 -1
  23. package/dist/config/feature-audit.js.map +1 -1
  24. package/dist/config/init.d.ts +4 -0
  25. package/dist/config/init.d.ts.map +1 -1
  26. package/dist/config/init.js +177 -44
  27. package/dist/config/init.js.map +1 -1
  28. package/dist/index.d.ts +4 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/marketplace/index.d.ts +7 -0
  33. package/dist/marketplace/index.d.ts.map +1 -1
  34. package/dist/marketplace/index.js +4 -0
  35. package/dist/marketplace/index.js.map +1 -1
  36. package/dist/marketplace/plugin-manifest.d.ts +113 -0
  37. package/dist/marketplace/plugin-manifest.d.ts.map +1 -0
  38. package/dist/marketplace/plugin-manifest.js +820 -0
  39. package/dist/marketplace/plugin-manifest.js.map +1 -0
  40. package/dist/marketplace/plugin-runtime.d.ts +37 -0
  41. package/dist/marketplace/plugin-runtime.d.ts.map +1 -0
  42. package/dist/marketplace/plugin-runtime.js +217 -0
  43. package/dist/marketplace/plugin-runtime.js.map +1 -0
  44. package/dist/marketplace/plugin-state.d.ts +89 -0
  45. package/dist/marketplace/plugin-state.d.ts.map +1 -0
  46. package/dist/marketplace/plugin-state.js +278 -0
  47. package/dist/marketplace/plugin-state.js.map +1 -0
  48. package/dist/memory/index.d.ts +262 -0
  49. package/dist/memory/index.d.ts.map +1 -0
  50. package/dist/memory/index.js +1122 -0
  51. package/dist/memory/index.js.map +1 -0
  52. package/dist/multi-squad.d.ts.map +1 -1
  53. package/dist/multi-squad.js +5 -2
  54. package/dist/multi-squad.js.map +1 -1
  55. package/dist/platform/azure-devops.d.ts.map +1 -1
  56. package/dist/platform/azure-devops.js +17 -3
  57. package/dist/platform/azure-devops.js.map +1 -1
  58. package/dist/platform/detect.d.ts.map +1 -1
  59. package/dist/platform/detect.js +12 -5
  60. package/dist/platform/detect.js.map +1 -1
  61. package/dist/platform/index.d.ts.map +1 -1
  62. package/dist/platform/index.js +26 -0
  63. package/dist/platform/index.js.map +1 -1
  64. package/dist/ralph/triage.js +1 -1
  65. package/dist/ralph/triage.js.map +1 -1
  66. package/dist/resolution.d.ts +18 -0
  67. package/dist/resolution.d.ts.map +1 -1
  68. package/dist/resolution.js +64 -2
  69. package/dist/resolution.js.map +1 -1
  70. package/dist/runtime/memory-value-benchmark.d.ts +61 -0
  71. package/dist/runtime/memory-value-benchmark.d.ts.map +1 -0
  72. package/dist/runtime/memory-value-benchmark.js +245 -0
  73. package/dist/runtime/memory-value-benchmark.js.map +1 -0
  74. package/dist/runtime/scheduler.d.ts +8 -0
  75. package/dist/runtime/scheduler.d.ts.map +1 -1
  76. package/dist/runtime/scheduler.js +52 -5
  77. package/dist/runtime/scheduler.js.map +1 -1
  78. package/dist/sharing/export.d.ts +1 -0
  79. package/dist/sharing/export.d.ts.map +1 -1
  80. package/dist/sharing/export.js +10 -0
  81. package/dist/sharing/export.js.map +1 -1
  82. package/dist/sharing/import.d.ts.map +1 -1
  83. package/dist/sharing/import.js +3 -2
  84. package/dist/sharing/import.js.map +1 -1
  85. package/dist/sharing/index.d.ts +1 -0
  86. package/dist/sharing/index.d.ts.map +1 -1
  87. package/dist/sharing/index.js +1 -0
  88. package/dist/sharing/index.js.map +1 -1
  89. package/dist/sharing/repo-sync.d.ts +80 -0
  90. package/dist/sharing/repo-sync.d.ts.map +1 -0
  91. package/dist/sharing/repo-sync.js +138 -0
  92. package/dist/sharing/repo-sync.js.map +1 -0
  93. package/dist/state-backend.d.ts +154 -9
  94. package/dist/state-backend.d.ts.map +1 -1
  95. package/dist/state-backend.js +729 -184
  96. package/dist/state-backend.js.map +1 -1
  97. package/dist/tools/index.d.ts +39 -1
  98. package/dist/tools/index.d.ts.map +1 -1
  99. package/dist/tools/index.js +395 -2
  100. package/dist/tools/index.js.map +1 -1
  101. package/dist/utils/map-with-limit.d.ts +37 -0
  102. package/dist/utils/map-with-limit.d.ts.map +1 -0
  103. package/dist/utils/map-with-limit.js +81 -0
  104. package/dist/utils/map-with-limit.js.map +1 -0
  105. package/package.json +6 -2
  106. package/templates/after-agent-reference.md +64 -0
  107. package/templates/ceremony-reference.md +82 -0
  108. package/templates/client-compatibility-reference.md +46 -0
  109. package/templates/copilot-agent.md +96 -0
  110. package/templates/copilot-instructions.md +14 -0
  111. package/templates/model-selection-reference.md +101 -0
  112. package/templates/prd-intake.md +105 -0
  113. package/templates/rai-charter.md +110 -0
  114. package/templates/rai-policy.md +103 -0
  115. package/templates/ralph-reference.md +141 -0
  116. package/templates/routing.md +1 -0
  117. package/templates/scribe-charter.md +18 -151
  118. package/templates/session-init-reference.md +199 -0
  119. package/templates/skills/e2e-template-testing/SKILL.md +557 -0
  120. package/templates/skills/fact-checking/SKILL.md +61 -0
  121. package/templates/spawn-reference.md +131 -0
  122. package/templates/squad.agent.md.template +200 -625
  123. package/templates/workflow-wiring-appendix-a-code-reviewer.md +131 -0
  124. package/templates/workflow-wiring-appendix-b-documenter.md +140 -0
  125. package/templates/workflow-wiring-guide.md +276 -0
  126. package/templates/workflows/squad-heartbeat.yml +167 -167
  127. package/templates/worktree-reference.md +126 -0
@@ -0,0 +1,820 @@
1
+ import { basename, extname, isAbsolute, normalize, sep } from 'node:path';
2
+ export const PLUGIN_MANIFEST_FILENAMES = [
3
+ 'plugin.manifest.json',
4
+ 'squad-plugin.json',
5
+ 'squad-plugin.yaml',
6
+ 'squad-plugin.yml',
7
+ 'plugin.json',
8
+ 'plugin.yaml',
9
+ 'plugin.yml',
10
+ ];
11
+ const EXECUTABLE_KEYS = new Set([
12
+ 'command',
13
+ 'commands',
14
+ 'exec',
15
+ 'executable',
16
+ 'install',
17
+ 'postinstall',
18
+ 'preinstall',
19
+ 'run',
20
+ 'script',
21
+ 'scripts',
22
+ ]);
23
+ const EXECUTABLE_EXTENSIONS = new Set([
24
+ '.bat',
25
+ '.cmd',
26
+ '.com',
27
+ '.cjs',
28
+ '.exe',
29
+ '.js',
30
+ '.mjs',
31
+ '.ps1',
32
+ '.sh',
33
+ '.ts',
34
+ '.tsx',
35
+ ]);
36
+ const ALLOWED_TARGET_ROOTS = new Set([
37
+ 'agents',
38
+ 'ceremonies',
39
+ 'decisions',
40
+ 'instructions',
41
+ 'knowledge',
42
+ 'memory',
43
+ 'plugins',
44
+ 'prompts',
45
+ 'routing',
46
+ 'templates',
47
+ 'workflows',
48
+ ]);
49
+ const COMPONENT_KINDS = [
50
+ 'agents',
51
+ 'ceremonies',
52
+ 'decisions',
53
+ 'instructions',
54
+ 'knowledge',
55
+ 'memory',
56
+ 'routing',
57
+ 'templates',
58
+ 'workflows',
59
+ 'hooks',
60
+ 'adapters',
61
+ ];
62
+ const PROVIDER_TYPES = [
63
+ 'memory',
64
+ 'knowledge',
65
+ 'persistence',
66
+ 'event',
67
+ 'policy',
68
+ ];
69
+ const PROVIDER_MODES = ['read', 'write', 'read-write'];
70
+ const PROVIDER_PROTOCOLS = ['static-artifact', 'mcp'];
71
+ const RUNTIME_CAPABILITY_TYPES = ['artifact-generation'];
72
+ const APPROVED_RUNTIME_PROVIDERS = ['graphify'];
73
+ const ALLOWED_LIFECYCLE_EVENTS = [
74
+ 'onInstall',
75
+ 'onEnable',
76
+ 'onDisable',
77
+ 'onBeforeSpawn',
78
+ 'onAfterTask',
79
+ 'onMemoryRefresh',
80
+ ];
81
+ export const PLUGIN_MANIFEST_SCHEMA_VERSION = '0.1';
82
+ export function parsePluginManifestContent(content, fileName = 'squad-plugin.json') {
83
+ const trimmed = content.trim();
84
+ if (!trimmed) {
85
+ throw new Error('Plugin manifest is empty');
86
+ }
87
+ const ext = extname(fileName).toLowerCase();
88
+ const raw = ext === '.json' || trimmed.startsWith('{')
89
+ ? JSON.parse(trimmed)
90
+ : parseDeclarativeYaml(trimmed);
91
+ return normalizePluginManifest(raw);
92
+ }
93
+ export function validatePluginManifest(manifest) {
94
+ const errors = [];
95
+ const warnings = [];
96
+ validateName('id', manifest.id, errors);
97
+ if (!manifest.name || typeof manifest.name !== 'string') {
98
+ errors.push('name is required and must be a string');
99
+ }
100
+ if (!manifest.version || typeof manifest.version !== 'string') {
101
+ errors.push('version is required and must be a string');
102
+ }
103
+ else if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(manifest.version)) {
104
+ errors.push('version must follow semver (e.g. 1.0.0)');
105
+ }
106
+ if (manifest.description !== undefined && typeof manifest.description !== 'string') {
107
+ errors.push('description must be a string when provided');
108
+ }
109
+ if (manifest.authors !== undefined && !manifest.authors.every((author) => typeof author === 'string')) {
110
+ errors.push('authors must be an array of strings when provided');
111
+ }
112
+ if (manifest.license !== undefined && typeof manifest.license !== 'string') {
113
+ errors.push('license must be a string when provided');
114
+ }
115
+ if (manifest.squad !== undefined && typeof manifest.squad !== 'string') {
116
+ errors.push('squad compatibility must be a string when provided');
117
+ }
118
+ validateComponents(manifest.components, errors);
119
+ validateCopilotRequirements(manifest.copilot, errors);
120
+ validateRepositoryMetadata(manifest.repository, errors);
121
+ validateUpstreamMetadata(manifest.upstream, errors, warnings);
122
+ validateMcpMetadata(manifest.mcp, errors, warnings);
123
+ validateProviderContracts(manifest.providers, errors, warnings);
124
+ validateRuntimeManifest(manifest.runtime, errors, warnings);
125
+ if (!Array.isArray(manifest.files) || manifest.files.length === 0) {
126
+ errors.push('files must include at least one static file deployment');
127
+ }
128
+ else {
129
+ for (const [index, file] of manifest.files.entries()) {
130
+ validatePluginFile(file, index, errors, warnings);
131
+ }
132
+ }
133
+ return {
134
+ valid: errors.length === 0,
135
+ errors,
136
+ warnings,
137
+ };
138
+ }
139
+ export function derivePluginRoles(manifest) {
140
+ if (!manifest.components) {
141
+ return [];
142
+ }
143
+ return COMPONENT_KINDS.filter((kind) => {
144
+ const value = manifest.components?.[kind];
145
+ if (value === undefined || value === null) {
146
+ return false;
147
+ }
148
+ return !(Array.isArray(value) && value.length === 0);
149
+ });
150
+ }
151
+ export function createPluginInstallPlan(manifest, options = {}) {
152
+ const validation = validatePluginManifest(manifest);
153
+ if (!validation.valid) {
154
+ throw new Error(`Invalid plugin manifest: ${validation.errors.join('; ')}`);
155
+ }
156
+ return {
157
+ manifest,
158
+ files: manifest.files.map((file) => ({
159
+ ...file,
160
+ targetRoot: file.target.split('/')[0],
161
+ })),
162
+ dryRun: options.dryRun ?? false,
163
+ };
164
+ }
165
+ function normalizePluginManifest(raw) {
166
+ if (!isRecord(raw)) {
167
+ throw new Error('Plugin manifest must be an object');
168
+ }
169
+ const executableKey = findExecutableKey(raw);
170
+ if (executableKey) {
171
+ throw new Error(`Plugin manifest may not declare executable key "${executableKey}"`);
172
+ }
173
+ const filesRaw = raw.files;
174
+ if (!Array.isArray(filesRaw)) {
175
+ return {
176
+ id: readString(raw, 'id'),
177
+ name: readString(raw, 'name'),
178
+ version: readString(raw, 'version'),
179
+ description: readOptionalString(raw, 'description'),
180
+ authors: readOptionalStringArray(raw, 'authors'),
181
+ license: readOptionalString(raw, 'license'),
182
+ squad: readOptionalString(raw, 'squad'),
183
+ components: normalizeComponents(raw.components),
184
+ copilot: normalizeCopilotRequirements(raw.copilot),
185
+ repository: normalizeRepositoryMetadata(raw.repository),
186
+ upstream: normalizeUpstreamMetadata(raw.upstream),
187
+ mcp: normalizeMcpMetadata(raw.mcp),
188
+ providers: normalizeProviderContracts(raw.providers),
189
+ runtime: normalizeRuntimeManifest(raw.runtime),
190
+ files: [],
191
+ };
192
+ }
193
+ return {
194
+ id: readString(raw, 'id'),
195
+ name: readString(raw, 'name'),
196
+ version: readString(raw, 'version'),
197
+ description: readOptionalString(raw, 'description'),
198
+ authors: readOptionalStringArray(raw, 'authors'),
199
+ license: readOptionalString(raw, 'license'),
200
+ squad: readOptionalString(raw, 'squad'),
201
+ components: normalizeComponents(raw.components),
202
+ copilot: normalizeCopilotRequirements(raw.copilot),
203
+ repository: normalizeRepositoryMetadata(raw.repository),
204
+ upstream: normalizeUpstreamMetadata(raw.upstream),
205
+ mcp: normalizeMcpMetadata(raw.mcp),
206
+ providers: normalizeProviderContracts(raw.providers),
207
+ runtime: normalizeRuntimeManifest(raw.runtime),
208
+ files: filesRaw.map((item, index) => normalizePluginFile(item, index)),
209
+ };
210
+ }
211
+ function normalizePluginFile(raw, index) {
212
+ if (!isRecord(raw)) {
213
+ throw new Error(`files[${index}] must be an object`);
214
+ }
215
+ const file = {
216
+ source: readString(raw, 'source'),
217
+ target: readString(raw, 'target'),
218
+ };
219
+ const type = readOptionalString(raw, 'type');
220
+ if (type !== undefined) {
221
+ file.type = type;
222
+ }
223
+ return file;
224
+ }
225
+ function validateName(field, value, errors) {
226
+ if (!value || typeof value !== 'string') {
227
+ errors.push(`${field} is required and must be a string`);
228
+ return;
229
+ }
230
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(value)) {
231
+ errors.push(`${field} must be lowercase alphanumeric with hyphens only`);
232
+ }
233
+ }
234
+ function validatePluginFile(file, index, errors, warnings) {
235
+ if (!file.source || typeof file.source !== 'string') {
236
+ errors.push(`files[${index}].source is required and must be a string`);
237
+ }
238
+ else {
239
+ validateRelativePath(`files[${index}].source`, file.source, errors);
240
+ validateStaticFileExtension(`files[${index}].source`, file.source, errors);
241
+ }
242
+ if (!file.target || typeof file.target !== 'string') {
243
+ errors.push(`files[${index}].target is required and must be a string`);
244
+ }
245
+ else {
246
+ validateRelativePath(`files[${index}].target`, file.target, errors);
247
+ validateStaticFileExtension(`files[${index}].target`, file.target, errors);
248
+ const root = file.target.split('/')[0];
249
+ if (!root || !ALLOWED_TARGET_ROOTS.has(root)) {
250
+ errors.push(`files[${index}].target must start with one of: ${[...ALLOWED_TARGET_ROOTS].join(', ')}`);
251
+ }
252
+ }
253
+ if (file.type !== undefined) {
254
+ const allowedTypes = ['agent', 'asset', 'doc', 'instruction', 'knowledge', 'prompt', 'template', 'workflow'];
255
+ if (!allowedTypes.includes(file.type)) {
256
+ errors.push(`files[${index}].type is not supported: ${file.type}`);
257
+ }
258
+ }
259
+ else {
260
+ warnings.push(`files[${index}].type is not specified`);
261
+ }
262
+ }
263
+ function validateRelativePath(field, value, errors) {
264
+ const normalized = normalize(value).replaceAll('\\', '/');
265
+ const segments = normalized.split('/');
266
+ if (isAbsolute(value) || normalized.startsWith('../') || normalized === '..' || segments.includes('..')) {
267
+ errors.push(`${field} must be a relative path that does not escape the plugin or .squad directory`);
268
+ }
269
+ if (segments.some((segment) => segment.length === 0)) {
270
+ errors.push(`${field} must not contain empty path segments`);
271
+ }
272
+ }
273
+ function validateStaticFileExtension(field, value, errors) {
274
+ const ext = extname(value).toLowerCase();
275
+ if (EXECUTABLE_EXTENSIONS.has(ext)) {
276
+ errors.push(`${field} points to executable or script file type "${ext}"`);
277
+ }
278
+ }
279
+ function readString(raw, key) {
280
+ const value = raw[key];
281
+ return typeof value === 'string' ? value : '';
282
+ }
283
+ function readOptionalString(raw, key) {
284
+ const value = raw[key];
285
+ return typeof value === 'string' ? value : undefined;
286
+ }
287
+ function readOptionalStringArray(raw, key) {
288
+ const value = raw[key];
289
+ if (!Array.isArray(value)) {
290
+ return undefined;
291
+ }
292
+ return value.filter((item) => typeof item === 'string');
293
+ }
294
+ function readOptionalBoolean(raw, key) {
295
+ const value = raw[key];
296
+ return typeof value === 'boolean' ? value : undefined;
297
+ }
298
+ function isRecord(value) {
299
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
300
+ }
301
+ function findExecutableKey(value, path = '') {
302
+ if (Array.isArray(value)) {
303
+ for (const [index, item] of value.entries()) {
304
+ const found = findExecutableKey(item, `${path}[${index}]`);
305
+ if (found)
306
+ return found;
307
+ }
308
+ return undefined;
309
+ }
310
+ if (!isRecord(value)) {
311
+ return undefined;
312
+ }
313
+ for (const [key, nested] of Object.entries(value)) {
314
+ const keyPath = path ? `${path}.${key}` : key;
315
+ if (EXECUTABLE_KEYS.has(key.toLowerCase())) {
316
+ return keyPath;
317
+ }
318
+ const found = findExecutableKey(nested, keyPath);
319
+ if (found)
320
+ return found;
321
+ }
322
+ return undefined;
323
+ }
324
+ function normalizeComponents(raw) {
325
+ if (raw === undefined) {
326
+ return undefined;
327
+ }
328
+ if (!isRecord(raw)) {
329
+ return {};
330
+ }
331
+ const components = {};
332
+ for (const kind of COMPONENT_KINDS) {
333
+ if (raw[kind] !== undefined) {
334
+ components[kind] = raw[kind];
335
+ }
336
+ }
337
+ for (const key of Object.keys(raw)) {
338
+ if (!COMPONENT_KINDS.includes(key)) {
339
+ components[key] = raw[key];
340
+ }
341
+ }
342
+ return components;
343
+ }
344
+ function validateComponents(components, errors) {
345
+ if (components === undefined) {
346
+ return;
347
+ }
348
+ if (!isRecord(components)) {
349
+ errors.push('components must be an object when provided');
350
+ return;
351
+ }
352
+ for (const key of Object.keys(components)) {
353
+ if (!COMPONENT_KINDS.includes(key)) {
354
+ errors.push(`components.${key} is not supported`);
355
+ }
356
+ }
357
+ }
358
+ function normalizeCopilotRequirements(raw) {
359
+ if (raw === undefined) {
360
+ return undefined;
361
+ }
362
+ if (!isRecord(raw)) {
363
+ return {};
364
+ }
365
+ const requires = raw.requires;
366
+ if (!Array.isArray(requires)) {
367
+ return {};
368
+ }
369
+ return {
370
+ requires: requires.map((item) => normalizeCopilotPluginDependency(item)),
371
+ };
372
+ }
373
+ function normalizeCopilotPluginDependency(raw) {
374
+ if (!isRecord(raw)) {
375
+ return { id: '' };
376
+ }
377
+ return {
378
+ id: readString(raw, 'id'),
379
+ version: readOptionalString(raw, 'version'),
380
+ optional: readOptionalBoolean(raw, 'optional'),
381
+ reason: readOptionalString(raw, 'reason'),
382
+ };
383
+ }
384
+ function validateCopilotRequirements(copilot, errors) {
385
+ if (copilot === undefined) {
386
+ return;
387
+ }
388
+ if (!isRecord(copilot)) {
389
+ errors.push('copilot must be an object when provided');
390
+ return;
391
+ }
392
+ if (copilot.requires === undefined) {
393
+ return;
394
+ }
395
+ if (!Array.isArray(copilot.requires)) {
396
+ errors.push('copilot.requires must be an array when provided');
397
+ return;
398
+ }
399
+ for (const [index, dependency] of copilot.requires.entries()) {
400
+ if (!isRecord(dependency)) {
401
+ errors.push(`copilot.requires[${index}] must be an object`);
402
+ continue;
403
+ }
404
+ validateCopilotPluginId(`copilot.requires[${index}].id`, dependency.id, errors);
405
+ if (dependency.version !== undefined && typeof dependency.version !== 'string') {
406
+ errors.push(`copilot.requires[${index}].version must be a string when provided`);
407
+ }
408
+ if (dependency.optional !== undefined && typeof dependency.optional !== 'boolean') {
409
+ errors.push(`copilot.requires[${index}].optional must be a boolean when provided`);
410
+ }
411
+ if (dependency.reason !== undefined && typeof dependency.reason !== 'string') {
412
+ errors.push(`copilot.requires[${index}].reason must be a string when provided`);
413
+ }
414
+ }
415
+ }
416
+ function validateCopilotPluginId(field, value, errors) {
417
+ if (!value || typeof value !== 'string') {
418
+ errors.push(`${field} is required and must be a string`);
419
+ return;
420
+ }
421
+ if (value.includes('..') || value.startsWith('/') || value.startsWith('\\')) {
422
+ errors.push(`${field} must be a package or owner/name identifier, not a path`);
423
+ }
424
+ }
425
+ function normalizeRepositoryMetadata(raw) {
426
+ if (raw === undefined) {
427
+ return undefined;
428
+ }
429
+ if (!isRecord(raw)) {
430
+ return { url: '' };
431
+ }
432
+ return {
433
+ type: readOptionalString(raw, 'type'),
434
+ url: readString(raw, 'url'),
435
+ };
436
+ }
437
+ function normalizeUpstreamMetadata(raw) {
438
+ if (raw === undefined) {
439
+ return undefined;
440
+ }
441
+ if (!isRecord(raw)) {
442
+ return {};
443
+ }
444
+ return {
445
+ package: readOptionalString(raw, 'package'),
446
+ registry: readOptionalString(raw, 'registry'),
447
+ installCommand: readOptionalString(raw, 'installCommand'),
448
+ docs: readOptionalString(raw, 'docs'),
449
+ };
450
+ }
451
+ function normalizeMcpMetadata(raw) {
452
+ if (raw === undefined) {
453
+ return undefined;
454
+ }
455
+ if (!isRecord(raw)) {
456
+ return {};
457
+ }
458
+ return {
459
+ available: readOptionalBoolean(raw, 'available'),
460
+ server: readOptionalString(raw, 'server'),
461
+ entryPoint: readOptionalString(raw, 'entryPoint'),
462
+ installCommand: readOptionalString(raw, 'installCommand'),
463
+ reason: readOptionalString(raw, 'reason'),
464
+ };
465
+ }
466
+ function normalizeProviderContracts(raw) {
467
+ if (raw === undefined) {
468
+ return undefined;
469
+ }
470
+ if (!Array.isArray(raw)) {
471
+ return [];
472
+ }
473
+ return raw.map((item) => normalizeProviderContract(item));
474
+ }
475
+ function normalizeProviderContract(raw) {
476
+ if (!isRecord(raw)) {
477
+ return { id: '', type: 'memory' };
478
+ }
479
+ const provider = {
480
+ id: readString(raw, 'id'),
481
+ type: readString(raw, 'type'),
482
+ mode: readOptionalString(raw, 'mode'),
483
+ protocol: readOptionalString(raw, 'protocol'),
484
+ description: readOptionalString(raw, 'description'),
485
+ artifact: readOptionalString(raw, 'artifact'),
486
+ mcp: normalizeProviderMcpBinding(raw.mcp),
487
+ capabilities: readOptionalStringArray(raw, 'capabilities'),
488
+ };
489
+ return provider;
490
+ }
491
+ function normalizeProviderMcpBinding(raw) {
492
+ if (raw === undefined) {
493
+ return undefined;
494
+ }
495
+ if (!isRecord(raw)) {
496
+ return {};
497
+ }
498
+ return {
499
+ server: readOptionalString(raw, 'server'),
500
+ tool: readOptionalString(raw, 'tool'),
501
+ capability: readOptionalString(raw, 'capability'),
502
+ };
503
+ }
504
+ function normalizeRuntimeManifest(raw) {
505
+ if (raw === undefined) {
506
+ return undefined;
507
+ }
508
+ if (!isRecord(raw)) {
509
+ return {};
510
+ }
511
+ return {
512
+ capabilities: normalizeRuntimeCapabilities(raw.capabilities),
513
+ };
514
+ }
515
+ function normalizeRuntimeCapabilities(raw) {
516
+ if (raw === undefined) {
517
+ return undefined;
518
+ }
519
+ if (!Array.isArray(raw)) {
520
+ return [];
521
+ }
522
+ return raw.map((item) => normalizeRuntimeCapability(item));
523
+ }
524
+ function normalizeRuntimeCapability(raw) {
525
+ if (!isRecord(raw)) {
526
+ return {
527
+ type: 'artifact-generation',
528
+ provider: 'graphify',
529
+ lifecycle: [],
530
+ outputPaths: [],
531
+ };
532
+ }
533
+ return {
534
+ type: readString(raw, 'type'),
535
+ provider: readString(raw, 'provider'),
536
+ lifecycle: readOptionalStringArray(raw, 'lifecycle') ?? [],
537
+ outputPaths: readOptionalStringArray(raw, 'outputPaths') ?? [],
538
+ description: readOptionalString(raw, 'description'),
539
+ };
540
+ }
541
+ function validateRepositoryMetadata(repository, errors) {
542
+ if (repository === undefined) {
543
+ return;
544
+ }
545
+ if (!isRecord(repository)) {
546
+ errors.push('repository must be an object when provided');
547
+ return;
548
+ }
549
+ if (repository.type !== undefined && typeof repository.type !== 'string') {
550
+ errors.push('repository.type must be a string when provided');
551
+ }
552
+ validateUrl('repository.url', repository.url, errors);
553
+ }
554
+ function validateUpstreamMetadata(upstream, errors, warnings) {
555
+ if (upstream === undefined) {
556
+ return;
557
+ }
558
+ if (!isRecord(upstream)) {
559
+ errors.push('upstream must be an object when provided');
560
+ return;
561
+ }
562
+ validateOptionalString('upstream.package', upstream.package, errors);
563
+ validateOptionalString('upstream.registry', upstream.registry, errors);
564
+ validateOptionalString('upstream.installCommand', upstream.installCommand, errors);
565
+ validateOptionalUrl('upstream.docs', upstream.docs, errors);
566
+ if (upstream.installCommand) {
567
+ warnings.push('upstream.installCommand is metadata only; Squad will not execute it');
568
+ }
569
+ }
570
+ function validateMcpMetadata(mcp, errors, warnings) {
571
+ if (mcp === undefined) {
572
+ return;
573
+ }
574
+ if (!isRecord(mcp)) {
575
+ errors.push('mcp must be an object when provided');
576
+ return;
577
+ }
578
+ if (mcp.available !== undefined && typeof mcp.available !== 'boolean') {
579
+ errors.push('mcp.available must be a boolean when provided');
580
+ }
581
+ validateOptionalString('mcp.server', mcp.server, errors);
582
+ validateOptionalString('mcp.entryPoint', mcp.entryPoint, errors);
583
+ validateOptionalString('mcp.installCommand', mcp.installCommand, errors);
584
+ validateOptionalString('mcp.reason', mcp.reason, errors);
585
+ if (mcp.installCommand) {
586
+ warnings.push('mcp.installCommand is metadata only; Squad will not execute it');
587
+ }
588
+ }
589
+ function validateProviderContracts(providers, errors, warnings) {
590
+ if (providers === undefined) {
591
+ return;
592
+ }
593
+ if (!Array.isArray(providers)) {
594
+ errors.push('providers must be an array when provided');
595
+ return;
596
+ }
597
+ for (const [index, provider] of providers.entries()) {
598
+ const prefix = `providers[${index}]`;
599
+ if (!isRecord(provider)) {
600
+ errors.push(`${prefix} must be an object`);
601
+ continue;
602
+ }
603
+ validateName(`${prefix}.id`, provider.id, errors);
604
+ if (!PROVIDER_TYPES.includes(provider.type)) {
605
+ errors.push(`${prefix}.type must be one of: ${PROVIDER_TYPES.join(', ')}`);
606
+ }
607
+ if (provider.mode !== undefined && !PROVIDER_MODES.includes(provider.mode)) {
608
+ errors.push(`${prefix}.mode must be one of: ${PROVIDER_MODES.join(', ')}`);
609
+ }
610
+ if (provider.protocol !== undefined && !PROVIDER_PROTOCOLS.includes(provider.protocol)) {
611
+ errors.push(`${prefix}.protocol must be one of: ${PROVIDER_PROTOCOLS.join(', ')}`);
612
+ }
613
+ validateOptionalString(`${prefix}.description`, provider.description, errors);
614
+ if (provider.artifact !== undefined) {
615
+ validateRelativePath(`${prefix}.artifact`, provider.artifact, errors);
616
+ validateStaticFileExtension(`${prefix}.artifact`, provider.artifact, errors);
617
+ const root = provider.artifact.split('/')[0];
618
+ if (!root || !ALLOWED_TARGET_ROOTS.has(root)) {
619
+ errors.push(`${prefix}.artifact must start with one of: ${[...ALLOWED_TARGET_ROOTS].join(', ')}`);
620
+ }
621
+ }
622
+ if (provider.capabilities !== undefined) {
623
+ if (!Array.isArray(provider.capabilities)) {
624
+ errors.push(`${prefix}.capabilities must be an array when provided`);
625
+ }
626
+ else if (!provider.capabilities.every((capability) => typeof capability === 'string' && capability.trim().length > 0)) {
627
+ errors.push(`${prefix}.capabilities must contain only non-empty strings`);
628
+ }
629
+ }
630
+ if (provider.mcp !== undefined) {
631
+ if (!isRecord(provider.mcp)) {
632
+ errors.push(`${prefix}.mcp must be an object when provided`);
633
+ }
634
+ else {
635
+ validateOptionalString(`${prefix}.mcp.server`, provider.mcp.server, errors);
636
+ validateOptionalString(`${prefix}.mcp.tool`, provider.mcp.tool, errors);
637
+ validateOptionalString(`${prefix}.mcp.capability`, provider.mcp.capability, errors);
638
+ warnings.push(`${prefix}.mcp is provider metadata only; Squad will not start MCP servers or call provider tools`);
639
+ }
640
+ }
641
+ }
642
+ }
643
+ function validateRuntimeManifest(runtime, errors, warnings) {
644
+ if (runtime === undefined) {
645
+ return;
646
+ }
647
+ if (!isRecord(runtime)) {
648
+ errors.push('runtime must be an object when provided');
649
+ return;
650
+ }
651
+ if (runtime.capabilities === undefined) {
652
+ return;
653
+ }
654
+ if (!Array.isArray(runtime.capabilities)) {
655
+ errors.push('runtime.capabilities must be an array when provided');
656
+ return;
657
+ }
658
+ for (const [index, capability] of runtime.capabilities.entries()) {
659
+ const prefix = `runtime.capabilities[${index}]`;
660
+ if (!isRecord(capability)) {
661
+ errors.push(`${prefix} must be an object`);
662
+ continue;
663
+ }
664
+ if (!RUNTIME_CAPABILITY_TYPES.includes(capability.type)) {
665
+ errors.push(`${prefix}.type must be one of: ${RUNTIME_CAPABILITY_TYPES.join(', ')}`);
666
+ }
667
+ if (!APPROVED_RUNTIME_PROVIDERS.includes(capability.provider)) {
668
+ errors.push(`${prefix}.provider "${capability.provider}" is not approved. Allowed providers: ${APPROVED_RUNTIME_PROVIDERS.join(', ')}`);
669
+ }
670
+ if (!Array.isArray(capability.lifecycle)) {
671
+ errors.push(`${prefix}.lifecycle must be an array`);
672
+ }
673
+ else if (capability.lifecycle.length === 0) {
674
+ errors.push(`${prefix}.lifecycle must not be empty`);
675
+ }
676
+ else {
677
+ for (const [lifecycleIndex, event] of capability.lifecycle.entries()) {
678
+ if (typeof event !== 'string') {
679
+ errors.push(`${prefix}.lifecycle[${lifecycleIndex}] must be a string`);
680
+ }
681
+ else if (!ALLOWED_LIFECYCLE_EVENTS.includes(event)) {
682
+ errors.push(`${prefix}.lifecycle[${lifecycleIndex}] "${event}" is not allowed. Allowed events: ${ALLOWED_LIFECYCLE_EVENTS.join(', ')}`);
683
+ }
684
+ }
685
+ }
686
+ if (!Array.isArray(capability.outputPaths)) {
687
+ errors.push(`${prefix}.outputPaths must be an array`);
688
+ }
689
+ else if (capability.outputPaths.length === 0) {
690
+ errors.push(`${prefix}.outputPaths must not be empty`);
691
+ }
692
+ else {
693
+ for (const [pathIndex, outputPath] of capability.outputPaths.entries()) {
694
+ const pathPrefix = `${prefix}.outputPaths[${pathIndex}]`;
695
+ if (typeof outputPath !== 'string') {
696
+ errors.push(`${pathPrefix} must be a string`);
697
+ continue;
698
+ }
699
+ if (outputPath.trim() === '') {
700
+ errors.push(`${pathPrefix} must not be empty`);
701
+ continue;
702
+ }
703
+ validateRelativePath(pathPrefix, outputPath, errors);
704
+ validateStaticFileExtension(pathPrefix, outputPath, errors);
705
+ const root = outputPath.split('/')[0];
706
+ if (!root || !ALLOWED_TARGET_ROOTS.has(root)) {
707
+ errors.push(`${pathPrefix} must start with one of: ${[...ALLOWED_TARGET_ROOTS].join(', ')}`);
708
+ }
709
+ }
710
+ }
711
+ validateOptionalString(`${prefix}.description`, capability.description, errors);
712
+ warnings.push(`${prefix} uses built-in artifact operations only; Squad will not execute plugin-supplied code`);
713
+ }
714
+ }
715
+ function validateOptionalString(field, value, errors) {
716
+ if (value !== undefined && typeof value !== 'string') {
717
+ errors.push(`${field} must be a string when provided`);
718
+ }
719
+ }
720
+ function validateOptionalUrl(field, value, errors) {
721
+ if (value === undefined) {
722
+ return;
723
+ }
724
+ validateUrl(field, value, errors);
725
+ }
726
+ function validateUrl(field, value, errors) {
727
+ if (!value || typeof value !== 'string') {
728
+ errors.push(`${field} is required and must be a URL string`);
729
+ return;
730
+ }
731
+ try {
732
+ const parsed = new URL(value);
733
+ if (parsed.protocol !== 'https:') {
734
+ errors.push(`${field} must use https`);
735
+ }
736
+ }
737
+ catch {
738
+ errors.push(`${field} must be a valid URL`);
739
+ }
740
+ }
741
+ function parseDeclarativeYaml(content) {
742
+ const lines = content
743
+ .split(/\r?\n/)
744
+ .map((line) => line.replace(/\s+#.*$/, '').trimEnd())
745
+ .filter((line) => line.trim().length > 0 && !line.trimStart().startsWith('#'));
746
+ const root = {};
747
+ let currentArrayKey;
748
+ let currentArrayItem;
749
+ for (const line of lines) {
750
+ if (!line.startsWith(' ') && !line.startsWith('-')) {
751
+ const [key, value] = splitYamlPair(line);
752
+ if (!key) {
753
+ throw new Error(`Invalid YAML line: ${line}`);
754
+ }
755
+ if (value === '') {
756
+ root[key] = [];
757
+ currentArrayKey = key;
758
+ currentArrayItem = undefined;
759
+ }
760
+ else {
761
+ root[key] = parseYamlScalar(value);
762
+ currentArrayKey = undefined;
763
+ currentArrayItem = undefined;
764
+ }
765
+ continue;
766
+ }
767
+ const trimmed = line.trimStart();
768
+ if (!currentArrayKey || !Array.isArray(root[currentArrayKey])) {
769
+ throw new Error(`Nested YAML is only supported for top-level arrays: ${line}`);
770
+ }
771
+ if (trimmed.startsWith('- ')) {
772
+ const itemText = trimmed.slice(2);
773
+ currentArrayItem = {};
774
+ root[currentArrayKey].push(currentArrayItem);
775
+ if (itemText.length > 0) {
776
+ const [key, value] = splitYamlPair(itemText);
777
+ currentArrayItem[key] = parseYamlScalar(value);
778
+ }
779
+ continue;
780
+ }
781
+ if (!currentArrayItem) {
782
+ throw new Error(`YAML array property appears before an array item: ${line}`);
783
+ }
784
+ const [key, value] = splitYamlPair(trimmed);
785
+ currentArrayItem[key] = parseYamlScalar(value);
786
+ }
787
+ return root;
788
+ }
789
+ function splitYamlPair(line) {
790
+ const index = line.indexOf(':');
791
+ if (index === -1) {
792
+ throw new Error(`Invalid YAML key/value line: ${line}`);
793
+ }
794
+ return [line.slice(0, index).trim(), line.slice(index + 1).trim()];
795
+ }
796
+ function parseYamlScalar(value) {
797
+ if (value === '')
798
+ return '';
799
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
800
+ return value.slice(1, -1);
801
+ }
802
+ if (value.startsWith('[') && value.endsWith(']')) {
803
+ const inner = value.slice(1, -1).trim();
804
+ if (!inner)
805
+ return [];
806
+ return inner.split(',').map((item) => String(parseYamlScalar(item.trim())));
807
+ }
808
+ if (value === 'true')
809
+ return true;
810
+ if (value === 'false')
811
+ return false;
812
+ return value;
813
+ }
814
+ export function toPosixRelativePath(pathValue) {
815
+ return normalize(pathValue).split(sep).join('/');
816
+ }
817
+ export function describePluginFile(file) {
818
+ return `${basename(file.source)} → ${file.target}`;
819
+ }
820
+ //# sourceMappingURL=plugin-manifest.js.map