@skillkit/core 1.15.0 → 1.17.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.
package/dist/index.js CHANGED
@@ -3290,7 +3290,19 @@ var AgentType = z.enum([
3290
3290
  "qoder",
3291
3291
  "qwen",
3292
3292
  "vercel",
3293
- "zencoder"
3293
+ "zencoder",
3294
+ "devin",
3295
+ "aider",
3296
+ "sourcegraph-cody",
3297
+ "amazon-q",
3298
+ "augment-code",
3299
+ "replit-agent",
3300
+ "bolt",
3301
+ "lovable",
3302
+ "tabby",
3303
+ "tabnine",
3304
+ "codegpt",
3305
+ "playcode-agent"
3294
3306
  ]);
3295
3307
  var GitProvider = z.enum(["github", "gitlab", "bitbucket", "local", "wellknown"]);
3296
3308
  var SkillFrontmatter = z.object({
@@ -3627,6 +3639,107 @@ var AGENT_CONFIG = {
3627
3639
  configFormat: "xml",
3628
3640
  usesFrontmatter: true,
3629
3641
  supportsAutoDiscovery: true
3642
+ },
3643
+ // Devin (Cognition Labs)
3644
+ devin: {
3645
+ skillsDir: ".devin/skills",
3646
+ configFile: "AGENTS.md",
3647
+ configFormat: "markdown",
3648
+ usesFrontmatter: true,
3649
+ supportsAutoDiscovery: true
3650
+ },
3651
+ // Aider
3652
+ aider: {
3653
+ skillsDir: ".aider/skills",
3654
+ configFile: "AGENTS.md",
3655
+ globalSkillsDir: "~/.aider/skills",
3656
+ configFormat: "markdown",
3657
+ usesFrontmatter: true,
3658
+ supportsAutoDiscovery: true
3659
+ },
3660
+ // Sourcegraph Cody
3661
+ "sourcegraph-cody": {
3662
+ skillsDir: ".cody/skills",
3663
+ configFile: "AGENTS.md",
3664
+ globalSkillsDir: "~/.cody/skills",
3665
+ configFormat: "markdown",
3666
+ usesFrontmatter: true,
3667
+ supportsAutoDiscovery: true
3668
+ },
3669
+ // Amazon Q Developer
3670
+ "amazon-q": {
3671
+ skillsDir: ".amazonq/skills",
3672
+ configFile: "AGENTS.md",
3673
+ globalSkillsDir: "~/.amazonq/skills",
3674
+ configFormat: "markdown",
3675
+ usesFrontmatter: true,
3676
+ supportsAutoDiscovery: true
3677
+ },
3678
+ // Augment Code
3679
+ "augment-code": {
3680
+ skillsDir: ".augment/skills",
3681
+ configFile: "AGENTS.md",
3682
+ configFormat: "markdown",
3683
+ usesFrontmatter: true,
3684
+ supportsAutoDiscovery: true
3685
+ },
3686
+ // Replit Agent
3687
+ "replit-agent": {
3688
+ skillsDir: ".replit/skills",
3689
+ configFile: "AGENTS.md",
3690
+ configFormat: "markdown",
3691
+ usesFrontmatter: true,
3692
+ supportsAutoDiscovery: true
3693
+ },
3694
+ // Bolt (Vercel)
3695
+ bolt: {
3696
+ skillsDir: ".bolt/skills",
3697
+ configFile: "AGENTS.md",
3698
+ configFormat: "markdown",
3699
+ usesFrontmatter: true,
3700
+ supportsAutoDiscovery: true
3701
+ },
3702
+ // Lovable
3703
+ lovable: {
3704
+ skillsDir: ".lovable/skills",
3705
+ configFile: "AGENTS.md",
3706
+ configFormat: "markdown",
3707
+ usesFrontmatter: true,
3708
+ supportsAutoDiscovery: true
3709
+ },
3710
+ // Tabby
3711
+ tabby: {
3712
+ skillsDir: ".tabby/skills",
3713
+ configFile: "AGENTS.md",
3714
+ globalSkillsDir: "~/.tabby/skills",
3715
+ configFormat: "markdown",
3716
+ usesFrontmatter: true,
3717
+ supportsAutoDiscovery: true
3718
+ },
3719
+ // Tabnine
3720
+ tabnine: {
3721
+ skillsDir: ".tabnine/skills",
3722
+ configFile: "AGENTS.md",
3723
+ globalSkillsDir: "~/.tabnine/skills",
3724
+ configFormat: "markdown",
3725
+ usesFrontmatter: true,
3726
+ supportsAutoDiscovery: true
3727
+ },
3728
+ // CodeGPT
3729
+ codegpt: {
3730
+ skillsDir: ".codegpt/skills",
3731
+ configFile: "AGENTS.md",
3732
+ configFormat: "markdown",
3733
+ usesFrontmatter: true,
3734
+ supportsAutoDiscovery: true
3735
+ },
3736
+ // PlayCode Agent
3737
+ "playcode-agent": {
3738
+ skillsDir: ".playcode/skills",
3739
+ configFile: "AGENTS.md",
3740
+ configFormat: "markdown",
3741
+ usesFrontmatter: true,
3742
+ supportsAutoDiscovery: true
3630
3743
  }
3631
3744
  };
3632
3745
  function getAgentDirectoryConfig(agent) {
@@ -4442,6 +4555,16 @@ import { existsSync as existsSync7, mkdirSync as mkdirSync2, writeFileSync as wr
4442
4555
  import { join as join7, basename as basename6, resolve as resolve3, sep as sep2 } from "path";
4443
4556
  import { tmpdir as tmpdir4 } from "os";
4444
4557
  import { randomUUID as randomUUID4 } from "crypto";
4558
+ function skillNameFromUrl(url) {
4559
+ try {
4560
+ const parsed = new URL(url);
4561
+ const segments = parsed.pathname.split("/").filter(Boolean);
4562
+ return segments[segments.length - 1] || parsed.hostname.split(".")[0] || "default";
4563
+ } catch {
4564
+ return basename6(url) || "default";
4565
+ }
4566
+ }
4567
+ var FRONTMATTER_REGEX = /^---\s*\n[\s\S]*?name:\s*.+/;
4445
4568
  function sanitizeSkillName(name) {
4446
4569
  if (!name || typeof name !== "string") return null;
4447
4570
  const base = basename6(name);
@@ -4453,6 +4576,11 @@ function sanitizeSkillName(name) {
4453
4576
  }
4454
4577
  return name;
4455
4578
  }
4579
+ var MINTLIFY_PATHS = [
4580
+ "/.well-known/skills/default/skill.md",
4581
+ "/skill.md",
4582
+ "/.well-known/skill.md"
4583
+ ];
4456
4584
  var WellKnownProvider = class {
4457
4585
  type = "wellknown";
4458
4586
  name = "Well-Known";
@@ -4484,6 +4612,51 @@ var WellKnownProvider = class {
4484
4612
  getSshUrl(_owner, _repo) {
4485
4613
  return "";
4486
4614
  }
4615
+ async discoverFromUrl(url) {
4616
+ const baseUrl = url.replace(/\/$/, "");
4617
+ const tempDir = join7(tmpdir4(), `skillkit-wellknown-${randomUUID4()}`);
4618
+ try {
4619
+ mkdirSync2(tempDir, { recursive: true });
4620
+ for (const mintlifyPath of MINTLIFY_PATHS) {
4621
+ const fullUrl = `${baseUrl}${mintlifyPath}`;
4622
+ try {
4623
+ const response = await fetch(fullUrl);
4624
+ if (response.ok) {
4625
+ const content = await response.text();
4626
+ if (FRONTMATTER_REGEX.test(content)) {
4627
+ const skillName = skillNameFromUrl(baseUrl);
4628
+ const safeName = sanitizeSkillName(skillName) ?? "default";
4629
+ const skillDir = join7(tempDir, safeName);
4630
+ mkdirSync2(skillDir, { recursive: true });
4631
+ writeFileSync2(join7(skillDir, "SKILL.md"), content);
4632
+ return {
4633
+ success: true,
4634
+ path: tempDir,
4635
+ tempRoot: tempDir,
4636
+ skills: [safeName],
4637
+ discoveredSkills: [{ name: safeName, dirName: safeName, path: skillDir }]
4638
+ };
4639
+ }
4640
+ }
4641
+ } catch {
4642
+ continue;
4643
+ }
4644
+ }
4645
+ const indexResult = await this.clone(baseUrl, "", {});
4646
+ if (indexResult.success) {
4647
+ rmSync4(tempDir, { recursive: true, force: true });
4648
+ return indexResult;
4649
+ }
4650
+ rmSync4(tempDir, { recursive: true, force: true });
4651
+ return { success: false, error: "No well-known skills found" };
4652
+ } catch (error) {
4653
+ if (existsSync7(tempDir)) {
4654
+ rmSync4(tempDir, { recursive: true, force: true });
4655
+ }
4656
+ const message = error instanceof Error ? error.message : String(error);
4657
+ return { success: false, error: `Failed to discover skills: ${message}` };
4658
+ }
4659
+ }
4487
4660
  async clone(source, _targetDir, _options = {}) {
4488
4661
  const tempDir = join7(tmpdir4(), `skillkit-wellknown-${randomUUID4()}`);
4489
4662
  try {
@@ -4508,6 +4681,32 @@ var WellKnownProvider = class {
4508
4681
  }
4509
4682
  }
4510
4683
  if (!index || !index.skills || index.skills.length === 0) {
4684
+ for (const mintlifyPath of MINTLIFY_PATHS) {
4685
+ const fullUrl = `${baseUrl}${mintlifyPath}`;
4686
+ try {
4687
+ const response = await fetch(fullUrl);
4688
+ if (response.ok) {
4689
+ const content = await response.text();
4690
+ if (FRONTMATTER_REGEX.test(content)) {
4691
+ const skillName = skillNameFromUrl(baseUrl);
4692
+ const safeName = sanitizeSkillName(skillName) ?? "default";
4693
+ const skillDir = join7(tempDir, safeName);
4694
+ mkdirSync2(skillDir, { recursive: true });
4695
+ writeFileSync2(join7(skillDir, "SKILL.md"), content);
4696
+ return {
4697
+ success: true,
4698
+ path: tempDir,
4699
+ tempRoot: tempDir,
4700
+ skills: [safeName],
4701
+ discoveredSkills: [{ name: safeName, dirName: safeName, path: skillDir }]
4702
+ };
4703
+ }
4704
+ }
4705
+ } catch {
4706
+ continue;
4707
+ }
4708
+ }
4709
+ rmSync4(tempDir, { recursive: true, force: true });
4511
4710
  return {
4512
4711
  success: false,
4513
4712
  error: `No skills found at ${baseUrl}/.well-known/skills/index.json`
@@ -4692,7 +4891,19 @@ var AGENT_FORMAT_MAP = {
4692
4891
  "qoder": "skill-md",
4693
4892
  "qwen": "skill-md",
4694
4893
  "vercel": "skill-md",
4695
- "zencoder": "skill-md"
4894
+ "zencoder": "skill-md",
4895
+ "devin": "external",
4896
+ "aider": "skill-md",
4897
+ "sourcegraph-cody": "skill-md",
4898
+ "amazon-q": "skill-md",
4899
+ "augment-code": "skill-md",
4900
+ "replit-agent": "external",
4901
+ "bolt": "external",
4902
+ "lovable": "external",
4903
+ "tabby": "skill-md",
4904
+ "tabnine": "skill-md",
4905
+ "codegpt": "skill-md",
4906
+ "playcode-agent": "external"
4696
4907
  };
4697
4908
  var TranslatableSkillFrontmatter = z2.object({
4698
4909
  name: z2.string(),
@@ -19992,6 +20203,90 @@ var AGENT_FORMATS = {
19992
20203
  directory: ".zencoder/commands",
19993
20204
  supportsSlashCommands: true,
19994
20205
  supportsCommandFiles: true
20206
+ },
20207
+ devin: {
20208
+ agent: "devin",
20209
+ extension: ".md",
20210
+ directory: ".devin/commands",
20211
+ supportsSlashCommands: false,
20212
+ supportsCommandFiles: true
20213
+ },
20214
+ aider: {
20215
+ agent: "aider",
20216
+ extension: ".md",
20217
+ directory: ".aider/commands",
20218
+ supportsSlashCommands: false,
20219
+ supportsCommandFiles: true
20220
+ },
20221
+ "sourcegraph-cody": {
20222
+ agent: "sourcegraph-cody",
20223
+ extension: ".md",
20224
+ directory: ".cody/commands",
20225
+ supportsSlashCommands: false,
20226
+ supportsCommandFiles: true
20227
+ },
20228
+ "amazon-q": {
20229
+ agent: "amazon-q",
20230
+ extension: ".md",
20231
+ directory: ".amazonq/commands",
20232
+ supportsSlashCommands: false,
20233
+ supportsCommandFiles: true
20234
+ },
20235
+ "augment-code": {
20236
+ agent: "augment-code",
20237
+ extension: ".md",
20238
+ directory: ".augment/commands",
20239
+ supportsSlashCommands: false,
20240
+ supportsCommandFiles: true
20241
+ },
20242
+ "replit-agent": {
20243
+ agent: "replit-agent",
20244
+ extension: ".md",
20245
+ directory: ".replit/commands",
20246
+ supportsSlashCommands: false,
20247
+ supportsCommandFiles: true
20248
+ },
20249
+ bolt: {
20250
+ agent: "bolt",
20251
+ extension: ".md",
20252
+ directory: ".bolt/commands",
20253
+ supportsSlashCommands: false,
20254
+ supportsCommandFiles: true
20255
+ },
20256
+ lovable: {
20257
+ agent: "lovable",
20258
+ extension: ".md",
20259
+ directory: ".lovable/commands",
20260
+ supportsSlashCommands: false,
20261
+ supportsCommandFiles: true
20262
+ },
20263
+ tabby: {
20264
+ agent: "tabby",
20265
+ extension: ".md",
20266
+ directory: ".tabby/commands",
20267
+ supportsSlashCommands: false,
20268
+ supportsCommandFiles: true
20269
+ },
20270
+ tabnine: {
20271
+ agent: "tabnine",
20272
+ extension: ".md",
20273
+ directory: ".tabnine/commands",
20274
+ supportsSlashCommands: false,
20275
+ supportsCommandFiles: true
20276
+ },
20277
+ codegpt: {
20278
+ agent: "codegpt",
20279
+ extension: ".md",
20280
+ directory: ".codegpt/commands",
20281
+ supportsSlashCommands: false,
20282
+ supportsCommandFiles: true
20283
+ },
20284
+ "playcode-agent": {
20285
+ agent: "playcode-agent",
20286
+ extension: ".md",
20287
+ directory: ".playcode/commands",
20288
+ supportsSlashCommands: false,
20289
+ supportsCommandFiles: true
19995
20290
  }
19996
20291
  };
19997
20292
  var CommandGenerator = class {
@@ -25148,7 +25443,19 @@ var AGENT_DISCOVERY_PATHS = {
25148
25443
  "qoder": [".qoder/agents"],
25149
25444
  "qwen": [".qwen/agents"],
25150
25445
  "vercel": [".vercel/agents"],
25151
- "zencoder": [".zencoder/agents"]
25446
+ "zencoder": [".zencoder/agents"],
25447
+ "devin": [".devin/agents"],
25448
+ "aider": [".aider/agents"],
25449
+ "sourcegraph-cody": [".cody/agents"],
25450
+ "amazon-q": [".amazonq/agents"],
25451
+ "augment-code": [".augment/agents"],
25452
+ "replit-agent": [".replit/agents"],
25453
+ "bolt": [".bolt/agents"],
25454
+ "lovable": [".lovable/agents"],
25455
+ "tabby": [".tabby/agents"],
25456
+ "tabnine": [".tabnine/agents"],
25457
+ "codegpt": [".codegpt/agents"],
25458
+ "playcode-agent": [".playcode/agents"]
25152
25459
  };
25153
25460
  var ALL_AGENT_DISCOVERY_PATHS = [
25154
25461
  "agents",
@@ -25191,7 +25498,19 @@ var ALL_AGENT_DISCOVERY_PATHS = [
25191
25498
  ".vercel/agents",
25192
25499
  ".windsurf/agents",
25193
25500
  ".windsurf/workflows",
25194
- ".zencoder/agents"
25501
+ ".zencoder/agents",
25502
+ ".devin/agents",
25503
+ ".aider/agents",
25504
+ ".cody/agents",
25505
+ ".amazonq/agents",
25506
+ ".augment/agents",
25507
+ ".replit/agents",
25508
+ ".bolt/agents",
25509
+ ".lovable/agents",
25510
+ ".tabby/agents",
25511
+ ".tabnine/agents",
25512
+ ".codegpt/agents",
25513
+ ".playcode/agents"
25195
25514
  ];
25196
25515
  var CUSTOM_AGENT_FORMAT_MAP = {
25197
25516
  "claude-code": "claude-agent",
@@ -25225,7 +25544,19 @@ var CUSTOM_AGENT_FORMAT_MAP = {
25225
25544
  "qoder": "claude-agent",
25226
25545
  "qwen": "claude-agent",
25227
25546
  "vercel": "claude-agent",
25228
- "zencoder": "claude-agent"
25547
+ "zencoder": "claude-agent",
25548
+ "devin": "universal",
25549
+ "aider": "universal",
25550
+ "sourcegraph-cody": "universal",
25551
+ "amazon-q": "universal",
25552
+ "augment-code": "universal",
25553
+ "replit-agent": "universal",
25554
+ "bolt": "universal",
25555
+ "lovable": "universal",
25556
+ "tabby": "universal",
25557
+ "tabnine": "universal",
25558
+ "codegpt": "universal",
25559
+ "playcode-agent": "universal"
25229
25560
  };
25230
25561
 
25231
25562
  // src/agents/parser.ts
@@ -26382,7 +26713,34 @@ function translateSkillToAll(skillPath, sourceAgent, options) {
26382
26713
  "roo",
26383
26714
  "trae",
26384
26715
  "windsurf",
26385
- "universal"
26716
+ "universal",
26717
+ "cline",
26718
+ "codebuddy",
26719
+ "commandcode",
26720
+ "continue",
26721
+ "crush",
26722
+ "factory",
26723
+ "mcpjam",
26724
+ "mux",
26725
+ "neovate",
26726
+ "openhands",
26727
+ "pi",
26728
+ "qoder",
26729
+ "qwen",
26730
+ "vercel",
26731
+ "zencoder",
26732
+ "devin",
26733
+ "aider",
26734
+ "sourcegraph-cody",
26735
+ "amazon-q",
26736
+ "augment-code",
26737
+ "replit-agent",
26738
+ "bolt",
26739
+ "lovable",
26740
+ "tabby",
26741
+ "tabnine",
26742
+ "codegpt",
26743
+ "playcode-agent"
26386
26744
  ];
26387
26745
  for (const agent of agents) {
26388
26746
  if (agent !== sourceAgent) {
@@ -27371,10 +27729,37 @@ function evaluateSkillFile(filePath) {
27371
27729
  return null;
27372
27730
  }
27373
27731
  }
27732
+ function checkNameDirectoryMatch(content, dirPath) {
27733
+ const frontmatter = extractFrontmatter4(content);
27734
+ if (!frontmatter || typeof frontmatter.name !== "string") return null;
27735
+ const dirName = basename17(dirPath);
27736
+ if (frontmatter.name !== dirName) {
27737
+ return `Skill name "${frontmatter.name}" does not match directory "${dirName}"`;
27738
+ }
27739
+ return null;
27740
+ }
27741
+ function checkTokenBudget(content) {
27742
+ const normalizedContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
27743
+ const fmMatch = normalizedContent.match(/^---\s*\n[\s\S]*?\n---\s*\n?/);
27744
+ const body = fmMatch ? normalizedContent.slice(fmMatch[0].length) : normalizedContent;
27745
+ const bodyLines = body.split("\n").length;
27746
+ if (bodyLines > 500) {
27747
+ return `Body is ${bodyLines} lines (exceeds 500). Consider moving content to references/ directory`;
27748
+ }
27749
+ return null;
27750
+ }
27374
27751
  function evaluateSkillDirectory(dirPath) {
27375
27752
  const skillMdPath = join42(dirPath, "SKILL.md");
27376
27753
  if (existsSync41(skillMdPath)) {
27377
- return evaluateSkillFile(skillMdPath);
27754
+ const result = evaluateSkillFile(skillMdPath);
27755
+ if (result) {
27756
+ const content = readFileSync30(skillMdPath, "utf-8");
27757
+ const nameWarning = checkNameDirectoryMatch(content, dirPath);
27758
+ if (nameWarning) result.warnings.push(nameWarning);
27759
+ const budgetWarning = checkTokenBudget(content);
27760
+ if (budgetWarning) result.warnings.push(budgetWarning);
27761
+ }
27762
+ return result;
27378
27763
  }
27379
27764
  const mdcFiles = ["index.mdc", `${basename17(dirPath)}.mdc`];
27380
27765
  for (const file of mdcFiles) {
@@ -32909,7 +33294,7 @@ var SkillScanner = class {
32909
33294
 
32910
33295
  // src/scanner/reporter.ts
32911
33296
  import { basename as basename21 } from "path";
32912
- var SCANNER_VERSION = true ? "1.15.0" : "0.0.0";
33297
+ var SCANNER_VERSION = true ? "1.17.0" : "0.0.0";
32913
33298
  var SEVERITY_COLORS = {
32914
33299
  ["critical" /* CRITICAL */]: "\x1B[91m",
32915
33300
  ["high" /* HIGH */]: "\x1B[31m",
@@ -33180,6 +33565,764 @@ function getThreatInfo(category) {
33180
33565
  function getDefaultSeverity(category) {
33181
33566
  return THREAT_TAXONOMY[category].defaultSeverity;
33182
33567
  }
33568
+
33569
+ // src/validation/spec-validator.ts
33570
+ import { readFileSync as readFileSync38, existsSync as existsSync53 } from "fs";
33571
+ import { join as join54, basename as basename22 } from "path";
33572
+ var NAME_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
33573
+ var SPEC_VERSION = "1.0";
33574
+ var SpecValidator = class {
33575
+ validate(skillPath, options) {
33576
+ const checks = [];
33577
+ const errors = [];
33578
+ const warnings = [];
33579
+ const skillMdPath = skillPath.endsWith(".md") ? skillPath : join54(skillPath, "SKILL.md");
33580
+ if (!existsSync53(skillMdPath)) {
33581
+ errors.push(`SKILL.md not found at ${skillMdPath}`);
33582
+ return { valid: false, errors, warnings, specVersion: SPEC_VERSION, checks };
33583
+ }
33584
+ const raw = readFileSync38(skillMdPath, "utf-8");
33585
+ const { frontmatter, body } = stripFrontmatter(raw);
33586
+ const hasName = typeof frontmatter.name === "string" && frontmatter.name.length > 0;
33587
+ checks.push({
33588
+ name: "name-present",
33589
+ passed: hasName,
33590
+ message: hasName ? "Name field is present" : 'Missing required "name" field in frontmatter',
33591
+ severity: hasName ? "info" : "error"
33592
+ });
33593
+ if (!hasName) errors.push('Missing required "name" field in frontmatter');
33594
+ const hasDescription = typeof frontmatter.description === "string" && frontmatter.description.length > 0;
33595
+ checks.push({
33596
+ name: "description-present",
33597
+ passed: hasDescription,
33598
+ message: hasDescription ? "Description field is present" : 'Missing required "description" field in frontmatter',
33599
+ severity: hasDescription ? "info" : "error"
33600
+ });
33601
+ if (!hasDescription) errors.push('Missing required "description" field in frontmatter');
33602
+ if (hasName) {
33603
+ const nameStr = frontmatter.name;
33604
+ const nameValid = NAME_PATTERN.test(nameStr);
33605
+ checks.push({
33606
+ name: "name-format",
33607
+ passed: nameValid,
33608
+ message: nameValid ? "Name matches required pattern" : `Name "${nameStr}" does not match pattern: lowercase alphanumeric with hyphens`,
33609
+ severity: nameValid ? "info" : "error"
33610
+ });
33611
+ if (!nameValid) errors.push(`Name "${nameStr}" does not match pattern: lowercase alphanumeric with hyphens`);
33612
+ }
33613
+ if (options?.strict) {
33614
+ if (frontmatter.version !== void 0) {
33615
+ warnings.push("version should be under metadata.skillkit-version");
33616
+ checks.push({
33617
+ name: "version-placement",
33618
+ passed: false,
33619
+ message: "version should be under metadata.skillkit-version",
33620
+ severity: "warning"
33621
+ });
33622
+ }
33623
+ if (frontmatter.author !== void 0) {
33624
+ warnings.push("author should be under metadata.skillkit-author");
33625
+ checks.push({
33626
+ name: "author-placement",
33627
+ passed: false,
33628
+ message: "author should be under metadata.skillkit-author",
33629
+ severity: "warning"
33630
+ });
33631
+ }
33632
+ if (frontmatter.tags !== void 0) {
33633
+ warnings.push("tags should be under metadata.skillkit-tags");
33634
+ checks.push({
33635
+ name: "tags-placement",
33636
+ passed: false,
33637
+ message: "tags should be under metadata.skillkit-tags",
33638
+ severity: "warning"
33639
+ });
33640
+ }
33641
+ if (frontmatter.agents !== void 0) {
33642
+ warnings.push("agents should be under metadata.skillkit-agents");
33643
+ checks.push({
33644
+ name: "agents-placement",
33645
+ passed: false,
33646
+ message: "agents should be under metadata.skillkit-agents",
33647
+ severity: "warning"
33648
+ });
33649
+ }
33650
+ if (hasName) {
33651
+ const skillDir = skillPath.endsWith(".md") ? join54(skillPath, "..") : skillPath;
33652
+ const dirName = basename22(skillDir);
33653
+ const nameStr = frontmatter.name;
33654
+ const nameMatchesDir = nameStr === dirName;
33655
+ checks.push({
33656
+ name: "name-directory-match",
33657
+ passed: nameMatchesDir,
33658
+ message: nameMatchesDir ? "Name matches directory" : `Name "${nameStr}" does not match directory "${dirName}"`,
33659
+ severity: nameMatchesDir ? "info" : "warning"
33660
+ });
33661
+ if (!nameMatchesDir) warnings.push(`Name "${nameStr}" does not match directory "${dirName}"`);
33662
+ }
33663
+ const bodyLines = body.split("\n").length;
33664
+ const bodyWithinLimit = bodyLines <= 500;
33665
+ checks.push({
33666
+ name: "body-length",
33667
+ passed: bodyWithinLimit,
33668
+ message: bodyWithinLimit ? `Body is ${bodyLines} lines` : `Body is ${bodyLines} lines (exceeds 500). Consider moving content to references/ directory`,
33669
+ severity: bodyWithinLimit ? "info" : "warning"
33670
+ });
33671
+ if (!bodyWithinLimit) warnings.push(`Body is ${bodyLines} lines (exceeds 500). Consider moving content to references/ directory`);
33672
+ }
33673
+ return {
33674
+ valid: errors.length === 0,
33675
+ errors,
33676
+ warnings,
33677
+ specVersion: SPEC_VERSION,
33678
+ checks
33679
+ };
33680
+ }
33681
+ };
33682
+
33683
+ // src/agents-md/generator.ts
33684
+ import { existsSync as existsSync54, readFileSync as readFileSync39 } from "fs";
33685
+ import { join as join55 } from "path";
33686
+ function escapeTableCell(text) {
33687
+ return text.replace(/\|/g, "\\|").replace(/\n/g, " ");
33688
+ }
33689
+ var MANAGED_START = "<!-- skillkit:managed:start -->";
33690
+ var MANAGED_END = "<!-- skillkit:managed:end -->";
33691
+ var AgentsMdGenerator = class {
33692
+ config;
33693
+ detector;
33694
+ constructor(config) {
33695
+ this.config = config;
33696
+ this.detector = new ProjectDetector(config.projectPath);
33697
+ }
33698
+ generate() {
33699
+ this.detector.analyze();
33700
+ const sections = [];
33701
+ sections.push({
33702
+ id: "project-overview",
33703
+ title: "Project Overview",
33704
+ content: this.generateProjectSection(),
33705
+ managed: true
33706
+ });
33707
+ sections.push({
33708
+ id: "technology-stack",
33709
+ title: "Technology Stack",
33710
+ content: this.generateStackSection(),
33711
+ managed: true
33712
+ });
33713
+ if (this.config.includeSkills !== false) {
33714
+ const skillsContent = this.generateSkillsSection(findAllSkills([join55(this.config.projectPath, "skills")]));
33715
+ if (skillsContent) {
33716
+ sections.push({
33717
+ id: "installed-skills",
33718
+ title: "Installed Skills",
33719
+ content: skillsContent,
33720
+ managed: true
33721
+ });
33722
+ }
33723
+ }
33724
+ if (this.config.includeBuildCommands !== false) {
33725
+ const buildContent = this.generateBuildSection();
33726
+ if (buildContent) {
33727
+ sections.push({
33728
+ id: "build-test",
33729
+ title: "Build & Test",
33730
+ content: buildContent,
33731
+ managed: true
33732
+ });
33733
+ }
33734
+ }
33735
+ if (this.config.includeCodeStyle !== false) {
33736
+ const styleContent = this.generateCodeStyleSection();
33737
+ if (styleContent) {
33738
+ sections.push({
33739
+ id: "code-style",
33740
+ title: "Code Style",
33741
+ content: styleContent,
33742
+ managed: true
33743
+ });
33744
+ }
33745
+ }
33746
+ const lines = ["# AGENTS.md", "", MANAGED_START];
33747
+ for (const section of sections) {
33748
+ lines.push(`## ${section.title}`);
33749
+ lines.push(section.content);
33750
+ lines.push("");
33751
+ }
33752
+ lines.push(MANAGED_END, "");
33753
+ const content = lines.join("\n");
33754
+ return {
33755
+ content,
33756
+ sections,
33757
+ path: join55(this.config.projectPath, "AGENTS.md")
33758
+ };
33759
+ }
33760
+ generateSkillsSection(skills) {
33761
+ if (skills.length === 0) {
33762
+ return "";
33763
+ }
33764
+ const lines = [
33765
+ "| Skill | Description | Tags |",
33766
+ "|-------|-------------|------|"
33767
+ ];
33768
+ for (const skill of skills) {
33769
+ const name = escapeTableCell(skill.name);
33770
+ const desc = escapeTableCell(skill.description);
33771
+ lines.push(`| ${name} | ${desc} | |`);
33772
+ }
33773
+ return lines.join("\n");
33774
+ }
33775
+ generateProjectSection() {
33776
+ const name = this.detector.getProjectName();
33777
+ const description = this.detector.getProjectDescription();
33778
+ const projectType = this.detector.detectProjectType();
33779
+ const lines = [];
33780
+ lines.push(`- **Name**: ${name}`);
33781
+ if (description) {
33782
+ lines.push(`- **Description**: ${description}`);
33783
+ }
33784
+ lines.push(`- **Type**: ${projectType}`);
33785
+ return lines.join("\n");
33786
+ }
33787
+ generateBuildSection() {
33788
+ const packageJsonPath = join55(this.config.projectPath, "package.json");
33789
+ if (!existsSync54(packageJsonPath)) {
33790
+ return "";
33791
+ }
33792
+ try {
33793
+ const pkg = JSON.parse(readFileSync39(packageJsonPath, "utf-8"));
33794
+ const scripts = pkg.scripts;
33795
+ if (!scripts || Object.keys(scripts).length === 0) {
33796
+ return "";
33797
+ }
33798
+ const relevantScripts = ["build", "dev", "start", "test", "lint", "format", "typecheck", "check"];
33799
+ const lines = ["```bash"];
33800
+ for (const key of relevantScripts) {
33801
+ if (scripts[key]) {
33802
+ lines.push(`# ${key}`);
33803
+ lines.push(scripts[key]);
33804
+ lines.push("");
33805
+ }
33806
+ }
33807
+ if (lines.length === 1) {
33808
+ return "";
33809
+ }
33810
+ lines.push("```");
33811
+ return lines.join("\n");
33812
+ } catch {
33813
+ return "";
33814
+ }
33815
+ }
33816
+ generateCodeStyleSection() {
33817
+ const patterns = this.detector.detectPatterns();
33818
+ const lines = [];
33819
+ if (patterns.linting) {
33820
+ lines.push(`- **Linting**: ${patterns.linting}`);
33821
+ }
33822
+ if (patterns.formatting) {
33823
+ lines.push(`- **Formatting**: ${patterns.formatting}`);
33824
+ }
33825
+ if (patterns.testing) {
33826
+ lines.push(`- **Testing**: ${patterns.testing}`);
33827
+ }
33828
+ if (patterns.styling) {
33829
+ lines.push(`- **Styling**: ${patterns.styling}`);
33830
+ }
33831
+ return lines.length > 0 ? lines.join("\n") : "";
33832
+ }
33833
+ generateStackSection() {
33834
+ const stack = this.detector.analyze();
33835
+ const lines = [];
33836
+ if (stack.languages.length > 0) {
33837
+ lines.push(`- **Languages**: ${stack.languages.map((l) => l.version ? `${l.name} ${l.version}` : l.name).join(", ")}`);
33838
+ }
33839
+ if (stack.frameworks.length > 0) {
33840
+ lines.push(`- **Frameworks**: ${stack.frameworks.map((f) => f.version ? `${f.name} ${f.version}` : f.name).join(", ")}`);
33841
+ }
33842
+ if (stack.libraries.length > 0) {
33843
+ lines.push(`- **Libraries**: ${stack.libraries.map((l) => l.name).join(", ")}`);
33844
+ }
33845
+ if (stack.databases.length > 0) {
33846
+ lines.push(`- **Databases**: ${stack.databases.map((d) => d.name).join(", ")}`);
33847
+ }
33848
+ if (stack.runtime.length > 0) {
33849
+ lines.push(`- **Runtime**: ${stack.runtime.map((r) => r.version ? `${r.name} ${r.version}` : r.name).join(", ")}`);
33850
+ }
33851
+ return lines.length > 0 ? lines.join("\n") : "No technology stack detected.";
33852
+ }
33853
+ };
33854
+
33855
+ // src/agents-md/parser.ts
33856
+ var MANAGED_START2 = "<!-- skillkit:managed:start -->";
33857
+ var MANAGED_END2 = "<!-- skillkit:managed:end -->";
33858
+ var AgentsMdParser = class {
33859
+ parse(content) {
33860
+ const sections = [];
33861
+ const lines = content.split("\n");
33862
+ let inManaged = false;
33863
+ let currentSection = null;
33864
+ for (const line of lines) {
33865
+ if (line.trim() === MANAGED_START2) {
33866
+ inManaged = true;
33867
+ continue;
33868
+ }
33869
+ if (line.trim() === MANAGED_END2) {
33870
+ if (currentSection) {
33871
+ sections.push({
33872
+ id: currentSection.id,
33873
+ title: currentSection.title,
33874
+ content: currentSection.lines.join("\n").trim(),
33875
+ managed: currentSection.managed
33876
+ });
33877
+ currentSection = null;
33878
+ }
33879
+ inManaged = false;
33880
+ continue;
33881
+ }
33882
+ const headingMatch = line.match(/^##\s+(.+)$/);
33883
+ if (headingMatch) {
33884
+ if (currentSection) {
33885
+ sections.push({
33886
+ id: currentSection.id,
33887
+ title: currentSection.title,
33888
+ content: currentSection.lines.join("\n").trim(),
33889
+ managed: currentSection.managed
33890
+ });
33891
+ }
33892
+ const title = headingMatch[1];
33893
+ currentSection = {
33894
+ id: title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""),
33895
+ title,
33896
+ lines: [],
33897
+ managed: inManaged
33898
+ };
33899
+ continue;
33900
+ }
33901
+ if (currentSection) {
33902
+ currentSection.lines.push(line);
33903
+ }
33904
+ }
33905
+ if (currentSection) {
33906
+ sections.push({
33907
+ id: currentSection.id,
33908
+ title: currentSection.title,
33909
+ content: currentSection.lines.join("\n").trim(),
33910
+ managed: currentSection.managed
33911
+ });
33912
+ }
33913
+ return sections;
33914
+ }
33915
+ hasManagedSections(content) {
33916
+ return content.includes(MANAGED_START2) && content.includes(MANAGED_END2);
33917
+ }
33918
+ updateManagedSections(existing, newManaged) {
33919
+ if (!this.hasManagedSections(existing)) {
33920
+ return existing;
33921
+ }
33922
+ const startIdx = existing.indexOf(MANAGED_START2);
33923
+ const endIdx = existing.indexOf(MANAGED_END2);
33924
+ if (startIdx >= endIdx) {
33925
+ return existing;
33926
+ }
33927
+ const managedBlock = this.buildManagedBlock(newManaged);
33928
+ const before = existing.substring(0, startIdx);
33929
+ const after = existing.substring(endIdx + MANAGED_END2.length);
33930
+ return `${before}${managedBlock}${after}`;
33931
+ }
33932
+ buildManagedBlock(sections) {
33933
+ const lines = [MANAGED_START2];
33934
+ for (const section of sections) {
33935
+ lines.push(`## ${section.title}`);
33936
+ lines.push(section.content);
33937
+ lines.push("");
33938
+ }
33939
+ lines.push(MANAGED_END2);
33940
+ return lines.join("\n");
33941
+ }
33942
+ };
33943
+
33944
+ // src/save/extractor.ts
33945
+ import { existsSync as existsSync55, readFileSync as readFileSync40 } from "fs";
33946
+ import { basename as basename23, extname as extname7 } from "path";
33947
+ import TurndownService from "turndown";
33948
+ var LANGUAGE_MAP = {
33949
+ ".ts": "typescript",
33950
+ ".tsx": "typescript",
33951
+ ".js": "javascript",
33952
+ ".jsx": "javascript",
33953
+ ".py": "python",
33954
+ ".rb": "ruby",
33955
+ ".go": "go",
33956
+ ".rs": "rust",
33957
+ ".java": "java",
33958
+ ".kt": "kotlin",
33959
+ ".swift": "swift",
33960
+ ".c": "c",
33961
+ ".cpp": "cpp",
33962
+ ".cs": "csharp",
33963
+ ".php": "php",
33964
+ ".sh": "shell",
33965
+ ".bash": "shell",
33966
+ ".zsh": "shell",
33967
+ ".yml": "yaml",
33968
+ ".yaml": "yaml",
33969
+ ".json": "json",
33970
+ ".toml": "toml",
33971
+ ".md": "markdown",
33972
+ ".mdx": "markdown",
33973
+ ".html": "html",
33974
+ ".css": "css",
33975
+ ".scss": "scss",
33976
+ ".sql": "sql",
33977
+ ".r": "r",
33978
+ ".lua": "lua",
33979
+ ".dart": "dart",
33980
+ ".ex": "elixir",
33981
+ ".exs": "elixir",
33982
+ ".zig": "zig",
33983
+ ".nim": "nim",
33984
+ ".vue": "vue",
33985
+ ".svelte": "svelte"
33986
+ };
33987
+ var GITHUB_URL_PATTERN = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/;
33988
+ var GITHUB_RAW_PATTERN = /^https?:\/\/raw\.githubusercontent\.com\//;
33989
+ var FETCH_TIMEOUT = 3e4;
33990
+ var ContentExtractor = class {
33991
+ turndown;
33992
+ constructor() {
33993
+ this.turndown = new TurndownService({
33994
+ headingStyle: "atx",
33995
+ codeBlockStyle: "fenced"
33996
+ });
33997
+ }
33998
+ async extractFromUrl(url, options) {
33999
+ if (this.isGitHubUrl(url)) {
34000
+ return this.fetchGitHubContent(url, options);
34001
+ }
34002
+ const response = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT) });
34003
+ if (!response.ok) {
34004
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
34005
+ }
34006
+ const contentType = response.headers.get("content-type") ?? "";
34007
+ const body = await response.text();
34008
+ if (contentType.includes("text/html")) {
34009
+ const { title: title2, content } = this.htmlToMarkdown(body, url);
34010
+ const finalContent2 = options?.maxLength ? content.slice(0, options.maxLength) : content;
34011
+ return {
34012
+ title: options?.preferredTitle ?? title2,
34013
+ content: finalContent2,
34014
+ sourceUrl: url,
34015
+ tags: [],
34016
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
34017
+ contentType: "webpage",
34018
+ metadata: { url }
34019
+ };
34020
+ }
34021
+ const title = options?.preferredTitle ?? new URL(url).pathname.split("/").pop() ?? "Untitled";
34022
+ const finalContent = options?.maxLength ? body.slice(0, options.maxLength) : body;
34023
+ return {
34024
+ title,
34025
+ content: finalContent,
34026
+ sourceUrl: url,
34027
+ tags: [],
34028
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
34029
+ contentType: "text",
34030
+ metadata: { url }
34031
+ };
34032
+ }
34033
+ extractFromText(text, options) {
34034
+ const firstLine = text.split("\n")[0]?.trim() ?? "";
34035
+ const title = options?.preferredTitle ?? (firstLine.length > 0 && firstLine.length <= 100 ? firstLine : "Untitled");
34036
+ const content = options?.maxLength ? text.slice(0, options.maxLength) : text;
34037
+ return {
34038
+ title,
34039
+ content,
34040
+ tags: [],
34041
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
34042
+ contentType: "text",
34043
+ metadata: {}
34044
+ };
34045
+ }
34046
+ extractFromFile(filePath, options) {
34047
+ if (!existsSync55(filePath)) {
34048
+ throw new Error(`File not found: ${filePath}`);
34049
+ }
34050
+ const raw = readFileSync40(filePath, "utf-8");
34051
+ const name = basename23(filePath);
34052
+ const language = this.detectLanguage(name);
34053
+ const isCode = language !== void 0 && language !== "markdown";
34054
+ const title = options?.preferredTitle ?? name;
34055
+ const content = options?.maxLength ? raw.slice(0, options.maxLength) : raw;
34056
+ return {
34057
+ title,
34058
+ content: isCode ? `\`\`\`${language}
34059
+ ${content}
34060
+ \`\`\`` : content,
34061
+ sourcePath: filePath,
34062
+ tags: language ? [language] : [],
34063
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
34064
+ contentType: isCode ? "code" : "file",
34065
+ language,
34066
+ metadata: { filename: name }
34067
+ };
34068
+ }
34069
+ isGitHubUrl(url) {
34070
+ return GITHUB_URL_PATTERN.test(url) || GITHUB_RAW_PATTERN.test(url);
34071
+ }
34072
+ async fetchGitHubContent(url, options) {
34073
+ let rawUrl = url;
34074
+ const match = url.match(GITHUB_URL_PATTERN);
34075
+ if (match) {
34076
+ const [, owner, repo, branch, path4] = match;
34077
+ rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path4}`;
34078
+ }
34079
+ const response = await fetch(rawUrl, { signal: AbortSignal.timeout(FETCH_TIMEOUT) });
34080
+ if (!response.ok) {
34081
+ throw new Error(`Failed to fetch GitHub content: ${response.status} ${response.statusText}`);
34082
+ }
34083
+ const body = await response.text();
34084
+ const filename = rawUrl.split("/").pop() ?? "file";
34085
+ const language = this.detectLanguage(filename);
34086
+ const isCode = language !== void 0 && language !== "markdown";
34087
+ const title = options?.preferredTitle ?? filename;
34088
+ const content = options?.maxLength ? body.slice(0, options.maxLength) : body;
34089
+ return {
34090
+ title,
34091
+ content: isCode ? `\`\`\`${language}
34092
+ ${content}
34093
+ \`\`\`` : content,
34094
+ sourceUrl: url,
34095
+ tags: language ? ["github", language] : ["github"],
34096
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString(),
34097
+ contentType: "github",
34098
+ language,
34099
+ metadata: {
34100
+ url,
34101
+ rawUrl,
34102
+ filename
34103
+ }
34104
+ };
34105
+ }
34106
+ htmlToMarkdown(html, url) {
34107
+ const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
34108
+ const title = titleMatch?.[1]?.trim() ?? new URL(url).hostname;
34109
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
34110
+ const bodyHtml = bodyMatch?.[1] ?? html;
34111
+ const content = this.turndown.turndown(bodyHtml);
34112
+ return { title, content };
34113
+ }
34114
+ detectLanguage(filename) {
34115
+ const ext = extname7(filename).toLowerCase();
34116
+ return LANGUAGE_MAP[ext];
34117
+ }
34118
+ };
34119
+
34120
+ // src/save/tagger.ts
34121
+ var TECH_KEYWORDS = /* @__PURE__ */ new Set([
34122
+ "react",
34123
+ "vue",
34124
+ "angular",
34125
+ "svelte",
34126
+ "nextjs",
34127
+ "nuxt",
34128
+ "remix",
34129
+ "typescript",
34130
+ "javascript",
34131
+ "python",
34132
+ "rust",
34133
+ "go",
34134
+ "java",
34135
+ "ruby",
34136
+ "node",
34137
+ "deno",
34138
+ "bun",
34139
+ "docker",
34140
+ "kubernetes",
34141
+ "terraform",
34142
+ "aws",
34143
+ "gcp",
34144
+ "azure",
34145
+ "vercel",
34146
+ "netlify",
34147
+ "cloudflare",
34148
+ "graphql",
34149
+ "rest",
34150
+ "grpc",
34151
+ "websocket",
34152
+ "redis",
34153
+ "postgres",
34154
+ "mongodb",
34155
+ "sqlite",
34156
+ "mysql",
34157
+ "prisma",
34158
+ "drizzle",
34159
+ "tailwind",
34160
+ "css",
34161
+ "html",
34162
+ "sass",
34163
+ "webpack",
34164
+ "vite",
34165
+ "esbuild",
34166
+ "git",
34167
+ "ci",
34168
+ "cd",
34169
+ "testing",
34170
+ "security",
34171
+ "authentication",
34172
+ "api",
34173
+ "cli",
34174
+ "sdk",
34175
+ "mcp",
34176
+ "llm",
34177
+ "ai",
34178
+ "ml",
34179
+ "openai",
34180
+ "anthropic"
34181
+ ]);
34182
+ var TAG_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
34183
+ var AutoTagger = class {
34184
+ detectTags(content) {
34185
+ const tagCounts = /* @__PURE__ */ new Map();
34186
+ this.extractFromUrl(content.sourceUrl, tagCounts);
34187
+ this.extractFromHeadings(content.content, tagCounts);
34188
+ this.extractFromCodeBlocks(content.content, tagCounts);
34189
+ this.extractFromKeywords(content.content, tagCounts);
34190
+ if (content.language) {
34191
+ this.addTag(content.language.toLowerCase(), tagCounts, 3);
34192
+ }
34193
+ if (content.contentType !== "text") {
34194
+ this.addTag(content.contentType, tagCounts, 1);
34195
+ }
34196
+ return Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([tag]) => tag);
34197
+ }
34198
+ extractFromUrl(url, counts) {
34199
+ if (!url) return;
34200
+ try {
34201
+ const parsed = new URL(url);
34202
+ const segments = parsed.pathname.split("/").filter(Boolean).map((s) => s.toLowerCase().replace(/[^a-z0-9-]/g, ""));
34203
+ for (const seg of segments) {
34204
+ if (seg.length >= 2 && seg.length <= 30 && TAG_PATTERN.test(seg)) {
34205
+ this.addTag(seg, counts, 2);
34206
+ }
34207
+ }
34208
+ } catch {
34209
+ }
34210
+ }
34211
+ extractFromHeadings(content, counts) {
34212
+ const headingRe = /^#{1,2}\s+(.+)$/gm;
34213
+ let match;
34214
+ while ((match = headingRe.exec(content)) !== null) {
34215
+ const words = match[1].toLowerCase().split(/\s+/);
34216
+ for (const word of words) {
34217
+ const cleaned = word.replace(/[^a-z0-9-]/g, "");
34218
+ if (cleaned.length >= 2 && TAG_PATTERN.test(cleaned)) {
34219
+ this.addTag(cleaned, counts, 2);
34220
+ }
34221
+ }
34222
+ }
34223
+ }
34224
+ extractFromCodeBlocks(content, counts) {
34225
+ const codeBlockRe = /^```(\w+)/gm;
34226
+ let match;
34227
+ while ((match = codeBlockRe.exec(content)) !== null) {
34228
+ const lang = match[1].toLowerCase();
34229
+ if (lang.length >= 2 && TAG_PATTERN.test(lang)) {
34230
+ this.addTag(lang, counts, 3);
34231
+ }
34232
+ }
34233
+ }
34234
+ extractFromKeywords(content, counts) {
34235
+ const lower = content.toLowerCase();
34236
+ for (const keyword of TECH_KEYWORDS) {
34237
+ const re = new RegExp(`\\b${keyword}\\b`, "i");
34238
+ if (re.test(lower)) {
34239
+ this.addTag(keyword, counts, 1);
34240
+ }
34241
+ }
34242
+ }
34243
+ addTag(tag, counts, weight) {
34244
+ if (!TAG_PATTERN.test(tag)) return;
34245
+ counts.set(tag, (counts.get(tag) ?? 0) + weight);
34246
+ }
34247
+ };
34248
+
34249
+ // src/save/skill-generator.ts
34250
+ import { mkdirSync as mkdirSync28, writeFileSync as writeFileSync28 } from "fs";
34251
+ import { join as join56 } from "path";
34252
+ import { homedir as homedir19 } from "os";
34253
+ var MAX_NAME_LENGTH = 64;
34254
+ var SUMMARY_LINE_LIMIT = 100;
34255
+ var SPLIT_THRESHOLD = 500;
34256
+ var DESCRIPTION_MAX = 200;
34257
+ var SkillGenerator = class {
34258
+ tagger = new AutoTagger();
34259
+ generate(content, options = {}) {
34260
+ const name = options.name ? this.slugify(options.name) : this.slugify(content.title || "untitled-skill");
34261
+ const tags = this.tagger.detectTags(content);
34262
+ const description = this.makeDescription(content.content);
34263
+ const source = content.sourceUrl ?? content.sourcePath ?? "";
34264
+ const frontmatter = this.buildFrontmatter(name, description, tags, source);
34265
+ const lines = content.content.split("\n");
34266
+ const needsSplit = lines.length > SPLIT_THRESHOLD;
34267
+ const body = needsSplit ? lines.slice(0, SUMMARY_LINE_LIMIT).join("\n") : content.content;
34268
+ const skillMd = `${frontmatter}
34269
+ ${body}
34270
+ `;
34271
+ const outputDir = options.outputDir ?? this.defaultOutputDir(name, options.global);
34272
+ mkdirSync28(outputDir, { recursive: true });
34273
+ const skillPath = join56(outputDir, "SKILL.md");
34274
+ writeFileSync28(skillPath, skillMd, "utf-8");
34275
+ if (needsSplit) {
34276
+ const refsDir = join56(outputDir, "references");
34277
+ mkdirSync28(refsDir, { recursive: true });
34278
+ writeFileSync28(
34279
+ join56(refsDir, "full-content.md"),
34280
+ content.content,
34281
+ "utf-8"
34282
+ );
34283
+ }
34284
+ return { skillPath, skillMd, name };
34285
+ }
34286
+ slugify(input) {
34287
+ const slug = input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
34288
+ const trimmed = slug.slice(0, MAX_NAME_LENGTH).replace(/-+$/, "");
34289
+ return trimmed || "untitled-skill";
34290
+ }
34291
+ makeDescription(content) {
34292
+ const firstLine = content.split("\n").find((l) => l.trim().length > 0) ?? "";
34293
+ const cleaned = firstLine.replace(/^#+\s*/, "").trim();
34294
+ return cleaned.length > DESCRIPTION_MAX ? cleaned.slice(0, DESCRIPTION_MAX - 3) + "..." : cleaned || "Saved skill";
34295
+ }
34296
+ buildFrontmatter(name, description, tags, source) {
34297
+ const yamlTags = tags.map((t) => ` - ${t}`).join("\n");
34298
+ const savedAt = (/* @__PURE__ */ new Date()).toISOString();
34299
+ const lines = [
34300
+ "---",
34301
+ `name: ${name}`,
34302
+ `description: ${this.yamlEscape(description)}`,
34303
+ tags.length > 0 ? `tags:
34304
+ ${yamlTags}` : null,
34305
+ "metadata:",
34306
+ source ? ` source: ${source}` : null,
34307
+ ` savedAt: ${savedAt}`,
34308
+ "---"
34309
+ ].filter((l) => l !== null);
34310
+ return lines.join("\n") + "\n";
34311
+ }
34312
+ yamlEscape(value) {
34313
+ const singleLine = value.replace(/\r?\n/g, " ").trim();
34314
+ if (/[:#{}[\],&*?|>!%@`]/.test(singleLine) || singleLine.startsWith("'") || singleLine.startsWith('"')) {
34315
+ return `"${singleLine.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
34316
+ }
34317
+ return singleLine;
34318
+ }
34319
+ defaultOutputDir(name, global) {
34320
+ if (global) {
34321
+ return join56(homedir19(), ".skillkit", "skills", name);
34322
+ }
34323
+ return join56(".skillkit", "skills", name);
34324
+ }
34325
+ };
33183
34326
  export {
33184
34327
  AGENT_CLI_CONFIGS,
33185
34328
  AGENT_CONFIG,
@@ -33199,8 +34342,11 @@ export {
33199
34342
  AgentOptimizer,
33200
34343
  AgentPermissionMode,
33201
34344
  AgentType,
34345
+ AgentsMdGenerator,
34346
+ AgentsMdParser,
33202
34347
  AnthropicProvider,
33203
34348
  AuditLogger,
34349
+ AutoTagger,
33204
34350
  BUILTIN_PIPELINES,
33205
34351
  BaseAIProvider,
33206
34352
  BitbucketProvider,
@@ -33221,6 +34367,7 @@ export {
33221
34367
  ConnectorConfigSchema,
33222
34368
  ConnectorMappingSchema,
33223
34369
  ConnectorPlaceholderSchema,
34370
+ ContentExtractor,
33224
34371
  ContextEngine,
33225
34372
  ContextLoader,
33226
34373
  ContextManager,
@@ -33325,6 +34472,7 @@ export {
33325
34472
  SkillComposer,
33326
34473
  SkillExecutionEngine,
33327
34474
  SkillFrontmatter,
34475
+ SkillGenerator,
33328
34476
  SkillInjector,
33329
34477
  SkillLocation,
33330
34478
  SkillMdTranslator,
@@ -33337,6 +34485,7 @@ export {
33337
34485
  SkillWizard,
33338
34486
  SkillkitConfig,
33339
34487
  SkillsSource,
34488
+ SpecValidator,
33340
34489
  StaticAnalyzer,
33341
34490
  TAG_TO_CATEGORY,
33342
34491
  TAG_TO_TECH,