ali-skills 0.0.7-beta.0 → 0.0.7
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.
|
@@ -450,14 +450,12 @@ async function getGitLabProject(ownerRepo, token) {
|
|
|
450
450
|
return null;
|
|
451
451
|
}
|
|
452
452
|
}
|
|
453
|
-
async function
|
|
453
|
+
async function getSkillLastCommitAuthor(projectId, filePath, token) {
|
|
454
454
|
try {
|
|
455
|
-
const url = `${CODE_HOST}/api/v3/projects/${projectId}/repository/files/
|
|
455
|
+
const url = `${CODE_HOST}/api/v3/projects/${projectId}/repository/files/last_commit?filepath=${encodeURIComponent(filePath)}&sha=HEAD&private_token=${token}&projectId=${projectId}`;
|
|
456
456
|
const res = await fetch(url);
|
|
457
457
|
if (!res.ok) return null;
|
|
458
|
-
const
|
|
459
|
-
if (!Array.isArray(entries) || entries.length === 0) return null;
|
|
460
|
-
const author = entries[0]?.commit?.user;
|
|
458
|
+
const author = (await res.json())?.user;
|
|
461
459
|
if (!author) return null;
|
|
462
460
|
return {
|
|
463
461
|
employeeId: author.extern_uid ?? "",
|
|
@@ -467,7 +465,7 @@ async function getSkillBlameAuthor(projectId, filePath, ref, token) {
|
|
|
467
465
|
username: author.username ?? ""
|
|
468
466
|
};
|
|
469
467
|
} catch (error) {
|
|
470
|
-
console.error("[GitLab] Failed to get skill
|
|
468
|
+
console.error("[GitLab] Failed to get skill last commit author:", error);
|
|
471
469
|
return null;
|
|
472
470
|
}
|
|
473
471
|
}
|
|
@@ -618,27 +616,74 @@ async function hasSkillMd(dir) {
|
|
|
618
616
|
return false;
|
|
619
617
|
}
|
|
620
618
|
}
|
|
619
|
+
function emitParseFailure(sink, failure) {
|
|
620
|
+
sink?.(failure);
|
|
621
|
+
}
|
|
621
622
|
async function parseSkillMd(skillMdPath, options) {
|
|
623
|
+
const onFailure = options?.onParseFailure;
|
|
624
|
+
let content;
|
|
622
625
|
try {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
626
|
+
content = await readFile(skillMdPath, "utf-8");
|
|
627
|
+
} catch (err) {
|
|
628
|
+
emitParseFailure(onFailure, {
|
|
629
|
+
path: skillMdPath,
|
|
630
|
+
code: "read_error",
|
|
631
|
+
message: err instanceof Error ? err.message : String(err)
|
|
632
|
+
});
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
let data;
|
|
636
|
+
try {
|
|
637
|
+
({data} = (0, import_gray_matter.default)(content));
|
|
638
|
+
} catch (err) {
|
|
639
|
+
emitParseFailure(onFailure, {
|
|
640
|
+
path: skillMdPath,
|
|
641
|
+
code: "yaml_parse",
|
|
642
|
+
message: err instanceof Error ? err.message : String(err),
|
|
643
|
+
hint: "YAML frontmatter 语法无效。值里含 []、:、#、引号等时请用单引号或双引号把整个标量包起来"
|
|
644
|
+
});
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
if (!data.name || !data.description) {
|
|
648
|
+
const missing = [];
|
|
649
|
+
if (!data.name) missing.push("name");
|
|
650
|
+
if (!data.description) missing.push("description");
|
|
651
|
+
emitParseFailure(onFailure, {
|
|
652
|
+
path: skillMdPath,
|
|
653
|
+
code: "missing_frontmatter_fields",
|
|
654
|
+
message: `缺少必填 frontmatter 字段: ${missing.join(", ")}`
|
|
655
|
+
});
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
if (typeof data.name !== "string" || typeof data.description !== "string") {
|
|
659
|
+
emitParseFailure(onFailure, {
|
|
660
|
+
path: skillMdPath,
|
|
661
|
+
code: "wrong_field_types",
|
|
662
|
+
message: `name 与 description 必须为字符串(当前 name 为 ${typeof data.name},description 为 ${typeof data.description})`,
|
|
663
|
+
hint: "在 YAML 里给值加引号,例如 name: \"my-skill\" 或 description: \"说明文字\""
|
|
664
|
+
});
|
|
640
665
|
return null;
|
|
641
666
|
}
|
|
667
|
+
if (!isValidSkillName(data.name)) {
|
|
668
|
+
const suggested = data.name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/g, "");
|
|
669
|
+
console.warn(`⚠ Skill "${data.name}" 的名称不符合命名规范,将无法在技能市场中展示。\n 规范要求:以英文字母开头,仅允许小写字母(a-z)、数字(0-9)、连字符(-)、下划线(_)。\n 建议修改为:${suggested || "my-skill"}\n 请联系 Skill 开发者修改 SKILL.md 中的 name 字段。`);
|
|
670
|
+
}
|
|
671
|
+
if (data.metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) {
|
|
672
|
+
emitParseFailure(onFailure, {
|
|
673
|
+
path: skillMdPath,
|
|
674
|
+
code: "skipped_internal",
|
|
675
|
+
message: "该 skill 在 frontmatter 中标记为 internal,默认不参与安装",
|
|
676
|
+
hint: "设置环境变量 INSTALL_INTERNAL_SKILLS=1 或使用 --skill 显式指定要安装的技能名"
|
|
677
|
+
});
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
return {
|
|
681
|
+
name: data.name,
|
|
682
|
+
description: data.description,
|
|
683
|
+
path: dirname(skillMdPath),
|
|
684
|
+
rawContent: content,
|
|
685
|
+
metadata: data.metadata
|
|
686
|
+
};
|
|
642
687
|
}
|
|
643
688
|
async function findSkillDirs(dir, depth = 0, maxDepth = 5) {
|
|
644
689
|
if (depth > maxDepth) return [];
|
|
@@ -659,6 +704,19 @@ function isSubpathSafe(basePath, subpath) {
|
|
|
659
704
|
async function discoverSkills(basePath, subpath, options) {
|
|
660
705
|
const skills = [];
|
|
661
706
|
const seenNames = /* @__PURE__ */ new Set();
|
|
707
|
+
const failureSink = options?.parseFailures;
|
|
708
|
+
const seenFailureKeys = /* @__PURE__ */ new Set();
|
|
709
|
+
const onParseFailure = (failure) => {
|
|
710
|
+
if (!failureSink) return;
|
|
711
|
+
const key = `${failure.code}\0${failure.path}`;
|
|
712
|
+
if (seenFailureKeys.has(key)) return;
|
|
713
|
+
seenFailureKeys.add(key);
|
|
714
|
+
failureSink.push(failure);
|
|
715
|
+
};
|
|
716
|
+
const mdOptions = {
|
|
717
|
+
includeInternal: options?.includeInternal,
|
|
718
|
+
onParseFailure
|
|
719
|
+
};
|
|
662
720
|
if (subpath && !isSubpathSafe(basePath, subpath)) throw new Error(`Invalid subpath: "${subpath}" resolves outside the repository directory. Subpath must not contain ".." segments that escape the base path.`);
|
|
663
721
|
const searchPath = subpath ? join(basePath, subpath) : basePath;
|
|
664
722
|
const pluginGroupings = await getPluginGroupings(searchPath);
|
|
@@ -668,7 +726,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
668
726
|
return skill;
|
|
669
727
|
};
|
|
670
728
|
if (await hasSkillMd(searchPath)) {
|
|
671
|
-
let skill = await parseSkillMd(join(searchPath, "SKILL.md"),
|
|
729
|
+
let skill = await parseSkillMd(join(searchPath, "SKILL.md"), mdOptions);
|
|
672
730
|
if (skill) {
|
|
673
731
|
skill = enhanceSkill(skill);
|
|
674
732
|
skills.push(skill);
|
|
@@ -713,7 +771,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
713
771
|
for (const entry of entries) if (entry.isDirectory()) {
|
|
714
772
|
const skillDir = join(dir, entry.name);
|
|
715
773
|
if (await hasSkillMd(skillDir)) {
|
|
716
|
-
let skill = await parseSkillMd(join(skillDir, "SKILL.md"),
|
|
774
|
+
let skill = await parseSkillMd(join(skillDir, "SKILL.md"), mdOptions);
|
|
717
775
|
if (skill && !seenNames.has(skill.name)) {
|
|
718
776
|
skill = enhanceSkill(skill);
|
|
719
777
|
skills.push(skill);
|
|
@@ -725,7 +783,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
725
783
|
if (skills.length === 0 || options?.fullDepth) {
|
|
726
784
|
const allSkillDirs = await findSkillDirs(searchPath);
|
|
727
785
|
for (const skillDir of allSkillDirs) {
|
|
728
|
-
let skill = await parseSkillMd(join(skillDir, "SKILL.md"),
|
|
786
|
+
let skill = await parseSkillMd(join(skillDir, "SKILL.md"), mdOptions);
|
|
729
787
|
if (skill && !seenNames.has(skill.name)) {
|
|
730
788
|
skill = enhanceSkill(skill);
|
|
731
789
|
skills.push(skill);
|
|
@@ -1946,7 +2004,17 @@ function createEmptyLocalLock() {
|
|
|
1946
2004
|
skills: {}
|
|
1947
2005
|
};
|
|
1948
2006
|
}
|
|
1949
|
-
|
|
2007
|
+
function logSkillParseFailures(failures) {
|
|
2008
|
+
if (failures.length === 0) return;
|
|
2009
|
+
console.log();
|
|
2010
|
+
M.error(import_picocolors.default.bold("以下 SKILL.md 解析未通过(请按提示修复)"));
|
|
2011
|
+
for (const f of failures) {
|
|
2012
|
+
M.message(import_picocolors.default.dim(f.path));
|
|
2013
|
+
M.message(import_picocolors.default.red(` [${f.code}] ${f.message}`));
|
|
2014
|
+
if (f.hint) M.message(import_picocolors.default.yellow(` 提示: ${f.hint}`));
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
var version$1 = "2.0.5";
|
|
1950
2018
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
1951
2019
|
async function isSourcePrivate(source) {
|
|
1952
2020
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -2521,11 +2589,14 @@ async function runAdd(args, options = {}) {
|
|
|
2521
2589
|
if (!options.skill.includes(parsed.skillFilter)) options.skill.push(parsed.skillFilter);
|
|
2522
2590
|
}
|
|
2523
2591
|
const includeInternal = !!(options.skill && options.skill.length > 0);
|
|
2524
|
-
|
|
2525
|
-
|
|
2592
|
+
const skillParseFailures = [];
|
|
2593
|
+
const discoverOpts = {
|
|
2526
2594
|
includeInternal,
|
|
2527
|
-
fullDepth: options.fullDepth
|
|
2528
|
-
|
|
2595
|
+
fullDepth: options.fullDepth,
|
|
2596
|
+
parseFailures: skillParseFailures
|
|
2597
|
+
};
|
|
2598
|
+
spinner.start("Discovering skills...");
|
|
2599
|
+
let skills = await discoverSkills(skillsDir, parsed.subpath, discoverOpts);
|
|
2529
2600
|
if (skills.length === 0 && !usedOssFallback && [
|
|
2530
2601
|
"github",
|
|
2531
2602
|
"internal",
|
|
@@ -2546,16 +2617,15 @@ async function runAdd(args, options = {}) {
|
|
|
2546
2617
|
usedOssFallback = true;
|
|
2547
2618
|
spinner.stop("Skill package downloaded");
|
|
2548
2619
|
spinner.start("Discovering skills...");
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
fullDepth: options.fullDepth
|
|
2552
|
-
});
|
|
2620
|
+
skillParseFailures.length = 0;
|
|
2621
|
+
skills = await discoverSkills(skillsDir, parsed.subpath, discoverOpts);
|
|
2553
2622
|
}
|
|
2554
2623
|
} catch {}
|
|
2555
2624
|
}
|
|
2556
2625
|
}
|
|
2557
2626
|
if (skills.length === 0) {
|
|
2558
2627
|
spinner.stop(import_picocolors.default.red("No skills found"));
|
|
2628
|
+
logSkillParseFailures(skillParseFailures);
|
|
2559
2629
|
Se(import_picocolors.default.red("No valid skills found. Skills require a SKILL.md with name and description."));
|
|
2560
2630
|
await cleanup(tempDir);
|
|
2561
2631
|
process.exit(1);
|
|
@@ -2858,7 +2928,7 @@ async function runAdd(args, options = {}) {
|
|
|
2858
2928
|
if (project) {
|
|
2859
2929
|
const firstSkillPath = Object.values(skillFiles)[0];
|
|
2860
2930
|
if (firstSkillPath) {
|
|
2861
|
-
const author = await
|
|
2931
|
+
const author = await getSkillLastCommitAuthor(project.id, firstSkillPath, gitlabToken);
|
|
2862
2932
|
if (author) skillAuthor = author;
|
|
2863
2933
|
}
|
|
2864
2934
|
}
|
|
@@ -4040,9 +4110,15 @@ async function runPublish(args, options = {}) {
|
|
|
4040
4110
|
const source = `${repoInfo.owner}/${repoInfo.repo}`;
|
|
4041
4111
|
spinner.stop(`Repository: ${import_picocolors.default.cyan(source)} ${import_picocolors.default.dim(`(${repoInfo.sourceType})`)}`);
|
|
4042
4112
|
spinner.start("Discovering skills...");
|
|
4043
|
-
const
|
|
4113
|
+
const cwd = process.cwd();
|
|
4114
|
+
const skillParseFailures = [];
|
|
4115
|
+
const skills = await discoverSkills(cwd, void 0, {
|
|
4116
|
+
includeInternal: true,
|
|
4117
|
+
parseFailures: skillParseFailures
|
|
4118
|
+
});
|
|
4044
4119
|
if (skills.length === 0) {
|
|
4045
4120
|
spinner.stop(import_picocolors.default.red("No skills found"));
|
|
4121
|
+
logSkillParseFailures(skillParseFailures);
|
|
4046
4122
|
Se(import_picocolors.default.red("No valid skills found. Skills require a SKILL.md with name and description."));
|
|
4047
4123
|
process.exit(1);
|
|
4048
4124
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ali/cli-skills",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"prepare": "husky",
|
|
25
25
|
"test": "vitest",
|
|
26
26
|
"type-check": "tsc --noEmit",
|
|
27
|
-
"publish:snapshot": "tnpm version prerelease --preid=snapshot --no-git-tag-version && tnpm publish --tag snapshot"
|
|
27
|
+
"publish:snapshot": "tnpm version prerelease --preid=snapshot --no-git-tag-version && tnpm publish --tag snapshot",
|
|
28
|
+
"publish:latest:patch": "tnpm version patch && tnpm publish && git push --follow-tags"
|
|
28
29
|
},
|
|
29
30
|
"lint-staged": {
|
|
30
31
|
"src/**/*.ts": "prettier --write",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ali-skills",
|
|
3
|
-
"version": "0.0.7
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,12 +25,13 @@
|
|
|
25
25
|
"url": "git+http://gitlab.alibaba-inc.com/trip-tools/ali-skills.git"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
|
-
"publish:beta": "npm version prerelease --preid=beta --no-git-tag-version && npm publish --tag beta"
|
|
28
|
+
"publish:beta": "npm version prerelease --preid=beta --no-git-tag-version && npm publish --tag beta",
|
|
29
|
+
"publish:latest:patch": "npm version patch && npm publish && git push --follow-tags"
|
|
29
30
|
},
|
|
30
31
|
"author": "",
|
|
31
32
|
"license": "MIT",
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@ali/cli-skills": "^2.0.
|
|
34
|
+
"@ali/cli-skills": "^2.0.5"
|
|
34
35
|
},
|
|
35
36
|
"bundleDependencies": [
|
|
36
37
|
"@ali/cli-skills"
|