fix(cc-config): stop pinning CLAUDE_CONFIG_DIR when CLAUDE_HOME is unset

runClaude() unconditionally set CLAUDE_CONFIG_DIR to getClaudeHome()
(~/.claude), so the CLI wrote ~/.claude/.claude.json — a file
cc-discovery.getClaudeJsonPath() never reads (it reads ~/.claude.json,
beside ~/.claude/, by design). Every default-install MCP add/remove and
plugin/marketplace mutation was inert: add toasted success and vanished on
refetch, remove 422'd for every server the panel lists.

With CLAUDE_HOME unset (the default), leave CLAUDE_CONFIG_DIR unset too —
the CLI's own default config dir already lines up with what cc-discovery
reads, so alignment comes from doing nothing. Only pin it when the operator
explicitly set CLAUDE_HOME, which is the one case the pin is meant for.
Left a residual, documented at both sites: with CLAUDE_HOME set, reads still
come from $HOME/.claude.json while the CLI writes
<CLAUDE_HOME>/.claude.json — a split predating this task, affecting a
configuration nobody here runs.

New tests pin the env contract directly (no real CLI needed): runClaude
must not set CLAUDE_CONFIG_DIR when CLAUDE_HOME is absent, and must set it
to CLAUDE_HOME's value when present.
This commit is contained in:
2026-09-09 09:48:28 +07:00
parent 6d27c115d8
commit 0ad4b8c920
3 changed files with 58 additions and 3 deletions
+36
View File
@@ -190,6 +190,42 @@ describe("cc-plugin-cli argv construction", () => {
});
});
describe("cc-plugin-cli CLAUDE_CONFIG_DIR pinning", () => {
// N1: pinning CLAUDE_CONFIG_DIR unconditionally to getClaudeHome() made the
// CLI write ~/.claude/.claude.json while cc-discovery reads ~/.claude.json
// — a different file, so every default-install add/remove was inert. The
// fix is to leave CLAUDE_CONFIG_DIR unset unless the operator explicitly
// set CLAUDE_HOME; this is the env contract that would have caught it.
const PRINT_ENV = "printf '%s\\n' \"CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR-<unset>}\"";
it("leaves CLAUDE_CONFIG_DIR unset when CLAUDE_HOME is not set", async () => {
const originalHome = process.env.CLAUDE_HOME;
delete process.env.CLAUDE_HOME;
const restore = stubClaude(PRINT_ENV);
try {
const result = await cli.marketplaceUpdate({ name: "m" });
assert.equal(result.stdout.trim(), "CLAUDE_CONFIG_DIR=<unset>");
} finally {
restore();
if (originalHome !== undefined) process.env.CLAUDE_HOME = originalHome;
}
});
it("pins CLAUDE_CONFIG_DIR to CLAUDE_HOME when the operator set it", async () => {
const originalHome = process.env.CLAUDE_HOME;
process.env.CLAUDE_HOME = "/tmp/some-other-claude-home";
const restore = stubClaude(PRINT_ENV);
try {
const result = await cli.marketplaceUpdate({ name: "m" });
assert.equal(result.stdout.trim(), "CLAUDE_CONFIG_DIR=/tmp/some-other-claude-home");
} finally {
restore();
if (originalHome === undefined) delete process.env.CLAUDE_HOME;
else process.env.CLAUDE_HOME = originalHome;
}
});
});
describe("cc-plugin-cli run outcomes", () => {
it("reports a non-zero exit as ok:false instead of throwing", async () => {
const restore = stubClaude('echo "✘ Failed to install plugin \\"nope\\"" >&2\nexit 1');