@skillkit/core 1.16.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.d.ts +118 -1
- package/dist/index.js +879 -2
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -4555,6 +4555,16 @@ import { existsSync as existsSync7, mkdirSync as mkdirSync2, writeFileSync as wr
|
|
|
4555
4555
|
import { join as join7, basename as basename6, resolve as resolve3, sep as sep2 } from "path";
|
|
4556
4556
|
import { tmpdir as tmpdir4 } from "os";
|
|
4557
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*.+/;
|
|
4558
4568
|
function sanitizeSkillName(name) {
|
|
4559
4569
|
if (!name || typeof name !== "string") return null;
|
|
4560
4570
|
const base = basename6(name);
|
|
@@ -4566,6 +4576,11 @@ function sanitizeSkillName(name) {
|
|
|
4566
4576
|
}
|
|
4567
4577
|
return name;
|
|
4568
4578
|
}
|
|
4579
|
+
var MINTLIFY_PATHS = [
|
|
4580
|
+
"/.well-known/skills/default/skill.md",
|
|
4581
|
+
"/skill.md",
|
|
4582
|
+
"/.well-known/skill.md"
|
|
4583
|
+
];
|
|
4569
4584
|
var WellKnownProvider = class {
|
|
4570
4585
|
type = "wellknown";
|
|
4571
4586
|
name = "Well-Known";
|
|
@@ -4597,6 +4612,51 @@ var WellKnownProvider = class {
|
|
|
4597
4612
|
getSshUrl(_owner, _repo) {
|
|
4598
4613
|
return "";
|
|
4599
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
|
+
}
|
|
4600
4660
|
async clone(source, _targetDir, _options = {}) {
|
|
4601
4661
|
const tempDir = join7(tmpdir4(), `skillkit-wellknown-${randomUUID4()}`);
|
|
4602
4662
|
try {
|
|
@@ -4621,6 +4681,32 @@ var WellKnownProvider = class {
|
|
|
4621
4681
|
}
|
|
4622
4682
|
}
|
|
4623
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 });
|
|
4624
4710
|
return {
|
|
4625
4711
|
success: false,
|
|
4626
4712
|
error: `No skills found at ${baseUrl}/.well-known/skills/index.json`
|
|
@@ -27643,10 +27729,37 @@ function evaluateSkillFile(filePath) {
|
|
|
27643
27729
|
return null;
|
|
27644
27730
|
}
|
|
27645
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
|
+
}
|
|
27646
27751
|
function evaluateSkillDirectory(dirPath) {
|
|
27647
27752
|
const skillMdPath = join42(dirPath, "SKILL.md");
|
|
27648
27753
|
if (existsSync41(skillMdPath)) {
|
|
27649
|
-
|
|
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;
|
|
27650
27763
|
}
|
|
27651
27764
|
const mdcFiles = ["index.mdc", `${basename17(dirPath)}.mdc`];
|
|
27652
27765
|
for (const file of mdcFiles) {
|
|
@@ -33181,7 +33294,7 @@ var SkillScanner = class {
|
|
|
33181
33294
|
|
|
33182
33295
|
// src/scanner/reporter.ts
|
|
33183
33296
|
import { basename as basename21 } from "path";
|
|
33184
|
-
var SCANNER_VERSION = true ? "1.
|
|
33297
|
+
var SCANNER_VERSION = true ? "1.17.0" : "0.0.0";
|
|
33185
33298
|
var SEVERITY_COLORS = {
|
|
33186
33299
|
["critical" /* CRITICAL */]: "\x1B[91m",
|
|
33187
33300
|
["high" /* HIGH */]: "\x1B[31m",
|
|
@@ -33452,6 +33565,764 @@ function getThreatInfo(category) {
|
|
|
33452
33565
|
function getDefaultSeverity(category) {
|
|
33453
33566
|
return THREAT_TAXONOMY[category].defaultSeverity;
|
|
33454
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
|
+
};
|
|
33455
34326
|
export {
|
|
33456
34327
|
AGENT_CLI_CONFIGS,
|
|
33457
34328
|
AGENT_CONFIG,
|
|
@@ -33471,8 +34342,11 @@ export {
|
|
|
33471
34342
|
AgentOptimizer,
|
|
33472
34343
|
AgentPermissionMode,
|
|
33473
34344
|
AgentType,
|
|
34345
|
+
AgentsMdGenerator,
|
|
34346
|
+
AgentsMdParser,
|
|
33474
34347
|
AnthropicProvider,
|
|
33475
34348
|
AuditLogger,
|
|
34349
|
+
AutoTagger,
|
|
33476
34350
|
BUILTIN_PIPELINES,
|
|
33477
34351
|
BaseAIProvider,
|
|
33478
34352
|
BitbucketProvider,
|
|
@@ -33493,6 +34367,7 @@ export {
|
|
|
33493
34367
|
ConnectorConfigSchema,
|
|
33494
34368
|
ConnectorMappingSchema,
|
|
33495
34369
|
ConnectorPlaceholderSchema,
|
|
34370
|
+
ContentExtractor,
|
|
33496
34371
|
ContextEngine,
|
|
33497
34372
|
ContextLoader,
|
|
33498
34373
|
ContextManager,
|
|
@@ -33597,6 +34472,7 @@ export {
|
|
|
33597
34472
|
SkillComposer,
|
|
33598
34473
|
SkillExecutionEngine,
|
|
33599
34474
|
SkillFrontmatter,
|
|
34475
|
+
SkillGenerator,
|
|
33600
34476
|
SkillInjector,
|
|
33601
34477
|
SkillLocation,
|
|
33602
34478
|
SkillMdTranslator,
|
|
@@ -33609,6 +34485,7 @@ export {
|
|
|
33609
34485
|
SkillWizard,
|
|
33610
34486
|
SkillkitConfig,
|
|
33611
34487
|
SkillsSource,
|
|
34488
|
+
SpecValidator,
|
|
33612
34489
|
StaticAnalyzer,
|
|
33613
34490
|
TAG_TO_CATEGORY,
|
|
33614
34491
|
TAG_TO_TECH,
|