claude-harness/dsh/README.md

232 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`.