232 lines
11 KiB
Markdown
232 lines
11 KiB
Markdown
# dsh — DeepSeek Harness adapter
|
||
|
||
Spike of the `claude-harness` port for DeepSeek Harness (DSH 0.2.1-rc.3).
|
||
The Claude Code installation in `~/.claude` stays untouched; this directory is a
|
||
separate Cordis bundle plus a thin install helper.
|
||
|
||
**What works in the spike:**
|
||
|
||
- `guard-main` / `require-plan` — the tested bash/Python hooks run through
|
||
`ctx.tools.guard`; the agent gets `BLOCKED` with the same messages as in
|
||
Claude Code;
|
||
- superpowers bootstrap — `using-superpowers` is injected once per session from
|
||
`agent/pre-step` and re-injected after compaction;
|
||
- roles — the model loads them with the `role` skill (`~/.dsh/roles`, then
|
||
`~/.claude/roles`, then repo `roles/`); `/role dev|admin|analyst|qa` remains
|
||
the user-facing command and injects the same text, without Claude's
|
||
auto-memory step;
|
||
- SDD — the `sdd` skill executes a plan through asynchronous `subagent`
|
||
dispatches (report files, task review, fix loop, ledger), with controller
|
||
modes for one plan and for several parallel plans; the optional overlay
|
||
enables model routes and depth 2.
|
||
- OpenSpec — воркфлоу идут проектными скиллами `.agents/skills/openspec-*`
|
||
(ставит `openspec init --tools agents`); глобальной копии нет, слэш-команд
|
||
нет. Установка в проект: `bash dsh/install.sh --openspec`.
|
||
|
||
- self-check — at activation the bundle verifies the DSH contract it depends on
|
||
and logs every missing service; the `dsh-harness-check` command re-runs the
|
||
same check on demand.
|
||
|
||
**Not yet:** `briefing`, the `harness-check` SessionStart hook, `role-hint`,
|
||
`harness-doctor`, auto-memory, caveman. See [PORT.md](PORT.md) for the design,
|
||
mapping table, and risks.
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
# Static check (offline; reports the configured pin and the current source)
|
||
bash dsh/install.sh --check
|
||
|
||
# Superpowers source. The default is a pinned clone: --skills clones or
|
||
# updates ~/.dsh/superpowers at $DSH_SUPERPOWERS_REF (default v7.0.1, fetched
|
||
# from $DSH_SUPERPOWERS_REPO), then runs sync + verify and copies the adapted
|
||
# skills. Offline it reuses the clone only when it is already on the ref.
|
||
bash dsh/install.sh --skills
|
||
# ...or sync a checkout directly (no network, no pin enforcement):
|
||
bash dsh/install.sh --skills --superpowers-from /path/to/superpowers
|
||
# ...or export DSH_SUPERPOWERS_SOURCE=/path/to/superpowers
|
||
|
||
# Global instructions, local role skill, and roles
|
||
# (run from a terminal; these write outside the session workspace)
|
||
bash dsh/install.sh --global
|
||
bash dsh/install.sh --roles
|
||
|
||
# В корне проекта, где уже есть openspec/: добавить agents-скиллы
|
||
bash <harness>/dsh/install.sh --openspec
|
||
|
||
# Install the bundle in DSH:
|
||
# Plugins -> install a bundle from dsh/bundle
|
||
# or ask the agent to call plugin_manager install_bundle with that target.
|
||
```
|
||
|
||
`--skills` resolves superpowers in this order: `--superpowers-from` →
|
||
`$DSH_SUPERPOWERS_SOURCE` → the pinned clone `~/.dsh/superpowers` at
|
||
`$DSH_SUPERPOWERS_REF` (default `v7.0.1`, cloned or updated from
|
||
`$DSH_SUPERPOWERS_REPO`; offline it is reused only when already on the ref) →
|
||
the Claude Code plugin cache (legacy, warns). Explicit sources are synced in
|
||
place with no network; they must be a checkout root or its `skills/`
|
||
directory — a bare skills directory outside that layout is a hard `FAIL` in
|
||
both `--skills` and `--check`. Explicit syncs stage into
|
||
`~/.dsh/.skills.synced`, never into `~/.dsh/superpowers` (reserved for the
|
||
pinned clone). Every sync runs `dsh/sync/sync-from-upstream.mjs` (DSH
|
||
adaptations) and `dsh/sync/verify-skills.mjs`; a tree that fails verification
|
||
or holds zero skills is never installed. Legacy sources are copied raw. A
|
||
clean HOME with no source and no clone fails with the clone command instead of
|
||
pretending success; the local `role` and `sdd` skills are copied either way.
|
||
|
||
Do not hand-edit `$DSH_HOME/profiles/desktop/package.json` or
|
||
`cordis.patch.yml`: the Desktop profile is Electron-managed and the plugin
|
||
manager owns those writes.
|
||
|
||
## Update
|
||
|
||
Upstream superpowers moves on its own schedule, so a pin bump is a deliberate
|
||
step, never a background pull (design D-2, cadence D-7):
|
||
|
||
1. One issue per `obra/superpowers` release (sync → tests → PR). Compare the
|
||
release tags with the pin in `dsh/install.sh`.
|
||
2. Bump `DSH_SUPERPOWERS_REF` in `dsh/install.sh` and the pin quoted in this
|
||
file and in `PORT.md`; then run the sync and the verifier by hand against a
|
||
checkout of the new tag:
|
||
`node dsh/sync/sync-from-upstream.mjs <checkout> --out <dir> --ref <tag>`,
|
||
then `node dsh/sync/verify-skills.mjs <dir>`. A changed transform anchor or a
|
||
changed skill set is a hard `FAIL`: adapt the transform or the expected set,
|
||
do not bypass it.
|
||
3. Reinstall and check: `bash dsh/install.sh --skills`, then
|
||
`bash dsh/install.sh --check`. The source line must name the new ref and the
|
||
synced version (`ref v7.0.2 (synced, version 7.0.2)`); `--check` prints the
|
||
configured pin next to the ref recorded by the last sync, so a pin that was
|
||
bumped without resyncing (or a sync that never ran) is visible instead of
|
||
silently old text.
|
||
4. `bash dsh/tests/run.sh` must stay green. The fixtures are a reduced upstream
|
||
tree, so a transform that stopped matching fails there first.
|
||
|
||
## Verify
|
||
|
||
1. In a new session, ask `what skills do you have?` — the superpowers catalog
|
||
should be visible.
|
||
2. In a repository on `main`, ask the agent to create a file — expect
|
||
`BLOCKED [guard-main]`.
|
||
3. Create a worktree on a feature branch, ask for an edit under `src/` — expect
|
||
`BLOCKED [require-plan]` until `.bounded` or a plan for the branch exists.
|
||
4. The model should load a role with `skill({name:"role"})`; `/role dev` is the
|
||
user-facing command with the same file. Expect the five-line role summary.
|
||
5. Prompt `Let's make a react todo list` — `brainstorming` must trigger before
|
||
any code.
|
||
6. In a project with `.agents/skills`, ask `what skills do you have?` — the six
|
||
`openspec-*` skills must be listed.
|
||
|
||
The injected bootstrap starts with a marker:
|
||
|
||
```text
|
||
<!-- DSH-HARNESS-BOOTSTRAP:<16-hex-hash> -->
|
||
```
|
||
|
||
After compaction the marker disappears from `session.surface.nodes` and the next
|
||
step injects a fresh copy.
|
||
|
||
## Tests
|
||
|
||
```bash
|
||
bash dsh/tests/run.sh
|
||
```
|
||
|
||
Pure Node; no DSH process is required. The guard tests create a throwaway git
|
||
repository and exercise the real `hooks/guard-main.sh` and
|
||
`hooks/require-plan.sh` through the adapter.
|
||
|
||
## Layout
|
||
|
||
```text
|
||
dsh/
|
||
bundle/
|
||
package.json DSH bundle manifest (dsh.bundle.patch)
|
||
cordis.patch.yml inserts the dsh-harness row
|
||
index.js ctx.tools.guard + agent/pre-step + /role
|
||
command-role.js /role command glue
|
||
command-check.js dsh-harness-check command glue
|
||
lib/guards.js DSH tool call -> claude-harness hook JSON
|
||
lib/bootstrap.js superpowers skill resolution, marker, dedupe
|
||
lib/role.js role layers and text (pure)
|
||
lib/selfcheck.js bundle contract self-check (pure)
|
||
dsh-tools.md superpowers tool mapping
|
||
hooks -> ../../hooks local install: tested hooks are reused, not copied
|
||
locale/{en,ru}.json
|
||
icon.svg
|
||
sync/ vendored sync machinery (Node builtins only)
|
||
sync-from-upstream.mjs pinned upstream -> adapted skills (asserted transforms)
|
||
verify-skills.mjs synced-tree validation (frontmatter, prefixes, links)
|
||
tests/
|
||
install.sh
|
||
skills/{role,sdd}/SKILL.md model-invocable skills installed into ~/.dsh
|
||
subagent-models.yml opt-in overlay: model routes + maxDepth 2
|
||
mcp.codegraph.yml optional CodeGraph MCP overlay
|
||
PORT.md design, mapping, risks, next phases
|
||
```
|
||
|
||
```text
|
||
В корне проекта (не в dsh/):
|
||
.agents/skills/openspec-* скиллы OpenSpec, генерирует CLI
|
||
```
|
||
|
||
## Configuration
|
||
|
||
The row is configurable from the profile patch; defaults target this
|
||
repository layout:
|
||
|
||
```yaml
|
||
- id: dsh-harness
|
||
config:
|
||
guards: true
|
||
bootstrap: true
|
||
roleCommand: true
|
||
selfCheck: true # activation check of the DSH contract; not-ok is logged
|
||
checkCommand: true # register the dsh-harness-check command
|
||
bootstrapSubagents: false
|
||
hooksDir: '' # empty -> bundle/hooks -> ../../hooks
|
||
rolesDir: '' # empty -> ~/.dsh/roles, legacy ~/.claude/roles, repo roles/
|
||
projectRolesDir: '' # empty -> <cwd>/.claude/roles
|
||
roleNames: [dev, admin, analyst, qa]
|
||
bootstrapSkillPath: '' # empty -> ~/.dsh/skills, then legacy Claude Code paths
|
||
toolMappingPath: '' # empty -> bundle/dsh-tools.md
|
||
bootstrapMaxBytes: 65536
|
||
```
|
||
|
||
## Known limits
|
||
|
||
- The guard adapter uses the repository's bash/Python hooks; if `python3` is
|
||
missing, the hooks fail open by design.
|
||
- `bundle/hooks` is a symlink for local-directory installation. A published
|
||
bundle needs a real copy of `hooks/` (already listed in `package.json`
|
||
`files`).
|
||
- `using-superpowers` is read from `~/.dsh/skills` after `--skills`. The
|
||
install-time source is `--superpowers-from`, `DSH_SUPERPOWERS_SOURCE`, or
|
||
the pinned clone `~/.dsh/superpowers` (`DSH_SUPERPOWERS_REF`, default
|
||
`v7.0.1`); Claude Code locations are a **legacy migration
|
||
fallback** and log one warning per process when used, so a partial Claude
|
||
Code rollback does not break the bootstrap. Set `bootstrapSkillPath` or
|
||
`DSH_SUPERPOWERS_SKILL` for another layout.
|
||
- Roles resolve `~/.dsh/roles` first; `~/.claude/roles` and the repository
|
||
`roles/` are fallbacks. After `--roles` the runtime never needs `~/.claude`.
|
||
- Role loading is model-driven through the `role` skill; there is no automatic
|
||
`agent/pre-step` injection. If the model skips the skill, use the `/role`
|
||
command or ask it to load the role explicitly.
|
||
- Model names in roles and superpowers (`sonnet`, `haiku`, `opus`) are not DSH
|
||
models. The `sdd` skill uses two live routes from the opt-in
|
||
`dsh/subagent-models.yml` overlay; without it every dispatch runs on the
|
||
session model.
|
||
- SDD on DSH is asynchronous, not a copy of Claude's `Task`. See the `sdd`
|
||
skill and `openspec/changes/dsh-sdd/`.
|
||
- OpenSpec-скиллы проект получает сам (`openspec init --tools agents` или
|
||
`--openspec`); обвязка ставит их только в проект, не в `~/.dsh`.
|
||
- The bundle self-checks the DSH contract it depends on at activation:
|
||
`ctx.tools.guard`, `ctx.on` for `agent/pre-step`, `ctx.inject` for commands,
|
||
`workingDirectory.get`. Each missing service is logged as
|
||
`[dsh-harness] self-check: <feature> not-ok: …`, and the feature whose
|
||
service is missing is skipped (guards, bootstrap, or commands — each gated
|
||
by its own check), so a DSH version without one of the services does not
|
||
crash the bundle; the remaining features keep working. The
|
||
`/dsh-harness-check` command re-runs the same check on demand (one
|
||
`ok` / `not-ok` line per feature). Disable with `selfCheck: false` /
|
||
`checkCommand: false`.
|