Add DeepSeek Harness as a supported skills provider (#746)

* Add DeepSeek Harness as a supported skills provider

npx impeccable install now detects ~/.dsh (or $DSH_HOME when it sits
under home) and installs into ~/.dsh/skills, the user-level skill root
DeepSeek Harness scans, with project-level .dsh/skills on the same
layout as other providers. Aliases: dsh, deepseek, deepseek-harness.

Engine: PROVIDER_DIRS / aliases / display / input order / global hint,
$DSH_HOME-aware user skills dir, provider id resolution from the skill
dir, pin harness dirs, bundle path normalization for hashing.

Build: dsh transformer target emitting the frontmatter DeepSeek Harness
reads (user-invocable, license, compatibility, metadata; unknown keys
are ignored there) with no emitHooks (DSH hooks are in-process plugins,
not on-disk manifests) and no agentFormat (no documented on-disk
subagent format); placeholders (AGENTS.md config file, ask_user_question
tool, / command prefix), provider block tags, universal README entry.

Docs: HARNESSES.md row and frontmatter column, CLI-CONTRACT constants,
README/DEVELOP/AGENTS provider lists.

Validation: cargo test --workspace; node scripts/run-tests.mjs core
(138 pass); bun run build (19 providers, dist/dsh artifact verified);
engine smoke against a fake HOME with a local bundle: install
--providers=dsh --scope=global, auto-detected install, and update all
resolve the .dsh provider.

Generated provider output intentionally omitted per repo policy; the
sync workflow regenerates tracked .dsh/skills after merge.

Prepared with AI assistance (DeepSeek Harness coding agent).

* Address review: DSH_HOME-only detection, generated-output pathspecs

- Detect DeepSeek Harness through the resolved $DSH_HOME (fallback
  ~/.dsh) instead of gating on a fixed ~/.dsh path, so a DSH_HOME-only
  setup is offered by a provider-less install; generalize the two
  env-relocated config-dir hints (OpenCode, DSH) into one shared probe.
- Add .dsh to the sync workflow's GENERATED_PATHS and CI's generated
  drift check so the tracked .dsh/skills payload is committed and
  validated.
- Cover both behaviors: new install_detection_tests (DSH_HOME-only,
  default ~/.dsh, refused outside-home override) and a CLI-CONTRACT
  note on the resolved detection path.

Validation: cargo test --workspace; node scripts/run-tests.mjs core
(138 pass); engine smoke: DSH_HOME-only fake HOME installs globally
into the resolved skills dir.

Prepared with AI assistance (DeepSeek Harness coding agent).

* Fix DeepSeek Harness home paths on Windows

Use native relative-path containment, cover case and drive boundaries, and verify relocated global install/update without changing project skills. Add DSH output coverage and correct the install documentation.

AI assistance: Codex, under pbakaus maintainer direction.

* Document the CLI limit on external DSH homes

Clarify that outside-home manual copies are not detected or updated by the CLI.

AI assistance: Codex, under pbakaus maintainer direction.

---------

Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com>
This commit is contained in:
Sheece Gardezi
2026-09-06 14:46:05 -07:00
committed by GitHub
co-authored by Paul Bakaus
parent f41300b3b2
commit 6ebc24ad66
17 changed files with 306 additions and 34 deletions
+2 -2
View File
@@ -354,7 +354,7 @@ retain their local-development trust behavior. See [bundle signing](BUNDLE-SIGNI
- **Invoked from**: README.md ("npx impeccable install / update"), README.npm.md Quick Start (`npx impeccable skills install`, `... install -y --providers=claude,codex --scope=project`, `... update`, `... install --no-hooks`, `... link --source=.impeccable --providers=claude,cursor`, `... skills help`), `README.md:360` (hook consent explanation).
- `run(args)`: `args[0]``undefined|help|--help|-h``showHelp()`; `install``install(rest)`; `link`; `update`; `check` (ignores flags); else `stderr> Unknown skills command: ${sub}` + `Run 'impeccable --help' for available commands.`, `exit 1`.
- Constants: `API_BASE = 'https://impeccable.style'`; `PROVIDER_DIRS = ['.claude','.cursor','.gemini','.agents','.agent','.github','.grok','.hermes','.kiro','.opencode','.pi','.qoder','.trae','.trae-cn','.rovodev','.vibe']`; aliases (`agent``.agent`, `agents`/`codex``.agents`, `antigravity``.agent`, `claude`/`claude-code``.claude`, `copilot`/`github``.github`, `cursor`, `gemini`, `grok`/`grok-build`/`xai``.grok`, `hermes`, `kiro`, `opencode`, `pi`, `qoder`, `rovo-dev`/`rovodev``.rovodev`, `trae`, `trae-cn`, `vibe`); leading `.` stripped and lowercased before alias lookup; a literal PROVIDER_DIR value is accepted as-is. `DEFAULT_TARGETS = ['.claude','.agents']`. User-scope skill dir overrides: `.agent``~/.gemini/config/skills`, `.hermes``$HERMES_HOME/skills` (only when HERMES_HOME under home) else `~/.hermes/skills`, `.pi``~/.pi/agent/skills`, `.opencode``$OPENCODE_CONFIG_DIR|$XDG_CONFIG_HOME/opencode|~/.config/opencode` + `/skills`; others `~/<provider>/skills`. Project scope: `<root>/<provider>/skills`.
- Constants: `API_BASE = 'https://impeccable.style'`; `PROVIDER_DIRS = ['.claude','.cursor','.dsh','.gemini','.agents','.agent','.github','.grok','.hermes','.kiro','.opencode','.pi','.qoder','.trae','.trae-cn','.rovodev','.vibe']`; aliases (`agent``.agent`, `agents`/`codex``.agents`, `antigravity``.agent`, `claude`/`claude-code``.claude`, `copilot`/`github``.github`, `cursor`, `deepseek`/`deepseek-harness`/`dsh``.dsh`, `gemini`, `grok`/`grok-build`/`xai``.grok`, `hermes`, `kiro`, `opencode`, `pi`, `qoder`, `rovo-dev`/`rovodev``.rovodev`, `trae`, `trae-cn`, `vibe`); leading `.` stripped and lowercased before alias lookup; a literal PROVIDER_DIR value is accepted as-is. `DEFAULT_TARGETS = ['.claude','.agents']`. User-scope skill dir overrides: `.agent``~/.gemini/config/skills`, `.dsh``$DSH_HOME/skills` (only when DSH_HOME under home) else `~/.dsh/skills`, `.hermes``$HERMES_HOME/skills` (only when HERMES_HOME under home) else `~/.hermes/skills`, `.pi``~/.pi/agent/skills`, `.opencode``$OPENCODE_CONFIG_DIR|$XDG_CONFIG_HOME/opencode|~/.config/opencode` + `/skills`; others `~/<provider>/skills`. Project scope: `<root>/<provider>/skills`.
- **help**: `fetch('https://impeccable.style/api/commands')` → JSON array `[{id, description}]`; failure → `stderr> Could not fetch command list from impeccable.style. Check your network connection.`, `exit 1`. Prints:
```
@@ -375,7 +375,7 @@ retain their local-development trust behavior. See [bundle signing](BUNDLE-SIGNI
```
sorted by `id.localeCompare`.
- **Flag parsing** (`getFlagValue`): `--name=value` or `--name value` (next arg not starting with `-`). Boolean flags via `includes`.
- **install flags**: `--force`, `-y|--yes`, `--no-hooks`, `--providers=<list>`, scope: `--user|--home|--global` → user; `--project|--local` → project; `--scope=<v>`/`--install-scope=<v>` normalized (`u|user|home|global`→user, `p|project|local|repo`→project; unknown → error `Unknown install scope: ${v}. Use --scope=project or --scope=global.`). Project root = nearest ancestor with `.git`, else cwd. Detection: project harness dirs present in root, plus `GLOBAL_HARNESS_HINTS` under home (`.agent`, `.gemini/antigravity*``.agent`, `.claude`, `.codex``.agents`, `.cursor`, `.gemini`, `.grok`, `.hermes`, `.kiro`, `.opencode`, opencode config dir, `.pi`, `.qoder`, `.rovodev`, `.vibe`). Targets: explicit list wins (invalid names → `Unknown provider(s): ...`); `-y` → detected project providers, else detected user providers, else DEFAULT_TARGETS; interactive → prints "Detected harnesses:" table then radio/checkbox prompts (raw-mode TTY) or line prompts (`Install target: [1] Detected only (...) [2] Customize [1]: `, `Select harnesses (comma-separated: ...)`). Scope: explicit; `-y` → project; interactive prompt `Install location` (Project/Global). Hooks: `decideHookInstall` reads `hook.consent` in config(.local).json; declined→false, accepted→true; all targets already have hook markers→true; `-y` or non-TTY→true; else prints HOOK_EXPLAINER and asks `Install the design hook? (Y/n) `, storing consent in `.impeccable/config.local.json`.
- **install flags**: `--force`, `-y|--yes`, `--no-hooks`, `--providers=<list>`, scope: `--user|--home|--global` → user; `--project|--local` → project; `--scope=<v>`/`--install-scope=<v>` normalized (`u|user|home|global`→user, `p|project|local|repo`→project; unknown → error `Unknown install scope: ${v}. Use --scope=project or --scope=global.`). Project root = nearest ancestor with `.git`, else cwd. Detection: project harness dirs present in root, plus `GLOBAL_HARNESS_HINTS` under home (`.agent`, `.gemini/antigravity*``.agent`, `.claude`, `.codex``.agents`, `.cursor`, `.dsh` (resolved `$DSH_HOME`, fallback `~/.dsh`), `.gemini`, `.grok`, `.hermes`, `.kiro`, `.opencode`, opencode config dir, `.pi`, `.qoder`, `.rovodev`, `.vibe`). Targets: explicit list wins (invalid names → `Unknown provider(s): ...`); `-y` → detected project providers, else detected user providers, else DEFAULT_TARGETS; interactive → prints "Detected harnesses:" table then radio/checkbox prompts (raw-mode TTY) or line prompts (`Install target: [1] Detected only (...) [2] Customize [1]: `, `Select harnesses (comma-separated: ...)`). Scope: explicit; `-y` → project; interactive prompt `Install location` (Project/Global). Hooks: `decideHookInstall` reads `hook.consent` in config(.local).json; declined→false, accepted→true; all targets already have hook markers→true; `-y` or non-TTY→true; else prints HOOK_EXPLAINER and asks `Install the design hook? (Y/n) `, storing consent in `.impeccable/config.local.json`.
Bundle: `IMPECCABLE_BUNDLE_PATH` (dir or zip) else download `https://impeccable.style/api/download/bundle/universal` (https `get`, one redirect followed, non-200 → `HTTP ${status}`) to `${tmpdir}/impeccable-update-${Date.now()}.zip`, extracted with `fflate.unzipSync` into `${tmpdir}/impeccable-update-${Date.now()}` (zip-slip guarded: `Refusing to extract entry outside target dir: ${entry}`). Bundle layout `<bundle>/<provider>/skills/<skill>/...`, `<bundle>/<provider>/agents/*.md`, hook manifests `<bundle>/.claude/settings.json`, `.cursor/hooks.json`, `.codex/hooks.json`, `.github/hooks/impeccable.json`, `.grok/hooks/impeccable.json`.
Fresh install: `stdout> \nDownloading impeccable skills...`; migrate `*-impeccable` prefixed dirs → `impeccable`; copy each skill dir (rm existing dest first; drop an in-project cross-provider symlinked skills dir); copy agents (`.github``.github/agents` or `~/.copilot/agents`; `.cursor``.cursor/agents` or `~/.cursor/agents`); write hooks (dest: `.claude/settings.local.json` [skips if `.claude/settings.json` already carries the marker and prunes the local copy], `.cursor/hooks.json`, `.codex/hooks.json` for `.agents`, `.github/hooks/impeccable.json`, `.grok/hooks/impeccable.json`); merged with existing JSON (`mergeHookManifests`: strips existing impeccable entries by markers `skills/impeccable/scripts/hook-probe.mjs|hook.mjs|hook-before-edit.mjs|hook-after-edit.mjs|hook-stop.mjs`, then appends fresh); invalid existing JSON → error `Existing hook manifest is not valid JSON: ${dest}. Re-run with --force to replace it.` (with `--force`, `.bak` written). Hook command rewriting: `[ ! -f '<abs>' ] || node '<abs>'` (POSIX, single-quote-escaped) when absolute (user scope or global skill), else `[ ! -f "<rel>" ] || node "<rel>"` where rel = `${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs` (Claude), `.agents/skills/impeccable/scripts/hook.mjs` (Codex; plus `commandWindows: if exist "<p>" (node "<p>" & exit /b)`), `.cursor/skills/impeccable/scripts/hook-before-edit.mjs`; on win32 for non-Codex: `node -e "<WIN32_HOOK_GUARD_SCRIPT>" "<path>"`. Output: `Installed impeccable into: ${targets.join(', ')} (${'global'|'project'})`, optional `Installed <Provider> agents into: <path>` (+ shadow warning), `Installed hooks into: ...`, then `\nDone! Now type /impeccable init in your AI coding agent's chat (not in this terminal) to set up design context.\n`. Errors: `Download failed: ...` / `Install failed: ...` / `Nothing was installed: the bundle had no variants for ...` → exit 1.
Already installed (and not `--force`): `Impeccable skills are already installed (found in ${provider}/).`; compares tree hashes (`sha256` of file content with `\.(claude|cursor|...)\/skills\/` normalized to `.PROVIDER/skills/`); if differs → refresh + `Updated ${n} skill(s) to v${v}.`; missing hooks repaired; else `Skills are up to date (v${v}).` + `Run with --force to reinstall.`; offline → `Could not check for skill updates: ${msg}` + `Existing skills were left unchanged.`; ends `Done!` or the above; `exit 0`. Version read from `^version:\s*(.+)$` in installed `impeccable/SKILL.md`.
+1
View File
@@ -162,6 +162,7 @@ The skill-behavior suite runs three providers (claude-haiku-4-5, gpt-5.4-mini, g
- [HARNESSES.md](HARNESSES.md) - Provider capabilities matrix
- [Cursor Skills](https://cursor.com/docs/context/skills)
- [Claude Code Skills](https://code.claude.com/docs/en/skills)
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
- [Gemini CLI Skills](https://geminicli.com/docs/cli/skills/)
- [Codex CLI Skills](https://developers.openai.com/codex/skills/)
- [VS Code Copilot Skills](https://code.visualstudio.com/docs/copilot/customization/agent-skills)
+20 -17
View File
@@ -3,7 +3,7 @@
Source of truth for what each AI coding harness supports in terms of agent skills.
Used to inform provider configs in `scripts/lib/transformers/providers.js`.
Last verified: 2026-04-28 (subagent landscape spot-checked 2026-06-28; Mistral Vibe row verified 2026-07-16; Grok Build skills row verified 2026-07-21; Grok Build hook stdin captured 2026-08-24)
Last verified: 2026-04-28 (subagent landscape spot-checked 2026-06-28; Mistral Vibe row verified 2026-07-16; Grok Build skills row verified 2026-07-21; Grok Build hook stdin captured 2026-08-24; DeepSeek Harness row verified 2026-09-06)
> This file is point-in-time. Capabilities move fast; verify live before relying
> on any "only X supports Y" claim. Notably, the subagent table below lists
@@ -15,6 +15,7 @@ Last verified: 2026-04-28 (subagent landscape spot-checked 2026-06-28; Mistral V
|---------|----------|
| Claude Code | https://code.claude.com/docs/en/skills |
| Cursor | https://cursor.com/docs/context/skills |
| DeepSeek Harness | https://github.com/deepseek-ai/deepseek-harness |
| Gemini CLI | https://geminicli.com/docs/cli/skills/ |
| Codex CLI | https://developers.openai.com/codex/skills |
| GitHub Copilot (Agents) | https://code.visualstudio.com/docs/copilot/customization/agent-skills |
@@ -39,22 +40,22 @@ Provider-specific extensions beyond the spec: `user-invocable`, `argument-hint`,
Fields marked with * are spec-standard. Others are provider extensions.
| Field | Claude Code | Cursor | Gemini | Codex | Copilot | Grok | Hermes | Kiro | OpenCode | Pi | Qoder | Rovo Dev | Mistral Vibe | Antigravity |
|-------|:-----------:|:------:|:------:|:-----:|:-------:|:----:|:------:|:----:|:--------:|:--:|:-----:|:--------:|:------------:|:-----------:|
| `name`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `description`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `license`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `compatibility`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `metadata`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `allowed-tools`* | Yes | No | Ignored | No | No | Yes | No | No | No | Yes | Yes | Yes | Yes | Yes |
| `user-invocable` | Yes | No | No | No | Yes | Yes | No | No | No | No | Yes | Yes | Yes | No |
| `argument-hint` | Yes | No | No | No | Yes | Yes | No | No | No | No | Yes | Yes | No | No |
| `disable-model-invocation` | Yes | Yes | No | No | Yes | Yes | No | No | Yes | Yes | TBD | TBD | No | No |
| `model` | Yes | No | No | No | No | Yes | No | No | No | No | No | No | No | No |
| `effort` | Yes | No | No | No | No | Yes | No | No | No | No | No | No | No | No |
| `context` | Yes | No | No | No | No | No | No | No | No | No | No | No | No | No |
| `agent` | Yes | No | No | No | No | No | No | No | No | No | No | No | No | No |
| `hooks` | Yes | No | No | Yes | No | Yes | No | No | No | No | No | No | No | No |
| Field | Claude Code | Cursor | Gemini | Codex | Copilot | Grok | Hermes | Kiro | OpenCode | Pi | Qoder | Rovo Dev | Mistral Vibe | Antigravity | DSH |
|-------|:-----------:|:------:|:------:|:-----:|:-------:|:----:|:------:|:----:|:--------:|:--:|:-----:|:--------:|:------------:|:-----------:|:------:|
| `name`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `description`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `license`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Ignored |
| `compatibility`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Ignored |
| `metadata`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `allowed-tools`* | Yes | No | Ignored | No | No | Yes | No | No | No | Yes | Yes | Yes | Yes | Yes | No |
| `user-invocable` | Yes | No | No | No | Yes | Yes | No | No | No | No | Yes | Yes | Yes | No | Yes |
| `argument-hint` | Yes | No | No | No | Yes | Yes | No | No | No | No | Yes | Yes | No | No | No |
| `disable-model-invocation` | Yes | Yes | No | No | Yes | Yes | No | No | Yes | Yes | TBD | TBD | No | No | Yes |
| `model` | Yes | No | No | No | No | Yes | No | No | No | No | No | No | No | No | No |
| `effort` | Yes | No | No | No | No | Yes | No | No | No | No | No | No | No | No | No |
| `context` | Yes | No | No | No | No | No | No | No | No | No | No | No | No | No | No |
| `agent` | Yes | No | No | No | No | No | No | No | No | No | No | No | No | No | No |
| `hooks` | Yes | No | No | Yes | No | Yes | No | No | No | No | No | No | No | No | No |
Notes:
- Gemini CLI validates only `name` and `description`; other spec fields are parsed but ignored.
@@ -65,6 +66,7 @@ Notes:
- Kiro recognizes `user-invocable` and `disable-model-invocation` per community reports but does not formally document them.
- Antigravity supports standard Agent Skills spec frontmatter fields (`name`, `description`, `license`, `compatibility`, `metadata`, `allowed-tools`).
- OpenCode 1.18.10 recognises only the spec subset on SKILL.md (`name`, `description`, `license`, `compatibility`, `metadata`). Claude-style extensions (`user-invocable`, `argument-hint`, `allowed-tools`, `model`, `agent`) are silently ignored; Impeccable still emits them today for other harnesses, but they have no effect in OpenCode. Use `commands/<name>.md` (see Placeholder / Variable Substitution below) for slash UX; OpenCode honours only `description`, `agent`, `model`, `variant`, `subtask` on command files.
- DeepSeek Harness parses the Agent Skills frontmatter and requires `name` and `description`; it reads `metadata`, `user-invocable`, and `disable-model-invocation`. Spec fields it does not consume (`license`, `compatibility`, `allowed-tools`) and Claude-style extensions (`argument-hint`, `model`, `effort`, `context`, `agent`, `hooks`) are silently ignored. Hooks are in-process plugins configured via cordis.yml, not on-disk manifests, so there is no hook surface to install. Subagents exist but are composed from preset config, not an on-disk skill-adjacent format. Verified against the [filesystem skill provider](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/skill/skill-filesystem/README.md).
- Unknown fields are silently ignored by all harnesses.
## Hook surface used by Impeccable
@@ -83,6 +85,7 @@ Notes:
|---------|-----------------|------------|
| Claude Code | `.claude/skills/` | - |
| Cursor | `.cursor/skills/` | `.agents/skills/`, `.claude/skills/` |
| DeepSeek Harness | `.dsh/skills/` (project), `~/.dsh/skills/` (global; `$DSH_HOME/skills` when set) | `.agents/skills/` (project), `~/.agents/skills/` (global) |
| Gemini CLI | `.gemini/skills/` | `.agents/skills/` |
| Codex CLI | `.agents/skills/` (primary) | - |
| GitHub Copilot | `.github/skills/` | `.agents/skills/`, `.claude/skills/` |