claude-git-hooks
Version:
Git hooks with Claude CLI for code analysis and automatic commit messages
146 lines (131 loc) • 5.06 kB
JavaScript
/**
* File: installation-diagnostics.js
* Purpose: Reusable error diagnostics and formatting utilities
*
* Key features:
* - Generic error formatting with installation diagnostics
* - Detects common installation issues
* - Provides actionable remediation steps
* - Extensible for future diagnostic checks
*
* Usage:
* import { formatError } from './installation-diagnostics.js';
*
* try {
* // ... operation that might fail
* } catch (error) {
* console.error(formatError('Presets not found'));
* process.exit(1);
* }
*/
import fs from 'fs';
import path from 'path';
import { getRepoRoot } from './git-operations.js';
/**
* Gets installation diagnostics
* Why: Centralized logic to detect common installation issues
*
* Future enhancements:
* - Check file permissions
* - Verify Claude CLI installation
* - Check Node.js version compatibility
* - Validate .gitignore entries
* - Check hook file integrity
* - Verify template files exist
* - Check config.json validity
*
* @returns {Object} Diagnostic information
*/
export const getInstallationDiagnostics = () => {
const diagnostics = {
currentDir: process.cwd(),
repoRoot: null,
isInRepoRoot: false,
claudeDirExists: false,
claudeDirPath: null,
presetsDirExists: false,
presetsDirPath: null,
gitHooksExists: false,
};
try {
diagnostics.repoRoot = getRepoRoot();
diagnostics.isInRepoRoot = diagnostics.currentDir === diagnostics.repoRoot;
diagnostics.claudeDirPath = path.join(diagnostics.repoRoot, '.claude');
diagnostics.claudeDirExists = fs.existsSync(diagnostics.claudeDirPath);
diagnostics.presetsDirPath = path.join(diagnostics.claudeDirPath, 'presets');
diagnostics.presetsDirExists = fs.existsSync(diagnostics.presetsDirPath);
const gitHooksPath = path.join(diagnostics.repoRoot, '.git', 'hooks');
diagnostics.gitHooksExists = fs.existsSync(gitHooksPath);
} catch (error) {
// Not in a git repository - diagnostics.repoRoot will be null
}
return diagnostics;
};
/**
* Formats error message with diagnostics and remediation steps
* Why: Provides consistent, actionable error messages across all errors
*
* @param {string} errorMessage - Description of what failed (e.g., "Presets not found", "Template file missing")
* @param {string[]} additionalContext - Optional additional context lines
* @returns {string} Formatted error message with diagnostics and remediation steps
*/
export const formatError = (errorMessage, additionalContext = []) => {
const diagnostics = getInstallationDiagnostics();
const lines = [];
lines.push(`⚠️ ${errorMessage}`);
lines.push('');
// Add any additional context first
if (additionalContext.length > 0) {
lines.push(...additionalContext);
lines.push('');
}
// Diagnostic information
lines.push('Installation diagnostics:');
lines.push(` Current directory: ${diagnostics.currentDir}`);
if (diagnostics.repoRoot) {
lines.push(` Repository root: ${diagnostics.repoRoot}`);
lines.push(` .claude/ exists: ${diagnostics.claudeDirExists ? '✓' : '✗'}`);
lines.push(` presets/ exists: ${diagnostics.presetsDirExists ? '✓' : '✗'}`);
} else {
lines.push(` Repository root: [Not in a git repository]`);
}
lines.push('');
// Remediation steps based on detected issues
lines.push('Recommended solution:');
if (!diagnostics.repoRoot) {
lines.push(' Not in a git repository');
lines.push(' → Navigate to your repository and try again');
} else if (!diagnostics.claudeDirExists) {
lines.push(' claude-hooks not installed');
if (!diagnostics.isInRepoRoot) {
lines.push(` → cd ${diagnostics.repoRoot}`);
lines.push(' → claude-hooks install');
} else {
lines.push(' → claude-hooks install');
}
} else if (!diagnostics.isInRepoRoot) {
lines.push(' Running from subdirectory (may cause path issues)');
lines.push(` → cd ${diagnostics.repoRoot}`);
lines.push(' → claude-hooks uninstall');
lines.push(' → claude-hooks install');
} else if (!diagnostics.presetsDirExists) {
lines.push(' Incomplete installation (presets missing)');
lines.push(' → claude-hooks install --force');
} else {
lines.push(' Unknown issue detected');
lines.push(' → claude-hooks install --force');
}
return lines.join('\n');
};
/**
* Checks if installation appears healthy
* Why: Quick validation before operations that require full installation
*
* @returns {boolean} True if installation looks healthy
*/
export const isInstallationHealthy = () => {
const diagnostics = getInstallationDiagnostics();
return diagnostics.claudeDirExists &&
diagnostics.presetsDirExists &&
diagnostics.gitHooksExists;
};