agentpacks 1.7.6 → 1.7.9

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 (133) hide show
  1. package/README.md +69 -742
  2. package/dist/api.d.ts +15 -15
  3. package/dist/api.js +2706 -2706
  4. package/dist/cli/export-cmd.js +364 -364
  5. package/dist/cli/generate.js +1409 -1409
  6. package/dist/cli/import-cmd.js +249 -249
  7. package/dist/cli/info.js +31 -31
  8. package/dist/cli/init.js +6 -6
  9. package/dist/cli/install.js +141 -141
  10. package/dist/cli/login.js +31 -31
  11. package/dist/cli/models-explain.js +514 -514
  12. package/dist/cli/pack/create.js +6 -6
  13. package/dist/cli/pack/enable.js +2 -2
  14. package/dist/cli/pack/list.js +362 -362
  15. package/dist/cli/pack/validate.js +119 -119
  16. package/dist/cli/publish.js +42 -42
  17. package/dist/cli/search.js +31 -31
  18. package/dist/core/config.js +3 -3
  19. package/dist/core/feature-merger.d.ts +5 -5
  20. package/dist/core/feature-merger.js +4 -4
  21. package/dist/core/index.d.ts +4 -4
  22. package/dist/core/index.js +633 -633
  23. package/dist/core/lockfile.js +1 -1
  24. package/dist/core/metarepo.js +3 -3
  25. package/dist/core/pack-loader.d.ts +6 -6
  26. package/dist/core/pack-loader.js +362 -362
  27. package/dist/core/profile-resolver.d.ts +1 -1
  28. package/dist/exporters/cursor-plugin.d.ts +12 -0
  29. package/dist/exporters/cursor-plugin.js +46 -46
  30. package/dist/exporters/index.d.ts +1 -1
  31. package/dist/exporters/index.js +46 -46
  32. package/dist/features/agents.js +4 -4
  33. package/dist/features/commands.js +4 -4
  34. package/dist/features/hooks.js +4 -4
  35. package/dist/features/index.d.ts +8 -8
  36. package/dist/features/index.js +350 -350
  37. package/dist/features/mcp.js +4 -4
  38. package/dist/features/models.js +4 -4
  39. package/dist/features/plugins.js +5 -5
  40. package/dist/features/rules.js +4 -4
  41. package/dist/features/skills.js +5 -5
  42. package/dist/importers/claude-code.js +6 -6
  43. package/dist/importers/cursor.js +7 -7
  44. package/dist/importers/opencode.js +7 -7
  45. package/dist/importers/rulesync.js +7 -7
  46. package/dist/index.js +2674 -2674
  47. package/dist/node/api.js +2706 -2706
  48. package/dist/node/cli/export-cmd.js +364 -364
  49. package/dist/node/cli/generate.js +1409 -1409
  50. package/dist/node/cli/import-cmd.js +249 -249
  51. package/dist/node/cli/info.js +31 -31
  52. package/dist/node/cli/init.js +6 -6
  53. package/dist/node/cli/install.js +141 -141
  54. package/dist/node/cli/login.js +31 -31
  55. package/dist/node/cli/models-explain.js +514 -514
  56. package/dist/node/cli/pack/create.js +6 -6
  57. package/dist/node/cli/pack/enable.js +2 -2
  58. package/dist/node/cli/pack/list.js +362 -362
  59. package/dist/node/cli/pack/validate.js +119 -119
  60. package/dist/node/cli/publish.js +42 -42
  61. package/dist/node/cli/search.js +31 -31
  62. package/dist/node/core/config.js +3 -3
  63. package/dist/node/core/feature-merger.js +4 -4
  64. package/dist/node/core/index.js +633 -633
  65. package/dist/node/core/lockfile.js +1 -1
  66. package/dist/node/core/metarepo.js +3 -3
  67. package/dist/node/core/pack-loader.js +362 -362
  68. package/dist/node/exporters/cursor-plugin.js +46 -46
  69. package/dist/node/exporters/index.js +46 -46
  70. package/dist/node/features/agents.js +4 -4
  71. package/dist/node/features/commands.js +4 -4
  72. package/dist/node/features/hooks.js +4 -4
  73. package/dist/node/features/index.js +350 -350
  74. package/dist/node/features/mcp.js +4 -4
  75. package/dist/node/features/models.js +4 -4
  76. package/dist/node/features/plugins.js +5 -5
  77. package/dist/node/features/rules.js +4 -4
  78. package/dist/node/features/skills.js +5 -5
  79. package/dist/node/importers/claude-code.js +6 -6
  80. package/dist/node/importers/cursor.js +7 -7
  81. package/dist/node/importers/opencode.js +7 -7
  82. package/dist/node/importers/rulesync.js +7 -7
  83. package/dist/node/index.js +2674 -2674
  84. package/dist/node/sources/git.js +2 -2
  85. package/dist/node/sources/index.js +138 -138
  86. package/dist/node/sources/local.js +1 -1
  87. package/dist/node/sources/npm.js +3 -3
  88. package/dist/node/sources/registry.js +35 -35
  89. package/dist/node/targets/additional-targets.js +43 -43
  90. package/dist/node/targets/agents-md.js +4 -4
  91. package/dist/node/targets/claude-code.js +86 -86
  92. package/dist/node/targets/codex-cli.js +6 -6
  93. package/dist/node/targets/copilot.js +46 -46
  94. package/dist/node/targets/cursor.js +68 -68
  95. package/dist/node/targets/gemini-cli.js +25 -25
  96. package/dist/node/targets/generic-md-target.js +43 -43
  97. package/dist/node/targets/index.js +911 -911
  98. package/dist/node/targets/mistral-vibe.js +46 -46
  99. package/dist/node/targets/opencode.js +68 -68
  100. package/dist/node/targets/registry.js +911 -911
  101. package/dist/node/utils/credentials.js +2 -2
  102. package/dist/node/utils/filesystem.js +4 -4
  103. package/dist/node/utils/global.js +1 -1
  104. package/dist/node/utils/tarball.js +2 -2
  105. package/dist/sources/git.js +2 -2
  106. package/dist/sources/index.d.ts +6 -6
  107. package/dist/sources/index.js +138 -138
  108. package/dist/sources/local.js +1 -1
  109. package/dist/sources/npm.d.ts +3 -0
  110. package/dist/sources/npm.js +3 -3
  111. package/dist/sources/registry.js +35 -35
  112. package/dist/targets/additional-targets.js +43 -43
  113. package/dist/targets/agents-md.js +4 -4
  114. package/dist/targets/claude-code.js +86 -86
  115. package/dist/targets/codex-cli.js +6 -6
  116. package/dist/targets/copilot.js +46 -46
  117. package/dist/targets/cursor.js +68 -68
  118. package/dist/targets/gemini-cli.js +25 -25
  119. package/dist/targets/generic-md-target.js +43 -43
  120. package/dist/targets/index.d.ts +6 -6
  121. package/dist/targets/index.js +911 -911
  122. package/dist/targets/mistral-vibe.js +46 -46
  123. package/dist/targets/opencode.js +68 -68
  124. package/dist/targets/registry.js +911 -911
  125. package/dist/utils/credentials.js +2 -2
  126. package/dist/utils/filesystem.js +4 -4
  127. package/dist/utils/global.d.ts +3 -0
  128. package/dist/utils/global.js +1 -1
  129. package/dist/utils/tarball.js +2 -2
  130. package/package.json +5 -5
  131. package/templates/pack/models.json +36 -36
  132. package/templates/pack/pack.json +8 -8
  133. package/templates/workspace/agentpacks.jsonc +24 -24
