ethan-skill 1.5.9 → 1.7.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/cli/index.js +32 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/loader/custom-pipeline-loader.d.ts +15 -0
- package/dist/loader/custom-pipeline-loader.d.ts.map +1 -0
- package/dist/loader/custom-pipeline-loader.js +131 -0
- package/dist/loader/custom-pipeline-loader.js.map +1 -0
- package/dist/loader/custom-skill-loader.d.ts +7 -2
- package/dist/loader/custom-skill-loader.d.ts.map +1 -1
- package/dist/loader/custom-skill-loader.js +112 -3
- package/dist/loader/custom-skill-loader.js.map +1 -1
- package/dist/loader/custom-skill-loader.test.d.ts +2 -0
- package/dist/loader/custom-skill-loader.test.d.ts.map +1 -0
- package/dist/loader/custom-skill-loader.test.js +201 -0
- package/dist/loader/custom-skill-loader.test.js.map +1 -0
- package/dist/skills/11-api-design.d.ts +3 -0
- package/dist/skills/11-api-design.d.ts.map +1 -0
- package/dist/skills/11-api-design.js +214 -0
- package/dist/skills/11-api-design.js.map +1 -0
- package/dist/skills/12-security-review.d.ts +3 -0
- package/dist/skills/12-security-review.d.ts.map +1 -0
- package/dist/skills/12-security-review.js +194 -0
- package/dist/skills/12-security-review.js.map +1 -0
- package/dist/skills/13-deployment.d.ts +3 -0
- package/dist/skills/13-deployment.d.ts.map +1 -0
- package/dist/skills/13-deployment.js +189 -0
- package/dist/skills/13-deployment.js.map +1 -0
- package/dist/skills/14-prd.d.ts +3 -0
- package/dist/skills/14-prd.d.ts.map +1 -0
- package/dist/skills/14-prd.js +214 -0
- package/dist/skills/14-prd.js.map +1 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +17 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/pipeline.d.ts +2 -1
- package/dist/skills/pipeline.d.ts.map +1 -1
- package/dist/skills/pipeline.js +41 -3
- package/dist/skills/pipeline.js.map +1 -1
- package/package.json +1 -1
- package/rules/claude-code/CLAUDE.md +641 -3
- package/rules/cline/.clinerules +578 -2
- package/rules/codebuddy/CODEBUDDY.md +618 -2
- package/rules/continue/.continuerules +578 -2
- package/rules/copilot/copilot-instructions.md +606 -2
- package/rules/cursor/.cursorrules +636 -2
- package/rules/cursor/smart-flow.mdc +636 -2
- package/rules/jetbrains/smart-flow.md +606 -2
- package/rules/lingma/smart-flow.md +615 -3
- package/rules/windsurf/.windsurf/rules/smart-flow.md +607 -3
- package/rules/zed/smart-flow.rules +573 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 自定义 Pipeline 加载器
|
|
4
|
+
* 从项目的 .ethan/pipelines/ 目录加载用户自定义 Pipeline(YAML 或 JSON 格式)
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.loadCustomPipelines = loadCustomPipelines;
|
|
41
|
+
exports.generatePipelineTemplate = generatePipelineTemplate;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const CUSTOM_PIPELINES_DIR = '.ethan/pipelines';
|
|
45
|
+
/**
|
|
46
|
+
* 从指定目录加载自定义 Pipeline 定义
|
|
47
|
+
* 支持 .yaml、.yml、.json 格式
|
|
48
|
+
*/
|
|
49
|
+
function loadCustomPipelines(cwd = process.cwd()) {
|
|
50
|
+
const pipelinesDir = path.join(cwd, CUSTOM_PIPELINES_DIR);
|
|
51
|
+
if (!fs.existsSync(pipelinesDir))
|
|
52
|
+
return [];
|
|
53
|
+
const files = fs.readdirSync(pipelinesDir).filter((f) => /\.(ya?ml|json)$/i.test(f));
|
|
54
|
+
const pipelines = [];
|
|
55
|
+
for (const file of files) {
|
|
56
|
+
const filePath = path.join(pipelinesDir, file);
|
|
57
|
+
try {
|
|
58
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
59
|
+
let data;
|
|
60
|
+
if (/\.json$/i.test(file)) {
|
|
61
|
+
data = JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
65
|
+
const yaml = require('js-yaml');
|
|
66
|
+
data = yaml.load(raw);
|
|
67
|
+
}
|
|
68
|
+
const pipeline = validateCustomPipeline(data, file);
|
|
69
|
+
if (pipeline) {
|
|
70
|
+
pipelines.push(pipeline);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
console.warn(` ⚠️ Failed to load custom pipeline: ${file} — ${err.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return pipelines;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 验证自定义 Pipeline 数据结构,返回合法的 PipelineDefinition 或 null
|
|
81
|
+
*/
|
|
82
|
+
function validateCustomPipeline(data, filename) {
|
|
83
|
+
if (typeof data !== 'object' || data === null) {
|
|
84
|
+
console.warn(` ⚠️ ${filename}: root must be an object`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const d = data;
|
|
88
|
+
const required = ['id', 'name', 'description', 'skillIds'];
|
|
89
|
+
for (const key of required) {
|
|
90
|
+
if (!d[key]) {
|
|
91
|
+
console.warn(` ⚠️ ${filename}: missing required field "${key}"`);
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!Array.isArray(d.skillIds) || d.skillIds.length === 0) {
|
|
96
|
+
console.warn(` ⚠️ ${filename}: "skillIds" must be a non-empty array`);
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
id: String(d.id),
|
|
101
|
+
name: String(d.name),
|
|
102
|
+
description: String(d.description),
|
|
103
|
+
skillIds: d.skillIds.map(String),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 生成自定义 Pipeline 的 YAML 模板文件
|
|
108
|
+
*/
|
|
109
|
+
function generatePipelineTemplate() {
|
|
110
|
+
return `# Ethan 自定义 Pipeline 模板
|
|
111
|
+
# Pipeline 是多个 Skill 的有序组合,支持链式工作流
|
|
112
|
+
|
|
113
|
+
id: my-pipeline # 唯一标识符(用于 ethan pipeline run <id>)
|
|
114
|
+
name: 我的工作流 # 显示名称
|
|
115
|
+
description: 一句话描述这条 Pipeline 的用途
|
|
116
|
+
|
|
117
|
+
# skillIds: 按执行顺序填写 Skill ID
|
|
118
|
+
# 可用内置 Skill ID:
|
|
119
|
+
# requirement-understanding, task-breakdown, solution-design,
|
|
120
|
+
# implementation, progress-tracking, task-report, weekly-report,
|
|
121
|
+
# code-review, debug, tech-research,
|
|
122
|
+
# api-design, security-review, deployment, prd
|
|
123
|
+
# 也可以使用自定义 Skill 的 ID(需先在 .ethan/skills/ 中定义)
|
|
124
|
+
skillIds:
|
|
125
|
+
- requirement-understanding
|
|
126
|
+
- solution-design
|
|
127
|
+
- implementation
|
|
128
|
+
- code-review
|
|
129
|
+
`;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=custom-pipeline-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-pipeline-loader.js","sourceRoot":"","sources":["../../src/loader/custom-pipeline-loader.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYH,kDAgCC;AAqCD,4DAqBC;AApGD,uCAAyB;AACzB,2CAA6B;AAG7B,MAAM,oBAAoB,GAAG,kBAAkB,CAAC;AAEhD;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC7D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAE5C,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAErF,MAAM,SAAS,GAAyB,EAAE,CAAC;IAE3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAE/C,IAAI,IAAa,CAAC;YAClB,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;iBAAM,CAAC;gBACN,iEAAiE;gBACjE,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAqC,CAAC;gBACpE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxB,CAAC;YAED,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACpD,IAAI,QAAQ,EAAE,CAAC;gBACb,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,yCAAyC,IAAI,MAAO,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5F,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,IAAa,EAAE,QAAgB;IAC7D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,CAAC,IAAI,CAAC,SAAS,QAAQ,0BAA0B,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,GAAG,IAA+B,CAAC;IAE1C,MAAM,QAAQ,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC;IAC3D,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,SAAS,QAAQ,6BAA6B,GAAG,GAAG,CAAC,CAAC;YACnE,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAK,CAAC,CAAC,QAAsB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzE,OAAO,CAAC,IAAI,CAAC,SAAS,QAAQ,wCAAwC,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC;QAClC,QAAQ,EAAG,CAAC,CAAC,QAAsB,CAAC,GAAG,CAAC,MAAM,CAAC;KAChD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,wBAAwB;IACtC,OAAO;;;;;;;;;;;;;;;;;;;CAmBR,CAAC;AACF,CAAC"}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 自定义 Skill 加载器
|
|
3
|
-
* 从项目的 .ethan/skills/ 目录加载用户自定义 Skill(YAML 或
|
|
3
|
+
* 从项目的 .ethan/skills/ 目录加载用户自定义 Skill(YAML、JSON 或 Markdown 格式)
|
|
4
4
|
*/
|
|
5
5
|
import type { SkillDefinition } from '../skills/types';
|
|
6
6
|
/**
|
|
7
7
|
* 从指定目录加载自定义 Skill 定义
|
|
8
|
-
* 支持 .yaml、.yml、.json 格式
|
|
8
|
+
* 支持 .yaml、.yml、.json、.md 格式
|
|
9
9
|
*/
|
|
10
10
|
export declare function loadCustomSkills(cwd?: string): SkillDefinition[];
|
|
11
11
|
/**
|
|
12
12
|
* 生成自定义 Skill 的 YAML 模板文件
|
|
13
13
|
*/
|
|
14
14
|
export declare function generateSkillTemplate(): string;
|
|
15
|
+
/**
|
|
16
|
+
* 生成自定义 Skill 的 Markdown 模板文件
|
|
17
|
+
* 格式:YAML frontmatter(元数据)+ Markdown 正文(步骤,每个 ## 标题为一步)
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateMdSkillTemplate(): string;
|
|
15
20
|
//# sourceMappingURL=custom-skill-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-skill-loader.d.ts","sourceRoot":"","sources":["../../src/loader/custom-skill-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"custom-skill-loader.d.ts","sourceRoot":"","sources":["../../src/loader/custom-skill-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,eAAe,EAAE,CAwC/E;AAwDD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CA2B9C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAiChD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* 自定义 Skill 加载器
|
|
4
|
-
* 从项目的 .ethan/skills/ 目录加载用户自定义 Skill(YAML 或
|
|
4
|
+
* 从项目的 .ethan/skills/ 目录加载用户自定义 Skill(YAML、JSON 或 Markdown 格式)
|
|
5
5
|
*/
|
|
6
6
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
7
|
if (k2 === undefined) k2 = k;
|
|
@@ -39,23 +39,31 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.loadCustomSkills = loadCustomSkills;
|
|
41
41
|
exports.generateSkillTemplate = generateSkillTemplate;
|
|
42
|
+
exports.generateMdSkillTemplate = generateMdSkillTemplate;
|
|
42
43
|
const fs = __importStar(require("fs"));
|
|
43
44
|
const path = __importStar(require("path"));
|
|
44
45
|
const CUSTOM_SKILLS_DIR = '.ethan/skills';
|
|
45
46
|
/**
|
|
46
47
|
* 从指定目录加载自定义 Skill 定义
|
|
47
|
-
* 支持 .yaml、.yml、.json 格式
|
|
48
|
+
* 支持 .yaml、.yml、.json、.md 格式
|
|
48
49
|
*/
|
|
49
50
|
function loadCustomSkills(cwd = process.cwd()) {
|
|
50
51
|
const skillsDir = path.join(cwd, CUSTOM_SKILLS_DIR);
|
|
51
52
|
if (!fs.existsSync(skillsDir))
|
|
52
53
|
return [];
|
|
53
|
-
const files = fs.readdirSync(skillsDir).filter((f) => /\.(ya?ml|json)$/i.test(f));
|
|
54
|
+
const files = fs.readdirSync(skillsDir).filter((f) => /\.(ya?ml|json|md)$/i.test(f));
|
|
54
55
|
const skills = [];
|
|
55
56
|
for (const file of files) {
|
|
56
57
|
const filePath = path.join(skillsDir, file);
|
|
57
58
|
try {
|
|
58
59
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
60
|
+
if (/\.md$/i.test(file)) {
|
|
61
|
+
// .md 格式:YAML frontmatter + Markdown body 步骤
|
|
62
|
+
const skill = parseMdSkill(raw, file);
|
|
63
|
+
if (skill)
|
|
64
|
+
skills.push(skill);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
59
67
|
let data;
|
|
60
68
|
if (/\.json$/i.test(file)) {
|
|
61
69
|
data = JSON.parse(raw);
|
|
@@ -155,4 +163,105 @@ notes:
|
|
|
155
163
|
- 使用注意事项(可选)
|
|
156
164
|
`;
|
|
157
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* 生成自定义 Skill 的 Markdown 模板文件
|
|
168
|
+
* 格式:YAML frontmatter(元数据)+ Markdown 正文(步骤,每个 ## 标题为一步)
|
|
169
|
+
*/
|
|
170
|
+
function generateMdSkillTemplate() {
|
|
171
|
+
return `---
|
|
172
|
+
id: my-custom-skill
|
|
173
|
+
name: 自定义技能
|
|
174
|
+
nameEn: my_custom_skill
|
|
175
|
+
description: 一句话描述这个 Skill 的作用
|
|
176
|
+
detailDescription: 详细描述(可选,用于规则文件头部)
|
|
177
|
+
triggers:
|
|
178
|
+
- 自定义触发词
|
|
179
|
+
- custom skill
|
|
180
|
+
- '@ethan custom'
|
|
181
|
+
outputFormat: 描述输出格式(如:Markdown 文档、JSON 数据等)
|
|
182
|
+
category: 质量侧
|
|
183
|
+
order: 100
|
|
184
|
+
notes:
|
|
185
|
+
- 使用注意事项(可选)
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 1. 第一步标题
|
|
189
|
+
|
|
190
|
+
- 步骤详细说明
|
|
191
|
+
- 支持任意 Markdown 格式
|
|
192
|
+
|
|
193
|
+
**重点内容**可以加粗,也可以使用代码块:
|
|
194
|
+
|
|
195
|
+
\`\`\`
|
|
196
|
+
示例代码
|
|
197
|
+
\`\`\`
|
|
198
|
+
|
|
199
|
+
## 2. 第二步标题
|
|
200
|
+
|
|
201
|
+
继续描述步骤内容...
|
|
202
|
+
`;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* 解析 .md 格式的 Skill 文件
|
|
206
|
+
* 格式:YAML frontmatter(元数据)+ Markdown 正文(## 标题为步骤)
|
|
207
|
+
*/
|
|
208
|
+
function parseMdSkill(raw, filename) {
|
|
209
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]+?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
210
|
+
if (!fmMatch) {
|
|
211
|
+
console.warn(` ⚠️ ${filename}: .md Skill 必须以 YAML frontmatter 开头(--- ... ---)`);
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
let meta;
|
|
215
|
+
try {
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
217
|
+
const yaml = require('js-yaml');
|
|
218
|
+
meta = yaml.load(fmMatch[1]);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
console.warn(` ⚠️ ${filename}: YAML frontmatter 解析失败 — ${err.message}`);
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
if (typeof meta !== 'object' || meta === null) {
|
|
225
|
+
console.warn(` ⚠️ ${filename}: frontmatter 必须是对象`);
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
const body = fmMatch[2].trim();
|
|
229
|
+
const mdSteps = parseMdSteps(body);
|
|
230
|
+
// body 中的步骤优先;若 body 无 ## 标题则 fallback 到 frontmatter.steps
|
|
231
|
+
const data = {
|
|
232
|
+
...meta,
|
|
233
|
+
...(mdSteps.length > 0 ? { steps: mdSteps } : {}),
|
|
234
|
+
};
|
|
235
|
+
return validateCustomSkill(data, filename);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 从 Markdown 正文中提取步骤
|
|
239
|
+
* 每个 ## 或 ### 标题 → 一个步骤(title = 标题文字,content = 标题到下一标题前的内容)
|
|
240
|
+
*/
|
|
241
|
+
function parseMdSteps(body) {
|
|
242
|
+
const steps = [];
|
|
243
|
+
if (!body)
|
|
244
|
+
return steps;
|
|
245
|
+
const lines = body.split(/\r?\n/);
|
|
246
|
+
let currentTitle = '';
|
|
247
|
+
const currentContent = [];
|
|
248
|
+
const flush = () => {
|
|
249
|
+
if (currentTitle) {
|
|
250
|
+
steps.push({ title: currentTitle, content: currentContent.join('\n').trim() });
|
|
251
|
+
currentContent.length = 0;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
for (const line of lines) {
|
|
255
|
+
const headingMatch = line.match(/^#{2,3}\s+(.+)/);
|
|
256
|
+
if (headingMatch) {
|
|
257
|
+
flush();
|
|
258
|
+
currentTitle = headingMatch[1].trim();
|
|
259
|
+
}
|
|
260
|
+
else if (currentTitle) {
|
|
261
|
+
currentContent.push(line);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
flush();
|
|
265
|
+
return steps;
|
|
266
|
+
}
|
|
158
267
|
//# sourceMappingURL=custom-skill-loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom-skill-loader.js","sourceRoot":"","sources":["../../src/loader/custom-skill-loader.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYH,
|
|
1
|
+
{"version":3,"file":"custom-skill-loader.js","sourceRoot":"","sources":["../../src/loader/custom-skill-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-skill-loader.test.d.ts","sourceRoot":"","sources":["../../src/loader/custom-skill-loader.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const vitest_1 = require("vitest");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const custom_skill_loader_1 = require("./custom-skill-loader");
|
|
41
|
+
// ─── helpers ──────────────────────────────────────────────────────────────────
|
|
42
|
+
function mkTmpDir() {
|
|
43
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'ethan-loader-'));
|
|
44
|
+
}
|
|
45
|
+
function writeSkillFile(dir, filename, content) {
|
|
46
|
+
const skillsDir = path.join(dir, '.ethan', 'skills');
|
|
47
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
48
|
+
fs.writeFileSync(path.join(skillsDir, filename), content, 'utf-8');
|
|
49
|
+
}
|
|
50
|
+
// ─── YAML loader (existing) ───────────────────────────────────────────────────
|
|
51
|
+
(0, vitest_1.describe)('loadCustomSkills — YAML', () => {
|
|
52
|
+
let tmpDir;
|
|
53
|
+
(0, vitest_1.beforeEach)(() => { tmpDir = mkTmpDir(); });
|
|
54
|
+
(0, vitest_1.afterEach)(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); });
|
|
55
|
+
(0, vitest_1.it)('returns [] when .ethan/skills does not exist', () => {
|
|
56
|
+
(0, vitest_1.expect)((0, custom_skill_loader_1.loadCustomSkills)(tmpDir)).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
(0, vitest_1.it)('loads a valid YAML skill', () => {
|
|
59
|
+
writeSkillFile(tmpDir, 'test.yaml', (0, custom_skill_loader_1.generateSkillTemplate)());
|
|
60
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
61
|
+
(0, vitest_1.expect)(skills).toHaveLength(1);
|
|
62
|
+
(0, vitest_1.expect)(skills[0].id).toBe('my-custom-skill');
|
|
63
|
+
(0, vitest_1.expect)(skills[0].order).toBe(100);
|
|
64
|
+
});
|
|
65
|
+
(0, vitest_1.it)('warns and skips invalid YAML skill (missing required field)', () => {
|
|
66
|
+
writeSkillFile(tmpDir, 'bad.yaml', 'id: only-id\nname: X\n');
|
|
67
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
68
|
+
(0, vitest_1.expect)(skills).toHaveLength(0);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
// ─── Markdown loader (new) ────────────────────────────────────────────────────
|
|
72
|
+
const VALID_MD = `---
|
|
73
|
+
id: md-skill
|
|
74
|
+
name: MD 技能
|
|
75
|
+
nameEn: md_skill
|
|
76
|
+
description: Markdown 格式的测试 Skill
|
|
77
|
+
triggers:
|
|
78
|
+
- md 技能
|
|
79
|
+
- md skill
|
|
80
|
+
outputFormat: Markdown 文档
|
|
81
|
+
order: 101
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 1. 第一步
|
|
85
|
+
|
|
86
|
+
- 步骤说明 A
|
|
87
|
+
- 步骤说明 B
|
|
88
|
+
|
|
89
|
+
## 2. 第二步
|
|
90
|
+
|
|
91
|
+
详细内容...
|
|
92
|
+
`;
|
|
93
|
+
(0, vitest_1.describe)('loadCustomSkills — Markdown (.md)', () => {
|
|
94
|
+
let tmpDir;
|
|
95
|
+
(0, vitest_1.beforeEach)(() => { tmpDir = mkTmpDir(); });
|
|
96
|
+
(0, vitest_1.afterEach)(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); });
|
|
97
|
+
(0, vitest_1.it)('loads a valid .md skill with frontmatter + body steps', () => {
|
|
98
|
+
writeSkillFile(tmpDir, 'test.md', VALID_MD);
|
|
99
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
100
|
+
(0, vitest_1.expect)(skills).toHaveLength(1);
|
|
101
|
+
const s = skills[0];
|
|
102
|
+
(0, vitest_1.expect)(s.id).toBe('md-skill');
|
|
103
|
+
(0, vitest_1.expect)(s.name).toBe('MD 技能');
|
|
104
|
+
(0, vitest_1.expect)(s.nameEn).toBe('md_skill');
|
|
105
|
+
(0, vitest_1.expect)(s.order).toBe(101);
|
|
106
|
+
(0, vitest_1.expect)(s.triggers).toContain('md skill');
|
|
107
|
+
});
|
|
108
|
+
(0, vitest_1.it)('parses body steps from ## headings', () => {
|
|
109
|
+
writeSkillFile(tmpDir, 'test.md', VALID_MD);
|
|
110
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
111
|
+
(0, vitest_1.expect)(skills[0].steps).toHaveLength(2);
|
|
112
|
+
(0, vitest_1.expect)(skills[0].steps[0].title).toBe('1. 第一步');
|
|
113
|
+
(0, vitest_1.expect)(skills[0].steps[0].content).toContain('步骤说明 A');
|
|
114
|
+
(0, vitest_1.expect)(skills[0].steps[1].title).toBe('2. 第二步');
|
|
115
|
+
});
|
|
116
|
+
(0, vitest_1.it)('warns and skips .md without frontmatter', () => {
|
|
117
|
+
writeSkillFile(tmpDir, 'no-fm.md', '## Step 1\n\ncontent\n');
|
|
118
|
+
(0, vitest_1.expect)((0, custom_skill_loader_1.loadCustomSkills)(tmpDir)).toHaveLength(0);
|
|
119
|
+
});
|
|
120
|
+
(0, vitest_1.it)('warns and skips .md missing required fields in frontmatter', () => {
|
|
121
|
+
writeSkillFile(tmpDir, 'missing.md', '---\nid: x\nname: X\n---\n\n## Step\nok\n');
|
|
122
|
+
(0, vitest_1.expect)((0, custom_skill_loader_1.loadCustomSkills)(tmpDir)).toHaveLength(0);
|
|
123
|
+
});
|
|
124
|
+
(0, vitest_1.it)('falls back to frontmatter steps when body has no ## headings', () => {
|
|
125
|
+
const mdWithFmSteps = `---
|
|
126
|
+
id: fm-steps-skill
|
|
127
|
+
name: FM Steps
|
|
128
|
+
nameEn: fm_steps_skill
|
|
129
|
+
description: fallback test
|
|
130
|
+
triggers:
|
|
131
|
+
- fm steps
|
|
132
|
+
outputFormat: doc
|
|
133
|
+
steps:
|
|
134
|
+
- title: "Step from FM"
|
|
135
|
+
content: "From frontmatter"
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
This body has no headings, just prose.
|
|
139
|
+
`;
|
|
140
|
+
writeSkillFile(tmpDir, 'fmsteps.md', mdWithFmSteps);
|
|
141
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
142
|
+
(0, vitest_1.expect)(skills).toHaveLength(1);
|
|
143
|
+
(0, vitest_1.expect)(skills[0].steps[0].title).toBe('Step from FM');
|
|
144
|
+
});
|
|
145
|
+
(0, vitest_1.it)('body steps take precedence over frontmatter steps when both present', () => {
|
|
146
|
+
const md = `---
|
|
147
|
+
id: both-steps
|
|
148
|
+
name: Both
|
|
149
|
+
nameEn: both_steps
|
|
150
|
+
description: test
|
|
151
|
+
triggers:
|
|
152
|
+
- both
|
|
153
|
+
outputFormat: doc
|
|
154
|
+
steps:
|
|
155
|
+
- title: "FM Step"
|
|
156
|
+
content: "should be ignored"
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Body Step
|
|
160
|
+
|
|
161
|
+
body step content
|
|
162
|
+
`;
|
|
163
|
+
writeSkillFile(tmpDir, 'both.md', md);
|
|
164
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
165
|
+
(0, vitest_1.expect)(skills[0].steps[0].title).toBe('Body Step');
|
|
166
|
+
});
|
|
167
|
+
(0, vitest_1.it)('loads both .yaml and .md files from the same directory', () => {
|
|
168
|
+
writeSkillFile(tmpDir, 'yaml-skill.yaml', (0, custom_skill_loader_1.generateSkillTemplate)());
|
|
169
|
+
writeSkillFile(tmpDir, 'md-skill.md', VALID_MD);
|
|
170
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
171
|
+
(0, vitest_1.expect)(skills).toHaveLength(2);
|
|
172
|
+
const ids = skills.map((s) => s.id);
|
|
173
|
+
(0, vitest_1.expect)(ids).toContain('my-custom-skill');
|
|
174
|
+
(0, vitest_1.expect)(ids).toContain('md-skill');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
// ─── generateMdSkillTemplate ──────────────────────────────────────────────────
|
|
178
|
+
(0, vitest_1.describe)('generateMdSkillTemplate', () => {
|
|
179
|
+
let tmpDir;
|
|
180
|
+
(0, vitest_1.beforeEach)(() => { tmpDir = mkTmpDir(); });
|
|
181
|
+
(0, vitest_1.afterEach)(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); });
|
|
182
|
+
(0, vitest_1.it)('returns a non-empty string starting with ---', () => {
|
|
183
|
+
const tpl = (0, custom_skill_loader_1.generateMdSkillTemplate)();
|
|
184
|
+
(0, vitest_1.expect)(typeof tpl).toBe('string');
|
|
185
|
+
(0, vitest_1.expect)(tpl.startsWith('---')).toBe(true);
|
|
186
|
+
});
|
|
187
|
+
(0, vitest_1.it)('generated template is parseable and produces a valid skill after editing', () => {
|
|
188
|
+
// Replace placeholder values with valid data
|
|
189
|
+
const filled = (0, custom_skill_loader_1.generateMdSkillTemplate)()
|
|
190
|
+
.replace('id: my-custom-skill', 'id: tpl-test')
|
|
191
|
+
.replace('name: 自定义技能', 'name: 模板测试')
|
|
192
|
+
.replace('nameEn: my_custom_skill', 'nameEn: tpl_test')
|
|
193
|
+
.replace('description: 一句话描述这个 Skill 的作用', 'description: template test');
|
|
194
|
+
writeSkillFile(tmpDir, 'tpl-test.md', filled);
|
|
195
|
+
const skills = (0, custom_skill_loader_1.loadCustomSkills)(tmpDir);
|
|
196
|
+
(0, vitest_1.expect)(skills).toHaveLength(1);
|
|
197
|
+
(0, vitest_1.expect)(skills[0].id).toBe('tpl-test');
|
|
198
|
+
(0, vitest_1.expect)(skills[0].steps.length).toBeGreaterThanOrEqual(1);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
//# sourceMappingURL=custom-skill-loader.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-skill-loader.test.js","sourceRoot":"","sources":["../../src/loader/custom-skill-loader.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"11-api-design.d.ts","sourceRoot":"","sources":["../../src/skills/11-api-design.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,cAAc,EAAE,eAkN5B,CAAC"}
|