mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-20 18:16:30 +03:00
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:
co-authored by
Paul Bakaus
parent
f41300b3b2
commit
6ebc24ad66
@@ -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`.
|
||||
|
||||
@@ -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
@@ -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/` |
|
||||
|
||||
Reference in New Issue
Block a user