From 01578c0cd486df4746dac11e52086c9fbdfbe136 Mon Sep 17 00:00:00 2001 From: DietrichGebert Date: Mon, 15 Jun 2026 00:00:01 +0200 Subject: [PATCH] fix: honor CLAUDE_CONFIG_DIR in hooks (#37) ponytail-activate.js and ponytail-runtime.js hardcoded ~/.claude for the flag file and settings lookup, ignoring CLAUDE_CONFIG_DIR. Add a shared getClaudeDir() to ponytail-config.js and use it in both. Regression test added to hooks.test.js. Co-authored-by: Claude Opus 4.8 (1M context) --- hooks/ponytail-activate.js | 5 ++--- hooks/ponytail-config.js | 6 ++++++ hooks/ponytail-runtime.js | 4 ++-- tests/hooks.test.js | 25 +++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/hooks/ponytail-activate.js b/hooks/ponytail-activate.js index be0c1b1..9610721 100644 --- a/hooks/ponytail-activate.js +++ b/hooks/ponytail-activate.js @@ -8,8 +8,7 @@ const fs = require('fs'); const path = require('path'); -const os = require('os'); -const { getDefaultMode } = require('./ponytail-config'); +const { getDefaultMode, getClaudeDir } = require('./ponytail-config'); const { getPonytailInstructions } = require('./ponytail-instructions'); const { clearMode, @@ -18,7 +17,7 @@ const { writeHookOutput, } = require('./ponytail-runtime'); -const claudeDir = path.join(os.homedir(), '.claude'); +const claudeDir = getClaudeDir(); const settingsPath = path.join(claudeDir, 'settings.json'); const mode = getDefaultMode(); diff --git a/hooks/ponytail-config.js b/hooks/ponytail-config.js index 43dbcc1..f32fb7c 100644 --- a/hooks/ponytail-config.js +++ b/hooks/ponytail-config.js @@ -50,6 +50,11 @@ function getConfigPath() { return path.join(getConfigDir(), 'config.json'); } +function getClaudeDir() { + // ponytail: CLAUDE_CONFIG_DIR overrides ~/.claude, matching Claude Code. + return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); +} + function getDefaultMode() { // 1. Environment variable (highest priority) const envMode = process.env.PONYTAIL_DEFAULT_MODE; @@ -89,6 +94,7 @@ module.exports = { getDefaultMode, getConfigDir, getConfigPath, + getClaudeDir, normalizeMode, normalizeConfigMode, normalizePersistedMode, diff --git a/hooks/ponytail-runtime.js b/hooks/ponytail-runtime.js index a6adf8b..e7056bd 100644 --- a/hooks/ponytail-runtime.js +++ b/hooks/ponytail-runtime.js @@ -1,11 +1,11 @@ const fs = require('fs'); const path = require('path'); -const os = require('os'); +const { getClaudeDir } = require('./ponytail-config'); const isCodex = Boolean(process.env.PLUGIN_DATA); const statePath = isCodex ? path.join(process.env.PLUGIN_DATA, '.ponytail-active') - : path.join(os.homedir(), '.claude', '.ponytail-active'); + : path.join(getClaudeDir(), '.ponytail-active'); function setMode(mode) { fs.mkdirSync(path.dirname(statePath), { recursive: true }); diff --git a/tests/hooks.test.js b/tests/hooks.test.js index 072a1d9..bd1df7d 100644 --- a/tests/hooks.test.js +++ b/tests/hooks.test.js @@ -16,6 +16,10 @@ function run(script, env, input = '') { }); } +// Keep the base env clean so the default-dir checks are deterministic; the +// CLAUDE_CONFIG_DIR case sets it explicitly. +delete process.env.CLAUDE_CONFIG_DIR; + const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'ponytail-hooks-')); const home = path.join(temp, 'home'); const pluginData = path.join(temp, 'plugin-data'); @@ -74,5 +78,26 @@ assert.equal( 'full', ); +// CLAUDE_CONFIG_DIR overrides ~/.claude for the flag file (issue #34). +const home2 = path.join(temp, 'home2'); +fs.mkdirSync(home2, { recursive: true }); +const customConfigDir = path.join(temp, 'custom-claude'); +result = run('ponytail-activate.js', { + HOME: home2, + USERPROFILE: home2, + CLAUDE_CONFIG_DIR: customConfigDir, + PONYTAIL_DEFAULT_MODE: 'lite', +}); +assert.equal(result.status, 0, result.stderr); +assert.equal( + fs.readFileSync(path.join(customConfigDir, '.ponytail-active'), 'utf8'), + 'lite', +); +assert.equal( + fs.existsSync(path.join(home2, '.claude', '.ponytail-active')), + false, + 'flag must not land in ~/.claude when CLAUDE_CONFIG_DIR is set', +); + fs.rmSync(temp, { recursive: true, force: true }); console.log('hook compatibility checks passed');