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 getSkillBlameAuthor(projectId, filePath, ref, token) {
453
+ async function getSkillLastCommitAuthor(projectId, filePath, token) {
454
454
  try {
455
- const url = `${CODE_HOST}/api/v3/projects/${projectId}/repository/files/blame?file_path=${encodeURIComponent(filePath)}&ref=${encodeURIComponent(ref)}&private_token=${token}&projectId=${projectId}`;
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 entries = await res.json();
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 blame author:", error);
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
- const content = await readFile(skillMdPath, "utf-8");
624
- const { data } = (0, import_gray_matter.default)(content);
625
- if (!data.name || !data.description) return null;
626
- if (typeof data.name !== "string" || typeof data.description !== "string") return null;
627
- if (!isValidSkillName(data.name)) {
628
- const suggested = data.name.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^[^a-z]+/, "").replace(/-+$/g, "");
629
- console.warn(`⚠ Skill "${data.name}" 的名称不符合命名规范,将无法在技能市场中展示。\n 规范要求:以英文字母开头,仅允许小写字母(a-z)、数字(0-9)、连字符(-)、下划线(_)。\n 建议修改为:${suggested || "my-skill"}\n 请联系 Skill 开发者修改 SKILL.md 中的 name 字段。`);
630
- }
631
- if (data.metadata?.internal === true && !shouldInstallInternalSkills() && !options?.includeInternal) return null;
632
- return {
633
- name: data.name,
634
- description: data.description,
635
- path: dirname(skillMdPath),
636
- rawContent: content,
637
- metadata: data.metadata
638
- };
639
- } catch {
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"), options);
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"), options);
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"), options);
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
- var version$1 = "2.0.4";
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
- spinner.start("Discovering skills...");
2525
- let skills = await discoverSkills(skillsDir, parsed.subpath, {
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
- skills = await discoverSkills(skillsDir, parsed.subpath, {
2550
- includeInternal,
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 getSkillBlameAuthor(project.id, firstSkillPath, parsed.ref || project.defaultBranch, gitlabToken);
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 skills = await discoverSkills(process.cwd(), void 0, { includeInternal: true });
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.4",
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-beta.0",
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.4"
34
+ "@ali/cli-skills": "^2.0.5"
34
35
  },
35
36
  "bundleDependencies": [
36
37
  "@ali/cli-skills"