feat(installer): add DeepSeek Harness (dsh) target - #1591
Conversation
Adds DeepSeek Harness (dsh) to `codegraph install` / `codegraph uninstall` as a supported agent, alongside the existing targets. dsh does not use an `mcpServers` JSON shape: MCP servers are Cordis plugin rows (`@deepseek-ai/dsh-mcp-client`), declared in layered YAML patch files. The target writes one root `- insert:` patch entry into `$DSH_HOME/cordis.patch.yml` (default `~/.dsh`, `DSH_HOME` env override) — the home-level layer that applies to every dsh profile at once and is hot-reloaded by a running session. Global-only, like Codex and Hermes; no instructions file (the MCP initialize instructions stay the single source of truth, issue colbymchenry#529). Editor details (hand-rolled line editor, no new dependencies): - Appending into an existing `- insert:` block MATCHES that block's item indent, and a differing pre-existing codegraph entry is canonicalized in place at its own indent. js-yaml (which dsh uses to parse the file) rejects block sequences with mixed item indents, so a naive fixed-indent append can produce a file that aborts the dsh boot — the same bug class as the Hermes PyYAML-style fix (colbymchenry#456). - A lone `[]` line (the documented empty state) is REPLACED with the entry, not appended after; a comments-only file (which also fails to boot) is repaired; comments and sibling patch entries are preserved through every operation. - Entry detection requires both the `mcp-codegraph` id and the plugin name, so a user's id-targeted override patch row is never touched. - Uninstall reverses cleanly: siblings survive, a user comments-only remainder is restored to `[]`, and a file this installer created is deleted when only its stamped header remains. - Install migrates a pre-existing per-profile codegraph entry up to the home layer (a duplicate `serverName` across live plugin instances fails the later instance at load); uninstall sweeps every profile patch for symmetry. - Hand-written flow-style codegraph entries are detected as configured but left untouched (`Kept` + a pointer at `codegraph install --print-config dsh`), rather than duplicated. Wiring: `TargetId` union, registry (appended last), install/uninstall command descriptions, installer header comment, and the `~/.dsh` uninstall location hint. Tests: 22 dedicated dsh tests in `__tests__/installer-targets.test.ts` (indent-matching regression, `[]` round-trip, comments-only repair, sibling + override-patch preservation, per-profile migration and uninstall symmetry, flow-style kept paths, `DSH_HOME` precedence, CRLF) plus the parameterized contract suite's automatic coverage. Docs: CHANGELOG `[Unreleased]` entry, README agent lists with a `projectPath` note (dsh passes no workspace root to MCP servers), and the six docs-site agent lists including a manual-setup snippet.
# Conflicts: # CHANGELOG.md
…tups Borrowed from colbymchenry#1572 (verified against the dsh-mcp-client schema: `cwd` is a supported stdio field): the README dsh bullet and the dsh.ts module doc now explain that the agent passes `projectPath` (per the server's no-root-index guidance) OR the user can pin `cwd` / `--path` on the entry for a single-project setup. Also rebased onto main (includes colbymchenry#1551 project-local Codex, colbymchenry#1463 deprioritize, and the merged contributor batch).
|
Quick status update for review: this PR is now rebased onto the latest One small improvement was borrowed from #1572 with credit in Happy to coordinate with the #1572 author or adjust in whatever direction the maintainer prefers — the main deltas beyond #1572 are the three verified fixes listed in the description (indent-matched append — the fixed-indent append produces a mixed-indent file that fails loud at dsh boot with js-yaml, repro provided; |
What
Adds DeepSeek Harness (dsh) as a supported agent for
codegraph install/codegraph uninstall, alongside the existing targets.dsh does not use an
mcpServersJSON shape: MCP servers are Cordis plugin rows (@deepseek-ai/dsh-mcp-client), declared in layered YAML patch files. This target writes one root- insert:patch entry into$DSH_HOME/cordis.patch.yml(default~/.dsh,DSH_HOMEenv override) — the home-level layer that applies to every dsh profile at once and is hot-reloaded by a running session:Global-only (like Codex and Hermes); no instructions file, per the #529 policy. Because dsh passes no workspace root to MCP servers, the README/site entries carry a note pointing at
projectPath.Why this PR (relationship to #1572)
#1572 covers the same feature with the right architecture — this PR is an independent implementation that also fixes three defects found in its approach, with the same overall shape and file coverage otherwise:
- insert:block. dsh parsescordis.patch.ymlwith js-yaml, which rejects a block sequence with mixed item indents — so a user file using 2-space style becomes unbootable (loadOptionalPatchesfails loud at boot). Same bug class as the Hermes PyYAML-style fix (Bug:codegraph install --target hermescorruptsconfig.yaml— dropshermes-cliand breaks nested YAML indentation #456). This target detects the block''s item indent and appends/canonicalizes at that indent (regression test included).[]handling. feat(installer): DeepSeek Harness (dsh) as a supported agent #1572 appends after a lone[]line, producing invalid YAML;[]is dsh''s own documented empty state. This target replaces the token in place, preserving surrounding comments, and repairs comments-only files (which also fail to boot).mcp-codegraphid and the plugin name, so a user''s id-targeted override patch row is never treated as ours or clobbered.Also included: install migrates a pre-existing per-profile codegraph entry up to the home layer (a duplicate
serverNameacross live plugin instances fails the later instance at load) and uninstall sweeps profiles for symmetry; hand-written flow-style entries are detected as configured but left untouched (Kept+ a pointer atcodegraph install --print-config dsh) instead of being duplicated.Implementation
src/installer/targets/dsh.ts— hand-rolled CRLF-safe line editor in thehermes.tshouse style (no new dependencies); full uninstall state matrix (siblings survive, comments-only remainder restored to[], installer-created file deleted when only its stamped header remains).TargetIdunion, registry (appended last), install/uninstall command descriptions, installer header comment,~/.dshuninstall location hint.[Unreleased]entry; README agent lists (incl. aprojectPathnote and the live-reload restart caveat); the six docs-site agent lists plus a manual-setup snippet inreference/integrations.md. Site-list scope is deliberately DSH-only (the pre-existing Copilot lag there is left as-is).Testing
__tests__/installer-targets.test.ts(indent-matching regression,[]round-trip, comments-only repair, sibling + override-patch preservation, per-profile migration + uninstall symmetry, flow-style kept paths,DSH_HOMEprecedence, CRLF) on top of the parameterized contract suite''s automatic coverage: 245 passed / 3 platform skips / 0 failed; legacyinstaller.test.ts3/3.tscandnpm run buildclean; full suite shows no new failures vsmain(pre-existing Windows/EPERM flakes documented in CLAUDE.md).dsh --profile web --dump-configwith exactly one canonicalmcp-codegraphrow (the profile+home duplicate-serverNamecollision was reproduced pre-fix and verified swept post-install); the 2-space-style file parses with dsh''s own js-yaml dialect and boots; install/uninstall round-trips byte-cleanly on adversarial files (comments + sibling patches + id-override rows + CRLF).