feat(cli): add DeepSeek Harness agent profile (#891)

* feat(cli): add DeepSeek Harness agent profile

Signed-off-by: dongmucat <1127093059@qq.com>

* docs(dsh): fix custom install troubleshooting command

Signed-off-by: dongmucat <1127093059@qq.com>

---------

Signed-off-by: dongmucat <1127093059@qq.com>
This commit is contained in:
dongmucat 2026-09-21 13:58:12 +08:00 committed by GitHub
parent be46c547d1
commit 6a89832bb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 325 additions and 5 deletions

View file

@ -569,6 +569,18 @@ protocol is not compatible with SkillHub; use the first-party CLI shown above.
📖 **[Complete Hermes Agent Integration Guide →](./docs/hermes-integration-en.md)**
### [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) discovers standard `SKILL.md` packages from `.dsh/skills` and the shared `.agents/skills` roots. Install directly into its native user directory with the first-party SkillHub CLI:
```bash
skillhub install my-skill --agent dsh --scope user
```
Project-scoped installs use `<repository>/.dsh/skills`; run them from the repository root. dsh watches its skill roots, so newly installed skills are discovered without restarting the process.
📖 **[Complete DeepSeek Harness Integration Guide →](./docs/dsh-integration-en.md)**
### [HarnessClaw Engine](https://github.com/harnessclaw/harnessclaw-engine)
[HarnessClaw Engine](https://github.com/harnessclaw/harnessclaw-engine) is a Go LLM programming assistant engine that exposes its capabilities over WebSocket. It loads skills from `SKILL.md` files with YAML frontmatter and parameter substitution, scanning each configured directory for `skill-name/SKILL.md` (default `~/.harnessclaw/workspace/skills/`, with earlier directories taking priority on name conflicts). Install a SkillHub package straight into that directory with the CLI's `--dir` option, no registry adapter required:

View file

@ -454,6 +454,18 @@ ClawHub 兼容范围包含搜索、查看和安装;其发布协议与 SkillHub
📖 **[完整 Hermes Agent 集成指南 →](./docs/hermes-integration.md)**
### [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)`dsh`)会从 `.dsh/skills` 和共享的 `.agents/skills` 根目录发现标准 `SKILL.md` 技能包。使用第一方 SkillHub CLI 可直接安装到它的原生用户目录:
```bash
skillhub install my-skill --agent dsh --scope user
```
项目级安装会写入 `<仓库>/.dsh/skills`请在仓库根目录执行。dsh 会监听技能根目录,因此安装后无需重启进程即可发现新技能。
📖 **[完整 DeepSeek Harness 集成指南 →](./docs/dsh-integration.md)**
### [HarnessClaw Engine](https://github.com/harnessclaw/harnessclaw-engine)
[HarnessClaw Engine](https://github.com/harnessclaw/harnessclaw-engine) 是基于 Go 的 LLM 编程助手引擎,通过 WebSocket 协议对外提供能力。它从 `SKILL.md` 文件加载技能,支持 YAML frontmatter 与参数替换,并按配置顺序扫描各目录下的 `skill-name/SKILL.md`(默认 `~/.harnessclaw/workspace/skills/`,靠前的目录在重名时优先)。通过 SkillHub CLI 的 `--dir` 参数即可把技能包直接安装到该目录,无需新增 registry 适配器:

View file

@ -6,6 +6,8 @@ All notable CLI behavior changes are documented in this file.
### Added
- Add the `dsh` agent profile, displayed as DeepSeek Harness, with automatic detection of
project-level and user-level `.dsh/skills` directories.
- Add OAuth Device Flow to `skillhub login` when no API token is supplied, including best-effort
browser launch, a `--no-open` headless mode, bounded polling, and non-secret JSON progress output.
- Add the `pi` agent profile, displayed as Pi, with `--agent pi`, project-level

View file

@ -185,6 +185,9 @@ skillhub install pdf-parser --agent astudio
# Install to Pi's user-level directory (use --scope project for the project directory)
skillhub install pdf-parser --agent pi
# Install to DeepSeek Harness (use --scope project from the repository root for project skills)
skillhub install pdf-parser --agent dsh
# Install to multiple Agents
skillhub install pdf-parser --agent codex --agent claude-code
@ -221,6 +224,7 @@ Most Agents have both project-level and user-level skills directories. Use `--sc
| `claude-code` | `<project>/.claude/skills/` | `~/.claude/skills/` |
| `codex` | `<project>/.codex/skills/` | `~/.codex/skills/` |
| `cursor` | `<project>/.cursor/skills/` | `~/.cursor/skills/` |
| `dsh` (DeepSeek Harness) | `<project>/.dsh/skills/` | `~/.dsh/skills/` |
| `github-copilot` | `<project>/.github-copilot/skills/` | `~/.github-copilot/skills/` |
| `gemini-cli` | `<project>/.gemini/skills/` | `~/.gemini/skills/` |
| `windsurf` | `<project>/.windsurf/skills/` | `~/.windsurf/skills/` |
@ -237,6 +241,8 @@ Most Agents have both project-level and user-level skills directories. Use `--sc
For a custom path or an unsupported Agent directory, use `--dir` to specify the installation path. In interactive user scope, the `generic` target is offered alongside detected Agent targets. AStudio appears in that selector when `~/.acode/skills/` exists. When `--scope user|project` finds no matching agent directory, the CLI falls back to the `_fallback_` row above.
DeepSeek Harness resolves project skills from the nearest Git repository root, while SkillHub CLI uses the current directory for project-scoped profiles. Run `--scope project --agent dsh` from the repository root. If `DSH_HOME` overrides the default `~/.dsh`, install with `--dir "$DSH_HOME/skills"`.
### File Structure After Installation
```

View file

@ -3,6 +3,7 @@ import { aStudioProfile } from './profiles/astudio'
import { claudeCodeProfile } from './profiles/claude-code'
import { codexProfile } from './profiles/codex'
import { cursorProfile } from './profiles/cursor'
import { dshProfile } from './profiles/dsh'
import { githubCopilotProfile } from './profiles/github-copilot'
import { geminiCliProfile } from './profiles/gemini-cli'
import { openhandsProfile } from './profiles/openhands'
@ -17,14 +18,14 @@ import { kiloProfile } from './profiles/kilo'
import { piProfile } from './profiles/pi'
export {
aStudioProfile, claudeCodeProfile, codexProfile, cursorProfile, githubCopilotProfile,
aStudioProfile, claudeCodeProfile, codexProfile, cursorProfile, dshProfile, githubCopilotProfile,
geminiCliProfile, openhandsProfile, windsurfProfile, openclawProfile,
kiroCliProfile, rooProfile, traeProfile, traeCnProfile,
opencodeProfile, kiloProfile, piProfile
}
export const allProfiles: AgentProfile[] = [
aStudioProfile, claudeCodeProfile, codexProfile, cursorProfile, githubCopilotProfile,
aStudioProfile, claudeCodeProfile, codexProfile, cursorProfile, dshProfile, githubCopilotProfile,
geminiCliProfile, openhandsProfile, windsurfProfile, openclawProfile,
kiroCliProfile, rooProfile, traeProfile, traeCnProfile,
opencodeProfile, kiloProfile, piProfile

View file

@ -0,0 +1,2 @@
import { makeProfile } from './make-profile'
export const dshProfile = makeProfile('dsh', 'DeepSeek Harness', '.dsh/skills', '.dsh/skills')

View file

@ -667,6 +667,68 @@ describe('install command — server errors', () => {
// ---------------------------------------------------------------------------
describe('install command — multi-agent & auto-detect', () => {
test('--agent dsh defaults to the user root and persists the DeepSeek Harness agent id', async () => {
const env = await createTempHome()
registry = await startFakeRegistry({
token: 'sk_ok',
user: { handle: 'u', displayName: 'U' },
skills: [{ namespace: 'global', slug: 'pdf-parser', version: '1.0.0', zipBytes: makeSkillZip() }]
})
await runCli(['login', '--registry', registry.url, '--token', 'sk_ok'], { HOME: env.home, USERPROFILE: env.home })
const result = await runCli(
[
'install', 'pdf-parser',
'--agent', 'dsh',
'--registry', registry.url,
'--token', 'sk_ok',
'--json'
],
{ HOME: env.home, USERPROFILE: env.home },
{ cwd: env.cwd }
)
expect(result.exitCode).toBe(0)
const installDir = join(env.home, '.dsh', 'skills', 'pdf-parser')
const parsed = JSON.parse(result.stdout) as { installed: Array<{ agent: string; dir: string }> }
expect(parsed.installed).toEqual([{ agent: 'dsh', dir: installDir }])
expect(JSON.parse(await readFile(
join(installDir, '.skillhub', 'metadata.json'),
'utf-8'
)).agent).toBe('dsh')
})
test('auto-detects an existing DeepSeek Harness project skills directory end to end', async () => {
const env = await createTempHome()
registry = await startFakeRegistry({
token: 'sk_ok',
user: { handle: 'u', displayName: 'U' },
skills: [{ namespace: 'global', slug: 'pdf-parser', version: '1.0.0', zipBytes: makeSkillZip() }]
})
await runCli(['login', '--registry', registry.url, '--token', 'sk_ok'], { HOME: env.home, USERPROFILE: env.home })
await mkdir(join(env.cwd, '.dsh', 'skills'), { recursive: true })
const result = await runCli(
['install', 'pdf-parser', '--registry', registry.url, '--token', 'sk_ok', '--json'],
{ HOME: env.home, USERPROFILE: env.home },
{ cwd: env.cwd }
)
expect(result.exitCode).toBe(0)
const parsed = JSON.parse(result.stdout) as { installed: Array<{ agent: string; dir: string }> }
expect(parsed.installed).toHaveLength(1)
expect(parsed.installed[0]?.agent).toBe('dsh')
expect(parsed.installed[0]?.dir).toMatch(/[/\\]\.dsh[/\\]skills[/\\]pdf-parser/)
expect(await Bun.file(join(
env.cwd,
'.dsh',
'skills',
'pdf-parser',
'.skillhub',
'metadata.json'
)).exists()).toBe(true)
})
test('--agent pi defaults to the user root and persists the Pi agent id', async () => {
const env = await createTempHome()
registry = await startFakeRegistry({

View file

@ -5,8 +5,8 @@ import { describe, expect, test } from 'bun:test'
import { allProfiles, profileMap } from '../../../src/agents/detector'
describe('agent profiles', () => {
test('has 16 tier 1 profiles', () => {
expect(allProfiles).toHaveLength(16)
test('has 17 tier 1 profiles', () => {
expect(allProfiles).toHaveLength(17)
})
test('all profiles have unique ids', () => {
@ -15,11 +15,12 @@ describe('agent profiles', () => {
})
test('profileMap contains all profiles', () => {
expect(profileMap.size).toBe(16)
expect(profileMap.size).toBe(17)
expect(profileMap.has('astudio')).toBe(true)
expect(profileMap.has('claude-code')).toBe(true)
expect(profileMap.has('codex')).toBe(true)
expect(profileMap.has('cursor')).toBe(true)
expect(profileMap.has('dsh')).toBe(true)
expect(profileMap.has('kilo')).toBe(true)
expect(profileMap.has('pi')).toBe(true)
})
@ -41,6 +42,45 @@ describe('agent profiles', () => {
expect(profile.projectRoots('/repo')).toEqual(['/repo/.cursor/skills'])
})
test('DeepSeek Harness exposes and detects its project and user skills directories', async () => {
const base = await mkdtemp(join(tmpdir(), 'skillhub-dsh-profile-'))
const cwd = join(base, 'repo')
const home = join(base, 'home')
const projectRoot = `${cwd}/.dsh/skills`
const userRoot = `${home}/.dsh/skills`
const profile = profileMap.get('dsh')!
try {
await mkdir(cwd, { recursive: true })
await mkdir(home, { recursive: true })
expect(profile.displayName).toBe('DeepSeek Harness')
expect(profile.projectRoots(cwd)).toEqual([projectRoot])
expect(profile.userRoots(home)).toEqual([userRoot])
expect(await profile.detectInstalled(cwd, home)).toEqual([])
await mkdir(projectRoot, { recursive: true })
await mkdir(userRoot, { recursive: true })
expect(await profile.detectInstalled(cwd, home)).toEqual([
{
agent: 'dsh',
rootDir: projectRoot,
scope: 'project',
source: 'detected'
},
{
agent: 'dsh',
rootDir: userRoot,
scope: 'user',
source: 'detected'
}
])
} finally {
await rm(base, { recursive: true, force: true })
}
})
test('Pi exposes and detects its project and user skills directories', async () => {
const base = await mkdtemp(join(tmpdir(), 'skillhub-pi-profile-'))
const cwd = join(base, 'repo')

View file

@ -133,6 +133,11 @@ skillhub CLI 遵循以下目录优先级,与 OpenSkills/Claude 保持互操作
安装后目录名等于 `skill.slug`SKILL.md 的 `name` 字段),确保其他兼容客户端可通过目录名发现。
DeepSeek Harness 的 `dsh` profile 使用项目级 `./.dsh/skills/` 和用户级
`~/.dsh/skills/`dsh 同时原生扫描上表的 `.agents/skills/` 通用目录。dsh 把最近的
`.git` 祖先作为项目根目录,因此项目级安装应从仓库根目录执行。若 `DSH_HOME` 指向
自定义目录,使用 `--dir "$DSH_HOME/skills"` 显式安装。
## 8.5 与 AGENTS.md 的关系
- skillhub CLI 安装技能后,通过 `sync` 命令在 AGENTS.md 中生成 `<skill>` 描述块

View file

@ -0,0 +1,83 @@
# DeepSeek Harness Integration Guide
This guide explains how to install SkillHub packages into
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), whose CLI is `dsh`.
## Verified scope
The directory behavior described here was verified on 2026-09-20 against
`@deepseek-ai/dsh-skill-filesystem` at DeepSeek Harness commit
[`ddefc45`](https://github.com/deepseek-ai/deepseek-harness/tree/ddefc45fbc7f8e46dd73185e68295696d1297887/packages/skill/skill-filesystem).
That release is still a `0.1.x` developer preview; recheck the roots after upgrading dsh.
## Install into native dsh roots
dsh uses `<repository>/.dsh/skills` for project skills and `$DSH_HOME/skills`
(default `~/.dsh/skills`) for user skills. With the default home:
```bash
# User scope: ~/.dsh/skills/<skill-slug>/
skillhub install my-skill --agent dsh --scope user
# Project scope: run from the repository root
cd "$(git rev-parse --show-toplevel)"
skillhub install my-skill --agent dsh --scope project
```
Explicit `--agent dsh` without `--scope` defaults to the user root. Verify the local
SkillHub record with:
```bash
skillhub list --agent dsh
```
dsh watches configured skill roots, so added, renamed, or removed skills appear after
the next catalog refresh without restarting the process.
## Use the shared `.agents/skills` roots
dsh also scans project `.agents/skills` and user `~/.agents/skills`. This works with an
older SkillHub CLI that lacks the profile and lets multiple agents share one installation:
```bash
skillhub install my-skill --dir "$HOME/.agents/skills"
skillhub install my-skill --dir "$(git rev-parse --show-toplevel)/.agents/skills"
```
## Custom DSH_HOME
The SkillHub profile maps the default `~/.dsh/skills` root. If dsh uses a custom
`DSH_HOME`, pass its actual path explicitly:
```bash
skillhub install my-skill --dir "${DSH_HOME:-$HOME/.dsh}/skills"
```
## Project-root difference
dsh finds the nearest `.git` ancestor and treats it as the project root. SkillHub CLI
profiles use the current working directory. Running `--scope project --agent dsh` from a
repository subdirectory would therefore write a `.dsh/skills` directory that dsh does not
treat as the project root. Change to the path returned by `git rev-parse --show-toplevel`
before project-scoped installation.
## Compatibility boundary
- SkillHub writes `<skill-slug>/SKILL.md`, matching dsh's one-level bundle discovery.
- dsh also accepts a flat `<name>.md`; SkillHub packages still require root-level `SKILL.md`.
- `SKILL.md` needs a valid kebab-case `name` and a non-empty `description` frontmatter field.
- Format compatibility does not guarantee runtime compatibility. Agent-specific tools,
commands, MCP servers, environment variables, and operating-system requirements still
need separate validation.
- Review package contents and the SkillHub security report before installation. Never put a
registry token in a skill package.
## Troubleshooting
If dsh does not discover an installed skill:
1. Run `skillhub list` and verify the recorded directory and status.
2. Confirm the layout is `<skill-root>/<skill-slug>/SKILL.md` without another nesting level.
3. Check the `name` and `description` frontmatter in `SKILL.md`.
4. For project scope, confirm the directory is under the nearest `.git` ancestor.
5. When using `DSH_HOME` or `DSH_AGENTS_HOME`, confirm installation used the actual configured root.

83
docs/dsh-integration.md Normal file
View file

@ -0,0 +1,83 @@
# DeepSeek Harness 集成指南
本文说明如何把 SkillHub 中的技能安装到
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)CLI 名为 `dsh`)。
## 已验证范围
本文依据 DeepSeek Harness 提交
[`ddefc45`](https://github.com/deepseek-ai/deepseek-harness/tree/ddefc45fbc7f8e46dd73185e68295696d1297887/packages/skill/skill-filesystem)
中的 `@deepseek-ai/dsh-skill-filesystem` 行为编写,验证日期为 2026-09-20。
该版本仍处于 `0.1.x` developer preview升级 dsh 后请重新核对技能根目录约定。
## 安装到 dsh 原生目录
dsh 的原生项目级和用户级技能根分别是 `<仓库>/.dsh/skills`
`$DSH_HOME/skills`(默认 `~/.dsh/skills`)。使用默认目录时:
```bash
# 用户级:安装到 ~/.dsh/skills/<skill-slug>/
skillhub install my-skill --agent dsh --scope user
# 项目级:从仓库根目录执行,安装到 .dsh/skills/<skill-slug>/
cd "$(git rev-parse --show-toplevel)"
skillhub install my-skill --agent dsh --scope project
```
省略 `--scope` 时,显式的 `--agent dsh` 默认选择用户级目录。安装后可用
SkillHub CLI 核对记录:
```bash
skillhub list --agent dsh
```
dsh 会监听已配置的技能根;新增、重命名或删除技能后,无需重启进程即可在后续技能目录
刷新中看到变化。
## 使用共享 `.agents/skills`
dsh 也原生扫描项目级 `.agents/skills` 和用户级 `~/.agents/skills`。因此在尚未升级到
`dsh` profile 的 SkillHub CLI 时,或需要与其他 Agent 共享同一份技能时,可以使用:
```bash
# 用户级共享目录
skillhub install my-skill --dir "$HOME/.agents/skills"
# 项目级共享目录;仍建议从仓库根目录执行
skillhub install my-skill --dir "$(git rev-parse --show-toplevel)/.agents/skills"
```
## 自定义 DSH_HOME
SkillHub CLI 的 `dsh` profile 对应默认的 `~/.dsh/skills`。如果 dsh 使用了自定义
`DSH_HOME`,请显式指定实际目录:
```bash
skillhub install my-skill --dir "${DSH_HOME:-$HOME/.dsh}/skills"
```
## 项目根目录差异
dsh 会向上查找最近的 `.git` 祖先作为项目根目录SkillHub CLI 的项目级 profile
则以当前工作目录为根。如果在仓库子目录执行 `--scope project --agent dsh`SkillHub
CLI 会写入子目录下的 `.dsh/skills`,而 dsh 不会把它当作项目根。项目级安装前应先
切换到 `git rev-parse --show-toplevel` 返回的目录。
## 兼容性边界
- SkillHub 安装目录采用 `<skill-slug>/SKILL.md`,符合 dsh 对根目录一级技能包的发现规则。
- dsh 还支持根目录中的单文件 `<name>.md`SkillHub 包仍以根级 `SKILL.md` 为规范入口。
- `SKILL.md` 至少需要合法的 kebab-case `name` 和非空 `description` frontmatter。
- 格式兼容不代表运行时能力完全相同。技能依赖的 Agent 专用工具、命令、MCP server、
环境变量和操作系统能力仍需单独验证。
- 安装前应审查技能内容和 SkillHub 安全报告Registry Token 不应写入技能包。
## 故障排查
如果 dsh 未发现已安装技能:
1. 运行 `skillhub list`,确认安装目录和状态。
2. 确认目录结构为 `<技能根>/<skill-slug>/SKILL.md`,没有额外嵌套层级。
3. 检查 `SKILL.md``name``description` frontmatter。
4. 项目级安装确认位于最近的 `.git` 祖先下,而不是仓库子目录。
5. 自定义 `DSH_HOME``DSH_AGENTS_HOME` 时,确认安装命令使用了对应实际路径。

View file

@ -160,6 +160,9 @@ skillhub install pdf-parser --agent astudio
# Install to Pi's user-level directory (use --scope project for the project directory)
skillhub install pdf-parser --agent pi
# Install to DeepSeek Harness (use --scope project from the repository root for project skills)
skillhub install pdf-parser --agent dsh
# Install to multiple Agents
skillhub install pdf-parser --agent codex --agent claude-code
@ -196,6 +199,7 @@ Most Agents have both project-level and user-level skills directories. Use `--sc
| `claude-code` | `<project>/.claude/skills/` | `~/.claude/skills/` |
| `codex` | `<project>/.codex/skills/` | `~/.codex/skills/` |
| `cursor` | `<project>/.cursor/skills/` | `~/.cursor/skills/` |
| `dsh` (DeepSeek Harness) | `<project>/.dsh/skills/` | `~/.dsh/skills/` |
| `github-copilot` | `<project>/.github-copilot/skills/` | `~/.github-copilot/skills/` |
| `gemini-cli` | `<project>/.gemini/skills/` | `~/.gemini/skills/` |
| `windsurf` | `<project>/.windsurf/skills/` | `~/.windsurf/skills/` |
@ -212,6 +216,8 @@ Most Agents have both project-level and user-level skills directories. Use `--sc
For a custom path or an unsupported Agent directory, use `--dir` to specify the installation path. In interactive user scope, the `generic` target is offered alongside detected Agent targets. AStudio appears in that selector when `~/.acode/skills/` exists. When `--scope user|project` finds no matching agent directory, the CLI falls back to the `_fallback_` row above.
DeepSeek Harness resolves project skills from the nearest Git repository root, while SkillHub CLI uses the current directory for project-scoped profiles. Run `--scope project --agent dsh` from the repository root. If `DSH_HOME` overrides the default `~/.dsh`, install with `--dir "$DSH_HOME/skills"`.
### File Structure After Installation
```

View file

@ -156,6 +156,9 @@ skillhub install pdf-parser --agent astudio
# 安装到 Pi 的用户级目录(添加 --scope project 可安装到项目级目录)
skillhub install pdf-parser --agent pi
# 安装到 DeepSeek Harness项目级安装请在仓库根目录执行
skillhub install pdf-parser --agent dsh
# 安装到多个 Agent
skillhub install pdf-parser --agent codex --agent claude-code
@ -192,6 +195,7 @@ CLI 按以下逻辑确定安装位置:
| `claude-code` | `<project>/.claude/skills/` | `~/.claude/skills/` |
| `codex` | `<project>/.codex/skills/` | `~/.codex/skills/` |
| `cursor` | `<project>/.cursor/skills/` | `~/.cursor/skills/` |
| `dsh`DeepSeek Harness | `<project>/.dsh/skills/` | `~/.dsh/skills/` |
| `github-copilot` | `<project>/.github-copilot/skills/` | `~/.github-copilot/skills/` |
| `gemini-cli` | `<project>/.gemini/skills/` | `~/.gemini/skills/` |
| `windsurf` | `<project>/.windsurf/skills/` | `~/.windsurf/skills/` |
@ -208,6 +212,8 @@ CLI 按以下逻辑确定安装位置:
对于自定义路径或不在列表中的 Agent 目录,使用 `--dir` 显式指定安装路径。交互式 user scope 下会与已探测 Agent 目标一同提供 `generic` 目标;当 `~/.acode/skills/` 存在时,选择器会显示 AStudio。当 `--scope user|project` 找不到匹配的 agent 目录时CLI 会回退到上表的 `_fallback_` 行。
DeepSeek Harness 从最近的 Git 仓库根目录解析项目技能,而 SkillHub CLI 的项目级 profile 使用当前目录。请在仓库根目录运行 `--scope project --agent dsh`。如果通过 `DSH_HOME` 覆盖了默认的 `~/.dsh`,请改用 `--dir "$DSH_HOME/skills"` 安装。
### 安装后的文件结构
```