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.
- package/README.md +69 -742
- package/dist/api.d.ts +15 -15
- package/dist/api.js +2706 -2706
- package/dist/cli/export-cmd.js +364 -364
- package/dist/cli/generate.js +1409 -1409
- package/dist/cli/import-cmd.js +249 -249
- package/dist/cli/info.js +31 -31
- package/dist/cli/init.js +6 -6
- package/dist/cli/install.js +141 -141
- package/dist/cli/login.js +31 -31
- package/dist/cli/models-explain.js +514 -514
- package/dist/cli/pack/create.js +6 -6
- package/dist/cli/pack/enable.js +2 -2
- package/dist/cli/pack/list.js +362 -362
- package/dist/cli/pack/validate.js +119 -119
- package/dist/cli/publish.js +42 -42
- package/dist/cli/search.js +31 -31
- package/dist/core/config.js +3 -3
- package/dist/core/feature-merger.d.ts +5 -5
- package/dist/core/feature-merger.js +4 -4
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +633 -633
- package/dist/core/lockfile.js +1 -1
- package/dist/core/metarepo.js +3 -3
- package/dist/core/pack-loader.d.ts +6 -6
- package/dist/core/pack-loader.js +362 -362
- package/dist/core/profile-resolver.d.ts +1 -1
- package/dist/exporters/cursor-plugin.d.ts +12 -0
- package/dist/exporters/cursor-plugin.js +46 -46
- package/dist/exporters/index.d.ts +1 -1
- package/dist/exporters/index.js +46 -46
- package/dist/features/agents.js +4 -4
- package/dist/features/commands.js +4 -4
- package/dist/features/hooks.js +4 -4
- package/dist/features/index.d.ts +8 -8
- package/dist/features/index.js +350 -350
- package/dist/features/mcp.js +4 -4
- package/dist/features/models.js +4 -4
- package/dist/features/plugins.js +5 -5
- package/dist/features/rules.js +4 -4
- package/dist/features/skills.js +5 -5
- package/dist/importers/claude-code.js +6 -6
- package/dist/importers/cursor.js +7 -7
- package/dist/importers/opencode.js +7 -7
- package/dist/importers/rulesync.js +7 -7
- package/dist/index.js +2674 -2674
- package/dist/node/api.js +2706 -2706
- package/dist/node/cli/export-cmd.js +364 -364
- package/dist/node/cli/generate.js +1409 -1409
- package/dist/node/cli/import-cmd.js +249 -249
- package/dist/node/cli/info.js +31 -31
- package/dist/node/cli/init.js +6 -6
- package/dist/node/cli/install.js +141 -141
- package/dist/node/cli/login.js +31 -31
- package/dist/node/cli/models-explain.js +514 -514
- package/dist/node/cli/pack/create.js +6 -6
- package/dist/node/cli/pack/enable.js +2 -2
- package/dist/node/cli/pack/list.js +362 -362
- package/dist/node/cli/pack/validate.js +119 -119
- package/dist/node/cli/publish.js +42 -42
- package/dist/node/cli/search.js +31 -31
- package/dist/node/core/config.js +3 -3
- package/dist/node/core/feature-merger.js +4 -4
- package/dist/node/core/index.js +633 -633
- package/dist/node/core/lockfile.js +1 -1
- package/dist/node/core/metarepo.js +3 -3
- package/dist/node/core/pack-loader.js +362 -362
- package/dist/node/exporters/cursor-plugin.js +46 -46
- package/dist/node/exporters/index.js +46 -46
- package/dist/node/features/agents.js +4 -4
- package/dist/node/features/commands.js +4 -4
- package/dist/node/features/hooks.js +4 -4
- package/dist/node/features/index.js +350 -350
- package/dist/node/features/mcp.js +4 -4
- package/dist/node/features/models.js +4 -4
- package/dist/node/features/plugins.js +5 -5
- package/dist/node/features/rules.js +4 -4
- package/dist/node/features/skills.js +5 -5
- package/dist/node/importers/claude-code.js +6 -6
- package/dist/node/importers/cursor.js +7 -7
- package/dist/node/importers/opencode.js +7 -7
- package/dist/node/importers/rulesync.js +7 -7
- package/dist/node/index.js +2674 -2674
- package/dist/node/sources/git.js +2 -2
- package/dist/node/sources/index.js +138 -138
- package/dist/node/sources/local.js +1 -1
- package/dist/node/sources/npm.js +3 -3
- package/dist/node/sources/registry.js +35 -35
- package/dist/node/targets/additional-targets.js +43 -43
- package/dist/node/targets/agents-md.js +4 -4
- package/dist/node/targets/claude-code.js +86 -86
- package/dist/node/targets/codex-cli.js +6 -6
- package/dist/node/targets/copilot.js +46 -46
- package/dist/node/targets/cursor.js +68 -68
- package/dist/node/targets/gemini-cli.js +25 -25
- package/dist/node/targets/generic-md-target.js +43 -43
- package/dist/node/targets/index.js +911 -911
- package/dist/node/targets/mistral-vibe.js +46 -46
- package/dist/node/targets/opencode.js +68 -68
- package/dist/node/targets/registry.js +911 -911
- package/dist/node/utils/credentials.js +2 -2
- package/dist/node/utils/filesystem.js +4 -4
- package/dist/node/utils/global.js +1 -1
- package/dist/node/utils/tarball.js +2 -2
- package/dist/sources/git.js +2 -2
- package/dist/sources/index.d.ts +6 -6
- package/dist/sources/index.js +138 -138
- package/dist/sources/local.js +1 -1
- package/dist/sources/npm.d.ts +3 -0
- package/dist/sources/npm.js +3 -3
- package/dist/sources/registry.js +35 -35
- package/dist/targets/additional-targets.js +43 -43
- package/dist/targets/agents-md.js +4 -4
- package/dist/targets/claude-code.js +86 -86
- package/dist/targets/codex-cli.js +6 -6
- package/dist/targets/copilot.js +46 -46
- package/dist/targets/cursor.js +68 -68
- package/dist/targets/gemini-cli.js +25 -25
- package/dist/targets/generic-md-target.js +43 -43
- package/dist/targets/index.d.ts +6 -6
- package/dist/targets/index.js +911 -911
- package/dist/targets/mistral-vibe.js +46 -46
- package/dist/targets/opencode.js +68 -68
- package/dist/targets/registry.js +911 -911
- package/dist/utils/credentials.js +2 -2
- package/dist/utils/filesystem.js +4 -4
- package/dist/utils/global.d.ts +3 -0
- package/dist/utils/global.js +1 -1
- package/dist/utils/tarball.js +2 -2
- package/package.json +5 -5
- package/templates/pack/models.json +36 -36
- package/templates/pack/pack.json +8 -8
- 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 {
|
|
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,
|
|
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/
|
|
248
|
-
import
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
377
|
-
|
|
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
|
|
407
|
-
const
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if (
|
|
414
|
-
|
|
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 (
|
|
417
|
-
|
|
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 (
|
|
420
|
-
|
|
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
|
-
|
|
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
|
|
434
|
-
const
|
|
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/
|
|
481
|
-
import {
|
|
482
|
-
import {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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/
|
|
533
|
-
import { existsSync as
|
|
534
|
-
import { join as join6 } from "path";
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
|
777
|
+
return skills;
|
|
567
778
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
-
|
|
637
|
-
|
|
638
|
-
config: parsed
|
|
813
|
+
content: serializeFrontmatter(normalized, content),
|
|
814
|
+
addedDescription
|
|
639
815
|
};
|
|
640
816
|
}
|
|
641
|
-
function
|
|
642
|
-
const
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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 (
|
|
709
|
-
|
|
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 (
|
|
714
|
-
|
|
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
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
-
|
|
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
|
|
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;
|