@x-otto/skill 0.0.1-alpha.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/README.md +40 -0
- package/dist/index.d.ts +222 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @x-otto/skill
|
|
2
|
+
|
|
3
|
+
## 模块定位
|
|
4
|
+
|
|
5
|
+
管理 SKILL.md 技能文件的发现、解析、匹配与注册。支持项目级和用户级技能自动发现。
|
|
6
|
+
|
|
7
|
+
## 核心功能
|
|
8
|
+
|
|
9
|
+
| 模块 | 功能 |
|
|
10
|
+
| -------------- | -------------------------------------------- |
|
|
11
|
+
| SkillRegistry | 技能注册/发现/生命周期事件 |
|
|
12
|
+
| SkillDiscovery | .otto/skills/ + ~/.otto/skills/ 扫描 |
|
|
13
|
+
| SkillParser | SKILL.md YAML frontmatter + Markdown 解析 |
|
|
14
|
+
| SkillMatcher | 多因子评分(名称 0.3 + 描述 0.5 + 标签 0.2) |
|
|
15
|
+
|
|
16
|
+
## 目录概览
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
src/
|
|
20
|
+
types.ts # 核心类型
|
|
21
|
+
skill-registry.ts # 注册表
|
|
22
|
+
skill-discovery.ts # 自动发现
|
|
23
|
+
skill-parser.ts # 解析器
|
|
24
|
+
skill-matcher.ts # 匹配器
|
|
25
|
+
skill-context.ts # 执行上下文
|
|
26
|
+
index.ts
|
|
27
|
+
tests/ # 3 个测试文件
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 开发命令
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm --filter @x-otto/skill build
|
|
34
|
+
pnpm --filter @x-otto/skill typecheck
|
|
35
|
+
pnpm --filter @x-otto/skill clean
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 关联包
|
|
39
|
+
|
|
40
|
+
`@x-otto/shared`、`@x-otto/env`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { TypedEventEmitter } from "@x-otto/shared";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface SkillMetadata {
|
|
5
|
+
/** kebab-case,例如 "test-driven-development"。 */
|
|
6
|
+
name: string;
|
|
7
|
+
/** 描述触发条件而非 skill 做什么;建议以 "Use when..." 开头,最长 500 字符。 */
|
|
8
|
+
description: string;
|
|
9
|
+
version?: string;
|
|
10
|
+
author?: string;
|
|
11
|
+
tags?: string[];
|
|
12
|
+
dependencies?: string[];
|
|
13
|
+
/** auto = 根据 description 自动匹配;manual = 仅显式调用。 */
|
|
14
|
+
trigger?: 'auto' | 'manual';
|
|
15
|
+
/** skill 优先级,数字越小优先级越高(默认 100) */
|
|
16
|
+
priority?: number;
|
|
17
|
+
}
|
|
18
|
+
interface SkillDefinition {
|
|
19
|
+
metadata: SkillMetadata;
|
|
20
|
+
/** SKILL.md 去除 frontmatter 后的正文内容 */
|
|
21
|
+
body: string;
|
|
22
|
+
directory: string;
|
|
23
|
+
filePath: string;
|
|
24
|
+
/** 不含 SKILL.md。 */
|
|
25
|
+
supportingFiles: string[];
|
|
26
|
+
}
|
|
27
|
+
type SkillStatus = 'available' | 'loaded' | 'disabled' | 'error';
|
|
28
|
+
interface RegisteredSkill {
|
|
29
|
+
definition: SkillDefinition;
|
|
30
|
+
status: SkillStatus;
|
|
31
|
+
source: SkillSource;
|
|
32
|
+
error?: string;
|
|
33
|
+
loadedAt?: number;
|
|
34
|
+
}
|
|
35
|
+
type SkillSourceType = 'project' | 'global' | 'plugin' | 'inline';
|
|
36
|
+
interface SkillSource {
|
|
37
|
+
type: SkillSourceType;
|
|
38
|
+
/** 来源根路径(如 project: ".otto/skills", global: "~/.otto/skills") */
|
|
39
|
+
root: string;
|
|
40
|
+
/** 贡献来源插件 id(仅 type==='plugin',供 provenance/启停展示,RFC-044)。 */
|
|
41
|
+
id?: string;
|
|
42
|
+
}
|
|
43
|
+
interface SkillLibraryConfig {
|
|
44
|
+
/** 项目本地 skill 目录(相对于 workspaceDir) */
|
|
45
|
+
projectDirs?: string[];
|
|
46
|
+
/** 全局 skill 目录(绝对路径) */
|
|
47
|
+
globalDirs?: string[];
|
|
48
|
+
/** 插件 skill 目录(绝对路径 + 来源 id,最低优先级,RFC-044 R7)。 */
|
|
49
|
+
pluginDirs?: Array<{
|
|
50
|
+
id: string;
|
|
51
|
+
dir: string;
|
|
52
|
+
}>;
|
|
53
|
+
/** 禁用的 skill 名称列表 */
|
|
54
|
+
disabled?: string[];
|
|
55
|
+
}
|
|
56
|
+
interface SkillExecutionContext {
|
|
57
|
+
/** skill 名称 */
|
|
58
|
+
skillName: string;
|
|
59
|
+
/** 工作目录 */
|
|
60
|
+
workspaceDir: string;
|
|
61
|
+
}
|
|
62
|
+
interface SkillExecutionResult {
|
|
63
|
+
success: boolean;
|
|
64
|
+
/** skill 正文内容(注入到 agent context) */
|
|
65
|
+
content?: string;
|
|
66
|
+
/** 错误信息 */
|
|
67
|
+
error?: string;
|
|
68
|
+
}
|
|
69
|
+
interface SkillEvents {
|
|
70
|
+
'skill.discovered': (info: {
|
|
71
|
+
source: SkillSource;
|
|
72
|
+
count: number;
|
|
73
|
+
}) => void;
|
|
74
|
+
'skill.loaded': (info: {
|
|
75
|
+
name: string;
|
|
76
|
+
source: SkillSource;
|
|
77
|
+
}) => void;
|
|
78
|
+
'skill.unloaded': (info: {
|
|
79
|
+
name: string;
|
|
80
|
+
}) => void;
|
|
81
|
+
'skill.error': (info: {
|
|
82
|
+
name: string;
|
|
83
|
+
error: string;
|
|
84
|
+
}) => void;
|
|
85
|
+
'skill.executed': (info: {
|
|
86
|
+
name: string;
|
|
87
|
+
success: boolean;
|
|
88
|
+
durationMs: number;
|
|
89
|
+
}) => void;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 上层(如 App)通过此接口注入 skill 能力。
|
|
93
|
+
* 未注入时 skill 相关工具返回"功能未配置"提示。
|
|
94
|
+
*/
|
|
95
|
+
interface SkillProvider {
|
|
96
|
+
load(path: string): Promise<{
|
|
97
|
+
success: boolean;
|
|
98
|
+
name?: string;
|
|
99
|
+
error?: string;
|
|
100
|
+
}>;
|
|
101
|
+
/** 按名解析 → 注入正文(resolve(name)→body),无模板/参数语义。 */
|
|
102
|
+
execute(name: string): Promise<SkillExecutionResult>;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/skill-registry.d.ts
|
|
106
|
+
interface SkillRegistryOptions {
|
|
107
|
+
/** 工作目录 */
|
|
108
|
+
workspaceDir: string;
|
|
109
|
+
/** skill 库配置 */
|
|
110
|
+
library?: SkillLibraryConfig;
|
|
111
|
+
/** 禁用的 skill 名称列表 */
|
|
112
|
+
disabled?: string[];
|
|
113
|
+
}
|
|
114
|
+
declare class SkillRegistry extends TypedEventEmitter<SkillEvents> {
|
|
115
|
+
private skills;
|
|
116
|
+
private readonly workspaceDir;
|
|
117
|
+
private readonly library?;
|
|
118
|
+
private disabledSet;
|
|
119
|
+
/** 可变的 plugin 目录列表(reloadCapabilities 更新为新安装 plugin 的 dirs) */
|
|
120
|
+
private pluginDirs?;
|
|
121
|
+
constructor({
|
|
122
|
+
workspaceDir,
|
|
123
|
+
library,
|
|
124
|
+
disabled
|
|
125
|
+
}: SkillRegistryOptions);
|
|
126
|
+
/**
|
|
127
|
+
* 更新 plugin 贡献的 skill 目录列表(用于 reloadCapabilities 热刷新)。
|
|
128
|
+
* 下次 discover() 将扫描新的 plugin 目录。
|
|
129
|
+
*/
|
|
130
|
+
updatePluginDirs(dirs: Array<{
|
|
131
|
+
id: string;
|
|
132
|
+
dir: string;
|
|
133
|
+
}>): void;
|
|
134
|
+
/**
|
|
135
|
+
* 发现并注册所有可用 skill
|
|
136
|
+
* 扫描项目本地 + 全局目录
|
|
137
|
+
*/
|
|
138
|
+
discover(): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* 手动注册一个 skill(用于内联/插件 skill)
|
|
141
|
+
*/
|
|
142
|
+
register(definition: SkillDefinition, source: SkillSource): void;
|
|
143
|
+
/**
|
|
144
|
+
* 注销 skill
|
|
145
|
+
*/
|
|
146
|
+
unregister(name: string): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* 获取所有已注册 skill
|
|
149
|
+
*/
|
|
150
|
+
getAll(): ReadonlyMap<string, RegisteredSkill>;
|
|
151
|
+
/**
|
|
152
|
+
* 获取所有可用(非 disabled/error)skill
|
|
153
|
+
*/
|
|
154
|
+
getAvailable(): RegisteredSkill[];
|
|
155
|
+
/**
|
|
156
|
+
* 按名称获取 skill
|
|
157
|
+
*/
|
|
158
|
+
get(name: string): RegisteredSkill | undefined;
|
|
159
|
+
execute(context: SkillExecutionContext): SkillExecutionResult;
|
|
160
|
+
/**
|
|
161
|
+
* 禁用 skill
|
|
162
|
+
*/
|
|
163
|
+
disable(name: string): void;
|
|
164
|
+
/**
|
|
165
|
+
* 启用 skill
|
|
166
|
+
*/
|
|
167
|
+
enable(name: string): void;
|
|
168
|
+
/**
|
|
169
|
+
* catalog 里每条 skill description 的最大字符数(渐进披露的正确边界——完整正文本就该
|
|
170
|
+
* 靠 skill 工具按需取回,不该把全文塞进常驻的 system prompt catalog)。
|
|
171
|
+
*
|
|
172
|
+
* 取值依据:本仓真实 skill(rfc-todos)description 长度 1339 字符,若装 10+ 个同量级
|
|
173
|
+
* skill,catalog 本身就会膨胀到与基座 prompt 同量级。200 字符能保留"这个 skill 是干
|
|
174
|
+
* 什么的、什么时候用"的判断信息,同时把单条上限压到基座 prompt 的 1/10 量级。
|
|
175
|
+
*/
|
|
176
|
+
private static readonly CATALOG_DESCRIPTION_MAX_CHARS;
|
|
177
|
+
/**
|
|
178
|
+
* 按字符边界截断 description(不切断多字节字符),超长时追加省略号标记。
|
|
179
|
+
* 截断是**渲染层**行为——`definition.metadata.description` 原文不变,`skill` 工具
|
|
180
|
+
* 取全文时仍拿到完整内容,catalog 只是给模型的"目录索引"而非替代正文。
|
|
181
|
+
*/
|
|
182
|
+
private static truncateDescription;
|
|
183
|
+
/**
|
|
184
|
+
* 构建 skill catalog(用于 system prompt 注入)
|
|
185
|
+
* 格式:简短列表,仅 name + description(description 截断到固定上限,见
|
|
186
|
+
* CATALOG_DESCRIPTION_MAX_CHARS——渐进披露:目录常驻、正文按需取回,不应本末倒置)
|
|
187
|
+
*/
|
|
188
|
+
buildCatalog(): string;
|
|
189
|
+
/**
|
|
190
|
+
* 获取 skill 总数
|
|
191
|
+
*/
|
|
192
|
+
get size(): number;
|
|
193
|
+
}
|
|
194
|
+
declare function createSkillRegistry(options: SkillRegistryOptions): SkillRegistry;
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/skill-parser.d.ts
|
|
197
|
+
/**
|
|
198
|
+
* 从 SKILL.md 原始文本解析出 SkillDefinition
|
|
199
|
+
*/
|
|
200
|
+
declare function parseSkillContent(content: string, filePath: string, directory: string, supportingFiles?: string[]): SkillDefinition;
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/skill-discovery.d.ts
|
|
203
|
+
/**
|
|
204
|
+
* 扫描指定目录列表,发现所有有效 skill
|
|
205
|
+
*/
|
|
206
|
+
declare function discoverSkills(config: SkillLibraryConfig, workspaceDir: string): Promise<Map<string, {
|
|
207
|
+
definition: SkillDefinition;
|
|
208
|
+
source: SkillSource;
|
|
209
|
+
}>>;
|
|
210
|
+
/**
|
|
211
|
+
* 获取默认的全局 skill 目录列表。
|
|
212
|
+
*
|
|
213
|
+
* 顺序即优先级:`discoverSkills` 的 global 循环对同名是 **first-win**(`!has` 才填),
|
|
214
|
+
* 故 `~/.otto/skills` 排在 `~/.claude/skills` 之前 = otto 原生覆盖 .claude(RFC-032)。
|
|
215
|
+
*/
|
|
216
|
+
declare function getDefaultGlobalSkillDirs(): string[];
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/registry-skill-provider.d.ts
|
|
219
|
+
declare function createRegistrySkillProvider(registry: SkillRegistry, workspaceDir: string): SkillProvider;
|
|
220
|
+
//#endregion
|
|
221
|
+
export { type RegisteredSkill, type SkillDefinition, type SkillEvents, type SkillExecutionContext, type SkillExecutionResult, type SkillLibraryConfig, type SkillMetadata, type SkillProvider, SkillRegistry, type SkillRegistryOptions, type SkillSource, type SkillSourceType, type SkillStatus, createRegistrySkillProvider, createSkillRegistry, discoverSkills, getDefaultGlobalSkillDirs, parseSkillContent };
|
|
222
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/skill-registry.ts","../src/skill-parser.ts","../src/skill-discovery.ts","../src/registry-skill-provider.ts"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{TypedEventEmitter as e,createLogger as t}from"@x-otto/shared";import*as n from"node:fs";import*as r from"node:path";import{isClaudeCompatEnabled as i}from"@x-otto/env";import{parse as a}from"yaml";const o=t(`@x-otto/skill:parser`),s=/^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;function c(e,t,n,r=[]){let i=s.exec(e.trim());if(!i)throw Error(`SKILL.md at "${t}" has no valid YAML frontmatter`);let[,o,c]=i,u;try{u=a(o)}catch(e){throw Error(`Failed to parse YAML frontmatter in "${t}": ${e.message}`)}if(!u||typeof u!=`object`)throw Error(`YAML frontmatter in "${t}" is not a valid object`);return{metadata:l(u,t),body:(c??``).trim(),directory:n,filePath:t,supportingFiles:r}}function l(e,t){let n=e.name;if(typeof n!=`string`||n.length===0)throw Error(`SKILL.md at "${t}" missing required "name" field in frontmatter`);/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(n)||o.warn(`Skill name "%s" in "%s" does not follow kebab-case convention`,n,t);let r=e.description;if(typeof r!=`string`||r.length===0)throw Error(`SKILL.md at "${t}" missing required "description" field in frontmatter`);let i=`${n}\n${r}`;return i.length>1024&&o.warn(`Skill "%s" frontmatter exceeds 1024 character limit (%d chars)`,n,i.length),{name:n,description:r,version:typeof e.version==`string`?e.version:void 0,author:typeof e.author==`string`?e.author:void 0,tags:Array.isArray(e.tags)?e.tags.filter(e=>typeof e==`string`):void 0,dependencies:Array.isArray(e.dependencies)?e.dependencies.filter(e=>typeof e==`string`):void 0,trigger:e.trigger===`manual`?`manual`:`auto`,priority:typeof e.priority==`number`?e.priority:100}}const u=t(`@x-otto/skill:discovery`),d=`SKILL.md`;async function f(e,t){let n=new Map,i=e.projectDirs??[`.otto/skills`];for(let e of i){let i=r.resolve(t,e),a=await p(i,{type:`project`,root:i});for(let[e,t]of a)n.set(e,t)}let a=e.globalDirs??[];for(let e of a){let t=await p(e,{type:`global`,root:e});for(let[e,r]of t)n.has(e)||n.set(e,r)}let o=e.pluginDirs??[];for(let{id:e,dir:t}of o){let r=await p(t,{type:`plugin`,root:t,id:e});for(let[e,t]of r)n.has(e)||n.set(e,t)}return u.info(`Discovered %d skills from %d directories`,n.size,i.length+a.length+o.length),n}async function p(e,t){let i=new Map;if(!n.existsSync(e))return u.debug(`Skill directory does not exist: %s`,e),i;let a;try{a=n.readdirSync(e,{withFileTypes:!0})}catch{return u.warn(`Cannot read skill directory: %s`,e),i}for(let o of a){if(!o.isDirectory()){if(o.name===d){let n=await m(e,t);n&&i.set(n.definition.metadata.name,n)}continue}let a=r.join(e,o.name),s=r.join(a,d);if(!n.existsSync(s))continue;let c=await m(a,t);c&&i.set(c.definition.metadata.name,c)}return i}async function m(e,t){let i=r.join(e,d);try{let r=c(n.readFileSync(i,`utf-8`),i,e,h(e));return u.debug(`Parsed skill "%s" from %s`,r.metadata.name,i),{definition:r,source:t}}catch(e){return u.warn(`Failed to parse skill at "%s": %s`,i,e.message),null}}function h(e){try{return n.readdirSync(e).filter(e=>e!==d).map(t=>r.join(e,t))}catch{return[]}}function g(){return i()?[`.claude/skills`,`.otto/skills`]:[`.otto/skills`]}function _(){let e=process.env.HOME??process.env.USERPROFILE??``;if(!e)return[];let t=[r.join(e,`.otto`,`skills`)];return i()&&t.push(r.join(e,`.claude`,`skills`)),t}const v=t(`@x-otto/skill:registry`);var y=class t extends e{skills=new Map;workspaceDir;library;disabledSet;pluginDirs;constructor({workspaceDir:e,library:t,disabled:n}){super(),this.workspaceDir=e,this.library=t,this.disabledSet=new Set(n??t?.disabled??[]),this.pluginDirs=t?.pluginDirs}updatePluginDirs(e){this.pluginDirs=e}async discover(){let e=await f({projectDirs:this.library?.projectDirs??g(),globalDirs:this.library?.globalDirs??_(),pluginDirs:this.pluginDirs??this.library?.pluginDirs,disabled:[...this.disabledSet]},this.workspaceDir);for(let[t,{definition:n,source:r}]of e){let e=this.disabledSet.has(t)?`disabled`:`available`;this.skills.set(t,{definition:n,source:r,status:e}),this.emit(`skill.discovered`,{source:r,count:1})}v.info(`Skill registry initialized: %d skills (%d available, %d disabled)`,this.skills.size,this.getAvailable().length,[...this.skills.values()].filter(e=>e.status===`disabled`).length)}register(e,t){let{name:n}=e.metadata,r=this.disabledSet.has(n)?`disabled`:`available`;this.skills.set(n,{definition:e,source:t,status:r}),v.debug(`Registered skill "%s" from %s`,n,t.type)}unregister(e){let t=this.skills.delete(e);return t&&this.emit(`skill.unloaded`,{name:e}),t}getAll(){return this.skills}getAvailable(){return[...this.skills.values()].filter(e=>e.status===`available`||e.status===`loaded`)}get(e){return this.skills.get(e)}execute(e){let t=Date.now(),n=this.skills.get(e.skillName);if(!n)return{success:!1,error:`Skill "${e.skillName}" not found`};if(n.status===`disabled`)return{success:!1,error:`Skill "${e.skillName}" is disabled`};n.status===`available`&&(n.status=`loaded`,n.loadedAt=Date.now());let r=Date.now()-t;return this.emit(`skill.executed`,{name:e.skillName,success:!0,durationMs:r}),{success:!0,content:n.definition.body}}disable(e){this.disabledSet.add(e);let t=this.skills.get(e);t&&(t.status=`disabled`)}enable(e){this.disabledSet.delete(e);let t=this.skills.get(e);t&&(t.status=`available`)}static CATALOG_DESCRIPTION_MAX_CHARS=200;static truncateDescription(e){let n=t.CATALOG_DESCRIPTION_MAX_CHARS,r=Array.from(e);return r.length<=n?e:`${r.slice(0,n).join(``)}…`}buildCatalog(){let e=this.getAvailable();if(e.length===0)return``;let n=[`## Available Skills`,``];for(let{definition:r}of e){let{name:e,description:i,trigger:a}=r.metadata,o=a===`manual`?` [manual]`:``;n.push(`- **${e}**${o}: ${t.truncateDescription(i)}`)}return n.join(`
|
|
2
|
+
`)}get size(){return this.skills.size}};function b(e){return new y(e)}function x(e){return e.startsWith(`.claude/`)&&!e.includes(`skills/`)?`.claude/skills/`+e.slice(8):null}function S(e,t){return{async load(n){try{let{readFile:r}=await import(`node:fs/promises`),{isAbsolute:i,resolve:a,dirname:o}=await import(`node:path`),s=async e=>{let n=i(e)?e:a(t,e);try{return await r(n,`utf-8`)}catch(e){if(e.code===`ENOENT`)return null;throw e}},l=await s(n),u=n;if(l===null){let e=x(n);if(e){let t=await s(e);t!==null&&(l=t,u=e)}}if(l===null)return{success:!1,error:`ENOENT: no such file, open '${n}'`};let d=i(u)?u:a(t,u),f=c(l,d,o(d));return e.register(f,{type:`inline`,root:o(d)}),{success:!0,name:f.metadata.name}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}},async execute(n){return e.execute({skillName:n,workspaceDir:t})}}}export{y as SkillRegistry,S as createRegistrySkillProvider,b as createSkillRegistry,f as discoverSkills,_ as getDefaultGlobalSkillDirs,c as parseSkillContent};
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["logger","parseYaml","logger"],"sources":["../src/skill-parser.ts","../src/skill-discovery.ts","../src/skill-registry.ts","../src/registry-skill-provider.ts"],"sourcesContent":["import { parse as parseYaml } from 'yaml'\nimport { createLogger } from '@x-otto/shared'\nimport type { SkillMetadata, SkillDefinition } from './types'\n\nconst logger = createLogger('@x-otto/skill:parser')\n\nconst FRONTMATTER_RE = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?([\\s\\S]*)$/\n\n/**\n * 从 SKILL.md 原始文本解析出 SkillDefinition\n */\nexport function parseSkillContent(\n content: string,\n filePath: string,\n directory: string,\n supportingFiles: string[] = [],\n): SkillDefinition {\n const match = FRONTMATTER_RE.exec(content.trim())\n\n if (!match) {\n throw new Error(`SKILL.md at \"${filePath}\" has no valid YAML frontmatter`)\n }\n\n const [, yamlStr, body] = match\n let raw: Record<string, unknown>\n\n try {\n raw = parseYaml(yamlStr!) as Record<string, unknown>\n } catch (err) {\n throw new Error(`Failed to parse YAML frontmatter in \"${filePath}\": ${(err as Error).message}`)\n }\n\n if (!raw || typeof raw !== 'object') {\n throw new Error(`YAML frontmatter in \"${filePath}\" is not a valid object`)\n }\n\n const metadata = extractMetadata(raw, filePath)\n\n return {\n metadata,\n body: (body ?? '').trim(),\n directory,\n filePath,\n supportingFiles,\n }\n}\n\n/**\n * 从 raw YAML 提取并验证 SkillMetadata\n */\nfunction extractMetadata(raw: Record<string, unknown>, filePath: string): SkillMetadata {\n const name = raw['name']\n if (typeof name !== 'string' || name.length === 0) {\n throw new Error(`SKILL.md at \"${filePath}\" missing required \"name\" field in frontmatter`)\n }\n\n if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(name)) {\n logger.warn('Skill name \"%s\" in \"%s\" does not follow kebab-case convention', name, filePath)\n }\n\n const description = raw['description']\n if (typeof description !== 'string' || description.length === 0) {\n throw new Error(`SKILL.md at \"${filePath}\" missing required \"description\" field in frontmatter`)\n }\n\n const frontmatterText = `${name}\\n${description}`\n if (frontmatterText.length > 1024) {\n logger.warn(\n 'Skill \"%s\" frontmatter exceeds 1024 character limit (%d chars)',\n name,\n frontmatterText.length,\n )\n }\n\n return {\n name,\n description,\n version: typeof raw['version'] === 'string' ? raw['version'] : undefined,\n author: typeof raw['author'] === 'string' ? raw['author'] : undefined,\n tags: Array.isArray(raw['tags'])\n ? raw['tags'].filter((t): t is string => typeof t === 'string')\n : undefined,\n dependencies: Array.isArray(raw['dependencies'])\n ? raw['dependencies'].filter((d): d is string => typeof d === 'string')\n : undefined,\n trigger: raw['trigger'] === 'manual' ? 'manual' : 'auto',\n priority: typeof raw['priority'] === 'number' ? raw['priority'] : 100,\n }\n}\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { createLogger } from '@x-otto/shared'\nimport { isClaudeCompatEnabled } from '@x-otto/env'\nimport { parseSkillContent } from './skill-parser'\nimport type { SkillDefinition, SkillSource, SkillLibraryConfig } from './types'\n\nconst logger = createLogger('@x-otto/skill:discovery')\n\nconst SKILL_FILENAME = 'SKILL.md'\n\n/**\n * 扫描指定目录列表,发现所有有效 skill\n */\nexport async function discoverSkills(\n config: SkillLibraryConfig,\n workspaceDir: string,\n): Promise<Map<string, { definition: SkillDefinition; source: SkillSource }>> {\n const results = new Map<string, { definition: SkillDefinition; source: SkillSource }>()\n\n const projectDirs = config.projectDirs ?? ['.otto/skills']\n for (const relDir of projectDirs) {\n const absDir = path.resolve(workspaceDir, relDir)\n const source: SkillSource = { type: 'project', root: absDir }\n const skills = await scanDirectory(absDir, source)\n\n for (const [name, entry] of skills) {\n results.set(name, entry)\n }\n }\n\n const globalDirs = config.globalDirs ?? []\n for (const absDir of globalDirs) {\n const source: SkillSource = { type: 'global', root: absDir }\n const skills = await scanDirectory(absDir, source)\n\n for (const [name, entry] of skills) {\n if (!results.has(name)) {\n results.set(name, entry)\n }\n }\n }\n\n const pluginDirs = config.pluginDirs ?? []\n for (const { id, dir } of pluginDirs) {\n const source: SkillSource = { type: 'plugin', root: dir, id }\n const skills = await scanDirectory(dir, source)\n\n for (const [name, entry] of skills) {\n if (!results.has(name)) {\n results.set(name, entry)\n }\n }\n }\n\n logger.info(\n 'Discovered %d skills from %d directories',\n results.size,\n projectDirs.length + globalDirs.length + pluginDirs.length,\n )\n\n return results\n}\n\n/**\n * 扫描单个目录下的所有 skill 子目录\n *\n * 目录结构:\n * skills/\n * skill-name/\n * SKILL.md\n * supporting-file.ts\n * another-skill/\n * SKILL.md\n */\nasync function scanDirectory(\n dir: string,\n source: SkillSource,\n): Promise<Map<string, { definition: SkillDefinition; source: SkillSource }>> {\n const results = new Map<string, { definition: SkillDefinition; source: SkillSource }>()\n\n if (!fs.existsSync(dir)) {\n logger.debug('Skill directory does not exist: %s', dir)\n return results\n }\n\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n logger.warn('Cannot read skill directory: %s', dir)\n return results\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) {\n if (entry.name === SKILL_FILENAME) {\n const skill = await tryParseSkill(dir, source)\n if (skill) {\n results.set(skill.definition.metadata.name, skill)\n }\n }\n continue\n }\n\n const skillDir = path.join(dir, entry.name)\n const skillFile = path.join(skillDir, SKILL_FILENAME)\n\n if (!fs.existsSync(skillFile)) {\n continue\n }\n\n const skill = await tryParseSkill(skillDir, source)\n if (skill) {\n results.set(skill.definition.metadata.name, skill)\n }\n }\n\n return results\n}\n\n/**\n * 尝试读取并解析单个 SKILL.md\n */\nasync function tryParseSkill(\n skillDir: string,\n source: SkillSource,\n): Promise<{ definition: SkillDefinition; source: SkillSource } | null> {\n const skillFile = path.join(skillDir, SKILL_FILENAME)\n\n try {\n const content = fs.readFileSync(skillFile, 'utf-8')\n const supportingFiles = listSupportingFiles(skillDir)\n const definition = parseSkillContent(content, skillFile, skillDir, supportingFiles)\n\n logger.debug('Parsed skill \"%s\" from %s', definition.metadata.name, skillFile)\n\n return { definition, source }\n } catch (err) {\n logger.warn('Failed to parse skill at \"%s\": %s', skillFile, (err as Error).message)\n return null\n }\n}\n\n/**\n * 列出 skill 目录下的附属文件(排除 SKILL.md 自身)\n */\nfunction listSupportingFiles(skillDir: string): string[] {\n try {\n return fs\n .readdirSync(skillDir)\n .filter((name) => name !== SKILL_FILENAME)\n .map((name) => path.join(skillDir, name))\n } catch {\n return []\n }\n}\n\n/**\n * 默认项目级 skill 目录。\n *\n * 顺序即优先级:`discoverSkills` 的 project 循环对同名是 **last-set wins**,\n * 故 `.otto/skills` 排在 `.claude/skills` 之后 = otto 原生覆盖 .claude(RFC-032)。\n */\nexport function getDefaultProjectSkillDirs(): string[] {\n return isClaudeCompatEnabled() ? ['.claude/skills', '.otto/skills'] : ['.otto/skills']\n}\n\n/**\n * 获取默认的全局 skill 目录列表。\n *\n * 顺序即优先级:`discoverSkills` 的 global 循环对同名是 **first-win**(`!has` 才填),\n * 故 `~/.otto/skills` 排在 `~/.claude/skills` 之前 = otto 原生覆盖 .claude(RFC-032)。\n */\nexport function getDefaultGlobalSkillDirs(): string[] {\n const home = process.env['HOME'] ?? process.env['USERPROFILE'] ?? ''\n if (!home) {\n return []\n }\n\n const dirs = [path.join(home, '.otto', 'skills')]\n if (isClaudeCompatEnabled()) {\n dirs.push(path.join(home, '.claude', 'skills'))\n }\n return dirs\n}\n","import { TypedEventEmitter, createLogger } from '@x-otto/shared'\nimport {\n discoverSkills,\n getDefaultGlobalSkillDirs,\n getDefaultProjectSkillDirs,\n} from './skill-discovery'\nimport type {\n SkillDefinition,\n SkillLibraryConfig,\n SkillSource,\n RegisteredSkill,\n SkillStatus,\n SkillExecutionContext,\n SkillExecutionResult,\n SkillEvents,\n} from './types'\n\nconst logger = createLogger('@x-otto/skill:registry')\n\nexport interface SkillRegistryOptions {\n /** 工作目录 */\n workspaceDir: string\n /** skill 库配置 */\n library?: SkillLibraryConfig\n /** 禁用的 skill 名称列表 */\n disabled?: string[]\n}\n\nexport class SkillRegistry extends TypedEventEmitter<SkillEvents> {\n private skills = new Map<string, RegisteredSkill>()\n private readonly workspaceDir: string\n private readonly library?: SkillLibraryConfig\n private disabledSet: Set<string>\n /** 可变的 plugin 目录列表(reloadCapabilities 更新为新安装 plugin 的 dirs) */\n private pluginDirs?: Array<{ id: string; dir: string }>\n\n constructor({ workspaceDir, library, disabled }: SkillRegistryOptions) {\n super()\n this.workspaceDir = workspaceDir\n this.library = library\n this.disabledSet = new Set(disabled ?? library?.disabled ?? [])\n this.pluginDirs = library?.pluginDirs\n }\n\n /**\n * 更新 plugin 贡献的 skill 目录列表(用于 reloadCapabilities 热刷新)。\n * 下次 discover() 将扫描新的 plugin 目录。\n */\n updatePluginDirs(dirs: Array<{ id: string; dir: string }>): void {\n this.pluginDirs = dirs\n }\n\n /**\n * 发现并注册所有可用 skill\n * 扫描项目本地 + 全局目录\n */\n async discover(): Promise<void> {\n const config: SkillLibraryConfig = {\n projectDirs: this.library?.projectDirs ?? getDefaultProjectSkillDirs(),\n globalDirs: this.library?.globalDirs ?? getDefaultGlobalSkillDirs(),\n pluginDirs: this.pluginDirs ?? this.library?.pluginDirs,\n disabled: [...this.disabledSet],\n }\n\n const discovered = await discoverSkills(config, this.workspaceDir)\n\n for (const [name, { definition, source }] of discovered) {\n const status: SkillStatus = this.disabledSet.has(name) ? 'disabled' : 'available'\n\n this.skills.set(name, {\n definition,\n source,\n status,\n })\n\n this.emit('skill.discovered', { source, count: 1 })\n }\n\n logger.info(\n 'Skill registry initialized: %d skills (%d available, %d disabled)',\n this.skills.size,\n this.getAvailable().length,\n [...this.skills.values()].filter((s) => s.status === 'disabled').length,\n )\n }\n\n /**\n * 手动注册一个 skill(用于内联/插件 skill)\n */\n register(definition: SkillDefinition, source: SkillSource): void {\n const { name } = definition.metadata\n const status: SkillStatus = this.disabledSet.has(name) ? 'disabled' : 'available'\n\n this.skills.set(name, { definition, source, status })\n logger.debug('Registered skill \"%s\" from %s', name, source.type)\n }\n\n /**\n * 注销 skill\n */\n unregister(name: string): boolean {\n const had = this.skills.delete(name)\n if (had) {\n this.emit('skill.unloaded', { name })\n }\n return had\n }\n\n /**\n * 获取所有已注册 skill\n */\n getAll(): ReadonlyMap<string, RegisteredSkill> {\n return this.skills\n }\n\n /**\n * 获取所有可用(非 disabled/error)skill\n */\n getAvailable(): RegisteredSkill[] {\n return [...this.skills.values()].filter(\n (s) => s.status === 'available' || s.status === 'loaded',\n )\n }\n\n /**\n * 按名称获取 skill\n */\n get(name: string): RegisteredSkill | undefined {\n return this.skills.get(name)\n }\n\n execute(context: SkillExecutionContext): SkillExecutionResult {\n const startTime = Date.now()\n const registered = this.skills.get(context.skillName)\n\n if (!registered) {\n return { success: false, error: `Skill \"${context.skillName}\" not found` }\n }\n\n if (registered.status === 'disabled') {\n return { success: false, error: `Skill \"${context.skillName}\" is disabled` }\n }\n\n if (registered.status === 'available') {\n registered.status = 'loaded'\n registered.loadedAt = Date.now()\n }\n\n const durationMs = Date.now() - startTime\n this.emit('skill.executed', { name: context.skillName, success: true, durationMs })\n\n return {\n success: true,\n content: registered.definition.body,\n }\n }\n\n /**\n * 禁用 skill\n */\n disable(name: string): void {\n this.disabledSet.add(name)\n const registered = this.skills.get(name)\n if (registered) {\n registered.status = 'disabled'\n }\n }\n\n /**\n * 启用 skill\n */\n enable(name: string): void {\n this.disabledSet.delete(name)\n const registered = this.skills.get(name)\n if (registered) {\n registered.status = 'available'\n }\n }\n\n /**\n * catalog 里每条 skill description 的最大字符数(渐进披露的正确边界——完整正文本就该\n * 靠 skill 工具按需取回,不该把全文塞进常驻的 system prompt catalog)。\n *\n * 取值依据:本仓真实 skill(rfc-todos)description 长度 1339 字符,若装 10+ 个同量级\n * skill,catalog 本身就会膨胀到与基座 prompt 同量级。200 字符能保留\"这个 skill 是干\n * 什么的、什么时候用\"的判断信息,同时把单条上限压到基座 prompt 的 1/10 量级。\n */\n private static readonly CATALOG_DESCRIPTION_MAX_CHARS = 200\n\n /**\n * 按字符边界截断 description(不切断多字节字符),超长时追加省略号标记。\n * 截断是**渲染层**行为——`definition.metadata.description` 原文不变,`skill` 工具\n * 取全文时仍拿到完整内容,catalog 只是给模型的\"目录索引\"而非替代正文。\n */\n private static truncateDescription(description: string): string {\n const max = SkillRegistry.CATALOG_DESCRIPTION_MAX_CHARS\n const chars = Array.from(description)\n if (chars.length <= max) return description\n return `${chars.slice(0, max).join('')}…`\n }\n\n /**\n * 构建 skill catalog(用于 system prompt 注入)\n * 格式:简短列表,仅 name + description(description 截断到固定上限,见\n * CATALOG_DESCRIPTION_MAX_CHARS——渐进披露:目录常驻、正文按需取回,不应本末倒置)\n */\n buildCatalog(): string {\n const available = this.getAvailable()\n if (available.length === 0) {\n return ''\n }\n\n const lines = ['## Available Skills', '']\n for (const { definition } of available) {\n const { name, description, trigger } = definition.metadata\n const triggerTag = trigger === 'manual' ? ' [manual]' : ''\n lines.push(`- **${name}**${triggerTag}: ${SkillRegistry.truncateDescription(description)}`)\n }\n\n return lines.join('\\n')\n }\n\n /**\n * 获取 skill 总数\n */\n get size(): number {\n return this.skills.size\n }\n}\n\nexport function createSkillRegistry(options: SkillRegistryOptions): SkillRegistry {\n return new SkillRegistry(options)\n}\n","import { parseSkillContent } from './skill-parser'\n\nimport type { SkillRegistry } from './skill-registry'\nimport type { SkillProvider } from './types'\n\n/**\n * 常见路径遗漏:`.claude/rfc-to-todos/SKILL.md` → `.claude/skills/rfc-to-todos/SKILL.md`。\n * 仅处理以 `.claude/` 开头、不含 `skills/` 的相对路径。返回 null 表示无需补全。\n */\nfunction resolveClaudeSkillsPath(path: string): string | null {\n if (path.startsWith('.claude/') && !path.includes('skills/')) {\n return '.claude/skills/' + path.slice('.claude/'.length)\n }\n return null\n}\n\nexport function createRegistrySkillProvider(\n registry: SkillRegistry,\n workspaceDir: string,\n): SkillProvider {\n return {\n async load(path) {\n try {\n const { readFile } = await import('node:fs/promises')\n const { isAbsolute, resolve, dirname: dir } = await import('node:path')\n\n const tryPath = async (p: string): Promise<string | null> => {\n const filePath = isAbsolute(p) ? p : resolve(workspaceDir, p)\n try {\n return await readFile(filePath, 'utf-8')\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null\n throw err\n }\n }\n\n let content = await tryPath(path)\n let usedPath = path\n\n if (content === null) {\n const altPath = resolveClaudeSkillsPath(path)\n if (altPath) {\n const altContent = await tryPath(altPath)\n if (altContent !== null) {\n content = altContent\n usedPath = altPath\n }\n }\n }\n\n if (content === null) {\n return { success: false, error: `ENOENT: no such file, open '${path}'` }\n }\n\n const filePath = isAbsolute(usedPath) ? usedPath : resolve(workspaceDir, usedPath)\n const definition = parseSkillContent(content, filePath, dir(filePath))\n\n registry.register(definition, { type: 'inline', root: dir(filePath) })\n return { success: true, name: definition.metadata.name }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n },\n\n async execute(name) {\n return registry.execute({ skillName: name, workspaceDir })\n },\n }\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@x-otto/skill",
|
|
3
|
+
"version": "0.0.1-alpha.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@x-otto/env": "0.1.0-alpha.1",
|
|
18
|
+
"@x-otto/shared": "0.1.0-alpha.1",
|
|
19
|
+
"yaml": "2.8.3"
|
|
20
|
+
},
|
|
21
|
+
"private": false,
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"registry": "https://registry.npmjs.org",
|
|
25
|
+
"tag": "alpha"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsdown",
|
|
29
|
+
"dev": "tsdown --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit"
|
|
31
|
+
}
|
|
32
|
+
}
|