@easbot/skills 0.3.10 → 0.3.12
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.en.md +166 -59
- package/README.md +172 -62
- package/dist/assets/txt/template/skill-template-en-US.txt +50 -0
- package/dist/assets/txt/template/skill-template-zh-CN.txt +50 -0
- package/dist/chunks/blob-DOVEGVIE.cjs +1 -0
- package/dist/chunks/blob-JSMTOYEN.mjs +1 -0
- package/dist/chunks/chunk-2VZUPTI7.cjs +1 -0
- package/dist/chunks/chunk-2YVJ7YP6.mjs +22 -0
- package/dist/chunks/chunk-4R6NKPNW.mjs +3 -0
- package/dist/chunks/chunk-676OG55M.mjs +5 -0
- package/dist/chunks/chunk-6PJ3YQA7.mjs +2 -0
- package/dist/chunks/chunk-72DXY4B7.mjs +1 -0
- package/dist/chunks/chunk-CFMOYU7R.mjs +2 -0
- package/dist/chunks/chunk-GR2NJUJ6.mjs +255 -0
- package/dist/chunks/chunk-JFEBB5CL.cjs +22 -0
- package/dist/chunks/chunk-KV7EP6AW.cjs +255 -0
- package/dist/chunks/chunk-OWG6N2K6.cjs +5 -0
- package/dist/chunks/chunk-RWSBKHQ2.cjs +3 -0
- package/dist/chunks/chunk-RWZ4LFSN.mjs +1 -0
- package/dist/chunks/chunk-S6YNGR7S.cjs +2 -0
- package/dist/chunks/chunk-TCFGU43L.cjs +2 -0
- package/dist/chunks/chunk-VRHWDHQV.cjs +1 -0
- package/dist/chunks/chunk-WQGSYA5D.mjs +1 -0
- package/dist/chunks/chunk-XGUPIAAF.cjs +1 -0
- package/dist/chunks/cli-handler-RAJ42VDN.mjs +1 -0
- package/dist/chunks/cli-handler-YAJ5J5VA.cjs +1 -0
- package/dist/chunks/installer-FB26E7JZ.mjs +1 -0
- package/dist/chunks/installer-GOQTCUJ4.cjs +1 -0
- package/dist/chunks/interfaces-S47ZGORM.mjs +1 -0
- package/dist/chunks/interfaces-UIS44Q7A.cjs +1 -0
- package/dist/chunks/local-lock-4AA55ORE.cjs +1 -0
- package/dist/chunks/local-lock-LG5YL33V.mjs +1 -0
- package/dist/chunks/scan-QEVID2TF.mjs +1 -0
- package/dist/chunks/scan-XWU3FCE3.cjs +1 -0
- package/dist/chunks/skill-lock-KHPVP7AD.mjs +1 -0
- package/dist/chunks/skill-lock-LET33FVT.cjs +1 -0
- package/dist/chunks/store-3NRBE47N.mjs +1 -0
- package/dist/chunks/store-EHYH46YY.cjs +1 -0
- package/dist/chunks/telemetry-7T6MUW5A.mjs +1 -0
- package/dist/chunks/telemetry-LCGTSJDM.cjs +1 -0
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +347 -106
- package/dist/index.d.ts +347 -106
- package/dist/index.mjs +1 -1
- package/package.json +4 -5
- package/dist/chunks/blob-3ZTTX2CZ.mjs +0 -1
- package/dist/chunks/blob-UAS6F4BG.cjs +0 -1
- package/dist/chunks/chunk-645XWVJB.mjs +0 -208
- package/dist/chunks/chunk-DLDPJGDD.cjs +0 -208
- package/dist/chunks/chunk-XDSGBJHO.cjs +0 -2
- package/dist/chunks/chunk-ZEMGS5WR.mjs +0 -2
package/README.md
CHANGED
|
@@ -2,19 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
# @easbot/skills
|
|
4
4
|
|
|
5
|
-
EASBot
|
|
5
|
+
EASBot 技能管理子系统。负责技能(SKILL.md)的发现、安装、移除、搜索、更新、一次性使用(use),以及 store / cache / index 的运维管理。同时承担 **Plugin Source 注册中心**角色(决策 0044),把 well-known / marketplace / 本地 plugins 等源抽象为 `PluginSource`,供 `@easbot/agent` 注入 `PluginDefinition.skills`。
|
|
6
6
|
|
|
7
|
-
既可作为独立 CLI(`easbot
|
|
7
|
+
既可作为独立 CLI(`easbot-skills`),也可被 `@easbot/agent` 主 CLI 通过 Commander.js 委派调用,`easbot skills ...` 与 `easbot-skills ...` 等价。
|
|
8
|
+
|
|
9
|
+
## 版本
|
|
10
|
+
|
|
11
|
+
v0.3.11
|
|
12
|
+
|
|
13
|
+
## 目标文件
|
|
14
|
+
|
|
15
|
+
包中只操作 **SKILL.md**(以及可选的本地 `plugin.json`):
|
|
16
|
+
- 技能名来自 `SKILL.md` frontmatter 的 `name` 字段
|
|
17
|
+
- 插件清单来自 `plugin.json`(若存在,否则默认为 `*.md`)
|
|
8
18
|
|
|
9
19
|
## 特性
|
|
10
20
|
|
|
11
|
-
- **类型安全**:完整 TypeScript 类型 + Zod frontmatter 校验
|
|
12
|
-
- **多源支持**:GitHub 仓库 shorthand
|
|
13
|
-
- **多 Agent 兼容**:
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
21
|
+
- **类型安全**:完整 TypeScript 类型 + Zod frontmatter 校验 + Zod remote-skill payload 校验
|
|
22
|
+
- **多源支持**:GitHub 仓库 shorthand(`owner/repo`)/ HTTPS / SSH / Well-Known 索引 / 本地路径 / GitHub API blob 回退
|
|
23
|
+
- **多 Agent 兼容**:72+ 个 Agent 类型(`easbot`, `claude-code`, `cursor`, `codex`, `amp`, `eve`, `gemini-cli`, `github-copilot`, `opencode`, `qwen-code`, `trae`, `trae-cn`, `windsurf`, `zed`, `roo`, `cline`, `iflow-cli`, `kimi-code-cli`, `augment`, `devin`, `goose`, `dexto` 等)
|
|
24
|
+
- **Universal 共享路径**:`eve` / `amp` / `gemini-cli` / `github-copilot` / `qwen-code` / `roo` / `windsurf` / `zed` / `claude-code` 等 16 个 agent 通过 `"universal"` 作用域共享 `.agents/skills` 目录
|
|
25
|
+
- **Non-universal agent**:各自维护独立技能目录
|
|
26
|
+
- **安装矩阵 / 后安装核对**:`AgentInstallMatrix`(agent × skill 二维表,`probeCell` 逐单元格判定),安装后对每个 cell 做 reconcile(缺失补链接,多余移除)
|
|
27
|
+
- **依赖注入** (决策 0043):通过 `setGlobalAdapter` / `setInstanceAdapter` / `setAgentAdapter` 注入 `IGlobal.Path` / `IInstance.directory`,让 skills 统一使用宿主的 XDG 数据树
|
|
28
|
+
- **增量同步**:基于本地 SHA-256 密文锁文件的 `add` / `remove` / `update` / `store` 增量操作(`skill-lock.ts` / `local-lock.ts`)
|
|
29
|
+
- **store 视角运维** (决策 36A):`skills store list / clean / update / remove` 管理 store 物理 + index + cache,**不**动 agent 前端分发
|
|
30
|
+
- **local 源硬保护**:`store clean --force` / `store remove` 对 local 源拒绝删 cache / 用户原目录
|
|
31
|
+
- **i18n**:内置 zh-CN / en-US 两套翻译;所有用户简文走 `t()`,未命中 key 时回退英文原文
|
|
32
|
+
- **遥测 + 安全审计**:内置 `skills.sh` partner audit 上报、OpenClaw 风险提示、遥测可冲刷(`flushTelemetry`)
|
|
18
33
|
|
|
19
34
|
## 安装
|
|
20
35
|
|
|
@@ -32,41 +47,104 @@ pnpm add @easbot/skills
|
|
|
32
47
|
}
|
|
33
48
|
```
|
|
34
49
|
|
|
50
|
+
## 目录结构
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
src/
|
|
54
|
+
├── types.ts # AgentType、Skill、AgentConfig、ParsedSource、RemoteSkill
|
|
55
|
+
├── agents.ts # Agent 配置矩阵(72+ 个)与分类函数
|
|
56
|
+
├── skills.ts # 技能发现: parseSkillMd、discoverSkills、filterSkills
|
|
57
|
+
├── installer/
|
|
58
|
+
│ ├── installer.ts # 物理安装:installSkillForAgent、installRemoteSkillForAgent、installBlobSkillForAgent
|
|
59
|
+
│ ├── post-install.ts # 安装后核对矩阵:planActions、buildMatrix、probeCell
|
|
60
|
+
│ ├── store.ts # Store 三层职责(source→cache→install):ensureCachedSource、acquireSourceClone、cleanCache
|
|
61
|
+
│ └── types.ts # InstallMode、InstalledSkill 等
|
|
62
|
+
├── add.ts # 添加技能主流程
|
|
63
|
+
├── find.ts # 搜索技能(skills.sh / fuzzy filter / 本地 store 查询)
|
|
64
|
+
├── list.ts # 列出已安装技能(项目/全局/按 agent 过滤)
|
|
65
|
+
├── remove.ts # 移除技能
|
|
66
|
+
├── update.ts # 增量更新(按 filter 更新一个或多个技能)
|
|
67
|
+
├── use.ts # 一次性使用(materializeUseSkill、buildUsePrompt、launchAgentInteractively)
|
|
68
|
+
├── doctor.ts # 检查 + 清理断链与坏符号链接
|
|
69
|
+
├── store-cmd.ts # store 运维子命令(list / clean / update / remove)
|
|
70
|
+
├── init-template.ts # 根据 locale 模板创建新技能
|
|
71
|
+
├── sanitize.ts # 控制字符净化:sanitizeMetadata、stripTerminalEscapes
|
|
72
|
+
├── telemetry.ts # 遥测发送:track、flushTelemetry、fetchAuditData、setVersion
|
|
73
|
+
├── detect-agent.ts # 探测运行的 AI Agent(detectAgent、isRunningInAgent)
|
|
74
|
+
├── skill-lock.ts # 全局安装锁定文件读写(skill-lock.json)
|
|
75
|
+
├── local-lock.ts # 项目级锁定文件(skills-lock.json)与文件夹 SHA-256
|
|
76
|
+
├── git.ts # 仓库克隆 / cleanupTempDir / parseGitHubRepoUrl
|
|
77
|
+
├── blob.ts # GitHub API 回退:fetchRepoTree、tryBlobInstall、toSkillSlug
|
|
78
|
+
├── source-parser.ts # 来源标准化:parseSource、parseOwnerRepo、sanitizeSubpath、isRepoPrivate
|
|
79
|
+
├── interfaces.ts # 依赖注入:setGlobalAdapter / setInstanceAdapter / getGlobal / getXdgData
|
|
80
|
+
├── constants.ts # AGENTS_DIR / SKILLS_SUBDIR / UNIVERSAL_SKILLS_DIR
|
|
81
|
+
├── frontmatter.ts # YAML frontmatter 解析
|
|
82
|
+
├── format.ts # ANSI 辅助函数(颜色)
|
|
83
|
+
├── providers/
|
|
84
|
+
│ ├── wellknown.ts # WellKnownProvider: fetch index + scan + 加载
|
|
85
|
+
│ └── registry.ts # Provider 注册中心 + HostProvider 接口
|
|
86
|
+
├── sources/
|
|
87
|
+
│ ├── index.ts # PluginSource 入口:createWellKnownSource / createLocalPluginSource 工厂
|
|
88
|
+
│ ├── registry.ts # 注册中心:registerSource / listSources / findSource / listAllEntries
|
|
89
|
+
│ ├── wellknown.ts # wellknown 源适配
|
|
90
|
+
│ ├── local.ts # 本地插件目录(plugin.json)适配
|
|
91
|
+
│ └── types.ts # PluginSource / PluginSourceCategory / PluginSourceEntry 接口
|
|
92
|
+
├── i18n/ # 国际化全套(zh-CN / en-US)
|
|
93
|
+
├── prompts/ # 搜索多选提示
|
|
94
|
+
├── utils/ # 辅助工具(ansi / format)
|
|
95
|
+
├── cli.ts # 独立CLI 入口(easbot-skills)
|
|
96
|
+
├── cli-handler.ts # CLI 处理器(供 agent 复用)
|
|
97
|
+
└── src/sources/ # PluginSource 注册中心 (决策 0044)
|
|
98
|
+
```
|
|
99
|
+
|
|
35
100
|
## CLI 用法
|
|
36
101
|
|
|
37
102
|
### 独立 CLI
|
|
38
103
|
|
|
39
104
|
```bash
|
|
40
105
|
# 添加技能
|
|
41
|
-
easbot
|
|
42
|
-
easbot
|
|
106
|
+
easbot-skills add houjallen/agent-skills
|
|
107
|
+
easbot-skills add https://github.com/houjallen/agent-skills -g
|
|
43
108
|
|
|
44
|
-
#
|
|
45
|
-
easbot
|
|
109
|
+
# 搜索(交互式 fzf 选出)
|
|
110
|
+
easbot-skills find typescript --owner houjallen
|
|
46
111
|
|
|
47
|
-
#
|
|
48
|
-
easbot
|
|
112
|
+
# 列出已安装(项目级/全局)
|
|
113
|
+
easbot-skills list --json
|
|
114
|
+
easbot-skills ls -g -a claude-code
|
|
49
115
|
|
|
50
116
|
# 移除
|
|
51
|
-
easbot
|
|
117
|
+
easbot-skills remove my-skill -y
|
|
118
|
+
easbot-skills rm --agent claude-code my-skill
|
|
52
119
|
|
|
53
120
|
# 更新
|
|
54
|
-
easbot
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
easbot
|
|
121
|
+
easbot-skills update -g
|
|
122
|
+
easbot-skills update my-skill --all
|
|
123
|
+
|
|
124
|
+
# 一次性使用(不写锁定文件)
|
|
125
|
+
easbot-skills use houjallen/agent-skills@houjallen
|
|
126
|
+
easbot-skills use --skill my-skill --agent claude
|
|
127
|
+
|
|
128
|
+
# store 视角运维(store + cache + index)
|
|
129
|
+
easbot-skills store list [keyword] # 列出 store skills(按 name 过滤)
|
|
130
|
+
easbot-skills store list --json # 完整 index 为 JSON
|
|
131
|
+
easbot-skills store clean # 重置 store(删 store 物理 + 清 index)
|
|
132
|
+
easbot-skills store clean --force # 同上 + 清 cache(local 源保护)
|
|
133
|
+
easbot-skills store remove <sourceKey> # 移除 source
|
|
134
|
+
easbot-skills store remove <sourceKey> --force # 额外清 cache
|
|
135
|
+
|
|
136
|
+
# 健康检查(医生说:检查 + 清理断链符号链接)
|
|
137
|
+
easbot-skills doctor
|
|
138
|
+
easbot-skills doctor -a easbot -y # 检查一个 agent + 自动清理
|
|
139
|
+
easbot-skills dr -g # 仅全局范围
|
|
140
|
+
|
|
141
|
+
# 健康检查(快速)
|
|
142
|
+
easyt-skills init my-skill
|
|
65
143
|
```
|
|
66
144
|
|
|
67
145
|
### 被宿主 CLI 委派
|
|
68
146
|
|
|
69
|
-
`@easbot/agent`
|
|
147
|
+
`@easbot/agent` 的 `easyt skills ...` 负责通过 `handleSkillsCli(args, deps)` 实现全子命令复用。看看源码:
|
|
70
148
|
|
|
71
149
|
```typescript
|
|
72
150
|
import { handleSkillsCli, setGlobalAdapter, setInstanceAdapter } from '@easbot/skills';
|
|
@@ -74,43 +152,66 @@ import { handleSkillsCli, setGlobalAdapter, setInstanceAdapter } from '@easbot/s
|
|
|
74
152
|
setGlobalAdapter({ Path: Global.Path });
|
|
75
153
|
setInstanceAdapter({ directory: ctx.config.directory, worktree: Instance.worktree });
|
|
76
154
|
|
|
77
|
-
await handleSkillsCli(['add', 'houjallen/agent-skills', '-y'], {
|
|
155
|
+
const result = await handleSkillsCli(['add', 'houjallen/agent-skills', '-y'], {
|
|
78
156
|
version: ctx.programVersion,
|
|
157
|
+
cwd: ctx.config.directory,
|
|
79
158
|
});
|
|
159
|
+
// result.code === 0 时退出进程
|
|
80
160
|
```
|
|
81
161
|
|
|
162
|
+
`easyt skills` 在 `packages/agent/src/cli/commands/skills.ts` 的 `registerSkillsCommand()` 中注册,通过 Commander.js `skills.<subcommand>` 把 flag 转为 argv 后 dispatch 给 `handleSkillsCli`。`skills store` 子族也在同一模块注册,并绑定其中的 `registerSkillsCommand()`。
|
|
163
|
+
|
|
82
164
|
## API
|
|
83
165
|
|
|
84
166
|
| 模块 | 导出 | 说明 |
|
|
85
167
|
|---|---|---|
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
168
|
+
| `types` | `AgentType`, `Skill`, `AgentConfig`, `ParsedSource`, `RemoteSkill` | 核心类型定义(70+ 个 AgentType 联合类型) |
|
|
169
|
+
| `agents` | `agents`, `detectInstalledAgents`, `getAgentConfig`, `getAgentSkillDirs`, `getUniversalAgents`, `getAgentGlobalSkillDirs`, `isUniversalAgent`, `matchesUniversalScope` 等 | Agent 元数据矩阵 + 分类函数 |
|
|
170
|
+
| `skills` | `parseSkillMd`, `discoverSkills`, `filterSkills`, `getSkillDisplayName`, `isSubpathSafe`, `shouldInstallInternalSkills` | SKILL.md 发现 / 解析 |
|
|
171
|
+
| `installer` | `installSkillForAgent`, `installRemoteSkillForAgent`, `installBlobSkillForAgent`, `installWellKnownSkillForAgent`, `listInstalledSkills`, `isSkillInstalled`, `getCanonicalSkillsDir`, `getInstallPath`, `sanitizeName` | 物理安装接口 |
|
|
172
|
+
| `installer/post-install` | `buildMatrix`, `planActions`, `applyActions`, `probeCell`, `runPostInstallReconcile` | 安装矩阵生成 / 差异应用 |
|
|
173
|
+
| `installer/store` | `acquireSourceClone`, `ensureCachedSource`, `installFromCache`, `cleanCache`, `getCacheRepoDir`, `getSourceDir` | 三层 source/cache/install 收敛架构 |
|
|
174
|
+
| `add` | `runAdd`, `parseAddOptions`, `initTelemetry`, `AddOptions` | 技能添加 |
|
|
175
|
+
| `find` | `runFind`, `parseFindOptions`, `searchSkillsAPI`, `FindOptions`, `SearchSkill` | 搜索(skills.sh + 本地 store) |
|
|
176
|
+
| `list` | `runList`, `parseListOptions` | 列出 |
|
|
177
|
+
| `remove` | `removeCommand`, `parseRemoveOptions`, `RemoveOptions` | 移除 |
|
|
178
|
+
| `update` | `runUpdate`, `parseUpdateOptions`, `hasProjectSkills`, `matchesSkillFilter`, `resolveUpdateScope`, `UpdateScope` | 增量更新 |
|
|
179
|
+
| `store-cmd` | `runStore`, `parseStoreOptions`, `runStoreList`, `runStoreClean`, `runStoreUpdate`, `runStoreRemove`, `StoreOptions` | 运维:以 store 为视角管理 store / cache / index |
|
|
180
|
+
| `use` | `runUse`, `parseUseOptions`, `buildUsePrompt`, `materializeUseSkill`, `launchAgentInteractively`, `UseOptions`, `MaterializedUseSkill` | 一次性使用 |
|
|
181
|
+
| `doctor` | `runDoctor` | 健康检查 + 断链清理 |
|
|
182
|
+
| `cli-handler` | `handleSkillsCli`, `renderBanner`, `renderHelp`, `SkillsCliDeps`, `SkillsCliResult` | CLI 桥接供 agent 复用 |
|
|
183
|
+
| `interfaces` | `setGlobalAdapter`, `setInstanceAdapter`, `setAgentAdapter`, `getGlobal`, `getInstance`, `getHomeDir`, `getXdgData`, `getXdgConfig`, `getXdgState`, `getXdgCache`, `getProjectDirectory`, `resetGlobalAdapter`, `resetInstanceAdapter`, `hasAgentAdapter` | 路径 / Agent 依赖注入 |
|
|
184
|
+
| `skill-lock` | `readSkillLock`, `writeSkillLock`, `addSkillToLock`, `removeSkillFromLock`, `getSkillFromLock`, `getGitHubToken`, `getAllLockedSkills`, `dismissPrompt`, `isPromptDismissed`, `resetGhAuthWarning` | 全局锁文件 |
|
|
185
|
+
| `local-lock` | `readLocalLock`, `writeLocalLock`, `addSkillToLocalLock`, `removeSkillFromLocalLock`, `computeSkillFolderHash`, `getLocalLockPath` | 项目锁文件 |
|
|
186
|
+
| `git` | `cloneRepo`, `cleanupTempDir`, `parseGitHubRepoUrl`, `isGitHubHttpsCloneUrl`, `isGitHubSsoAuthError`, `GitCloneError` | Git/SSH 回退 / 仓库克隆 |
|
|
187
|
+
| `blob` | `fetchRepoTree`, `findSkillMdPaths`, `getSkillFolderHashFromTree`, `tryBlobInstall`, `toSkillSlug`, `resetRepoTreeAuthState` | GitHub API blob 回退 |
|
|
188
|
+
| `telemetry` | `track`, `flushTelemetry`, `setVersion`, `fetchAuditData`, `setDetectedAgent` | 使用统计 + 安全审计 |
|
|
189
|
+
| `sanitize` | `sanitizeMetadata`, `stripTerminalEscapes` | 输出净化 |
|
|
190
|
+
| `detect-agent` | `detectAgent`, `getAgentName`, `getAgentType`, `isRunningInAgent` | Agent 环境探测 |
|
|
191
|
+
| `providers/wellknown` | `wellKnownProvider`, `WellKnownProvider` | WellKnown 主机提供方:解析索引 + 拉取 SKILL.md |
|
|
192
|
+
| `providers/registry` | `findProvider`, `registerProvider`, `getProviders`, `registry` | 提供方注册中心 + 匹配器 |
|
|
193
|
+
| `sources` | `registerSource`, `listSources`, `findSource`, `findSourcesByCategory`, `listAllEntries`, `createWellKnownSource`, `createLocalPluginSource` | PluginSource 注册中心(决策 0044) |
|
|
102
194
|
| `frontmatter` | `parseFrontmatter` | YAML frontmatter 解析 |
|
|
103
|
-
| `
|
|
195
|
+
| `constants` | `AGENTS_DIR`, `SKILLS_SUBDIR`, `UNIVERSAL_SKILLS_DIR` | 约定的目录常量 |
|
|
196
|
+
| `utils/ansi` | `BOLD`, `DIM`, `RESET`, `TEXT`, `CYAN` 等 | ANSI 终端辅助函数 |
|
|
197
|
+
|
|
198
|
+
## 支持的Agent列表(完整)
|
|
199
|
+
|
|
200
|
+
`types.ts` 中的 `AgentType` 联合类型包含 70+ 个 Agent,其中包括:
|
|
104
201
|
|
|
105
|
-
|
|
202
|
+
`easbot`, `claude-code`, `cursor`, `codex`, `eve`, `opencode`, `aider-desk`, `amp`, `antigravity`, `astrbot`, `augment`, `autohand-code`, `bob`, `openclaw`, `cline`, `codearts-agent`, `codebuddy`, `codemaker`, `codestudio`, `command-code`, `continue`, `cortex`, `crush`, `deepagents`, `devin`, `dexto`, `droid`, `firebender`, `forgecode`, `gemini-cli`, `github-copilot`, `goose`, `hermes-agent`, `inference-sh`, `iflow-cli`, `jazz`, `junie`, `kilo`, `kimi-code-cli`, `kiro-cli`, `kode`, `lingma`, `loaf`, `zanbura`, `mistral-vibe`, `moxby`, `mux`, `neovate`, `openhands`, `om`, `pi`, `qoder`, `qoder-cn`, `qwen-code`, `replit`, `reasonix`, `roo`, `rovodev`, `tabnine-cli`, `terramind`, `tinycloud`, `trae`, `trae-cn`, `warp`, `windsurf`, `zed`, `zencoder`, `zenflow`, `pochi`, `promptscript`, `adal`, `antigravity-cli`, `mistral`,以及虚拟类型 `universal`。
|
|
106
203
|
|
|
107
|
-
|
|
204
|
+
所有这些 Agent 的配置(displayName、skillsDir、globalSkillsDir、是否导入universal、安装判断函数等)都在 `src/agents.ts` 中定义。
|
|
205
|
+
|
|
206
|
+
## 全局路径依赖注入
|
|
207
|
+
|
|
208
|
+
为避免与宿主(agent/gateway)抢 XDG/home 目录,`@easbot/skills` 默认通过依赖注入获取路径:
|
|
108
209
|
|
|
109
210
|
```typescript
|
|
110
211
|
import {
|
|
111
212
|
setGlobalAdapter, // 注入 IGlobal.Path
|
|
112
213
|
setInstanceAdapter, // 注入 IInstance.directory / worktree
|
|
113
|
-
setAgentAdapter, // 注入
|
|
214
|
+
setAgentAdapter, // 注入 SkillsAgentAdapter (subAgentRunner / localAgentRunner / triggerEvent)
|
|
114
215
|
getHomeDir,
|
|
115
216
|
getXdgConfig,
|
|
116
217
|
getXdgData,
|
|
@@ -135,19 +236,28 @@ setInstanceAdapter({
|
|
|
135
236
|
});
|
|
136
237
|
```
|
|
137
238
|
|
|
138
|
-
未注入时,skills 包回退到 `xdg-basedir` + `homedir()
|
|
239
|
+
未注入时,skills 包回退到 `xdg-basedir` + `homedir()`,与 v1 独立 CLI 完全兼容。
|
|
139
240
|
|
|
140
241
|
## 数据目录约定
|
|
141
242
|
|
|
142
243
|
| 路径 | 用途 |
|
|
143
244
|
|---|---|
|
|
144
|
-
| `$
|
|
145
|
-
| `$
|
|
146
|
-
|
|
|
245
|
+
| `$Global.Path.data/skills/` | Agent 运行时加载的技能目录(运行时扫描) |
|
|
246
|
+
| `$Global.Path.data/skills/.skill-lock.json` | 全局安装锁定文件(与 store 同根) |
|
|
247
|
+
| `$Global.Path.data/skills/store/<sourceKey>/` | **store 物理目录**(按 sourceKey hash 归一化源;add 写一次,update 复用) |
|
|
248
|
+
| `$Global.Path.data/skills/store/index.json` | **store defs 索引**(key = skillName;记录 storeKey / type / url / skillPath / computedHash) |
|
|
249
|
+
| `$Global.Path.cache/skills/cache/<owner>/<repo>/` | **cache 物理**(remote 源下载/物化缓存;local 源 = 用户原目录) |
|
|
250
|
+
| `<cwd>/.agents/skills/` 或 `<cwd>/skills/` | 项目级技能目录 |
|
|
147
251
|
| `<cwd>/skills-lock.json` | 项目级锁定文件 |
|
|
148
252
|
| `<home>/.config/easbot/skills/` | XDG 配置兜底 |
|
|
149
253
|
|
|
150
|
-
> 当被 `@easbot/agent`
|
|
254
|
+
> 当被 `@easbot/agent` 委派时,所有路径落到 `{Global.Path.data}` 这颗统一数据树下,与 Gateway / MCP / Session / Contact 等模块并列。
|
|
255
|
+
|
|
256
|
+
## 链接标头外部审核反馈(skills.sh)
|
|
257
|
+
|
|
258
|
+
`telemetry.ts` 会往回向 skills.sh 发送遥测 / 审计数据(可配置 `SKILLS_API_URL`),用于:
|
|
259
|
+
- skill 审计(`fetchAuditData` 返回技能级的 `PartnerAudit` 信息)
|
|
260
|
+
- 安全 gate 检查(`.dangerously-accept-openclaw-risks` flag 可跳过)
|
|
151
261
|
|
|
152
262
|
## 开发
|
|
153
263
|
|
|
@@ -155,13 +265,13 @@ setInstanceAdapter({
|
|
|
155
265
|
# 安装依赖
|
|
156
266
|
pnpm install
|
|
157
267
|
|
|
158
|
-
# 构建(产出 dist
|
|
268
|
+
# 构建(产出 dist/ + .d.ts)
|
|
159
269
|
pnpm build
|
|
160
270
|
|
|
161
271
|
# 监听构建
|
|
162
272
|
pnpm dev
|
|
163
273
|
|
|
164
|
-
# 测试(vitest
|
|
274
|
+
# 测试(vitest)
|
|
165
275
|
pnpm test
|
|
166
276
|
pnpm test:run
|
|
167
277
|
|
|
@@ -181,21 +291,21 @@ pnpm clean
|
|
|
181
291
|
|
|
182
292
|
```bash
|
|
183
293
|
# Linux / macOS
|
|
184
|
-
|
|
294
|
+
bash scripts/publish.sh
|
|
185
295
|
|
|
186
296
|
# Windows PowerShell
|
|
187
|
-
|
|
297
|
+
powershell -ExecutionPolicy Bypass -File scripts/publish.ps1
|
|
188
298
|
|
|
189
|
-
#
|
|
299
|
+
# 等价手动
|
|
190
300
|
pnpm build
|
|
191
301
|
pnpm publish --access public --git-checks false --registry https://registry.npmjs.org
|
|
192
302
|
```
|
|
193
303
|
|
|
194
304
|
## 浏览器 / 跨运行时说明
|
|
195
305
|
|
|
196
|
-
-
|
|
197
|
-
-
|
|
198
|
-
- `engines.node >= 22.22.3
|
|
306
|
+
- 使用 Node 内置模块(`fs/promises`、`node:os`、`node:path`、`node:child_process`),不能在浏览器运行。
|
|
307
|
+
- 依赖 `simple-git`、`@vercel/detect-agent`、`@clack/prompts`、`picocolors`、`xdg-basedir`、`zod`、`yaml` 等。
|
|
308
|
+
- `engines.node >= 22.22.3` — Node 22 LTS 推荐。
|
|
199
309
|
|
|
200
310
|
## 许可证
|
|
201
311
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{skill_name}}
|
|
3
|
+
description: This skill should be used when [describe the scenario and trigger conditions], to [describe the main capability this skill provides].
|
|
4
|
+
category: [category, e.g. builtin, development, tool]
|
|
5
|
+
version: {{version}}
|
|
6
|
+
date: {{date}}
|
|
7
|
+
tags: [easbot, skill, {{skill_name}}]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# {{skill_title}}
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
[Briefly state what this skill does in 1-2 sentences.]
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
Use this skill when:
|
|
19
|
+
- [trigger condition 1]
|
|
20
|
+
- [trigger condition 2]
|
|
21
|
+
- [trigger condition 3]
|
|
22
|
+
|
|
23
|
+
## Quick reference
|
|
24
|
+
|
|
25
|
+
- [List key points or usage tips]
|
|
26
|
+
|
|
27
|
+
## [Main content section]
|
|
28
|
+
|
|
29
|
+
[Add the main content here. Pick whatever shape fits:]
|
|
30
|
+
- Core pattern — for technique or pattern skills
|
|
31
|
+
- Implementation — code samples and concrete details
|
|
32
|
+
- Commands and tools — available CLI / tooling
|
|
33
|
+
- Relationships with other skills — how this skill interacts with siblings
|
|
34
|
+
|
|
35
|
+
## References
|
|
36
|
+
|
|
37
|
+
For deeper reading, see the [references](./references/) directory.
|
|
38
|
+
|
|
39
|
+
## Decision records
|
|
40
|
+
|
|
41
|
+
<!--
|
|
42
|
+
Creation decisions for this skill (produced at step 1.5) live under:
|
|
43
|
+
- 0001-initial-design.md ← first requirement decision
|
|
44
|
+
- subsequent decisions appended as 0002-<topic>.md, 0003-<topic>.md, ...
|
|
45
|
+
|
|
46
|
+
Template & spec: see <eas-skill-creator>/references/requirement-decision-guide.md
|
|
47
|
+
Template file: <eas-skill-creator>/references/templates/00NN-requirement.md
|
|
48
|
+
-->
|
|
49
|
+
|
|
50
|
+
- [0001-initial-design.md](./0001-initial-design.md) — initial requirement and design decision
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{skill_name}}
|
|
3
|
+
description: 该技能应在[在这里描述使用场景和触发条件]时使用,[在这里描述技能的主要功能]。
|
|
4
|
+
category: [在这里填写类别,如 builtin, development, tool 等]
|
|
5
|
+
version: {{version}}
|
|
6
|
+
date: {{date}}
|
|
7
|
+
tags: [easbot, skill, {{skill_name}}]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# {{skill_title}}
|
|
11
|
+
|
|
12
|
+
## 概述
|
|
13
|
+
|
|
14
|
+
[在这里简明扼要地说明技能用途,1-2 句话描述该技能提供的能力]
|
|
15
|
+
|
|
16
|
+
## 何时使用
|
|
17
|
+
|
|
18
|
+
该技能应在以下情况使用:
|
|
19
|
+
- [具体触发条件 1]
|
|
20
|
+
- [具体触发条件 2]
|
|
21
|
+
- [具体触发条件 3]
|
|
22
|
+
|
|
23
|
+
## 快速参考
|
|
24
|
+
|
|
25
|
+
- [简要列出关键要点或使用方法]
|
|
26
|
+
|
|
27
|
+
## [在这里添加主要内容部分]
|
|
28
|
+
|
|
29
|
+
[在这里添加具体内容,根据需要可以是:]
|
|
30
|
+
- 核心模式 — 适用于技术或模式类技能
|
|
31
|
+
- 实现 — 代码示例和具体实现
|
|
32
|
+
- 命令和工具 — 可用的命令行工具
|
|
33
|
+
- 与其他技能的关系 — 与其他技能的交互
|
|
34
|
+
|
|
35
|
+
## 参考资料
|
|
36
|
+
|
|
37
|
+
如需更详细的参考资料,请参见 [references](./references/) 目录下的相关文档。
|
|
38
|
+
|
|
39
|
+
## 决策记录
|
|
40
|
+
|
|
41
|
+
<!--
|
|
42
|
+
本技能的创建决策(步骤 1.5 产出)保存在:
|
|
43
|
+
- 0001-initial-design.md ← 第一份需求决策
|
|
44
|
+
- 后续决策按 0002-<topic>.md、0003-<topic>.md 编号追加
|
|
45
|
+
|
|
46
|
+
模板与规范:见 <eas-skill-creator>/references/requirement-decision-guide.md
|
|
47
|
+
模板文件:<eas-skill-creator>/references/templates/00NN-requirement.md
|
|
48
|
+
-->
|
|
49
|
+
|
|
50
|
+
- [0001-initial-design.md](./0001-initial-design.md) — 初始需求与设计决策
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var chunkTCFGU43L_cjs=require('./chunk-TCFGU43L.cjs');require('./chunk-XGUPIAAF.cjs'),require('./chunk-VRHWDHQV.cjs');Object.defineProperty(exports,"BLOB_ALLOWED_OWNERS",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.x}});Object.defineProperty(exports,"BLOB_ALLOWED_REPOS",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.w}});Object.defineProperty(exports,"fetchRepoTree",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.A}});Object.defineProperty(exports,"findSkillMdPaths",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.C}});Object.defineProperty(exports,"getSkillFolderHashFromTree",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.B}});Object.defineProperty(exports,"resetRepoTreeAuthState",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.z}});Object.defineProperty(exports,"toSkillSlug",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.y}});Object.defineProperty(exports,"tryBlobInstall",{enumerable:true,get:function(){return chunkTCFGU43L_cjs.D}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{x as BLOB_ALLOWED_OWNERS,w as BLOB_ALLOWED_REPOS,A as fetchRepoTree,C as findSkillMdPaths,B as getSkillFolderHashFromTree,z as resetRepoTreeAuthState,y as toSkillSlug,D as tryBlobInstall}from'./chunk-6PJ3YQA7.mjs';import'./chunk-RWZ4LFSN.mjs';import'./chunk-WQGSYA5D.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var chunkOWG6N2K6_cjs=require('./chunk-OWG6N2K6.cjs'),chunkJFEBB5CL_cjs=require('./chunk-JFEBB5CL.cjs'),chunkTCFGU43L_cjs=require('./chunk-TCFGU43L.cjs'),utils=require('@easbot/utils'),promises=require('fs/promises'),path=require('path');async function v(i={}){let o=i.cwd??process.cwd(),e=i.scope??"project",r=chunkTCFGU43L_cjs.u({cwd:o,agentFilter:i.agentFilter}),s;if(e==="project"?s=r.project:e==="global"?s=r.global:s=r.all,s.length===0)return [];let a=new Set,c=[],T=await Promise.all(s.map(n=>utils.Glob.scan(n,{cwd:o,absolute:true,ignore:utils.Glob.DEFAULT_IGNORE_PATTERNS,follow:true})));for(let n of T)for(let l of n)a.has(l)||(a.add(l),c.push(l));let $=Object.keys(chunkTCFGU43L_cjs.e),G="/",m=[],F=o.replace(/\\/g,"/"),M=chunkTCFGU43L_cjs.v();for(let n of c){let l=path.dirname(n),g=l.replace(/\\/g,"/"),p=g.split(G),B=p.length>=3?`${p[p.length-3]??""}/${p[p.length-2]??""}`:"",d=await chunkJFEBB5CL_cjs.z(n);if(!d)continue;let E=g.startsWith(`${F}/`),I=M.some(t=>t!==""&&g.startsWith(`${t}/`)),f=E&&!I?"project":"global",k=[],R=e==="both"?f==="project":e==="project",C=e==="both"?f==="global":e==="global",b=t=>t.replace(/\\/g,"/"),L=b(l).replace(/\/[^/]+\/?$/,"");for(let t of $){let P=chunkTCFGU43L_cjs.e[t],S=[];R&&P.skillsDir!==void 0&&S.push(chunkOWG6N2K6_cjs.g(t,false,o)),C&&P.globalSkillsDir!==void 0&&S.push(chunkOWG6N2K6_cjs.g(t,true,o));for(let w of S)if(b(w).replace(/\/+$/,"")===L)try{let D=path.join(w,d.name);if(!(await promises.stat(D)).isDirectory())continue;await promises.realpath(D),k.includes(t)||k.push(t);break}catch{}}m.push({name:d.name,skillDir:l,skillMdPath:n,parentDir:B,scope:f,agents:k});}return m}async function Y(i={}){let o=await v(i),e=new Set,r=[];for(let s of o){let a=`${s.scope}:${s.name}`;if(e.has(a))continue;e.add(a);let c=await chunkJFEBB5CL_cjs.z(s.skillMdPath);c&&r.push(c);}return r}exports.a=v;exports.b=Y;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {c,b as b$1,n}from'./chunk-6PJ3YQA7.mjs';import {p,m,o}from'./chunk-RWZ4LFSN.mjs';import {b}from'./chunk-WQGSYA5D.mjs';import {rm,mkdtemp,readFile,rename,mkdir,writeFile,rmdir,cp,readdir,stat}from'fs/promises';import {dirname,join,normalize,resolve,sep,basename,relative,isAbsolute}from'path';import {createHash}from'crypto';import {existsSync}from'fs';import fe from'simple-git';import {Filesystem,Glob,Wildcard}from'@easbot/utils';import {tmpdir}from'os';import {execFile}from'child_process';import {promisify}from'util';import {gunzipSync,inflateRawSync}from'zlib';var U=6;function ce(n){let t=n.indexOf(":");return t<0||t===n.length-1?null:{marketId:n.slice(0,t),pluginSlug:n.slice(t+1)}}function ue(n){let t=ce(n);return t?`plugins/${t.pluginSlug}`:null}function St(n,t,e){if(!n.marketId||!n.pluginId)return null;let r=ue(n.pluginId);return r?`${t.replace(/\\/g,"/").replace(/\/+$/,"")}/${n.marketId}/${r}/${e}`:null}var Pt=3e5,Z=(()=>{let n=process.env.SKILLS_CLONE_TIMEOUT;if(!n)return Pt;let t=Number.parseInt(n,10);return Number.isFinite(t)&&t>0?t:Pt})(),bt=promisify(execFile),L=class extends Error{constructor(e,r,i=false,s=false){super(e);b(this,"url");b(this,"isTimeout");b(this,"isAuthError");this.name="GitCloneError",this.url=r,this.isTimeout=i,this.isAuthError=s;}};function Se(n){let t=n.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);if(t){let e=t[1],r=t[2];return {owner:e,repo:r,slug:`${e}/${r}`,sshUrl:`git@github.com:${e}/${r}.git`}}try{let e=new URL(n);if(e.hostname!=="github.com")return null;let r=e.pathname.match(/^\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);if(!r)return null;let i=r[1],s=r[2];return {owner:i,repo:s,slug:`${i}/${s}`,sshUrl:`git@github.com:${i}/${s}.git`}}catch{return null}}function xe(n){try{let t=new URL(n);return t.protocol==="https:"&&t.hostname==="github.com"}catch{return false}}function It(n){let t=n.toLowerCase();return t.includes("saml sso")||t.includes("enforced sso")||t.includes("enabled or enforced saml")||t.includes("re-authorize the oauth application")}function Ee(n){return n.includes("Authentication failed")||n.includes("could not read Username")||n.includes("Permission denied")||n.includes("Repository not found")||n.includes("requested URL returned error: 403")||It(n)}function J(n){let t={...process.env};return delete t.GIT_ASKPASS,delete t.GIT_EDITOR,delete t.GIT_SEQUENCE_EDITOR,delete t.GIT_PAGER,delete t.GIT_SSH_COMMAND,delete t.GIT_CONFIG_GLOBAL,delete t.GIT_CONFIG_SYSTEM,delete t.PAGER,delete t.EDITOR,delete t.VISUAL,t.GIT_TERMINAL_PROMPT="0",t.GIT_LFS_SKIP_SMUDGE="1",Object.assign(t,n),fe({timeout:{block:Z},config:["filter.lfs.required=false","protocol.version=2","remote.origin.promisor=false"]}).env(t)}async function Lt(n){try{await rm(n,{recursive:!0,force:!0});}catch{}await mkdir(n,{recursive:true});}async function Pe(n,t,e){let r=n.slug;try{let{stdout:s,stderr:o}=await bt("gh",["auth","status","-h","github.com"],{timeout:5e3,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),l=`${s}${o}`;/Git operations protocol:\s+ssh/i.test(l)&&(r=n.sshUrl);}catch{return false}return await bt("gh",["repo","clone",r,t,"--",...e?["--depth=1","--branch",e]:["--depth=1"]],{timeout:Z,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),true}function be(n,t,e){return t&&It(e)?`GitHub blocked HTTPS access to ${n} because the organization enforces SAML SSO.
|
|
2
|
+
skills tried your existing git credentials and available fallbacks, but none succeeded.
|
|
3
|
+
- Re-authorize your GitHub credentials/app for that org's SSO policy
|
|
4
|
+
- Or rerun with SSH: easbot skills add ${t.sshUrl}
|
|
5
|
+
- Verify access with: gh auth status -h github.com or ssh -T git@github.com`:t?`Authentication failed for ${n}.
|
|
6
|
+
- For private repos, ensure you have access
|
|
7
|
+
- Retry with SSH: easbot skills add ${t.sshUrl}
|
|
8
|
+
- Check access with: gh auth status -h github.com or ssh -T git@github.com`:`Authentication failed for ${n}.
|
|
9
|
+
- For private repos, ensure you have access
|
|
10
|
+
- For SSH: Check your keys with 'ssh -T git@github.com'
|
|
11
|
+
- For HTTPS: Run 'gh auth login' or configure git credentials`}async function $t(n,t,e){if(e){await Filesystem.mkdir(dirname(e),{recursive:true});let o=t?["--depth","1","--branch",t]:["--depth","1"];try{return await J().clone(n,e,o),e}catch(l){throw await rm(e,{recursive:true,force:true}).catch(()=>{}),l}}let r=await mkdtemp(join(tmpdir(),"skills-")),i=t?["--depth","1","--branch",t]:["--depth","1"],s=Se(n);try{return await J().clone(n,r,i),r}catch(o){let l=o instanceof Error?o.message:String(o),a=l.includes("block timeout")||l.includes("timed out"),c=Ee(l);if(a){try{await rm(r,{recursive:!0,force:!0});}catch{}let f=Math.round(Z/1e3);throw new L(`Clone timed out after ${f}s. Common causes:
|
|
12
|
+
- Large repository: raise the timeout with SKILLS_CLONE_TIMEOUT=600000 (10m)
|
|
13
|
+
- Slow network: retry, or clone manually and pass the local path to 'skills add'
|
|
14
|
+
- Private repo without credentials: ensure auth is configured
|
|
15
|
+
- For SSH: ssh-add -l (to check loaded keys)
|
|
16
|
+
- For HTTPS: gh auth status (if using GitHub CLI)`,n,true,false)}if(c&&s&&xe(n)){try{if(await Lt(r),await Pe(s,r,t))return r}catch{}try{return await Lt(r),await J({GIT_SSH_COMMAND:process.env.GIT_SSH_COMMAND??"ssh -o BatchMode=yes"}).clone(s.sshUrl,r,i),r}catch{}}try{await rm(r,{recursive:!0,force:!0});}catch{}if(c)throw new L(be(n,s,l),n,false,true);let u=l.includes("filter.smudge")?`
|
|
17
|
+
- The repo defines a smudge/clean filter that Git considers unsafe.
|
|
18
|
+
Enable it with: git config --global protocol.allowUnsafeFilter true
|
|
19
|
+
Or upgrade Git, or unset filter.<driver>.* in the source repo.`:"";throw new L(`Failed to clone ${n}: ${l}${u}`,n,false,false)}}async function In(n){let t=normalize(resolve(n)),e=normalize(resolve(tmpdir()));if(!t.startsWith(e+sep)&&t!==e)throw new Error("Attempted to clean up directory outside of temp directory");await rm(n,{recursive:true,force:true});}function Q(n){if(n.type==="local")return null;let t=n.url.match(/^git@[^:]+:(.+)$/);if(t){let e=t[1];return e=e.replace(/\.git$/,""),e.includes("/")?e:null}if(n.url.startsWith("ssh://"))try{let r=new URL(n.url).pathname.slice(1);return r=r.replace(/\.git$/,""),r.includes("/")?r:null}catch{return null}if(!n.url.startsWith("http://")&&!n.url.startsWith("https://"))return null;try{let r=new URL(n.url).pathname.slice(1);if(r=r.replace(/\.git$/,""),r.includes("/"))return r}catch{}return null}function Kn(n){let t=n.match(/^([^/]+)\/([^/]+)$/);return t?{owner:t[1],repo:t[2]}:null}async function Un(n,t){try{let e=await fetch(`https://api.github.com/repos/${n}/${t}`);return e.ok?(await e.json()).private===!0:null}catch{return null}}async function Wt(n,t){try{let e=await fetch(`https://api.github.com/repos/${n}/${t}`,{headers:{Accept:"application/vnd.github+json"}});if(!e.ok)return null;let r=await e.json();return typeof r.default_branch=="string"&&r.default_branch.length>0?r.default_branch:null}catch{return null}}function O(n){let e=n.replace(/\\/g,"/").split("/");for(let r of e)if(r==="..")throw new Error(`Unsafe subpath: "${n}" contains path traversal segments. Subpaths must not contain ".." components.`);return n}function Ie(n){return isAbsolute(n)||n.startsWith("./")||n.startsWith("../")||n==="."||n===".."||/^[a-zA-Z]:[/\\]/.test(n)}var $e={"coinbase/agentWallet":"coinbase/agentic-wallet-skills"};function Y(n){try{return decodeURIComponent(n)}catch{return n}}function Ae(n){if(n.startsWith("github:")||n.startsWith("gitlab:")||n.startsWith("git@")||/^ssh:\/\/.+\.git(?:$|[/?])/i.test(n))return true;if(n.startsWith("http://")||n.startsWith("https://"))try{let t=new URL(n),e=t.pathname;if(t.hostname==="github.com")return /^\/[^/]+\/[^/]+(?:\.git)?(?:\/tree\/[^/]+(?:\/.*)?)?\/?$/.test(e);if(t.hostname==="gitlab.com")return /^\/.+?\/[^/]+(?:\.git)?(?:\/-\/tree\/[^/]+(?:\/.*)?)?\/?$/.test(e)}catch{}return /^https?:\/\/.+\.git(?:$|[/?])/i.test(n)?true:!n.includes(":")&&!n.startsWith(".")&&!n.startsWith("/")&&/^([^/]+)\/([^/]+)(?:\/(.+)|@(.+))?$/.test(n)}function Re(n){let t=n.indexOf("#");if(t<0)return {inputWithoutFragment:n};let e=n.slice(0,t),r=n.slice(t+1);if(!r)return {inputWithoutFragment:e};if(!Ae(e))return {inputWithoutFragment:n};let i=r.indexOf("@");if(i===-1)return {inputWithoutFragment:e,ref:Y(r)};let s=r.slice(0,i),o=r.slice(i+1);return {inputWithoutFragment:e,ref:s?Y(s):void 0,subpath:o?Y(o):void 0}}function At(n,t,e){return t?`${n}#${t}${e?`@${e}`:""}`:n}function Rt(n){if(Ie(n)){let g=resolve(n);return {type:"local",url:g,localPath:g}}let{inputWithoutFragment:t,ref:e,subpath:r}=Re(n);n=t;let i=$e[n];i&&(n=i);let s=n.match(/^github:(.+)$/);if(s)return Rt(At(s[1],e,r));let o=n.match(/^gitlab:(.+)$/);if(o)return Rt(At(`https://gitlab.com/${o[1]}`,e,r));let l=n.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/);if(l){let[,g,m,w,S]=l;return {type:"github",url:`https://github.com/${g}/${m}.git`,ref:w||e,subpath:S&&O(S)}}let a=n.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);if(a){let[,g,m,w]=a;return {type:"github",url:`https://github.com/${g}/${m}.git`,ref:w||e}}let c=n.match(/github\.com\/([^/]+)\/([^/]+)/);if(c){let[,g,m]=c,w=m.replace(/\.git$/,"");return {type:"github",url:`https://github.com/${g}/${w}.git`,...e?{ref:e}:{}}}let u=n.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)\/(.+)/);if(u){let[,g,m,w,S,p]=u;if(m!=="github.com"&&w)return {type:"gitlab",url:`${g}://${m}/${w.replace(/\.git$/,"")}.git`,ref:S||e,subpath:p&&O(p)}}let f=n.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)$/);if(f){let[,g,m,w,S]=f;if(m!=="github.com"&&w)return {type:"gitlab",url:`${g}://${m}/${w.replace(/\.git$/,"")}.git`,ref:S||e}}let k=n.match(/gitlab\.com\/(.+?)(?:\.git)?\/?$/);if(k){let g=k[1];if(g.includes("/"))return {type:"gitlab",url:`https://gitlab.com/${g}.git`,...e?{ref:e}:{}}}let E=n.match(/^([^/@]+)\/([^/@]+)@(.+)?$/);if(E&&!n.includes(":")&&!n.startsWith(".")&&!n.startsWith("/")){let g=E[1],m=E[2],w=E[3];if(!w)return {type:"github",url:`https://github.com/${g}/${m}.git`,...e?{ref:e}:{}};let S=O(w);return {type:"github",url:`https://github.com/${g}/${m}.git`,...e?{ref:e}:{},...S?{subpath:S}:{}}}let P=n.match(/^([^/@]+)\/([^/@]+)(?:\/(.+?))?\/?$/);if(P&&!n.includes(":")&&!n.startsWith(".")&&!n.startsWith("/")){let[,g,m,w]=P;return {type:"github",url:`https://github.com/${g}/${m}.git`,...e?{ref:e}:{},subpath:r??(w&&O(w))}}return We(n)?{type:"well-known",url:n}:{type:"git",url:n,...e?{ref:e}:{},...r?{subpath:r}:{}}}function We(n){if(!n.startsWith("http://")&&!n.startsWith("https://"))return false;try{let t=new URL(n);return !(["github.com","gitlab.com","raw.githubusercontent.com"].includes(t.hostname)||n.endsWith(".git"))}catch{return false}}var Kt="https://schemas.agentskills.io/discovery/0.2.0/schema.json",De=/\/schemas\/agent-skills\/index\.v1\.json$/,Fe=50*1024*1024,Ce=1e3;function Te(n,t){let e=n.skillPath;if(typeof e=="string"&&e.trim()!==""&&e!==null)return e.replace(/^\/+/,"").replace(/\/+$/,"");if(typeof n.sourceUrl=="string"&&n.sourceUrl)try{let i=new URL(n.sourceUrl).pathname,s=/\/\.well-known\/(?:agent-skills|skills)\/(.*?)\/SKILL\.md$/,o=i.match(s);if(o?.[1])return o[1];let l=i.match(/^(.*?)\/[^/]+\/SKILL\.md$/);if(l?.[1])return l[1]}catch{}if(typeof n.installName=="string"&&n.installName){let r=n.installName.lastIndexOf("@");if(r>=0){let i=n.installName.slice(r+1);if(i.includes("/"))return i.replace(/^\/+/,"")}}return t}var tt=class{constructor(){b(this,"id","well-known");b(this,"displayName","Well-Known Skills");b(this,"WELL_KNOWN_PATHS",[".well-known/agent-skills",".well-known/skills"]);b(this,"INDEX_FILE","index.json");}match(t){if(!t.startsWith("http://")&&!t.startsWith("https://"))return {matches:false};try{let e=new URL(t);return ["github.com","gitlab.com","huggingface.co"].includes(e.hostname)?{matches:!1}:{matches:!0,sourceIdentifier:`wellknown/${e.hostname}`}}catch{return {matches:false}}}async fetchIndex(t){return (await this.fetchIndexCandidates(t))[0]??null}async fetchIndexCandidates(t){try{let e=new URL(t),r=e.pathname.replace(/\/$/,""),i=[],s=r.match(/\/\.well-known\/(agent-skills|skills)\/?$/);if(s?.[1]){let l=`.well-known/${s[1]}`;i.push({indexUrl:`${e.protocol}//${e.host}${r}/${this.INDEX_FILE}`,baseUrl:`${e.protocol}//${e.host}${r.replace(/\/\.well-known\/(?:agent-skills|skills)$/,"")}`,wellKnownPath:l});}for(let l of this.WELL_KNOWN_PATHS)i.push({indexUrl:`${e.protocol}//${e.host}${r}/${l}/${this.INDEX_FILE}`,baseUrl:`${e.protocol}//${e.host}${r}`,wellKnownPath:l}),r&&r!==""&&i.push({indexUrl:`${e.protocol}//${e.host}/${l}/${this.INDEX_FILE}`,baseUrl:`${e.protocol}//${e.host}`,wellKnownPath:l});let o=[];for(let{indexUrl:l,baseUrl:a,wellKnownPath:c}of i)try{let u=await fetch(l);if(!u.ok)continue;let f=await u.json(),k=this.normalizeIndex(f,l,c);if(!k)continue;o.push({index:k.index,entries:k.entries,resolvedBaseUrl:a,resolvedWellKnownPath:c,indexUrl:l});}catch{}return o}catch{return []}}normalizeIndex(t,e,r){if(!t||typeof t!="object")return null;let i=t;if(!Array.isArray(i.skills))return null;let s=i.$schema;if(s===Kt){let a=[],c=[];for(let u of i.skills){if(!this.isValidSkillEntryV2(u))continue;let f=new URL(u.url,e).toString();a.push({version:"0.2.0",name:u.name,description:u.description,type:u.type,artifactUrl:f,digest:u.digest,indexEntry:u}),c.push(u);}return a.length===0?null:{index:{$schema:Kt,skills:c},entries:a}}if(typeof s=="string"&&De.test(s)){let a=[],c=[];for(let u of i.skills){if(!this.isValidSkillEntryEasbotV1(u))continue;let f=new URL(u.sourceUrl,e).toString();a.push({version:"easbot-v1",name:u.name,description:u.description,skillMdUrl:f,installName:u.installName,indexEntry:u}),c.push(u);}return a.length===0?null:{index:{$schema:s,skills:c},entries:a}}if(s!==void 0)return null;let o=[],l=[];for(let a of i.skills){if(!this.isValidSkillEntryV1(a))return null;o.push(a),l.push({version:"0.1.0",name:a.name,description:a.description,files:a.files,baseUrl:this.getLegacySkillBaseUrl(e,r),wellKnownPath:r,indexEntry:a});}return {index:{skills:o},entries:l}}getLegacySkillBaseUrl(t,e){let r=new URL(t),i=`/${e}/${this.INDEX_FILE}`;return `${r.protocol}//${r.host}${r.pathname.slice(0,-i.length)}`}isValidSkillName(t){return !(typeof t!="string"||t.length===0||t.length>64||!/^[a-z0-9-]+$/.test(t)||t.startsWith("-")||t.endsWith("-")||t.includes("--"))}isSafeLegacyFilePath(t){return !(typeof t!="string"||t.length===0||t.startsWith("/")||t.startsWith("\\")||t.includes("..")||t.includes("\0"))}isValidSkillEntryEasbotV1(t){if(!t||typeof t!="object")return false;let e=t;if(!this.isValidSkillName(e.name)||typeof e.description!="string"||!e.description||e.description.length>1024||typeof e.sourceUrl!="string"||!e.sourceUrl)return false;try{new URL(e.sourceUrl,"https://easbot.cn/.well-known/agent-skills/index.json");}catch{return false}return true}isValidSkillEntryV1(t){if(!t||typeof t!="object")return false;let e=t;if(!this.isValidSkillName(e.name)||typeof e.description!="string"||!e.description||!Array.isArray(e.files)||e.files.length===0)return false;for(let i of e.files)if(!this.isSafeLegacyFilePath(i))return false;return e.files.some(i=>typeof i=="string"&&i.toLowerCase()==="skill.md")}isValidSkillEntryV2(t){if(!t||typeof t!="object")return false;let e=t;if(!this.isValidSkillName(e.name)||typeof e.description!="string"||!e.description||e.description.length>1024||e.type!=="skill-md"&&e.type!=="archive"||typeof e.url!="string"||!e.url||typeof e.digest!="string"||!/^sha256:[a-f0-9]{64}$/.test(e.digest))return false;try{new URL(e.url,"https://example.com/.well-known/agent-skills/index.json");}catch{return false}return true}async fetchSkill(t){try{let e=new URL(t),r=await this.fetchIndexCandidates(t);for(let i of r){let{entries:s}=i,o=null,l=e.pathname.match(/\/.well-known\/(?:agent-skills|skills)\/([^/]+)\/?$/);if(l?.[1]&&l[1]!=="index.json"?o=l[1]:s.length===1&&(o=s[0].name),!o)continue;let a=s.find(u=>u.name===o);if(!a)continue;let c=await this.fetchSkillByEntry(a);if(c)return c}return null}catch{return null}}async fetchSkillByEntry(t,e,r){return typeof t=="string"?e?this.fetchLegacySkillByEntry({version:"0.1.0",name:e.name,description:e.description,files:e.files,baseUrl:t,wellKnownPath:r??this.WELL_KNOWN_PATHS[0],indexEntry:e}):null:t.version==="0.1.0"?this.fetchLegacySkillByEntry(t):t.version==="easbot-v1"?this.fetchEasbotV1SkillByEntry(t):this.fetchArtifactSkillByEntry(t)}async fetchEasbotV1SkillByEntry(t){try{let e=await fetch(t.skillMdUrl);if(!e.ok)return null;let r=await e.text(),{data:i}=c(r);if(typeof i.name!="string"||typeof i.description!="string")return null;let s=new Map;s.set("SKILL.md",r);let o=Te(t.indexEntry,i.name);return this.createSkill({name:i.name,description:i.description,content:r,installName:o,sourceUrl:t.skillMdUrl,metadata:i.metadata,files:s,indexEntry:t.indexEntry})}catch{return null}}async fetchLegacySkillByEntry(t){try{let e=`${t.baseUrl.replace(/\/$/,"")}/${t.wellKnownPath}/${t.name}`,r=`${e}/SKILL.md`,i=await fetch(r);if(!i.ok)return null;let s=await i.text(),{data:o}=c(s);if(typeof o.name!="string"||typeof o.description!="string")return null;let l=new Map;l.set("SKILL.md",s);let c$1=t.files.filter(f=>f.toLowerCase()!=="skill.md").map(async f=>{try{let k=`${e}/${f}`,E=await fetch(k);if(E.ok){let P=await E.arrayBuffer();return {path:f,content:new Uint8Array(P)}}}catch{}return null}),u=await Promise.all(c$1);for(let f of u)f&&l.set(f.path,f.content);return this.createSkill({name:o.name,description:o.description,content:s,installName:t.name,sourceUrl:r,metadata:o.metadata,files:l,indexEntry:t.indexEntry})}catch{return null}}async fetchArtifactSkillByEntry(t){try{let e=await fetch(t.artifactUrl);if(!e.ok)return null;let r=e.headers.get("content-type")??"",i=new Uint8Array(await e.arrayBuffer());if(this.computeDigest(i)!==t.digest)return null;if(t.type==="skill-md"){let c$1=new TextDecoder().decode(i),{data:u}=c(c$1);if(typeof u.name!="string"||typeof u.description!="string")return null;let f=new Map;return f.set("SKILL.md",c$1),this.createSkill({name:u.name,description:u.description,content:c$1,installName:t.name,sourceUrl:t.artifactUrl,metadata:u.metadata,files:f,indexEntry:t.indexEntry})}let s=this.extractArchive(i,t.artifactUrl,r),o=s.get("SKILL.md");if(!o)return null;let l=typeof o=="string"?o:new TextDecoder().decode(o);s.set("SKILL.md",l);let{data:a}=c(l);return typeof a.name!="string"||typeof a.description!="string"?null:this.createSkill({name:a.name,description:a.description,content:l,installName:t.name,sourceUrl:t.artifactUrl,metadata:a.metadata,files:s,indexEntry:t.indexEntry})}catch{return null}}createSkill(t){return {name:b$1(t.name),description:b$1(t.description),content:t.content,installName:t.installName,sourceUrl:t.sourceUrl,metadata:t.metadata&&typeof t.metadata=="object"?t.metadata:void 0,files:t.files,indexEntry:t.indexEntry}}async fetchAllSkills(t){try{let e=await this.fetchIndexCandidates(t);for(let r of e){let i=r.entries.map(l=>this.fetchSkillByEntry(l)),o=(await Promise.all(i)).filter(l=>l!==null);if(o.length>0)return o}return []}catch{return []}}computeDigest(t){return `sha256:${createHash("sha256").update(t).digest("hex")}`}extractArchive(t,e,r){if(this.isZipArchive(t,e,r))return this.extractZip(t);if(this.isTarGzArchive(t,e,r))return this.extractTarGz(t);throw new Error("Unsupported archive format")}isZipArchive(t,e,r){return r.includes("application/zip")||e.toLowerCase().endsWith(".zip")||t[0]===80&&t[1]===75}isTarGzArchive(t,e,r){let i=e.toLowerCase();return r.includes("application/gzip")||r.includes("application/x-gzip")||i.endsWith(".tar.gz")||i.endsWith(".tgz")||t[0]===31&&t[1]===139}normalizeArchivePath(t){if(!t||t.includes("\0")||t.startsWith("/")||t.startsWith("\\")||/^[A-Za-z]:/.test(t)||t.includes("\\"))return null;let e=t.split("/").filter(Boolean);return e.length===0||e.some(r=>r==="."||r==="..")?null:e.join("/")}addArchiveFile(t,e,r,i){let s=this.normalizeArchivePath(e);if(!s)throw new Error(`Unsafe archive path: ${e}`);if(i.bytes+=r.byteLength,i.bytes>Fe)throw new Error("Archive exceeds maximum unpacked size");if(t.size>=Ce)throw new Error("Archive contains too many files");t.set(s,r);}extractTarGz(t){let e=gunzipSync(Buffer.from(t)),r=new Map,i={bytes:0},s=0;for(;s+512<=e.length;){let o=e.subarray(s,s+512);if(o.every(P=>P===0))break;let l=this.readTarString(o,0,100),a=this.readTarString(o,124,12).trim(),c=o[156],u=this.readTarString(o,345,155),f=u?`${u}/${l}`:l,k=Number.parseInt(a||"0",8);if(!Number.isFinite(k)||k<0)throw new Error("Invalid tar entry size");if(s+=512,c===50||c===49)throw new Error("Archive links are not supported");if(c===0||c===48){let P=e.subarray(s,s+k);this.addArchiveFile(r,f,new Uint8Array(P),i);}s+=Math.ceil(k/512)*512;}if(!r.has("SKILL.md"))throw new Error("Archive missing root SKILL.md");return r}readTarString(t,e,r){let i=t.subarray(e,e+r),s=i.indexOf(0);return new TextDecoder().decode(s>=0?i.subarray(0,s):i)}extractZip(t){let e=Buffer.from(t),r=this.findZipEndOfCentralDirectory(e);if(r<0)throw new Error("Invalid zip archive");let i=e.readUInt16LE(r+10),s=e.readUInt32LE(r+16),o=new Map,l={bytes:0},a=s;for(let c=0;c<i;c++){if(e.readUInt32LE(a)!==33639248)throw new Error("Invalid zip directory");let u=e.readUInt16LE(a+8),f=e.readUInt16LE(a+10),k=e.readUInt32LE(a+20),E=e.readUInt32LE(a+24),P=e.readUInt16LE(a+28),g=e.readUInt16LE(a+30),m=e.readUInt16LE(a+32),w=e.readUInt32LE(a+38),S=e.readUInt32LE(a+42),p=a+46,y=e.subarray(p,p+P),x=new TextDecoder(u&2048?"utf-8":void 0).decode(y);if(a=p+P+g+m,x.endsWith("/"))continue;if(u&1)throw new Error("Encrypted zip entries are not supported");let M=w>>>16&61440;if(M===40960||M===4096)throw new Error("Archive links are not supported");if(e.readUInt32LE(S)!==67324752)throw new Error("Invalid zip local header");let z=e.readUInt16LE(S+26),ae=e.readUInt16LE(S+28),ht=S+30+z+ae,mt=e.subarray(ht,ht+k),_;if(f===0)_=mt;else if(f===8)_=inflateRawSync(mt);else throw new Error(`Unsupported zip compression method: ${f}`);if(_.byteLength!==E)throw new Error("Zip entry size mismatch");this.addArchiveFile(o,x,new Uint8Array(_),l);}if(!o.has("SKILL.md"))throw new Error("Archive missing root SKILL.md");return o}findZipEndOfCentralDirectory(t){let e=Math.max(0,t.length-65535-22);for(let r=t.length-22;r>=e;r--)if(t.readUInt32LE(r)===101010256)return r;return -1}toRawUrl(t){try{let e=new URL(t);if(t.toLowerCase().endsWith("/skill.md"))return t;let r=this.WELL_KNOWN_PATHS[0],i=e.pathname.match(/\/.well-known\/(?:agent-skills|skills)\/([^/]+)\/?$/);if(i?.[1]){let o=e.pathname.replace(/\/.well-known\/(?:agent-skills|skills)\/.*$/,"");return `${e.protocol}//${e.host}${o}/${r}/${i[1]}/SKILL.md`}let s=e.pathname.replace(/\/$/,"");return `${e.protocol}//${e.host}${s}/${r}/${this.INDEX_FILE}`}catch{return t}}getSourceIdentifier(t){try{return new URL(t).hostname.replace(/^www\./,"")}catch{return "unknown"}}async hasSkillsIndex(t){return await this.fetchIndex(t)!==null}},Ut=new tt;function et(n,t){let e=normalize(resolve(t)),r=normalize(resolve(n));return r.startsWith(e+sep)||r===e}function j(n){return n.startsWith("./")}async function Ft(n){let t=new Map;try{let e=await readFile(join(n,".claude-plugin/marketplace.json"),"utf-8"),r=JSON.parse(e),i=r.metadata?.pluginRoot;if(i===void 0||j(i))for(let o of r.plugins??[]){if(!o.name||typeof o.source!="string"&&o.source!==void 0||o.source!==void 0&&!j(o.source))continue;let l=join(n,i??"",o.source??"");if(et(l,n)&&o.skills&&o.skills.length>0)for(let a of o.skills){if(!j(a))continue;let c=join(l,a);et(c,n)&&t.set(resolve(c),o.name);}}}catch{}try{let e=await readFile(join(n,".claude-plugin/plugin.json"),"utf-8"),r=JSON.parse(e);if(r.name&&r.skills&&r.skills.length>0)for(let i of r.skills){if(!j(i))continue;let s=join(n,i);et(s,n)&&t.set(resolve(s),r.name);}}catch{}return t}var je=["node_modules",".git","dist","build","__pycache__"];async function He(n){let t=join(n,".gitignore");try{let e=await readFile(t,"utf-8");return Glob.parseGitignoreContent(e)}catch{return []}}function nt(n){return n.toLowerCase().replace(/[\s_]+/g,"-")}function Ve(n){return n.split(sep).join("/").replace(/\/+/g,"/")}function Ge(){let n=process.env.INSTALL_INTERNAL_SKILLS;return n==="1"||n==="true"}async function jt(n){try{let t=join(n,"SKILL.md");return (await stat(t)).isFile()}catch{return false}}async function Tt(n,t){try{let e=await readFile(n,"utf-8"),{data:r}=c(e);if(!r.name||!r.description||typeof r.name!="string"||typeof r.description!="string")return null;let i=r.metadata&&typeof r.metadata=="object"&&!Array.isArray(r.metadata)?r.metadata:void 0;return i?.internal===!0&&!Ge()&&!t?.includeInternal?null:{name:b$1(r.name),description:b$1(r.description),path:dirname(n),rawContent:e,metadata:i}}catch{return null}}async function Ht(n,t=0,e=5){if(t>e)return [];try{let[r,i]=await Promise.all([jt(n),readdir(n,{withFileTypes:!0}).catch(()=>[])]),s=r?[n]:[],o=await Promise.all(i.filter(l=>l.isDirectory()&&!je.includes(l.name)).map(l=>Ht(join(n,l.name),t+1,e)));return [...s,...o.flat()]}catch{return []}}function Be(n,t){let e=normalize(resolve(n)),r=normalize(resolve(join(n,t)));return r.startsWith(e+sep)||r===e}async function st(n$1,t,e){let r=[],i=new Set,s=await G(n$1),o=new Set(Object.keys(s.skills).map(nt));if(t&&!Be(n$1,t))throw new Error(`Invalid subpath: "${t}" resolves outside the repository directory. Subpath must not contain ".." segments that escape the base path.`);let l=t?join(n$1,t):n$1,a=await Ft(l),c=p=>{let y=resolve(p.path);return a.has(y)&&(p.pluginName=a.get(y)),p},u=n(),f=p=>{if(o.size===0)return false;let y=Ve(relative(n$1,p.path));if(!u.some(z=>y===z||y.startsWith(`${z}/`)))return false;let pt=nt(p.name),M=nt(basename(p.path));return o.has(pt)||o.has(M)},k=async p=>{if(!await jt(p))return false;let y=await Tt(join(p,"SKILL.md"),e);return !y||i.has(y.name)||f(y)||(y=c(y),r.push(y),i.add(y.name)),true},E=t?t.replace(/^\/+|\/+$/g,"").split("/").filter(p=>p.length>0):[],P=E.length>0?`**/${E.join("/")}/**/SKILL.md`:"**/SKILL.md",g=await Glob.scan(P,{cwd:l,absolute:true,ignore:[...Glob.DEFAULT_IGNORE_PATTERNS,...await He(l)]}),m="/",w=l.split(sep).join(m);g.sort((p,y)=>p.split(m).length-y.split(m).length);let S=[];for(let p of g){if(S.some(x=>p.startsWith(`${x}${m}`)||p===x))continue;let y=dirname(p);if(await k(y)){let x=y.split(sep).join(m);if(x===w)continue;S.push(x);}}if(r.length===0||e?.fullDepth){let p=await Ht(l);for(let y of p){let x=await Tt(join(y,"SKILL.md"),e);x&&!i.has(x.name)&&!f(x)&&(x=c(x),r.push(x),i.add(x.name));}}return r}function qe(n){return n.name||basename(n.path)}function nr(n,t){let e=t.map(r=>r.toLowerCase());return n.filter(r=>{let i=r.name.toLowerCase(),s=qe(r).toLowerCase();return e.some(o=>o===i||o===s)})}var Vt="*";function rr(n,t){if(!n||n.length===0)return [];let e=new Set(t),r=[];for(let i of n)if(i!==Vt){if(i.includes("/")||i.includes("**")){r.push(i);continue}Gt.test(i)?t.some(s=>Wildcard.match(s,i))||r.push(i):e.has(i)||r.push(i);}return r}function ir(n,t){let e={wildcard:false,matched:[],invalid:[]};if(!n||n.length===0)return e;let r=new Set(t),i=new Set;for(let s of n){if(s===Vt){e.wildcard=true;continue}if(s.includes("/")||s.includes("**")){e.invalid.push(s);continue}if(Gt.test(s))for(let o of t)Wildcard.match(o,s)&&i.add(o);else r.has(s)?i.add(s):e.invalid.push(s);}return e.matched=Array.from(i),e}var Gt=/[*?[{}]/;var ot="houjallen/agent-skills";async function xr(n){let t=n.defaultPlugin;if(!t||typeof t.marketId!="string"||t.marketId.length===0)return ot;let e=n.markets[t.marketId];if(!e)return ot;let r=e.localPath??e.url;if(!r)return ot;let i=r;return e.subpath&&(i=`${i}@${e.subpath}`),e.ref&&(i=`${i}#${e.ref}`),i}function tn(n){if(!n||typeof n!="object"||Array.isArray(n))return null;let t=n;if(typeof t.marketId!="string"||t.marketId.length===0)return null;let e={marketId:t.marketId};return typeof t.pluginId=="string"&&t.pluginId.length>0&&(e.pluginId=t.pluginId),e}function h(n){return Filesystem.normalize(n)}function qt(n){if(n==="general"||n==="coder"||n==="all")return n}function en({scope:n}){let t=n==="project"?p():m(),e=Filesystem.join(t,n==="project"?".easbot":"easbot","skills");return Filesystem.normalize(e)}function T(){return Filesystem.join(m(),"skills","store")}function Yt(){return Filesystem.join(T(),"index.json")}function ct(n){let t=`${n.type}|${n.url}|${n.ref??""}`;return createHash("sha256").update(t).digest("hex")}function Qt(n){let t=ct(n);return Filesystem.join(T(),t)}function D(){return Filesystem.join(o(),"skills","cache")}function nn(n,t,e){if(e&&e.length>0)return N(e);let r=rn(n);if(r)return r;if(t?.includes("/")){let i=t.split("/")[0];if(i)return N(i)}return "default"}function rn(n){let t=n.localPath??n.url;if(!t)return null;if(n.type==="well-known")try{let i=new URL(t),s=i.hostname.replace(/^www\./,"").replace(/\./g,"-"),o=i.pathname.replace(/^\/+/,"").split("/")[0]||"";return o&&!s.endsWith(`-${o}`)?N(`${s}-${o}`):N(s)}catch{return null}let e=t.match(/^(?:https?:\/\/[^/]+\/|git@[^:]+:)([^/]+)\/([^/]+?)(?:\.git)?\/?$/);if(e)return N(e[2]??"");let r=t.split(/[\\/]/).filter(Boolean).pop();return r?N(r.replace(/\.git$/,"")):null}function N(n){let t=n.trim().replace(/[:\\/\0]/g,"-").replace(/\.git$/,"").replace(/\s+/g,"-");return t.length>64&&(t=t.slice(0,64)),t.length>0?t:"default"}function dt(n){if(n.type==="well-known")return sn(n.url);let t=Q(n);if(t)return Filesystem.join(D(),n.type,t);let e=n.url.replace(/^https?:\/\//,"").replace(/^git@[^:]+:/,"").replace(/\.git$/,"").replace(/[/:]/g,"-");return Filesystem.join(D(),n.type,e)}function sn(n){let t,e;try{let r=new URL(n);t=r.hostname.replace(/^www\./,"");let i=r.pathname.match(/\/\.well-known\/(agent-skills|skills)/);i?.[1]?e=`.well-known/${i[1]}`:e=".well-known/agent-skills";}catch{let r=createHash("sha256").update(n).digest("hex").slice(0,16);return Filesystem.join(D(),"wellknown",`unknown-${r}`)}return Filesystem.join(D(),"wellknown",t,e)}async function te(n){n.skillNames??[];if(n.type==="local")return on(n);let e=n.mode??"install",{cacheDir:r,source:i,cloneError:s}=await ln(n,e);return {tempDir:r,cacheHit:false,source:i,cloneError:s}}var ut=class extends Error{constructor(e){super(`Local source path does not exist: ${e}`);b(this,"localPath",e);this.name="LocalPathNotFoundError";}};async function on(n,t){let e=n.localPath??n.url;if(!existsSync(e))throw new ut(e);let r=Qt(n);return existsSync(r)&&await ee(r)?{tempDir:r,cacheHit:true,source:"local-cp",cloneError:null}:{tempDir:e,cacheHit:false,source:"local-cp",cloneError:null}}async function ln(n,t){let e=n.ref;if(!e){let l=Q(n);if(l){let[a,c]=l.split("/");if(a&&c){let u=await Wt(a,c);u&&(e=u);}}}e||(e="HEAD");let r=dt(n);if(n.type==="well-known")return existsSync(r)?{cacheDir:r,source:"cache-pull",cloneError:null}:(await Filesystem.mkdir(h(r),{recursive:true}),await fn(n.url,r),{cacheDir:r,source:"well-known-fetch",cloneError:null});if(existsSync(r))try{let l=fe(r);return t==="update"?(await l.fetch(),await l.checkout(e),{cacheDir:r,source:"cache-pull",cloneError:null}):(await l.checkout(e),{cacheDir:r,source:"cache-pull",cloneError:null})}catch{return {cacheDir:r,source:"cache-pull",cloneError:null}}async function i(l,a,c){return $t(l,a,c)}let s=null,o=null;try{return await Filesystem.mkdir(h(Filesystem.dirname(r)),{recursive:!0}),s=`${r}.clone-tmp`,await i(n.url,e,s),await rename(h(s),h(r)),{cacheDir:r,source:"cache-clone",cloneError:null}}catch(l){return l instanceof L?o=l:l instanceof Error&&(o=new L(`Failed to clone ${n.url}: ${l.message}`,n.url,false,false)),await Filesystem.mkdir(h(r),{recursive:true}).catch(()=>{}),{cacheDir:r,source:"cache-pull",cloneError:o}}finally{if(s)try{await rm(h(s),{recursive:!0,force:!0});}catch{}}}async function W(){let n=h(Yt()),t;try{t=await Filesystem.readText(n);}catch{return {version:6,markets:{}}}let e;try{e=JSON.parse(t);}catch{try{await rename(h(n),`${h(n)}.corrupt-${Date.now()}`);}catch{}return {version:6,markets:{}}}let r=e;if(r.version!==6||!r.markets)return {version:6,markets:{}};let i={version:6,markets:r.markets};if("defaultPlugin"in r){let s=tn(r.defaultPlugin);s&&(i.defaultPlugin=s);}return i}async function q(n){await Filesystem.writeJson(h(Yt()),n);}async function Er(){let n=await W(),t={};for(let e of Object.values(n.markets))for(let r of Object.values(e.plugins))for(let[i,s]of Object.entries(r.skills))t[i]={marketId:e.id,pluginId:r.id,pluginName:r.name,storePath:e.storePath,pluginSlug:r.id.split(":").slice(1).join(":")||r.id,url:e.url,type:e.type,localPath:e.localPath,subpath:e.subpath,ref:e.ref,addedAt:e.addedAt,skillPath:s.skillPath,computedHash:s.computedHash,sourceKey:e.id,sourcePath:e.localPath??e.storePath};return t}async function Pr(n,t,e,r,i){let s=await W(),o=s.markets[n];o||(o={...i,id:n,addedAt:i.addedAt??new Date().toISOString(),plugins:{}},s.markets[n]=o);let l=o.plugins[t];l||(l={id:t,name:t.split(":").slice(1).join(":")||t,skills:{}},o.plugins[t]=l),l.skills[e]=r,await q(s);}async function an(){let n=await W(),t=[];for(let e of Object.values(n.markets))for(let r of Object.values(e.plugins))for(let i of Object.values(r.skills))t.push({skillName:i.name,marketId:e.id,pluginId:r.id,entry:i});return t.sort((e,r)=>{let i=e.marketId.localeCompare(r.marketId);if(i!==0)return i;let s=e.pluginId.localeCompare(r.pluginId);return s!==0?s:e.skillName.localeCompare(r.skillName)}),t}async function cn(n){return (await an()).filter(e=>e.skillName===n)}async function br(n,t,e){let i=(await W()).markets[n];if(!i)return;let s=i.plugins[t];if(!s)return;let o=s.skills[e];if(o)return {skillName:o.name,marketId:n,pluginId:t,entry:o}}async function Lr(n,t,e){let r=await W(),i=r.markets[n];if(!i)return false;let s=i.plugins[t];return !s||!(e in s.skills)?false:(delete s.skills[e],await q(r),true)}async function un(n){let t=await cn(n);if(t.length===0)return [];let e=await W(),r=[];for(let i of t){let s=e.markets[i.marketId]?.plugins[i.pluginId];s&&n in s.skills&&(delete s.skills[n],r.push(i));}return r.length>0&&await q(e),r}async function vr(n,t,e,r,i,s,o){await ft([{skillName:n,parsed:t,sourceKey:e,sourcePath:r,storePath:i,skillPath:s,...o&&{computedHash:o}}]);}async function ft(n){if(n.length===0)return;let t=await W(),e=new Date().toISOString();for(let r of n){let i=r.sourceKey,s=nn(r.parsed,r.skillPath,r.pluginName),o=`${i}:${s}`,l=t.markets[i];if(!l){let u=r.parsed.type==="local"?r.parsed.localPath??r.parsed.url:dt(r.parsed);l={id:i,type:r.parsed.type,url:r.parsed.url,localPath:r.parsed.localPath,subpath:r.parsed.subpath,ref:r.parsed.ref,storePath:r.storePath,cachePath:u,addedAt:e,plugins:{}},t.markets[i]=l;}let a=l.plugins[o];a||(a={id:o,name:s,sourcePath:r.parsed.subpath,skills:{}},l.plugins[o]=a);let c={name:r.skillName,description:r.description??"",skillPath:r.skillPath,computedHash:r.computedHash};r.scope&&(c.scope=r.scope),a.skills[r.skillName]=c;}await q(t);}async function Ir(n){return (await un(n)).length>0}async function fn(n,t){let e;try{e=await Ut.fetchAllSkills(n);}catch{return}if(e.length!==0)for(let r of e)try{await dn(r,t);}catch{}}async function dn(n,t){let e=Filesystem.join(t,n.installName),r=Filesystem.normalize(e),i=Filesystem.normalize(t);if(!r.startsWith(`${i}/`)&&r!==i)throw new Error(`Unsafe skill dir: ${n.installName}`);await mkdir(h(e),{recursive:true});for(let[s,o]of n.files){let l=Filesystem.join(e,s),a=Filesystem.normalize(l);if(!a.startsWith(`${r}/`)&&a!==r||s.includes("\0"))continue;let c=dirname(h(l));c!==h(e)&&await mkdir(c,{recursive:true}),await writeFile(h(l),typeof o=="string"?o:Buffer.from(o));}}async function $r(n){return (await te({type:"git",url:n.url,ref:n.ref})).tempDir}async function Ar(n,t={}){let e=Qt(n),r=false;if(existsSync(e)&&(r=await ee(e),!r))try{await rmdir(h(e));}catch{}if(r){let a=await st(e,void 0,{includeInternal:t.includeInternal,fullDepth:t.fullDepth});if(a.length>0){let u=await Promise.all(a.map(async f=>{let k=lt(e,f.path);return {skillName:f.name,parsed:n,sourceKey:ct(n),sourcePath:e,storePath:e,skillPath:k,description:f.description,scope:qt(f.metadata?.scope),...f.pluginName&&{pluginName:f.pluginName},computedHash:await at(f.path)}}));await ft(u);}let c=n.type==="local"?"local-cp":"cache-pull";return {storeDir:e,cacheHit:true,source:c,skills:a,blobSkills:[],cloneError:null}}let i=await te(n);await Filesystem.mkdir(h(e),{recursive:true}).catch(()=>{});let s=await st(i.tempDir,void 0,{includeInternal:t.includeInternal,fullDepth:t.fullDepth}),o=[],l=Filesystem.normalize(e).replace(/\\/g,"/");for(let a of s){let c=lt(i.tempDir,a.path),u=Filesystem.join(e,c),f=`${lt(i.tempDir,a.path)}/SKILL.md`,k=Filesystem.normalize(u).replace(/\\/g,"/"),E="/",P=l.endsWith(E)?l:l+E;if(!k.startsWith(P)&&k!==l)throw new Error(`[INVARIANT 0048-yyy] cp target escapes storeRoot: target=${k} storeRoot=${l}
|
|
20
|
+
source skill path=${a.path} (parsed.type=${n.type})
|
|
21
|
+
\u4E1A\u52A1\u4EFB\u4F55\u65F6\u5019\u4E0D\u80FD\u64CD\u4F5C\u7528\u6237\u7684 local \u76EE\u5F55\u6216\u6587\u4EF6\u3002`);let g;try{await Filesystem.mkdir(h(Filesystem.dirname(u)),{recursive:!0}),await cp(h(a.path),h(u),{recursive:!0}),g=await at(u);}catch{}a.path=u,o.push({skill:a,skillRel:c,sourceSkillPath:f,targetPath:u,computedHash:g});}if(o.length>0){let a=o.filter(c=>c.computedHash).map(c=>({skillName:c.skill.name,parsed:n,sourceKey:ct(n),sourcePath:i.tempDir,storePath:e,skillPath:c.skillRel,description:c.skill.description,scope:qt(c.skill.metadata?.scope),...c.skill.pluginName&&{pluginName:c.skill.pluginName},computedHash:c.computedHash}));await ft(a);}return {storeDir:e,cacheHit:i.cacheHit,source:i.source,skills:s,blobSkills:[],cloneError:i.cloneError??null}}function lt(n,t){let r=Filesystem.normalize(n).replace(/\\/g,"/"),i=Filesystem.normalize(t).replace(/\\/g,"/"),s=r.toLowerCase();return i.toLowerCase().startsWith(`${s}/`)?i.slice(s.length+1):""}async function ee(n){try{return (await readdir(h(n))).length>0}catch{return false}}function Rr(n){try{let t=Filesystem.normalize(n);for(let e of [D(),T()]){let r=Filesystem.normalize(e);if(t===r||t.startsWith(`${r}/`))return !0}return !1}catch{return false}}async function Wr({cachePath:n,skillName:t,scope:e}){let r=Filesystem.join(en({scope:e}),t);return existsSync(r)&&await rm(h(r),{recursive:true,force:true}),await cp(h(n),h(r),{recursive:true}),r}async function Kr(n){let t=dt(n);existsSync(t)&&await rm(h(t),{recursive:true,force:true});}async function Ur(){let n=D();existsSync(n)&&await rm(h(n),{recursive:true,force:true});}var kn="skills-lock.json",ie=4;function se(n){return join(n||process.cwd(),kn)}function Or(n,t){return n?St(n,T(),t):null}async function G(n){let t=se(n);try{let e=await readFile(t,"utf-8"),r=JSON.parse(e);return typeof r.version!="number"||!r.skills||r.version<ie?gt():r}catch{return gt()}}async function oe(n,t){let e=se(t),r={};for(let o of Object.keys(n.skills).sort())r[o]=n.skills[o];let i={version:n.version,skills:r},s=JSON.stringify(i,null,2)+`
|
|
22
|
+
`;await writeFile(e,s,"utf-8");}async function at(n){let t=[];await le(n,n,t),t.sort((r,i)=>r.relativePath.localeCompare(i.relativePath));let e=createHash("sha256");for(let r of t)e.update(r.relativePath),e.update(r.content);return e.digest("hex")}async function le(n,t,e){let r=await readdir(t,{withFileTypes:true});await Promise.all(r.map(async i=>{let s=join(t,i.name);if(i.isDirectory()){if(i.name===".git"||i.name==="node_modules")return;await le(n,s,e);}else if(i.isFile()){let o=await readFile(s),l=relative(n,s).split("\\").join("/");e.push({relativePath:l,content:o});}}));}async function jr(n,t,e){let r=await G(e),i=new Date().toISOString(),s=r.skills[n];r.skills[n]={...t,installedAt:s?.installedAt??i,updatedAt:i},await oe(r,e);}async function Hr(n,t){let e=await G(t);return n in e.skills?(delete e.skills[n],await oe(e,t),true):false}function gt(){return {version:ie,skills:{}}}export{un as $,Be as A,st as B,qe as C,nr as D,rr as E,ir as F,ot as G,xr as H,tn as I,en as J,T as K,Yt as L,ct as M,Qt as N,D as O,nn as P,dt as Q,te as R,ut as S,W as T,q as U,Er as V,Pr as W,an as X,cn as Y,br as Z,Lr as _,Q as a,vr as aa,Kn as b,ft as ba,Un as c,Ir as ca,O as d,$r as da,Rt as e,Ar as ea,Pt as f,lt as fa,L as g,ee as ga,Se as h,Rr as ha,xe as i,Wr as ia,It as j,Kr as ja,$t as k,Ur as ka,In as l,tt as m,Ut as n,U as o,St as p,ie as q,se as r,Or as s,G as t,oe as u,at as v,jr as w,Hr as x,Ge as y,Tt as z};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import {p,K as K$1}from'./chunk-2YVJ7YP6.mjs';import {m as m$1}from'./chunk-RWZ4LFSN.mjs';import {readFile,mkdir,writeFile}from'fs/promises';import {join,dirname}from'path';import {createHash}from'crypto';import {execSync}from'child_process';import n from'picocolors';var x=".skill-lock.json",d=6;function m(){return join(m$1(),"skills",x)}async function o(){let t=m();return await E(t)??F()}async function E(t){try{let i=await readFile(t,"utf-8"),e=JSON.parse(i);return typeof e.version!="number"||!e.skills||e.version<d?null:e}catch{return null}}async function c(t){let i=m();await mkdir(dirname(i),{recursive:true});let e=JSON.stringify(t,null,2);await writeFile(i,e,"utf-8");}function _(t){return createHash("sha256").update(t,"utf-8").digest("hex")}var l=false;function D(){l=false;}function $(){if(process.env.GITHUB_TOKEN)return process.env.GITHUB_TOKEN;if(process.env.GH_TOKEN)return process.env.GH_TOKEN;l||(process.stderr.write(`${n.yellow("\u2502")} ${n.yellow("GitHub rate limit reached")} \u2014 using your ${n.cyan("gh")} login to continue.
|
|
2
|
+
${n.yellow("\u2502")} ${n.dim(`Tip: set ${n.cyan("GITHUB_TOKEN")} to avoid this prompt, or use ${n.cyan("--full-depth")} to clone instead.
|
|
3
|
+
`)}`),l=true);try{let t=execSync("gh auth token",{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();if(t)return t}catch{}return null}async function K(t,i,e,r){let{fetchRepoTree:s,getSkillFolderHashFromTree:f}=await import('./blob-JSMTOYEN.mjs'),a=await s(t,r,e??void 0);return a?f(a,i):null}async function B(t,i){let e=await o(),r=new Date().toISOString(),s=e.skills[t];e.skills[t]={...i,installedAt:s?.installedAt??r,updatedAt:r},await c(e);}async function U(t){let i=await o();return t in i.skills?(delete i.skills[t],await c(i),true):false}async function j(t){return (await o()).skills[t]??null}async function C(){return (await o()).skills}async function J(){let t=await o(),i=new Map;for(let[e,r]of Object.entries(t.skills)){let s=i.get(r.source);s?s.skills.push(e):i.set(r.source,{skills:[e],entry:r});}return i}function F(){return {version:d,skills:{},dismissed:{}}}async function M(t){return (await o()).dismissed?.[t]===true}function W(t,i){return t?p(t,K$1(),i):null}async function V(t){let i=await o();i.dismissed||(i.dismissed={}),i.dismissed[t]=true,await c(i);}export{d as a,m as b,o as c,c as d,_ as e,D as f,$ as g,K as h,B as i,U as j,j as k,C as l,J as m,M as n,W as o,V as p};
|