@easbot/skills 0.2.48
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/LICENSE +21 -0
- package/README.en.md +202 -0
- package/README.md +202 -0
- package/dist/chunks/blob-BHATBRFW.cjs +1 -0
- package/dist/chunks/blob-SJSODFTU.mjs +1 -0
- package/dist/chunks/chunk-EOOPF3RI.cjs +2 -0
- package/dist/chunks/chunk-MH2XZDJ5.mjs +2 -0
- package/dist/index.cjs +208 -0
- package/dist/index.d.cts +648 -0
- package/dist/index.d.ts +648 -0
- package/dist/index.mjs +208 -0
- package/package.json +86 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 houjallen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[中文](./README.md) | English
|
|
2
|
+
|
|
3
|
+
# @easbot/skills
|
|
4
|
+
|
|
5
|
+
Skill package management subsystem for the EAS Agent ecosystem. Discovers, installs, removes, searches, updates, syncs and one-shot uses skills (SKILL.md) from GitHub, Well-Known hosts, or local paths.
|
|
6
|
+
|
|
7
|
+
Usable both as a standalone CLI (`easbot skills ...`) and as a library that the `@easbot/agent` host CLI delegates to via Commander.js, so `easbot agent skills ...` and `easbot skills ...` are equivalent.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Type Safety**: full TypeScript types + Zod frontmatter validation
|
|
12
|
+
- **Multi-Source**: GitHub shorthand / HTTPS / SSH / Well-Known / local path
|
|
13
|
+
- **Multi-Agent**: Claude Code, Cursor, AiderDesk, Amp, Antigravity, Zed, Codex, Hermes, Vibe, AutoHand, Eve, ...
|
|
14
|
+
- **Dependency Injection**: `setGlobalAdapter / setInstanceAdapter / setAgentAdapter` lets the host (agent / gateway) inject `IGlobal.Path`, `IInstance.directory`, and SubAgentRunner / LocalAgentRunner — all data lives under one tree
|
|
15
|
+
- **Incremental Sync**: lockfile-based add / remove / update / sync with local SHA-256 hashing
|
|
16
|
+
- **i18n**: built-in zh-CN / en-US translations; user-facing strings go through `t()`, with fallback to the English literal when a key is missing
|
|
17
|
+
- **Telemetry + Audit**: built-in `skills.sh` partner-audit reporting and OpenClaw risk gating
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @easbot/skills
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or as a monorepo workspace dependency:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@easbot/skills": "workspace:*"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## CLI Usage
|
|
36
|
+
|
|
37
|
+
### Standalone CLI
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Add a skill
|
|
41
|
+
easbot skills add houjallen/agent-skills
|
|
42
|
+
easbot skills add https://github.com/houjallen/agent-skills -g
|
|
43
|
+
|
|
44
|
+
# Search
|
|
45
|
+
easbot skills find typescript --owner houjallen
|
|
46
|
+
|
|
47
|
+
# List installed
|
|
48
|
+
easbot skills list --json
|
|
49
|
+
|
|
50
|
+
# Remove
|
|
51
|
+
easbot skills remove my-skill -y
|
|
52
|
+
|
|
53
|
+
# Update
|
|
54
|
+
easbot skills update -g
|
|
55
|
+
|
|
56
|
+
# One-shot use (no lockfile mutation)
|
|
57
|
+
easbot skills use houjallen/agent-skills@houjallen
|
|
58
|
+
|
|
59
|
+
# Sync from node_modules / restore from lockfile
|
|
60
|
+
easbot skills experimental_sync
|
|
61
|
+
easbot skills experimental_install
|
|
62
|
+
|
|
63
|
+
# Initialize a new skill
|
|
64
|
+
easbot skills init my-skill
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Delegated from a host CLI
|
|
68
|
+
|
|
69
|
+
`@easbot/agent` reuses every sub-command through `handleSkillsCli(args, deps)`:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { handleSkillsCli, setGlobalAdapter, setInstanceAdapter } from '@easbot/skills';
|
|
73
|
+
|
|
74
|
+
setGlobalAdapter({ Path: Global.Path });
|
|
75
|
+
setInstanceAdapter({ directory: ctx.config.directory, worktree: Instance.worktree });
|
|
76
|
+
|
|
77
|
+
await handleSkillsCli(['add', 'houjallen/agent-skills', '-y'], {
|
|
78
|
+
version: ctx.programVersion,
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API
|
|
83
|
+
|
|
84
|
+
| Module | Exports | Notes |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `add` | `runAdd / parseAddOptions / initTelemetry` | Skill add pipeline |
|
|
87
|
+
| `remove` | `removeCommand / parseRemoveOptions` | Skill removal |
|
|
88
|
+
| `list` | `runList / parseListOptions` | List installed skills |
|
|
89
|
+
| `find` | `runFind / parseFindOptions / searchSkillsAPI` | skills.sh search |
|
|
90
|
+
| `update` | `runUpdate / parseUpdateOptions` | Incremental update |
|
|
91
|
+
| `install` | `runInstallFromLock` | Restore from skills-lock.json |
|
|
92
|
+
| `sync` | `runSync / parseSyncOptions` | Sync from node_modules |
|
|
93
|
+
| `use` | `runUse / parseUseOptions / buildUsePrompt` | One-shot use |
|
|
94
|
+
| `cli-handler` | `handleSkillsCli / renderBanner / renderHelp / SkillsCliDeps / SkillsCliResult` | CLI bridge |
|
|
95
|
+
| `interfaces` | `setGlobalAdapter / setInstanceAdapter / setAgentAdapter / getGlobal / getInstance / ...` | Dependency injection |
|
|
96
|
+
| `agents` | `agents / detectInstalledAgents / getUniversalAgents / ...` | Agent metadata |
|
|
97
|
+
| `installer` | `installSkillForAgent / listInstalledSkills / getCanonicalSkillsDir` | Physical install |
|
|
98
|
+
| `skill-lock` | `readSkillLock / writeSkillLock / addSkillToLock / removeSkillFromLock` | Global lockfile |
|
|
99
|
+
| `local-lock` | `readLocalLock / writeLocalLock / computeSkillFolderHash` | Project lockfile |
|
|
100
|
+
| `telemetry` | `track / flushTelemetry / setVersion / fetchAuditData` | Usage stats + security audit |
|
|
101
|
+
| `i18n` | `t / initI18n / registerI18nResources / changeI18nLanguage` | Internationalization |
|
|
102
|
+
| `frontmatter` | `parseFrontmatter` | YAML frontmatter parser |
|
|
103
|
+
| `providers` | `wellKnownProvider / registry / findProvider` | Well-Known & custom host providers |
|
|
104
|
+
|
|
105
|
+
## Global Path Injection
|
|
106
|
+
|
|
107
|
+
To avoid fighting with the host (agent / gateway) over XDG / home, `@easbot/skills` resolves paths through dependency injection by default:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import {
|
|
111
|
+
setGlobalAdapter, // inject IGlobal.Path
|
|
112
|
+
setInstanceAdapter, // inject IInstance.directory / worktree
|
|
113
|
+
setAgentAdapter, // inject SubAgentRunner / LocalAgentRunner / triggerEvent
|
|
114
|
+
getHomeDir,
|
|
115
|
+
getXdgConfig,
|
|
116
|
+
getXdgData,
|
|
117
|
+
getProjectDirectory,
|
|
118
|
+
} from '@easbot/skills';
|
|
119
|
+
|
|
120
|
+
setGlobalAdapter({
|
|
121
|
+
Path: {
|
|
122
|
+
home: '/Users/me',
|
|
123
|
+
data: '/Users/me/.local/share/easbot',
|
|
124
|
+
config: '/Users/me/.config/easbot',
|
|
125
|
+
state: '/Users/me/.local/state/easbot',
|
|
126
|
+
cache: '/Users/me/.cache/easbot',
|
|
127
|
+
log: '/Users/me/.local/state/easbot/log',
|
|
128
|
+
bin: '/Users/me/.local/bin',
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
setInstanceAdapter({
|
|
133
|
+
directory: '/repo',
|
|
134
|
+
worktree: '/repo',
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
When no adapter is set, `@easbot/skills` falls back to `xdg-basedir` + `homedir()`, keeping full backward compatibility with the v1 standalone CLI.
|
|
139
|
+
|
|
140
|
+
## Data Layout
|
|
141
|
+
|
|
142
|
+
| Path | Purpose |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `${Global.Path.data}/skills/` | Skill directory loaded by the agent at runtime |
|
|
145
|
+
| `${Global.Path.data}/state/skills/.skill-lock.json` | Global install lockfile |
|
|
146
|
+
| `<cwd>/.agents/skills/` / `<cwd>/skills/` | Project-level skill directories |
|
|
147
|
+
| `<cwd>/skills-lock.json` | Project-level lockfile |
|
|
148
|
+
| `<home>/.config/easbot/skills/` | XDG config fallback |
|
|
149
|
+
|
|
150
|
+
> When delegated from `@easbot/agent`, everything lands under `${Global.Path.data}` — sibling to Gateway / MCP / Session / Contact modules.
|
|
151
|
+
|
|
152
|
+
## Development
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Install dependencies
|
|
156
|
+
pnpm install
|
|
157
|
+
|
|
158
|
+
# Build (produces dist/ + .d.ts)
|
|
159
|
+
pnpm build
|
|
160
|
+
|
|
161
|
+
# Watch build
|
|
162
|
+
pnpm dev
|
|
163
|
+
|
|
164
|
+
# Tests (vitest; single file: npx vitest run src/__tests__/xxx.test.ts)
|
|
165
|
+
pnpm test
|
|
166
|
+
pnpm test:run
|
|
167
|
+
|
|
168
|
+
# Code quality
|
|
169
|
+
pnpm biome-lint # biome check .
|
|
170
|
+
pnpm biome-fix # biome check --write .
|
|
171
|
+
pnpm biome-format # biome format .
|
|
172
|
+
|
|
173
|
+
# Type check
|
|
174
|
+
pnpm type-check
|
|
175
|
+
|
|
176
|
+
# Clean
|
|
177
|
+
pnpm clean
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Publishing
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Linux / macOS
|
|
184
|
+
pnpm publish:npm # = bash scripts/publish.sh
|
|
185
|
+
|
|
186
|
+
# Windows PowerShell
|
|
187
|
+
pnpm publish:npm:win # = powershell -ExecutionPolicy Bypass -File scripts/publish.ps1
|
|
188
|
+
|
|
189
|
+
# Equivalent manual flow
|
|
190
|
+
pnpm build
|
|
191
|
+
pnpm publish --access public --git-checks false --registry https://registry.npmjs.org
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Runtime Notes
|
|
195
|
+
|
|
196
|
+
- Uses Node built-ins (`fs/promises`, `node:os`, `node:path`, `node:child_process`); **not** browser-compatible.
|
|
197
|
+
- Pulls in `simple-git`, `@vercel/detect-agent`, `@clack/prompts`, `picocolors`, `xdg-basedir`, `zod`, `yaml` for GitHub access, agent detection, interactive prompts, terminal styling, XDG resolution, and frontmatter validation.
|
|
198
|
+
- `engines.node >= 22.22.3` — Node 22 LTS recommended.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[English](./README.en.md) | 中文
|
|
2
|
+
|
|
3
|
+
# @easbot/skills
|
|
4
|
+
|
|
5
|
+
EASBot 技能包管理子系统:负责从 GitHub / Well-Known / 本地路径发现、安装、移除、搜索、更新、同步与一次性使用技能(SKILL.md)。
|
|
6
|
+
|
|
7
|
+
既可作为独立 CLI(`easbot skills ...`),也可被 `@easbot/agent` 主 CLI 通过 Commander.js 委派调用,实现 `easbot agent skills ...` 与 `easbot skills ...` 等价。
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- **类型安全**:完整 TypeScript 类型 + Zod frontmatter 校验
|
|
12
|
+
- **多源支持**:GitHub 仓库 shorthand / HTTPS / SSH / Well-Known / 本地路径
|
|
13
|
+
- **多 Agent 兼容**:Claude Code、Cursor、AiderDesk、Amp、Antigravity、Zed、Codex、Hermes、Vibe、AutoHand、Eve 等
|
|
14
|
+
- **依赖注入**:`setGlobalAdapter / setInstanceAdapter / setAgentAdapter` 让宿主(agent / gateway)注入 `IGlobal.Path`、`IInstance.directory`、SubAgentRunner / LocalAgentRunner,统一数据树
|
|
15
|
+
- **增量同步**:基于本地 SHA-256 锁文件的 `add / remove / update / sync` 增量操作
|
|
16
|
+
- **i18n**:内置 zh-CN / en-US 两套翻译,所有面向用户文案走 `t()`,未命中 key 时回退原文
|
|
17
|
+
- **遥测与安全审计**:内置 `skills.sh` partner audit 上报与 OpenClaw 风险提示
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @easbot/skills
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
或作为 monorepo 工作区依赖:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@easbot/skills": "workspace:*"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## CLI 用法
|
|
36
|
+
|
|
37
|
+
### 独立 CLI
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# 添加技能
|
|
41
|
+
easbot skills add houjallen/agent-skills
|
|
42
|
+
easbot skills add https://github.com/houjallen/agent-skills -g
|
|
43
|
+
|
|
44
|
+
# 搜索
|
|
45
|
+
easbot skills find typescript --owner houjallen
|
|
46
|
+
|
|
47
|
+
# 列出已安装
|
|
48
|
+
easbot skills list --json
|
|
49
|
+
|
|
50
|
+
# 移除
|
|
51
|
+
easbot skills remove my-skill -y
|
|
52
|
+
|
|
53
|
+
# 更新
|
|
54
|
+
easbot skills update -g
|
|
55
|
+
|
|
56
|
+
# 一次性使用(不修改锁定文件)
|
|
57
|
+
easbot skills use houjallen/agent-skills@houjallen
|
|
58
|
+
|
|
59
|
+
# 从 node_modules 同步 / 从锁定文件还原
|
|
60
|
+
easbot skills experimental_sync
|
|
61
|
+
easbot skills experimental_install
|
|
62
|
+
|
|
63
|
+
# 初始化新技能
|
|
64
|
+
easbot skills init my-skill
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 被宿主 CLI 委派
|
|
68
|
+
|
|
69
|
+
`@easbot/agent` 通过 `handleSkillsCli(args, deps)` 复用所有子命令:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { handleSkillsCli, setGlobalAdapter, setInstanceAdapter } from '@easbot/skills';
|
|
73
|
+
|
|
74
|
+
setGlobalAdapter({ Path: Global.Path });
|
|
75
|
+
setInstanceAdapter({ directory: ctx.config.directory, worktree: Instance.worktree });
|
|
76
|
+
|
|
77
|
+
await handleSkillsCli(['add', 'houjallen/agent-skills', '-y'], {
|
|
78
|
+
version: ctx.programVersion,
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API
|
|
83
|
+
|
|
84
|
+
| 模块 | 导出 | 说明 |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `add` | `runAdd / parseAddOptions / initTelemetry` | 技能添加主流程 |
|
|
87
|
+
| `remove` | `removeCommand / parseRemoveOptions` | 技能移除 |
|
|
88
|
+
| `list` | `runList / parseListOptions` | 列出已安装技能 |
|
|
89
|
+
| `find` | `runFind / parseFindOptions / searchSkillsAPI` | skills.sh 搜索 |
|
|
90
|
+
| `update` | `runUpdate / parseUpdateOptions` | 增量更新 |
|
|
91
|
+
| `install` | `runInstallFromLock` | 从 skills-lock.json 还原 |
|
|
92
|
+
| `sync` | `runSync / parseSyncOptions` | 从 node_modules 同步 |
|
|
93
|
+
| `use` | `runUse / parseUseOptions / buildUsePrompt` | 一次性使用 |
|
|
94
|
+
| `cli-handler` | `handleSkillsCli / renderBanner / renderHelp / SkillsCliDeps / SkillsCliResult` | CLI 桥接 |
|
|
95
|
+
| `interfaces` | `setGlobalAdapter / setInstanceAdapter / setAgentAdapter / getGlobal / getInstance / ...` | 依赖注入 |
|
|
96
|
+
| `agents` | `agents / detectInstalledAgents / getUniversalAgents / ...` | Agent 元数据 |
|
|
97
|
+
| `installer` | `installSkillForAgent / listInstalledSkills / getCanonicalSkillsDir` | 物理安装 |
|
|
98
|
+
| `skill-lock` | `readSkillLock / writeSkillLock / addSkillToLock / removeSkillFromLock` | 全局锁定文件 |
|
|
99
|
+
| `local-lock` | `readLocalLock / writeLocalLock / computeSkillFolderHash` | 项目级锁定文件 |
|
|
100
|
+
| `telemetry` | `track / flushTelemetry / setVersion / fetchAuditData` | 使用统计 + 安全审计 |
|
|
101
|
+
| `i18n` | `t / initI18n / registerI18nResources / changeI18nLanguage` | 国际化 |
|
|
102
|
+
| `frontmatter` | `parseFrontmatter` | YAML frontmatter 解析 |
|
|
103
|
+
| `providers` | `wellKnownProvider / registry / findProvider` | Well-Known 等主机提供方 |
|
|
104
|
+
|
|
105
|
+
## 全局路径注入
|
|
106
|
+
|
|
107
|
+
为避免与宿主(agent / gateway)抢用 XDG / home 目录,`@easbot/skills` 默认通过依赖注入获取路径:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import {
|
|
111
|
+
setGlobalAdapter, // 注入 IGlobal.Path
|
|
112
|
+
setInstanceAdapter, // 注入 IInstance.directory / worktree
|
|
113
|
+
setAgentAdapter, // 注入 SubAgentRunner / LocalAgentRunner / triggerEvent
|
|
114
|
+
getHomeDir,
|
|
115
|
+
getXdgConfig,
|
|
116
|
+
getXdgData,
|
|
117
|
+
getProjectDirectory,
|
|
118
|
+
} from '@easbot/skills';
|
|
119
|
+
|
|
120
|
+
setGlobalAdapter({
|
|
121
|
+
Path: {
|
|
122
|
+
home: '/Users/me',
|
|
123
|
+
data: '/Users/me/.local/share/easbot',
|
|
124
|
+
config: '/Users/me/.config/easbot',
|
|
125
|
+
state: '/Users/me/.local/state/easbot',
|
|
126
|
+
cache: '/Users/me/.cache/easbot',
|
|
127
|
+
log: '/Users/me/.local/state/easbot/log',
|
|
128
|
+
bin: '/Users/me/.local/bin',
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
setInstanceAdapter({
|
|
133
|
+
directory: '/repo',
|
|
134
|
+
worktree: '/repo',
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
未注入时,skills 包回退到 `xdg-basedir` + `homedir()`,保持与 v1 CLI 完全兼容。
|
|
139
|
+
|
|
140
|
+
## 数据目录约定
|
|
141
|
+
|
|
142
|
+
| 路径 | 用途 |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `${Global.Path.data}/skills/` | Agent 运行时加载的技能目录(运行时扫描) |
|
|
145
|
+
| `${Global.Path.data}/state/skills/.skill-lock.json` | 全局安装锁定文件 |
|
|
146
|
+
| `<cwd>/.agents/skills/` / `<cwd>/skills/` | 项目级技能目录 |
|
|
147
|
+
| `<cwd>/skills-lock.json` | 项目级锁定文件 |
|
|
148
|
+
| `<home>/.config/easbot/skills/` | XDG 配置兜底 |
|
|
149
|
+
|
|
150
|
+
> 当被 `@easbot/agent` 委派时,所有路径会落到 `${Global.Path.data}` 这棵统一数据树下,与 Gateway / MCP / Session / Contact 等模块并列。
|
|
151
|
+
|
|
152
|
+
## 开发
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# 安装依赖
|
|
156
|
+
pnpm install
|
|
157
|
+
|
|
158
|
+
# 构建(产出 dist/,同时生成 .d.ts)
|
|
159
|
+
pnpm build
|
|
160
|
+
|
|
161
|
+
# 监听构建
|
|
162
|
+
pnpm dev
|
|
163
|
+
|
|
164
|
+
# 测试(vitest,单跑某个文件:npx vitest run src/__tests__/xxx.test.ts)
|
|
165
|
+
pnpm test
|
|
166
|
+
pnpm test:run
|
|
167
|
+
|
|
168
|
+
# 代码质量
|
|
169
|
+
pnpm biome-lint # biome check .
|
|
170
|
+
pnpm biome-fix # biome check --write .
|
|
171
|
+
pnpm biome-format # biome format .
|
|
172
|
+
|
|
173
|
+
# 类型检查
|
|
174
|
+
pnpm type-check
|
|
175
|
+
|
|
176
|
+
# 清理
|
|
177
|
+
pnpm clean
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 发布
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Linux / macOS
|
|
184
|
+
pnpm publish:npm # = bash scripts/publish.sh
|
|
185
|
+
|
|
186
|
+
# Windows PowerShell
|
|
187
|
+
pnpm publish:npm:win # = powershell -ExecutionPolicy Bypass -File scripts/publish.ps1
|
|
188
|
+
|
|
189
|
+
# 手动等价
|
|
190
|
+
pnpm build
|
|
191
|
+
pnpm publish --access public --git-checks false --registry https://registry.npmjs.org
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## 浏览器 / 跨运行时说明
|
|
195
|
+
|
|
196
|
+
- 内部使用 Node 内置模块(`fs/promises`、`node:os`、`node:path`、`node:child_process`),不能直接在浏览器运行。
|
|
197
|
+
- 通过 `simple-git`、`@vercel/detect-agent`、`@clack/prompts`、`picocolors`、`xdg-basedir`、`zod`、`yaml` 等实现 GitHub 拉取、Agent 环境探测、交互问答、终端着色、XDG 解析与 frontmatter 校验。
|
|
198
|
+
- `engines.node >= 22.22.3`,推荐 Node 22 LTS。
|
|
199
|
+
|
|
200
|
+
## 许可证
|
|
201
|
+
|
|
202
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var chunkEOOPF3RI_cjs=require('./chunk-EOOPF3RI.cjs');Object.defineProperty(exports,"BLOB_ALLOWED_REPOS",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.f}});Object.defineProperty(exports,"fetchRepoTree",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.i}});Object.defineProperty(exports,"findSkillMdPaths",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.k}});Object.defineProperty(exports,"getSkillFolderHashFromTree",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.j}});Object.defineProperty(exports,"resetRepoTreeAuthState",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.h}});Object.defineProperty(exports,"toSkillSlug",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.g}});Object.defineProperty(exports,"tryBlobInstall",{enumerable:true,get:function(){return chunkEOOPF3RI_cjs.l}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{e as BLOB_ALLOWED_REPOS,h as fetchRepoTree,j as findSkillMdPaths,i as getSkillFolderHashFromTree,g as resetRepoTreeAuthState,f as toSkillSlug,k as tryBlobInstall}from'./chunk-MH2XZDJ5.mjs';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var crypto=require('crypto'),yaml=require('yaml');var C=Object.defineProperty;var y=(n,t,e)=>t in n?C(n,t,{enumerable:true,configurable:true,writable:true,value:e}):n[t]=e;var N=(n,t,e)=>y(n,typeof t!="symbol"?t+"":t,e);var P=()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,S=P();function b(n){let t=n.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);return t?{data:yaml.parse(t[1])??{},content:t[2]??""}:{data:{},content:n}}var I=/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g,E=/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g,T=/\x1b[P^_][\s\S]*?(?:\x1b\\)/g,F=/\x1b[\x20-\x7e]/g,U=/[\x80-\x9f]/g,$=/[\x00-\x06\x07\x08\x0b\x0c\x0d-\x1a\x1c-\x1f\x7f]/g;function A(n){return n.replace(E,"").replace(T,"").replace(I,"").replace(F,"").replace(U,"").replace($,"")}function x(n){return A(n).replace(/[\r\n]+/g," ").trim()}var B=process.env.SKILLS_DOWNLOAD_URL||"https://skills.sh",D={"zapier/connectors":{downloadUrl:n=>`https://connectors-skills.zapier.com/download/${encodeURIComponent(n)}/snapshot.json`}},L=1e4;function m(n){return n.toLowerCase().replace(/[\s_]+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}var R=false;function et(){R=false;}async function w(n,t,e){try{let i=`https://api.github.com/repos/${n}/git/trees/${encodeURIComponent(t)}?recursive=1`,l={Accept:"application/vnd.github.v3+json","User-Agent":"skills-cli"};e&&(l.Authorization=`Bearer ${e}`);let a=await fetch(i,{headers:l,signal:AbortSignal.timeout(L)});if(a.ok){let c=await a.json();return {tree:{sha:c.sha,branch:t,tree:c.tree},rateLimited:!1}}return {tree:null,rateLimited:a.status===403&&a.headers.get("x-ratelimit-remaining")==="0"}}catch{return {tree:null,rateLimited:false}}}async function O(n,t,e){let i=t?[t]:["HEAD","main","master"];if(R&&e){let o=e();if(!o)return null;for(let c of i){let h=await w(n,c,o);if(h.tree)return h.tree}return null}let l=false;for(let o of i){let c=await w(n,o,null);if(c.tree)return c.tree;if(c.rateLimited){l=true;break}}if(!l||!e)return null;R=true;let a=e();if(!a)return null;for(let o of i){let c=await w(n,o,a);if(c.tree)return c.tree}return null}function nt(n,t){let e=t.replace(/\\/g,"/");return e.toLowerCase().endsWith("/skill.md")?e=e.slice(0,-9):e.toLowerCase().endsWith("skill.md")&&(e=e.slice(0,-8)),e.endsWith("/")&&(e=e.slice(0,-1)),e?n.tree.find(l=>l.type==="tree"&&l.path===e)?.sha??null:n.sha}var W=["","skills/","skills/.curated/","skills/.experimental/","skills/.system/",".agents/skills/",".claude/skills/",".cline/skills/",".codebuddy/skills/",".codex/skills/",".commandcode/skills/",".continue/skills/",".github/skills/",".goose/skills/",".iflow/skills/",".junie/skills/",".kilocode/skills/",".kiro/skills/",".mux/skills/",".neovate/skills/",".opencode/skills/",".openhands/skills/",".pi/skills/",".qoder/skills/",".roo/skills/",".trae/skills/",".windsurf/skills/",".zencoder/skills/"];function H(n,t){let e=n.tree.filter(d=>d.type==="blob"&&d.path.toLowerCase().endsWith("skill.md")).map(d=>d.path),i=t?t.endsWith("/")?t:t+"/":"",l=i?e.filter(d=>d.startsWith(i)||d===i+"SKILL.md"):e;if(l.length===0)return [];let a=[],o=new Set,c=new Set(["node_modules",".git","dist","build","__pycache__"]),h=new Set(l.map(d=>d.toLowerCase()));for(let d of W){let g=i+d,r=d!=="";for(let s of l){if(!s.startsWith(g))continue;let u=s.slice(g.length);if(u.toLowerCase()==="skill.md"){o.has(s)||(a.push(s),o.add(s));continue}let f=u.split("/");if(f.length===2&&f[1].toLowerCase()==="skill.md"){o.has(s)||(a.push(s),o.add(s));continue}if(r&&f.length===3&&f[2].toLowerCase()==="skill.md"&&!c.has(f[0])&&!c.has(f[1])){let p=`${g}${f[0]}/SKILL.md`.toLowerCase();!h.has(p)&&!o.has(s)&&(a.push(s),o.add(s));}}}return a.length>0?a:l.filter(d=>d.split("/").length<=6)}async function z(n,t,e){try{let i=`https://raw.githubusercontent.com/${n}/${t}/${e}`,l=await fetch(i,{signal:AbortSignal.timeout(L)});return l.ok?await l.text():null}catch{return null}}async function v(n,t){try{let[e,i]=n.split("/"),l=`${B}/api/download/${encodeURIComponent(e)}/${encodeURIComponent(i)}/${encodeURIComponent(t)}`,o=D[n.toLowerCase()]?.downloadUrl(t)??l,c=await fetch(o,{signal:AbortSignal.timeout(L)});return c.ok?await c.json():null}catch{return null}}function j(n){let t=crypto.createHash("sha256");for(let e of [...n].sort((i,l)=>i.path.localeCompare(l.path)))t.update(e.path),t.update(e.contents);return t.digest("hex")}async function st(n,t={}){let e=await O(n,t.ref,t.getToken);if(!e)return null;let i=H(e,t.subpath);if(i.length===0)return null;if(t.skillFilter){let r=m(t.skillFilter),s=i.filter(u=>{let f=u.split("/");if(f.length<2)return false;let p=f[f.length-2];return m(p)===r});s.length>0&&(i=s);}let l=await Promise.all(i.map(async r=>{let s=await z(n,e.branch,r);return {mdPath:r,content:s}})),a=[];for(let{mdPath:r,content:s}of l){if(!s)continue;let{data:u}=b(s);if(!u.name||!u.description||typeof u.name!="string"||typeof u.description!="string"||u.metadata?.internal===true&&!t.includeInternal)continue;let p=x(u.name),k=x(u.description);a.push({mdPath:r,name:p,description:k,content:s,slug:m(p),metadata:u.metadata});}if(a.length===0)return null;let o=a;if(t.skillFilter){let r=m(t.skillFilter),s=a.filter(u=>u.slug===r);if(s.length>0&&(o=s),o.length===0)return null}let c=n.toLowerCase(),h=await Promise.all(o.map(async r=>{let s=await v(c,r.slug);return {skill:r,download:s}}));return h.every(r=>r.download!==null)?{skills:h.map(({skill:r,download:s})=>{let u=r.mdPath.toLowerCase(),p=(u.endsWith("/skill.md")?r.mdPath.slice(0,-9):u==="skill.md"?"":r.mdPath.slice(0,-9))?s.files:s.files.filter(k=>k.path.toLowerCase()==="skill.md");return {name:r.name,description:r.description,path:"",rawContent:r.content,metadata:r.metadata,files:p,snapshotHash:p.length===s.files.length?s.hash:j(p),repoPath:r.mdPath}}),tree:e}:null}
|
|
2
|
+
exports.a=N;exports.b=S;exports.c=b;exports.d=A;exports.e=x;exports.f=D;exports.g=m;exports.h=et;exports.i=O;exports.j=nt;exports.k=H;exports.l=st;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {createHash}from'crypto';import {parse}from'yaml';var C=Object.defineProperty;var P=(n,t,e)=>t in n?C(n,t,{enumerable:true,configurable:true,writable:true,value:e}):n[t]=e;var K=(n,t,e)=>P(n,typeof t!="symbol"?t+"":t,e);function y(n){let t=n.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);return t?{data:parse(t[1])??{},content:t[2]??""}:{data:{},content:n}}var I=/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g,E=/\x1b\][\s\S]*?(?:\x07|\x1b\\)/g,F=/\x1b[P^_][\s\S]*?(?:\x1b\\)/g,T=/\x1b[\x20-\x7e]/g,$=/[\x80-\x9f]/g,A=/[\x00-\x06\x07\x08\x0b\x0c\x0d-\x1a\x1c-\x1f\x7f]/g;function B(n){return n.replace(E,"").replace(F,"").replace(I,"").replace(T,"").replace($,"").replace(A,"")}function S(n){return B(n).replace(/[\r\n]+/g," ").trim()}var U=process.env.SKILLS_DOWNLOAD_URL||"https://skills.sh",M={"zapier/connectors":{downloadUrl:n=>`https://connectors-skills.zapier.com/download/${encodeURIComponent(n)}/snapshot.json`}},L=1e4;function m(n){return n.toLowerCase().replace(/[\s_]+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}var w=false;function tt(){w=false;}async function x(n,t,e){try{let l=`https://api.github.com/repos/${n}/git/trees/${encodeURIComponent(t)}?recursive=1`,i={Accept:"application/vnd.github.v3+json","User-Agent":"skills-cli"};e&&(i.Authorization=`Bearer ${e}`);let a=await fetch(l,{headers:i,signal:AbortSignal.timeout(L)});if(a.ok){let c=await a.json();return {tree:{sha:c.sha,branch:t,tree:c.tree},rateLimited:!1}}return {tree:null,rateLimited:a.status===403&&a.headers.get("x-ratelimit-remaining")==="0"}}catch{return {tree:null,rateLimited:false}}}async function O(n,t,e){let l=t?[t]:["HEAD","main","master"];if(w&&e){let o=e();if(!o)return null;for(let c of l){let p=await x(n,c,o);if(p.tree)return p.tree}return null}let i=false;for(let o of l){let c=await x(n,o,null);if(c.tree)return c.tree;if(c.rateLimited){i=true;break}}if(!i||!e)return null;w=true;let a=e();if(!a)return null;for(let o of l){let c=await x(n,o,a);if(c.tree)return c.tree}return null}function et(n,t){let e=t.replace(/\\/g,"/");return e.toLowerCase().endsWith("/skill.md")?e=e.slice(0,-9):e.toLowerCase().endsWith("skill.md")&&(e=e.slice(0,-8)),e.endsWith("/")&&(e=e.slice(0,-1)),e?n.tree.find(i=>i.type==="tree"&&i.path===e)?.sha??null:n.sha}var W=["","skills/","skills/.curated/","skills/.experimental/","skills/.system/",".agents/skills/",".claude/skills/",".cline/skills/",".codebuddy/skills/",".codex/skills/",".commandcode/skills/",".continue/skills/",".github/skills/",".goose/skills/",".iflow/skills/",".junie/skills/",".kilocode/skills/",".kiro/skills/",".mux/skills/",".neovate/skills/",".opencode/skills/",".openhands/skills/",".pi/skills/",".qoder/skills/",".roo/skills/",".trae/skills/",".windsurf/skills/",".zencoder/skills/"];function H(n,t){let e=n.tree.filter(d=>d.type==="blob"&&d.path.toLowerCase().endsWith("skill.md")).map(d=>d.path),l=t?t.endsWith("/")?t:t+"/":"",i=l?e.filter(d=>d.startsWith(l)||d===l+"SKILL.md"):e;if(i.length===0)return [];let a=[],o=new Set,c=new Set(["node_modules",".git","dist","build","__pycache__"]),p=new Set(i.map(d=>d.toLowerCase()));for(let d of W){let g=l+d,r=d!=="";for(let s of i){if(!s.startsWith(g))continue;let u=s.slice(g.length);if(u.toLowerCase()==="skill.md"){o.has(s)||(a.push(s),o.add(s));continue}let f=u.split("/");if(f.length===2&&f[1].toLowerCase()==="skill.md"){o.has(s)||(a.push(s),o.add(s));continue}if(r&&f.length===3&&f[2].toLowerCase()==="skill.md"&&!c.has(f[0])&&!c.has(f[1])){let h=`${g}${f[0]}/SKILL.md`.toLowerCase();!p.has(h)&&!o.has(s)&&(a.push(s),o.add(s));}}}return a.length>0?a:i.filter(d=>d.split("/").length<=6)}async function z(n,t,e){try{let l=`https://raw.githubusercontent.com/${n}/${t}/${e}`,i=await fetch(l,{signal:AbortSignal.timeout(L)});return i.ok?await i.text():null}catch{return null}}async function v(n,t){try{let[e,l]=n.split("/"),i=`${U}/api/download/${encodeURIComponent(e)}/${encodeURIComponent(l)}/${encodeURIComponent(t)}`,o=M[n.toLowerCase()]?.downloadUrl(t)??i,c=await fetch(o,{signal:AbortSignal.timeout(L)});return c.ok?await c.json():null}catch{return null}}function j(n){let t=createHash("sha256");for(let e of [...n].sort((l,i)=>l.path.localeCompare(i.path)))t.update(e.path),t.update(e.contents);return t.digest("hex")}async function nt(n,t={}){let e=await O(n,t.ref,t.getToken);if(!e)return null;let l=H(e,t.subpath);if(l.length===0)return null;if(t.skillFilter){let r=m(t.skillFilter),s=l.filter(u=>{let f=u.split("/");if(f.length<2)return false;let h=f[f.length-2];return m(h)===r});s.length>0&&(l=s);}let i=await Promise.all(l.map(async r=>{let s=await z(n,e.branch,r);return {mdPath:r,content:s}})),a=[];for(let{mdPath:r,content:s}of i){if(!s)continue;let{data:u}=y(s);if(!u.name||!u.description||typeof u.name!="string"||typeof u.description!="string"||u.metadata?.internal===true&&!t.includeInternal)continue;let h=S(u.name),k=S(u.description);a.push({mdPath:r,name:h,description:k,content:s,slug:m(h),metadata:u.metadata});}if(a.length===0)return null;let o=a;if(t.skillFilter){let r=m(t.skillFilter),s=a.filter(u=>u.slug===r);if(s.length>0&&(o=s),o.length===0)return null}let c=n.toLowerCase(),p=await Promise.all(o.map(async r=>{let s=await v(c,r.slug);return {skill:r,download:s}}));return p.every(r=>r.download!==null)?{skills:p.map(({skill:r,download:s})=>{let u=r.mdPath.toLowerCase(),h=(u.endsWith("/skill.md")?r.mdPath.slice(0,-9):u==="skill.md"?"":r.mdPath.slice(0,-9))?s.files:s.files.filter(k=>k.path.toLowerCase()==="skill.md");return {name:r.name,description:r.description,path:"",rawContent:r.content,metadata:r.metadata,files:h,snapshotHash:h.length===s.files.length?s.hash:j(h),repoPath:r.mdPath}}),tree:e}:null}
|
|
2
|
+
export{K as a,y as b,B as c,S as d,M as e,m as f,tt as g,O as h,et as i,H as j,nt as k};
|