Files
msd-core/hooks/gsd-check-update.js
Tibsfox 90e7d30839 fix(hooks): respect CLAUDE_CONFIG_DIR for custom config directories
Hooks hardcode ~/.claude/ as the config directory, breaking setups
where Claude Code uses a custom config directory (e.g. multi-account
with CLAUDE_CONFIG_DIR=~/.claude-personal/). The update check hook
shows stale notifications and the statusline reads from wrong paths.

- gsd-check-update.js: check CLAUDE_CONFIG_DIR before filesystem scan
- gsd-statusline.js: use CLAUDE_CONFIG_DIR for todos and cache paths

Closes #870

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 08:01:54 -06:00

82 lines
2.8 KiB
JavaScript
Executable File

#!/usr/bin/env node
// Check for GSD updates in background, write result to cache
// Called by SessionStart hook - runs once per session
const fs = require('fs');
const path = require('path');
const os = require('os');
const { spawn } = require('child_process');
const homeDir = os.homedir();
const cwd = process.cwd();
// Detect runtime config directory (supports Claude, OpenCode, Gemini)
// Respects CLAUDE_CONFIG_DIR for custom config directory setups
function detectConfigDir(baseDir) {
// Check env override first (supports multi-account setups)
const envDir = process.env.CLAUDE_CONFIG_DIR;
if (envDir && fs.existsSync(path.join(envDir, 'get-shit-done', 'VERSION'))) {
return envDir;
}
for (const dir of ['.config/opencode', '.opencode', '.gemini', '.claude']) {
if (fs.existsSync(path.join(baseDir, dir, 'get-shit-done', 'VERSION'))) {
return path.join(baseDir, dir);
}
}
return envDir || path.join(baseDir, '.claude');
}
const globalConfigDir = detectConfigDir(homeDir);
const projectConfigDir = detectConfigDir(cwd);
const cacheDir = path.join(globalConfigDir, 'cache');
const cacheFile = path.join(cacheDir, 'gsd-update-check.json');
// VERSION file locations (check project first, then global)
const projectVersionFile = path.join(projectConfigDir, 'get-shit-done', 'VERSION');
const globalVersionFile = path.join(globalConfigDir, 'get-shit-done', 'VERSION');
// Ensure cache directory exists
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
}
// Run check in background (spawn background process, windowsHide prevents console flash)
const child = spawn(process.execPath, ['-e', `
const fs = require('fs');
const { execSync } = require('child_process');
const cacheFile = ${JSON.stringify(cacheFile)};
const projectVersionFile = ${JSON.stringify(projectVersionFile)};
const globalVersionFile = ${JSON.stringify(globalVersionFile)};
// Check project directory first (local install), then global
let installed = '0.0.0';
try {
if (fs.existsSync(projectVersionFile)) {
installed = fs.readFileSync(projectVersionFile, 'utf8').trim();
} else if (fs.existsSync(globalVersionFile)) {
installed = fs.readFileSync(globalVersionFile, 'utf8').trim();
}
} catch (e) {}
let latest = null;
try {
latest = execSync('npm view get-shit-done-cc version', { encoding: 'utf8', timeout: 10000, windowsHide: true }).trim();
} catch (e) {}
const result = {
update_available: latest && installed !== latest,
installed,
latest: latest || 'unknown',
checked: Math.floor(Date.now() / 1000)
};
fs.writeFileSync(cacheFile, JSON.stringify(result));
`], {
stdio: 'ignore',
windowsHide: true,
detached: true // Required on Windows for proper process detachment
});
child.unref();