@@ -2,10 +2,10 @@
2
2
  var __require = import.meta.require;
3
3
 
4
4
  // src/core/config.ts
5
- import { z } from "zod";
6
- import { readFileSync, existsSync } from "fs";
7
- import { resolve } from "path";
5
+ import { existsSync, readFileSync } from "fs";
8
6
  import { parse as parseJsonc } from "jsonc-parser";
7
+ import { resolve } from "path";
8
+ import { z } from "zod";
9
9
  var TARGET_IDS = [
10
10
  "opencode",
11
11
  "cursor",
@@ -138,13 +138,13 @@ function resolveTargets(config) {
138
138
  import {
139
139
  existsSync as existsSync2,
140
140
  mkdirSync,
141
- readFileSync as readFileSync2,
142
- writeFileSync,
143
141
  readdirSync,
142
+ readFileSync as readFileSync2,
144
143
  rmSync,
145
- statSync
144
+ statSync,
145
+ writeFileSync
146
146
  } from "fs";
147
- import { dirname, relative, join } from "path";
147
+ import { dirname, join, relative } from "path";
148
148
  var GENERATED_HEADER_MD = "<!-- Generated by agentpacks. DO NOT EDIT. -->";
149
149
  var GENERATED_HEADER_JSON = "// Generated by agentpacks. DO NOT EDIT.";
150
150
  var GENERATED_HEADER_JS = "// Generated by agentpacks. DO NOT EDIT.";
@@ -244,194 +244,355 @@ function getHeader(type) {
244
244
  }
245
245
  }
246
246
 
247
- // src/utils/frontmatter.ts
248
- import matter from "gray-matter";
249
- function parseFrontmatter(source) {
250
- const { data, content } = matter(source);
251
- return {
252
- data,
253
- content: content.trim(),
254
- raw: source
255
- };
256
- }
257
- function serializeFrontmatter(data, content) {
258
- const filtered = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
259
- if (Object.keys(filtered).length === 0) {
260
- return content;
261
- }
262
- return matter.stringify(content, filtered);
263
- }
264
-
265
- // src/features/rules.ts
266
- import { readFileSync as readFileSync3 } from "fs";
267
- import { basename } from "path";
268
- function parseRules(rulesDir, packName) {
269
- const files = listFiles(rulesDir, { extension: ".md" });
270
- return files.map((filepath) => parseRuleFile(filepath, packName));
271
- }
272
- function parseRuleFile(filepath, packName) {
273
- const raw = readFileSync3(filepath, "utf-8");
274
- const { data, content } = parseFrontmatter(raw);
275
- return {
276
- name: basename(filepath, ".md"),
277
- sourcePath: filepath,
278
- packName,
279
- meta: data,
280
- content
281
- };
282
- }
283
- function ruleMatchesTarget(rule, targetId) {
284
- const { targets } = rule.meta;
285
- if (!targets || targets === "*")
286
- return true;
287
- if (Array.isArray(targets) && targets.includes("*"))
288
- return true;
289
- return Array.isArray(targets) && targets.includes(targetId);
290
- }
291
- function getRootRules(rules) {
292
- return rules.filter((r) => r.meta.root === true);
293
- }
294
- function getDetailRules(rules) {
295
- return rules.filter((r) => r.meta.root !== true);
296
- }
297
-
298
- // src/features/commands.ts
299
- import { readFileSync as readFileSync4 } from "fs";
300
- import { basename as basename2 } from "path";
301
- function parseCommands(commandsDir, packName) {
302
- const files = listFiles(commandsDir, { extension: ".md" });
303
- return files.map((filepath) => parseCommandFile(filepath, packName));
304
- }
305
- function parseCommandFile(filepath, packName) {
306
- const raw = readFileSync4(filepath, "utf-8");
307
- const { data, content } = parseFrontmatter(raw);
308
- return {
309
- name: basename2(filepath, ".md"),
310
- sourcePath: filepath,
311
- packName,
312
- meta: data,
313
- content
314
- };
315
- }
316
- function commandMatchesTarget(cmd, targetId) {
317
- const { targets } = cmd.meta;
318
- if (!targets || targets === "*")
319
- return true;
320
- if (Array.isArray(targets) && targets.includes("*"))
321
- return true;
322
- return Array.isArray(targets) && targets.includes(targetId);
323
- }
324
-
325
- // src/features/agents.ts
326
- import { readFileSync as readFileSync5 } from "fs";
327
- import { basename as basename3 } from "path";
328
- function parseAgents(agentsDir, packName) {
329
- const files = listFiles(agentsDir, { extension: ".md" });
330
- return files.map((filepath) => parseAgentFile(filepath, packName));
331
- }
332
- function parseAgentFile(filepath, packName) {
333
- const raw = readFileSync5(filepath, "utf-8");
334
- const { data, content } = parseFrontmatter(raw);
335
- return {
336
- name: data.name ?? basename3(filepath, ".md"),
337
- sourcePath: filepath,
338
- packName,
339
- meta: data,
340
- content
341
- };
342
- }
343
- function agentMatchesTarget(agent, targetId) {
344
- const { targets } = agent.meta;
345
- if (!targets || targets === "*")
346
- return true;
347
- if (Array.isArray(targets) && targets.includes("*"))
348
- return true;
349
- return Array.isArray(targets) && targets.includes(targetId);
350
- }
351
-
352
- // src/features/skills.ts
353
- import { readFileSync as readFileSync6, existsSync as existsSync3 } from "fs";
354
- import { basename as basename4, join as join2 } from "path";
355
- var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
356
- var SKILL_NAME_MAX_LENGTH = 64;
357
- function parseSkills(skillsDir, packName) {
358
- const dirs = listDirs(skillsDir);
359
- const skills = [];
360
- for (const dir of dirs) {
361
- const skillMd = join2(dir, "SKILL.md");
362
- if (existsSync3(skillMd)) {
363
- skills.push(parseSkillFile(skillMd, dir, packName));
364
- }
365
- }
366
- return skills;
367
- }
368
- function parseSkillFile(filepath, skillDir, packName) {
369
- const raw = readFileSync6(filepath, "utf-8");
370
- const { data, content } = parseFrontmatter(raw);
247
+ // src/features/models.ts
248
+ import { join as join2 } from "path";
249
+ import { z as z2 } from "zod";
250
+ var SECRET_PATTERNS = [
251
+ /["']api[_-]?key["']\s*:/i,
252
+ /["']apiKey["']\s*:/i,
253
+ /["']secret["']\s*:/i,
254
+ /["']password["']\s*:/i,
255
+ /["'](?:auth_token|access_token|bearer_token)["']\s*:/i,
256
+ /["']private[_-]?key["']\s*:/i,
257
+ /-----BEGIN\s+(RSA|EC|DSA|OPENSSH|PGP)\s+PRIVATE\s+KEY-----/,
258
+ /sk-[a-zA-Z0-9]{20,}/,
259
+ /Bearer\s+[a-zA-Z0-9._-]{20,}/
260
+ ];
261
+ var AgentModelSchema = z2.object({
262
+ model: z2.string(),
263
+ temperature: z2.number().min(0).max(2).optional(),
264
+ top_p: z2.number().min(0).max(1).optional()
265
+ });
266
+ var ModelProfileSchema = z2.object({
267
+ extends: z2.string().optional(),
268
+ description: z2.string().optional(),
269
+ default: z2.string().optional(),
270
+ small: z2.string().optional(),
271
+ agents: z2.record(z2.string(), AgentModelSchema).optional()
272
+ });
273
+ var RoutingConditionSchema = z2.object({
274
+ complexity: z2.enum(["low", "medium", "high", "critical"]).optional().describe("Task complexity level"),
275
+ urgency: z2.enum(["low", "normal", "high"]).optional().describe("Time sensitivity"),
276
+ budget: z2.enum(["minimal", "standard", "premium"]).optional().describe("Cost/token budget tier"),
277
+ contextWindowNeed: z2.enum(["small", "medium", "large", "max"]).optional().describe("Required context window size"),
278
+ toolUseIntensity: z2.enum(["none", "light", "heavy"]).optional().describe("Expected tool/function calling intensity")
279
+ });
280
+ var RoutingRuleSchema = z2.object({
281
+ when: z2.record(z2.string(), z2.string()),
282
+ use: z2.string(),
283
+ description: z2.string().optional(),
284
+ priority: z2.number().optional()
285
+ });
286
+ var ProviderModelSchema = z2.object({
287
+ options: z2.record(z2.string(), z2.unknown()).optional(),
288
+ variants: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional()
289
+ });
290
+ var ProviderConfigSchema = z2.object({
291
+ options: z2.record(z2.string(), z2.unknown()).optional(),
292
+ models: z2.record(z2.string(), ProviderModelSchema).optional()
293
+ });
294
+ var ModelsSchema = z2.object({
295
+ default: z2.string().optional(),
296
+ small: z2.string().optional(),
297
+ agents: z2.record(z2.string(), AgentModelSchema).optional(),
298
+ profiles: z2.record(z2.string(), ModelProfileSchema).optional(),
299
+ providers: z2.record(z2.string(), ProviderConfigSchema).optional(),
300
+ routing: z2.array(RoutingRuleSchema).optional(),
301
+ overrides: z2.record(z2.string(), z2.object({
302
+ default: z2.string().optional(),
303
+ small: z2.string().optional(),
304
+ agents: z2.record(z2.string(), AgentModelSchema).optional()
305
+ })).optional()
306
+ });
307
+ function parseModels(packDir, packName) {
308
+ const modelsPath = join2(packDir, "models.json");
309
+ const raw = readJsonOrNull(modelsPath);
310
+ if (!raw)
311
+ return null;
312
+ const parsed = ModelsSchema.parse(raw);
371
313
  return {
372
- name: data.name ?? basename4(skillDir),
373
- sourcePath: filepath,
374
- sourceDir: skillDir,
375
314
  packName,
376
- meta: data,
377
- content
378
- };
379
- }
380
- function buildSkillFrontmatter(skill) {
381
- return {
382
- ...skill.meta,
383
- name: skill.name
384
- };
385
- }
386
- function serializeSkill(skill) {
387
- return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
388
- }
389
- function normalizeImportedSkillMarkdown(source, skillName) {
390
- const { data, content } = parseFrontmatter(source);
391
- const normalized = {
392
- ...data,
393
- name: skillName
394
- };
395
- let addedDescription = false;
396
- const description = normalized.description;
397
- if (typeof description !== "string" || description.trim().length === 0) {
398
- normalized.description = `Imported skill: ${skillName}`;
399
- addedDescription = true;
400
- }
401
- return {
402
- content: serializeFrontmatter(normalized, content),
403
- addedDescription
315
+ sourcePath: modelsPath,
316
+ config: parsed
404
317
  };
405
318
  }
406
- function validateAgentSkillsFrontmatter(skill) {
407
- const errors = [];
408
- const dirName = basename4(skill.sourceDir);
409
- const declaredName = skill.meta.name;
410
- if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
411
- errors.push('Missing required frontmatter field "name".');
412
- } else {
413
- if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
414
- errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
319
+ function mergeModelsConfigs(configs) {
320
+ const warnings = [];
321
+ const result = {};
322
+ for (const entry of configs) {
323
+ const { config, packName } = entry;
324
+ if (config.default !== undefined && result.default === undefined) {
325
+ result.default = config.default;
326
+ } else if (config.default !== undefined && result.default !== undefined) {
327
+ warnings.push(`Models "default" from pack "${packName}" skipped (already defined).`);
415
328
  }
416
- if (!SKILL_NAME_PATTERN.test(declaredName)) {
417
- errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
329
+ if (config.small !== undefined && result.small === undefined) {
330
+ result.small = config.small;
331
+ } else if (config.small !== undefined && result.small !== undefined) {
332
+ warnings.push(`Models "small" from pack "${packName}" skipped (already defined).`);
418
333
  }
419
- if (declaredName !== dirName) {
420
- errors.push(`Invalid "name": must match containing directory "${dirName}".`);
334
+ if (config.agents) {
335
+ if (!result.agents)
336
+ result.agents = {};
337
+ for (const [name, assignment] of Object.entries(config.agents)) {
338
+ if (name in result.agents) {
339
+ warnings.push(`Models agent "${name}" from pack "${packName}" skipped (already defined).`);
340
+ continue;
341
+ }
342
+ result.agents[name] = assignment;
343
+ }
344
+ }
345
+ if (config.profiles) {
346
+ if (!result.profiles)
347
+ result.profiles = {};
348
+ for (const [name, profile] of Object.entries(config.profiles)) {
349
+ if (name in result.profiles) {
350
+ warnings.push(`Models profile "${name}" from pack "${packName}" skipped (already defined).`);
351
+ continue;
352
+ }
353
+ result.profiles[name] = profile;
354
+ }
355
+ }
356
+ if (config.providers) {
357
+ if (!result.providers)
358
+ result.providers = {};
359
+ for (const [providerName, providerConfig] of Object.entries(config.providers)) {
360
+ if (!(providerName in result.providers)) {
361
+ result.providers[providerName] = providerConfig;
362
+ } else {
363
+ const existing = result.providers[providerName];
364
+ if (!existing) {
365
+ result.providers[providerName] = providerConfig;
366
+ continue;
367
+ }
368
+ if (providerConfig.options) {
369
+ existing.options = {
370
+ ...providerConfig.options,
371
+ ...existing.options
372
+ };
373
+ }
374
+ if (providerConfig.models) {
375
+ if (!existing.models)
376
+ existing.models = {};
377
+ for (const [modelName, modelConfig] of Object.entries(providerConfig.models)) {
378
+ if (!(modelName in existing.models)) {
379
+ existing.models[modelName] = modelConfig;
380
+ }
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ if (config.routing) {
387
+ if (!result.routing)
388
+ result.routing = [];
389
+ result.routing.push(...config.routing);
390
+ }
391
+ if (config.overrides) {
392
+ if (!result.overrides)
393
+ result.overrides = {};
394
+ for (const [targetId, override] of Object.entries(config.overrides)) {
395
+ if (targetId in result.overrides) {
396
+ warnings.push(`Models override for target "${targetId}" from pack "${packName}" skipped (already defined).`);
397
+ continue;
398
+ }
399
+ result.overrides[targetId] = override;
400
+ }
421
401
  }
422
402
  }
423
- const description = skill.meta.description;
424
- if (typeof description !== "string" || description.trim().length === 0) {
425
- errors.push('Missing required frontmatter field "description".');
426
- }
427
- const allowedTools = skill.meta["allowed-tools"];
428
- if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
429
- errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
430
- }
431
- return errors;
403
+ return { config: result, warnings };
432
404
  }
433
- function skillMatchesTarget(skill, targetId) {
434
- const { targets } = skill.meta;
405
+ function scanModelsForSecrets(config) {
406
+ const warnings = [];
407
+ const json = JSON.stringify(config);
408
+ for (const pattern of SECRET_PATTERNS) {
409
+ if (pattern.test(json)) {
410
+ warnings.push(`Potential secret detected in models.json matching pattern: ${pattern.source}`);
411
+ }
412
+ }
413
+ return warnings;
414
+ }
415
+
416
+ // src/core/feature-merger.ts
417
+ class FeatureMerger {
418
+ packs;
419
+ warnings = [];
420
+ constructor(packs) {
421
+ this.packs = packs;
422
+ }
423
+ merge() {
424
+ this.warnings = [];
425
+ const features = {
426
+ rules: this.mergeRules(),
427
+ commands: this.mergeByName("commands"),
428
+ agents: this.mergeByName("agents"),
429
+ skills: this.mergeByName("skills"),
430
+ hooks: this.mergeHooks(),
431
+ plugins: this.mergePlugins(),
432
+ mcpServers: this.mergeMcp(),
433
+ ignorePatterns: this.mergeIgnore(),
434
+ models: this.mergeModels()
435
+ };
436
+ return { features, warnings: this.warnings };
437
+ }
438
+ mergeRules() {
439
+ const seen = new Map;
440
+ const result = [];
441
+ for (const pack of this.packs) {
442
+ for (const rule of pack.rules) {
443
+ const existing = seen.get(rule.name);
444
+ if (existing) {
445
+ this.warnings.push(`Rule "${rule.name}" from pack "${rule.packName}" skipped (already defined by "${existing}").`);
446
+ continue;
447
+ }
448
+ seen.set(rule.name, rule.packName);
449
+ result.push(rule);
450
+ }
451
+ }
452
+ return result;
453
+ }
454
+ mergeByName(featureKey) {
455
+ const seen = new Map;
456
+ const result = [];
457
+ for (const pack of this.packs) {
458
+ const items = pack[featureKey];
459
+ for (const item of items) {
460
+ const existing = seen.get(item.name);
461
+ if (existing) {
462
+ this.warnings.push(`${featureKey.slice(0, -1)} "${item.name}" from pack "${item.packName}" skipped (already defined by "${existing}").`);
463
+ continue;
464
+ }
465
+ seen.set(item.name, item.packName);
466
+ result.push(item);
467
+ }
468
+ }
469
+ return result;
470
+ }
471
+ mergeHooks() {
472
+ return this.packs.map((p) => p.hooks).filter((h) => h !== null);
473
+ }
474
+ mergePlugins() {
475
+ const seen = new Map;
476
+ const result = [];
477
+ for (const pack of this.packs) {
478
+ for (const plugin of pack.plugins) {
479
+ const key = `${plugin.name}.${plugin.extension}`;
480
+ const existing = seen.get(key);
481
+ if (existing) {
482
+ this.warnings.push(`Plugin "${key}" from pack "${plugin.packName}" skipped (already defined by "${existing}").`);
483
+ continue;
484
+ }
485
+ seen.set(key, plugin.packName);
486
+ result.push(plugin);
487
+ }
488
+ }
489
+ return result;
490
+ }
491
+ mergeMcp() {
492
+ const servers = {};
493
+ for (const pack of this.packs) {
494
+ if (!pack.mcp)
495
+ continue;
496
+ for (const [name, entry] of Object.entries(pack.mcp.servers)) {
497
+ if (name in servers) {
498
+ this.warnings.push(`MCP server "${name}" from pack "${pack.manifest.name}" skipped (already defined).`);
499
+ continue;
500
+ }
501
+ servers[name] = entry;
502
+ }
503
+ }
504
+ return servers;
505
+ }
506
+ mergeIgnore() {
507
+ const seen = new Set;
508
+ const result = [];
509
+ for (const pack of this.packs) {
510
+ if (!pack.ignore)
511
+ continue;
512
+ for (const pattern of pack.ignore.patterns) {
513
+ if (!seen.has(pattern)) {
514
+ seen.add(pattern);
515
+ result.push(pattern);
516
+ }
517
+ }
518
+ }
519
+ return result;
520
+ }
521
+ mergeModels() {
522
+ const configs = this.packs.map((p) => p.models).filter((m) => m != null);
523
+ if (configs.length === 0)
524
+ return null;
525
+ const { config, warnings } = mergeModelsConfigs(configs);
526
+ this.warnings.push(...warnings);
527
+ return config;
528
+ }
529
+ }
530
+
531
+ // src/utils/frontmatter.ts
532
+ import matter from "gray-matter";
533
+ function parseFrontmatter(source) {
534
+ const { data, content } = matter(source);
535
+ return {
536
+ data,
537
+ content: content.trim(),
538
+ raw: source
539
+ };
540
+ }
541
+ function serializeFrontmatter(data, content) {
542
+ const filtered = Object.fromEntries(Object.entries(data).filter(([, v]) => v !== undefined));
543
+ if (Object.keys(filtered).length === 0) {
544
+ return content;
545
+ }
546
+ return matter.stringify(content, filtered);
547
+ }
548
+
549
+ // src/features/agents.ts
550
+ import { readFileSync as readFileSync3 } from "fs";
551
+ import { basename } from "path";
552
+ function parseAgents(agentsDir, packName) {
553
+ const files = listFiles(agentsDir, { extension: ".md" });
554
+ return files.map((filepath) => parseAgentFile(filepath, packName));
555
+ }
556
+ function parseAgentFile(filepath, packName) {
557
+ const raw = readFileSync3(filepath, "utf-8");
558
+ const { data, content } = parseFrontmatter(raw);
559
+ return {
560
+ name: data.name ?? basename(filepath, ".md"),
561
+ sourcePath: filepath,
562
+ packName,
563
+ meta: data,
564
+ content
565
+ };
566
+ }
567
+ function agentMatchesTarget(agent, targetId) {
568
+ const { targets } = agent.meta;
569
+ if (!targets || targets === "*")
570
+ return true;
571
+ if (Array.isArray(targets) && targets.includes("*"))
572
+ return true;
573
+ return Array.isArray(targets) && targets.includes(targetId);
574
+ }
575
+
576
+ // src/features/commands.ts
577
+ import { readFileSync as readFileSync4 } from "fs";
578
+ import { basename as basename2 } from "path";
579
+ function parseCommands(commandsDir, packName) {
580
+ const files = listFiles(commandsDir, { extension: ".md" });
581
+ return files.map((filepath) => parseCommandFile(filepath, packName));
582
+ }
583
+ function parseCommandFile(filepath, packName) {
584
+ const raw = readFileSync4(filepath, "utf-8");
585
+ const { data, content } = parseFrontmatter(raw);
586
+ return {
587
+ name: basename2(filepath, ".md"),
588
+ sourcePath: filepath,
589
+ packName,
590
+ meta: data,
591
+ content
592
+ };
593
+ }
594
+ function commandMatchesTarget(cmd, targetId) {
595
+ const { targets } = cmd.meta;
435
596
  if (!targets || targets === "*")
436
597
  return true;
437
598
  if (Array.isArray(targets) && targets.includes("*"))
@@ -477,28 +638,41 @@ function resolveHooksForTarget(hooks, targetId) {
477
638
  return merged;
478
639
  }
479
640
 
480
- // src/features/plugins.ts
481
- import { readFileSync as readFileSync7, existsSync as existsSync4 } from "fs";
482
- import { basename as basename5, join as join4 } from "path";
483
- function parsePlugins(packDir, packName) {
484
- const pluginsDir = join4(packDir, "plugins");
485
- if (!existsSync4(pluginsDir))
486
- return [];
487
- const tsFiles = listFiles(pluginsDir, { extension: ".ts" });
488
- const jsFiles = listFiles(pluginsDir, { extension: ".js" });
489
- const allFiles = [...tsFiles, ...jsFiles];
490
- return allFiles.map((filepath) => parsePluginFile(filepath, packName));
641
+ // src/features/ignore.ts
642
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
643
+ import { join as join4 } from "path";
644
+ var IGNORE_FILES = ["ignore", ".aiignore"];
645
+ function parseIgnore(packDir, packName) {
646
+ for (const filename of IGNORE_FILES) {
647
+ const filepath = join4(packDir, filename);
648
+ if (existsSync3(filepath)) {
649
+ const raw = readFileSync5(filepath, "utf-8");
650
+ const patterns = parseIgnoreContent(raw);
651
+ return {
652
+ packName,
653
+ sourcePath: filepath,
654
+ patterns
655
+ };
656
+ }
657
+ }
658
+ return null;
491
659
  }
492
- function parsePluginFile(filepath, packName) {
493
- const content = readFileSync7(filepath, "utf-8");
494
- const ext = filepath.endsWith(".ts") ? "ts" : "js";
495
- return {
496
- name: basename5(filepath, `.${ext}`),
497
- sourcePath: filepath,
498
- packName,
499
- content,
500
- extension: ext
501
- };
660
+ function parseIgnoreContent(content) {
661
+ return content.split(`
662
+ `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
663
+ }
664
+ function mergeIgnorePatterns(configs) {
665
+ const seen = new Set;
666
+ const result = [];
667
+ for (const config of configs) {
668
+ for (const pattern of config.patterns) {
669
+ if (!seen.has(pattern)) {
670
+ seen.add(pattern);
671
+ result.push(pattern);
672
+ }
673
+ }
674
+ }
675
+ return result;
502
676
  }
503
677
 
504
678
  // src/features/mcp.ts
@@ -529,215 +703,156 @@ function mergeMcpConfigs(configs) {
529
703
  return { servers, warnings };
530
704
  }
531
705
 
532
- // src/features/ignore.ts
533
- import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
534
- import { join as join6 } from "path";
535
- var IGNORE_FILES = ["ignore", ".aiignore"];
536
- function parseIgnore(packDir, packName) {
537
- for (const filename of IGNORE_FILES) {
538
- const filepath = join6(packDir, filename);
539
- if (existsSync5(filepath)) {
540
- const raw = readFileSync8(filepath, "utf-8");
541
- const patterns = parseIgnoreContent(raw);
542
- return {
543
- packName,
544
- sourcePath: filepath,
545
- patterns
546
- };
547
- }
548
- }
549
- return null;
706
+ // src/features/plugins.ts
707
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
708
+ import { basename as basename3, join as join6 } from "path";
709
+ function parsePlugins(packDir, packName) {
710
+ const pluginsDir = join6(packDir, "plugins");
711
+ if (!existsSync4(pluginsDir))
712
+ return [];
713
+ const tsFiles = listFiles(pluginsDir, { extension: ".ts" });
714
+ const jsFiles = listFiles(pluginsDir, { extension: ".js" });
715
+ const allFiles = [...tsFiles, ...jsFiles];
716
+ return allFiles.map((filepath) => parsePluginFile(filepath, packName));
550
717
  }
551
- function parseIgnoreContent(content) {
552
- return content.split(`
553
- `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
718
+ function parsePluginFile(filepath, packName) {
719
+ const content = readFileSync6(filepath, "utf-8");
720
+ const ext = filepath.endsWith(".ts") ? "ts" : "js";
721
+ return {
722
+ name: basename3(filepath, `.${ext}`),
723
+ sourcePath: filepath,
724
+ packName,
725
+ content,
726
+ extension: ext
727
+ };
554
728
  }
555
- function mergeIgnorePatterns(configs) {
556
- const seen = new Set;
557
- const result = [];
558
- for (const config of configs) {
559
- for (const pattern of config.patterns) {
560
- if (!seen.has(pattern)) {
561
- seen.add(pattern);
562
- result.push(pattern);
563
- }
729
+
730
+ // src/features/rules.ts
731
+ import { readFileSync as readFileSync7 } from "fs";
732
+ import { basename as basename4 } from "path";
733
+ function parseRules(rulesDir, packName) {
734
+ const files = listFiles(rulesDir, { extension: ".md" });
735
+ return files.map((filepath) => parseRuleFile(filepath, packName));
736
+ }
737
+ function parseRuleFile(filepath, packName) {
738
+ const raw = readFileSync7(filepath, "utf-8");
739
+ const { data, content } = parseFrontmatter(raw);
740
+ return {
741
+ name: basename4(filepath, ".md"),
742
+ sourcePath: filepath,
743
+ packName,
744
+ meta: data,
745
+ content
746
+ };
747
+ }
748
+ function ruleMatchesTarget(rule, targetId) {
749
+ const { targets } = rule.meta;
750
+ if (!targets || targets === "*")
751
+ return true;
752
+ if (Array.isArray(targets) && targets.includes("*"))
753
+ return true;
754
+ return Array.isArray(targets) && targets.includes(targetId);
755
+ }
756
+ function getRootRules(rules) {
757
+ return rules.filter((r) => r.meta.root === true);
758
+ }
759
+ function getDetailRules(rules) {
760
+ return rules.filter((r) => r.meta.root !== true);
761
+ }
762
+
763
+ // src/features/skills.ts
764
+ import { existsSync as existsSync5, readFileSync as readFileSync8 } from "fs";
765
+ import { basename as basename5, join as join7 } from "path";
766
+ var SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
767
+ var SKILL_NAME_MAX_LENGTH = 64;
768
+ function parseSkills(skillsDir, packName) {
769
+ const dirs = listDirs(skillsDir);
770
+ const skills = [];
771
+ for (const dir of dirs) {
772
+ const skillMd = join7(dir, "SKILL.md");
773
+ if (existsSync5(skillMd)) {
774
+ skills.push(parseSkillFile(skillMd, dir, packName));
564
775
  }
565
776
  }
566
- return result;
777
+ return skills;
567
778
  }
568
-
569
- // src/features/models.ts
570
- import { join as join7 } from "path";
571
- import { z as z2 } from "zod";
572
- var SECRET_PATTERNS = [
573
- /["']api[_-]?key["']\s*:/i,
574
- /["']apiKey["']\s*:/i,
575
- /["']secret["']\s*:/i,
576
- /["']password["']\s*:/i,
577
- /["'](?:auth_token|access_token|bearer_token)["']\s*:/i,
578
- /["']private[_-]?key["']\s*:/i,
579
- /-----BEGIN\s+(RSA|EC|DSA|OPENSSH|PGP)\s+PRIVATE\s+KEY-----/,
580
- /sk-[a-zA-Z0-9]{20,}/,
581
- /Bearer\s+[a-zA-Z0-9._-]{20,}/
582
- ];
583
- var AgentModelSchema = z2.object({
584
- model: z2.string(),
585
- temperature: z2.number().min(0).max(2).optional(),
586
- top_p: z2.number().min(0).max(1).optional()
587
- });
588
- var ModelProfileSchema = z2.object({
589
- extends: z2.string().optional(),
590
- description: z2.string().optional(),
591
- default: z2.string().optional(),
592
- small: z2.string().optional(),
593
- agents: z2.record(z2.string(), AgentModelSchema).optional()
594
- });
595
- var RoutingConditionSchema = z2.object({
596
- complexity: z2.enum(["low", "medium", "high", "critical"]).optional().describe("Task complexity level"),
597
- urgency: z2.enum(["low", "normal", "high"]).optional().describe("Time sensitivity"),
598
- budget: z2.enum(["minimal", "standard", "premium"]).optional().describe("Cost/token budget tier"),
599
- contextWindowNeed: z2.enum(["small", "medium", "large", "max"]).optional().describe("Required context window size"),
600
- toolUseIntensity: z2.enum(["none", "light", "heavy"]).optional().describe("Expected tool/function calling intensity")
601
- });
602
- var RoutingRuleSchema = z2.object({
603
- when: z2.record(z2.string(), z2.string()),
604
- use: z2.string(),
605
- description: z2.string().optional(),
606
- priority: z2.number().optional()
607
- });
608
- var ProviderModelSchema = z2.object({
609
- options: z2.record(z2.string(), z2.unknown()).optional(),
610
- variants: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional()
611
- });
612
- var ProviderConfigSchema = z2.object({
613
- options: z2.record(z2.string(), z2.unknown()).optional(),
614
- models: z2.record(z2.string(), ProviderModelSchema).optional()
615
- });
616
- var ModelsSchema = z2.object({
617
- default: z2.string().optional(),
618
- small: z2.string().optional(),
619
- agents: z2.record(z2.string(), AgentModelSchema).optional(),
620
- profiles: z2.record(z2.string(), ModelProfileSchema).optional(),
621
- providers: z2.record(z2.string(), ProviderConfigSchema).optional(),
622
- routing: z2.array(RoutingRuleSchema).optional(),
623
- overrides: z2.record(z2.string(), z2.object({
624
- default: z2.string().optional(),
625
- small: z2.string().optional(),
626
- agents: z2.record(z2.string(), AgentModelSchema).optional()
627
- })).optional()
628
- });
629
- function parseModels(packDir, packName) {
630
- const modelsPath = join7(packDir, "models.json");
631
- const raw = readJsonOrNull(modelsPath);
632
- if (!raw)
633
- return null;
634
- const parsed = ModelsSchema.parse(raw);
779
+ function parseSkillFile(filepath, skillDir, packName) {
780
+ const raw = readFileSync8(filepath, "utf-8");
781
+ const { data, content } = parseFrontmatter(raw);
782
+ return {
783
+ name: data.name ?? basename5(skillDir),
784
+ sourcePath: filepath,
785
+ sourceDir: skillDir,
786
+ packName,
787
+ meta: data,
788
+ content
789
+ };
790
+ }
791
+ function buildSkillFrontmatter(skill) {
792
+ return {
793
+ ...skill.meta,
794
+ name: skill.name
795
+ };
796
+ }
797
+ function serializeSkill(skill) {
798
+ return serializeFrontmatter(buildSkillFrontmatter(skill), skill.content);
799
+ }
800
+ function normalizeImportedSkillMarkdown(source, skillName) {
801
+ const { data, content } = parseFrontmatter(source);
802
+ const normalized = {
803
+ ...data,
804
+ name: skillName
805
+ };
806
+ let addedDescription = false;
807
+ const description = normalized.description;
808
+ if (typeof description !== "string" || description.trim().length === 0) {
809
+ normalized.description = `Imported skill: ${skillName}`;
810
+ addedDescription = true;
811
+ }
635
812
  return {
636
- packName,
637
- sourcePath: modelsPath,
638
- config: parsed
813
+ content: serializeFrontmatter(normalized, content),
814
+ addedDescription
639
815
  };
640
816
  }
641
- function mergeModelsConfigs(configs) {
642
- const warnings = [];
643
- const result = {};
644
- for (const entry of configs) {
645
- const { config, packName } = entry;
646
- if (config.default !== undefined && result.default === undefined) {
647
- result.default = config.default;
648
- } else if (config.default !== undefined && result.default !== undefined) {
649
- warnings.push(`Models "default" from pack "${packName}" skipped (already defined).`);
650
- }
651
- if (config.small !== undefined && result.small === undefined) {
652
- result.small = config.small;
653
- } else if (config.small !== undefined && result.small !== undefined) {
654
- warnings.push(`Models "small" from pack "${packName}" skipped (already defined).`);
655
- }
656
- if (config.agents) {
657
- if (!result.agents)
658
- result.agents = {};
659
- for (const [name, assignment] of Object.entries(config.agents)) {
660
- if (name in result.agents) {
661
- warnings.push(`Models agent "${name}" from pack "${packName}" skipped (already defined).`);
662
- continue;
663
- }
664
- result.agents[name] = assignment;
665
- }
666
- }
667
- if (config.profiles) {
668
- if (!result.profiles)
669
- result.profiles = {};
670
- for (const [name, profile] of Object.entries(config.profiles)) {
671
- if (name in result.profiles) {
672
- warnings.push(`Models profile "${name}" from pack "${packName}" skipped (already defined).`);
673
- continue;
674
- }
675
- result.profiles[name] = profile;
676
- }
677
- }
678
- if (config.providers) {
679
- if (!result.providers)
680
- result.providers = {};
681
- for (const [providerName, providerConfig] of Object.entries(config.providers)) {
682
- if (!(providerName in result.providers)) {
683
- result.providers[providerName] = providerConfig;
684
- } else {
685
- const existing = result.providers[providerName];
686
- if (!existing) {
687
- result.providers[providerName] = providerConfig;
688
- continue;
689
- }
690
- if (providerConfig.options) {
691
- existing.options = {
692
- ...providerConfig.options,
693
- ...existing.options
694
- };
695
- }
696
- if (providerConfig.models) {
697
- if (!existing.models)
698
- existing.models = {};
699
- for (const [modelName, modelConfig] of Object.entries(providerConfig.models)) {
700
- if (!(modelName in existing.models)) {
701
- existing.models[modelName] = modelConfig;
702
- }
703
- }
704
- }
705
- }
706
- }
817
+ function validateAgentSkillsFrontmatter(skill) {
818
+ const errors = [];
819
+ const dirName = basename5(skill.sourceDir);
820
+ const declaredName = skill.meta.name;
821
+ if (typeof declaredName !== "string" || declaredName.trim().length === 0) {
822
+ errors.push('Missing required frontmatter field "name".');
823
+ } else {
824
+ if (declaredName.length > SKILL_NAME_MAX_LENGTH) {
825
+ errors.push(`Invalid "name": must be at most ${SKILL_NAME_MAX_LENGTH} characters.`);
707
826
  }
708
- if (config.routing) {
709
- if (!result.routing)
710
- result.routing = [];
711
- result.routing.push(...config.routing);
827
+ if (!SKILL_NAME_PATTERN.test(declaredName)) {
828
+ errors.push('Invalid "name": use lowercase letters, numbers, and single hyphens only.');
712
829
  }
713
- if (config.overrides) {
714
- if (!result.overrides)
715
- result.overrides = {};
716
- for (const [targetId, override] of Object.entries(config.overrides)) {
717
- if (targetId in result.overrides) {
718
- warnings.push(`Models override for target "${targetId}" from pack "${packName}" skipped (already defined).`);
719
- continue;
720
- }
721
- result.overrides[targetId] = override;
722
- }
830
+ if (declaredName !== dirName) {
831
+ errors.push(`Invalid "name": must match containing directory "${dirName}".`);
723
832
  }
724
833
  }
725
- return { config: result, warnings };
726
- }
727
- function scanModelsForSecrets(config) {
728
- const warnings = [];
729
- const json = JSON.stringify(config);
730
- for (const pattern of SECRET_PATTERNS) {
731
- if (pattern.test(json)) {
732
- warnings.push(`Potential secret detected in models.json matching pattern: ${pattern.source}`);
733
- }
834
+ const description = skill.meta.description;
835
+ if (typeof description !== "string" || description.trim().length === 0) {
836
+ errors.push('Missing required frontmatter field "description".');
734
837
  }
735
- return warnings;
838
+ const allowedTools = skill.meta["allowed-tools"];
839
+ if (allowedTools !== undefined && (!Array.isArray(allowedTools) || allowedTools.some((tool) => typeof tool !== "string" || tool.length === 0))) {
840
+ errors.push('Invalid "allowed-tools": expected an array of non-empty strings.');
841
+ }
842
+ return errors;
843
+ }
844
+ function skillMatchesTarget(skill, targetId) {
845
+ const { targets } = skill.meta;
846
+ if (!targets || targets === "*")
847
+ return true;
848
+ if (Array.isArray(targets) && targets.includes("*"))
849
+ return true;
850
+ return Array.isArray(targets) && targets.includes(targetId);
736
851
  }
737
852
 
738
853
  // src/core/pack-loader.ts
739
854
  import { existsSync as existsSync6 } from "fs";
740
- import { resolve as resolve2, isAbsolute } from "path";
855
+ import { isAbsolute, resolve as resolve2 } from "path";
741
856
  class PackLoader {
742
857
  projectRoot;
743
858
  config;
@@ -838,121 +953,6 @@ class PackLoader {
838
953
  }
839
954
  }
840
955
 
841
- // src/core/feature-merger.ts
842
- class FeatureMerger {
843
- packs;
844
- warnings = [];
845
- constructor(packs) {
846
- this.packs = packs;
847
- }
848
- merge() {
849
- this.warnings = [];
850
- const features = {
851
- rules: this.mergeRules(),
852
- commands: this.mergeByName("commands"),
853
- agents: this.mergeByName("agents"),
854
- skills: this.mergeByName("skills"),
855
- hooks: this.mergeHooks(),
856
- plugins: this.mergePlugins(),
857
- mcpServers: this.mergeMcp(),
858
- ignorePatterns: this.mergeIgnore(),
859
- models: this.mergeModels()
860
- };
861
- return { features, warnings: this.warnings };
862
- }
863
- mergeRules() {
864
- const seen = new Map;
865
- const result = [];
866
- for (const pack of this.packs) {
867
- for (const rule of pack.rules) {
868
- const existing = seen.get(rule.name);
869
- if (existing) {
870
- this.warnings.push(`Rule "${rule.name}" from pack "${rule.packName}" skipped (already defined by "${existing}").`);
871
- continue;
872
- }
873
- seen.set(rule.name, rule.packName);
874
- result.push(rule);
875
- }
876
- }
877
- return result;
878
- }
879
- mergeByName(featureKey) {
880
- const seen = new Map;
881
- const result = [];
882
- for (const pack of this.packs) {
883
- const items = pack[featureKey];
884
- for (const item of items) {
885
- const existing = seen.get(item.name);
886
- if (existing) {
887
- this.warnings.push(`${featureKey.slice(0, -1)} "${item.name}" from pack "${item.packName}" skipped (already defined by "${existing}").`);
888
- continue;
889
- }
890
- seen.set(item.name, item.packName);
891
- result.push(item);
892
- }
893
- }
894
- return result;
895
- }
896
- mergeHooks() {
897
- return this.packs.map((p) => p.hooks).filter((h) => h !== null);
898
- }
899
- mergePlugins() {
900
- const seen = new Map;
901
- const result = [];
902
- for (const pack of this.packs) {
903
- for (const plugin of pack.plugins) {
904
- const key = `${plugin.name}.${plugin.extension}`;
905
- const existing = seen.get(key);
906
- if (existing) {
907
- this.warnings.push(`Plugin "${key}" from pack "${plugin.packName}" skipped (already defined by "${existing}").`);
908
- continue;
909
- }
910
- seen.set(key, plugin.packName);
911
- result.push(plugin);
912
- }
913
- }
914
- return result;
915
- }
916
- mergeMcp() {
917
- const servers = {};
918
- for (const pack of this.packs) {
919
- if (!pack.mcp)
920
- continue;
921
- for (const [name, entry] of Object.entries(pack.mcp.servers)) {
922
- if (name in servers) {
923
- this.warnings.push(`MCP server "${name}" from pack "${pack.manifest.name}" skipped (already defined).`);
924
- continue;
925
- }
926
- servers[name] = entry;
927
- }
928
- }
929
- return servers;
930
- }
931
- mergeIgnore() {
932
- const seen = new Set;
933
- const result = [];
934
- for (const pack of this.packs) {
935
- if (!pack.ignore)
936
- continue;
937
- for (const pattern of pack.ignore.patterns) {
938
- if (!seen.has(pattern)) {
939
- seen.add(pattern);
940
- result.push(pattern);
941
- }
942
- }
943
- }
944
- return result;
945
- }
946
- mergeModels() {
947
- const configs = this.packs.map((p) => p.models).filter((m) => m != null);
948
- if (configs.length === 0)
949
- return null;
950
- const { config, warnings } = mergeModelsConfigs(configs);
951
- this.warnings.push(...warnings);
952
- return config;
953
- }
954
- }
955
-
956
956
  // src/core/profile-resolver.ts
957
957
  function resolveModels(merged, modelProfile, targetId) {
958
958
  let defaultModel = merged.default;