fix(setup): resolve DeepSeek Harness home overrides (#2461)

This commit is contained in:
Guangqi Zhang 2026-10-13 20:05:31 +08:00 committed by GitHub
parent c95315d2ce
commit 1dd6c2dff9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 190 additions and 11 deletions

View File

@ -209,8 +209,15 @@ replaces) the REA entry in the home patch `$DSH_HOME/cordis.patch.yml`
patches and comments. `rea doctor --client dsh` checks that same entry id.
`rea uninstall` removes it by scanning supported clients; uninstall takes no
`--client` flag. The home patch applies to every Harness profile, so the
registration is written once and no per-profile edit is needed. The written
entry is:
registration is written once and no per-profile edit is needed.
Setup, doctor and uninstall resolve `DSH_HOME` and `DSH_AGENTS_HOME` as Harness
does: unset, empty or whitespace-only values use `~/.dsh` and `~/.agents`,
respectively. Bare `~`, `~/` and `~\` expand to the user home; other relative
values resolve against the command's working directory. Nonblank values retain
their original spaces, and the agents root is independent of the Harness home.
Run REA and Harness from the same working directory when using relative overrides.
The written entry is:
```yaml
- insert:

View File

@ -157,8 +157,27 @@ const qwenCodeDirectory = ({ home, env }: ClientPathContext): string => {
const grokDirectory = ({ home, env }: ClientPathContext): string =>
env.GROK_HOME || join(home, ".grok");
/** Match Harness home-paths for both independent roots; trim only to detect blanks. */
const dshDirectory = (
home: string,
override: string | undefined,
fallback: string,
): string => {
const selected =
override !== undefined && override.trim().length > 0
? override
: join(home, fallback);
const expanded =
selected === "~"
? home
: selected.startsWith("~/") || selected.startsWith("~\\")
? join(home, selected.slice(2))
: selected;
return resolve(expanded);
};
const dshHomeDirectory = ({ home, env }: ClientPathContext): string =>
env.DSH_HOME ?? join(home, ".dsh");
dshDirectory(home, env.DSH_HOME, ".dsh");
const dshPatchDirectory = (context: ClientPathContext): string =>
join(dshHomeDirectory(context), "cordis.patch.yml");
@ -555,7 +574,7 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
// The home Cordis patch applies to every profile; the skill directory is
// shared agent storage Harness discovers.
skillPath: ({ home, env }: ClientPathContext) =>
join(env.DSH_AGENTS_HOME ?? join(home, ".agents"), "skills"),
join(dshDirectory(home, env.DSH_AGENTS_HOME, ".agents"), "skills"),
configPath: dshPatchDirectory,
markerPath: dshHomeDirectory,
format: "dsh",

View File

@ -1,5 +1,5 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { dirname, join } from "node:path";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { expect } from "vitest";
@ -8,6 +8,111 @@ import { z } from "zod";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest.for([
{
label: "blank defaults",
harness: "",
agents: " \t ",
dshRoot: "home/.dsh",
agentsRoot: "home/.agents",
},
{
label: "tilde roots",
harness: "~/selected harness",
agents: "~\\shared agents",
dshRoot: "home/selected harness",
agentsRoot: "home/shared agents",
},
{
label: "relative roots",
harness: "./selected harness",
agents: "../shared agents",
dshRoot: "workspace/selected harness",
agentsRoot: "shared agents",
},
])(
"DSH CLI uses Harness path semantics through setup, doctor and uninstall for $label",
async ({ harness, agents, dshRoot, agentsRoot }, { cli }) => {
const root = await createTestTempDirectory("rea-dsh-paths-");
const home = join(root, "home");
const cwd = join(root, "workspace");
const config = join(root, dshRoot, "cordis.patch.yml");
const skillRoot = join(
root,
agentsRoot,
"skills",
"reverse-engineer-anything",
);
const skill = join(skillRoot, "SKILL.md");
await mkdir(home);
await mkdir(cwd);
await mkdir(dirname(config), { recursive: true });
const original =
"# caller patch\n- insert: [{id: caller, name: caller-plugin}]\n";
await writeFile(config, original);
const run = (args: readonly string[]) =>
cli.run({
arguments: [...args, "--json"],
cwd,
environment: {
HOME: home,
USERPROFILE: home,
DSH_HOME: harness,
DSH_AGENTS_HOME: agents,
},
});
const planned = await run(["setup", "--client", "dsh", "--dry-run"]);
expect(planned.exitCode).toBe(0);
expect(planned.json).toMatchObject({
status: "planned",
appliedActions: [],
plannedActions: expect.arrayContaining([
expect.objectContaining({ id: "configure_client:dsh", target: config }),
expect.objectContaining({ kind: "install_skill", target: skillRoot }),
]),
});
expect(await readFile(config, "utf8")).toBe(original);
await expect(readFile(skill)).rejects.toMatchObject({ code: "ENOENT" });
const configured = await run(["setup", "--client", "dsh", "--yes"]);
expect(configured.exitCode).toBe(0);
expect(configured.json).toMatchObject({
status: "ready",
clients: { dsh: { status: "configured" } },
});
const text = await readFile(config, "utf8");
expect(text).toContain("# caller patch");
expect(text).toContain("rea-mcp");
expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original);
expect(await readFile(skill, "utf8")).toContain(
"name: reverse-engineer-anything",
);
const doctor = await run(["doctor", "--client", "dsh", "--skill"]);
expect(doctor.exitCode).toBe(0);
expect(doctor.json).toMatchObject({ healthy: true });
const repeated = await run(["setup", "--client", "dsh", "--yes"]);
expect(repeated.exitCode).toBe(0);
expect(repeated.json).toMatchObject({
status: "ready",
plannedActions: [],
appliedActions: [],
});
expect(await readFile(config, "utf8")).toBe(text);
const removed = await run(["uninstall"]);
expect(removed.exitCode).toBe(0);
expect(removed.json).toMatchObject({
status: "complete",
items: expect.arrayContaining([
expect.objectContaining({ name: "dsh", status: "removed" }),
]),
});
expect(parse(await readFile(config, "utf8"))).toEqual([
{ insert: [{ id: "caller", name: "caller-plugin" }] },
]);
expect(await readFile(`${config}.rea.backup`, "utf8")).toBe(original);
await expect(readFile(skill)).rejects.toMatchObject({ code: "ENOENT" });
},
);
cliTest(
"DSH CLI previews, approves, repairs, launches MCP, repeats and uninstalls while preserving caller YAML",
async ({ cli }) => {

View File

@ -1,5 +1,5 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { join, resolve } from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
@ -37,28 +37,76 @@ const dshClient = (
env: Parameters<typeof supportedClients>[2],
platform: NodeJS.Platform = "linux",
) =>
supportedClients(join("/home/a"), platform, env).find(
supportedClients(resolve("/home/a"), platform, env).find(
({ name }) => name === "dsh",
);
describe("DeepSeek Harness configuration paths", () => {
it.each([
[undefined, ".dsh", ".agents", "shared"],
["", ".dsh", ".agents", "shared"],
[" \t ", ".dsh", ".agents", "shared"],
["~", "", "", "dsh"],
["~/selected home", "selected home", "selected home", "dsh"],
["~\\selected home", "selected home", "selected home", "dsh"],
] as const)(
"resolves Harness and agents roots for %j",
(override, dshRoot, agentsRoot, client) => {
const home = resolve("/home/a");
const environment = { DSH_HOME: override, DSH_AGENTS_HOME: override };
expect(
supportedClients(home, process.platform, environment).find(
({ name }) => name === "dsh",
),
).toMatchObject({
configPath: join(home, dshRoot, "cordis.patch.yml"),
markerPath: join(home, dshRoot),
});
expect(clientSkillDirectories(home, ["dsh"], environment)).toEqual([
{ client, directory: join(home, agentsRoot, "skills") },
]);
},
);
it.each(["relative home", " literal home ", "~someone"])(
"resolves a literal root %j relative to cwd without trimming it",
(override) => {
const home = resolve("/home/a");
const root = resolve(override);
const environment = { DSH_HOME: override, DSH_AGENTS_HOME: override };
expect(
supportedClients(home, process.platform, environment).find(
({ name }) => name === "dsh",
),
).toMatchObject({
configPath: join(root, "cordis.patch.yml"),
markerPath: root,
});
expect(clientSkillDirectories(home, ["dsh"], environment)).toEqual([
{ client: "dsh", directory: join(root, "skills") },
]);
},
);
it("resolves the home Cordis patch under the Harness home", () => {
expect(dshClient({})).toEqual({
name: "dsh",
displayName: "DeepSeek Harness",
configPath: join("/home/a", ".dsh", "cordis.patch.yml"),
markerPath: join("/home/a", ".dsh"),
configPath: resolve("/home/a", ".dsh", "cordis.patch.yml"),
markerPath: resolve("/home/a", ".dsh"),
format: "dsh",
});
// The home patch applies to every profile; DSH_HOME relocates it.
expect(dshClient({ DSH_HOME: "/custom/harness" })?.configPath).toBe(
join("/custom/harness", "cordis.patch.yml"),
resolve("/custom/harness", "cordis.patch.yml"),
);
expect(
clientSkillDirectories(join("/home/a"), ["dsh"], {
DSH_AGENTS_HOME: "/custom/agents",
}),
).toEqual([{ client: "dsh", directory: join("/custom/agents", "skills") }]);
).toEqual([
{ client: "dsh", directory: resolve("/custom/agents", "skills") },
]);
});
});