claude-git-hooks 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,145 @@
1
+ /**
2
+ * File: installation-diagnostics.js
3
+ * Purpose: Reusable error diagnostics and formatting utilities
4
+ *
5
+ * Key features:
6
+ * - Generic error formatting with installation diagnostics
7
+ * - Detects common installation issues
8
+ * - Provides actionable remediation steps
9
+ * - Extensible for future diagnostic checks
10
+ *
11
+ * Usage:
12
+ * import { formatError } from './installation-diagnostics.js';
13
+ *
14
+ * try {
15
+ * // ... operation that might fail
16
+ * } catch (error) {
17
+ * console.error(formatError('Presets not found'));
18
+ * process.exit(1);
19
+ * }
20
+ */
21
+
22
+ import fs from 'fs';
23
+ import path from 'path';
24
+ import { getRepoRoot } from './git-operations.js';
25
+
26
+ /**
27
+ * Gets installation diagnostics
28
+ * Why: Centralized logic to detect common installation issues
29
+ *
30
+ * Future enhancements:
31
+ * - Check file permissions
32
+ * - Verify Claude CLI installation
33
+ * - Check Node.js version compatibility
34
+ * - Validate .gitignore entries
35
+ * - Check hook file integrity
36
+ * - Verify template files exist
37
+ * - Check config.json validity
38
+ *
39
+ * @returns {Object} Diagnostic information
40
+ */
41
+ export const getInstallationDiagnostics = () => {
42
+ const diagnostics = {
43
+ currentDir: process.cwd(),
44
+ repoRoot: null,
45
+ isInRepoRoot: false,
46
+ claudeDirExists: false,
47
+ claudeDirPath: null,
48
+ presetsDirExists: false,
49
+ presetsDirPath: null,
50
+ gitHooksExists: false,
51
+ };
52
+
53
+ try {
54
+ diagnostics.repoRoot = getRepoRoot();
55
+ diagnostics.isInRepoRoot = diagnostics.currentDir === diagnostics.repoRoot;
56
+
57
+ diagnostics.claudeDirPath = path.join(diagnostics.repoRoot, '.claude');
58
+ diagnostics.claudeDirExists = fs.existsSync(diagnostics.claudeDirPath);
59
+
60
+ diagnostics.presetsDirPath = path.join(diagnostics.claudeDirPath, 'presets');
61
+ diagnostics.presetsDirExists = fs.existsSync(diagnostics.presetsDirPath);
62
+
63
+ const gitHooksPath = path.join(diagnostics.repoRoot, '.git', 'hooks');
64
+ diagnostics.gitHooksExists = fs.existsSync(gitHooksPath);
65
+ } catch (error) {
66
+ // Not in a git repository - diagnostics.repoRoot will be null
67
+ }
68
+
69
+ return diagnostics;
70
+ };
71
+
72
+ /**
73
+ * Formats error message with diagnostics and remediation steps
74
+ * Why: Provides consistent, actionable error messages across all errors
75
+ *
76
+ * @param {string} errorMessage - Description of what failed (e.g., "Presets not found", "Template file missing")
77
+ * @param {string[]} additionalContext - Optional additional context lines
78
+ * @returns {string} Formatted error message with diagnostics and remediation steps
79
+ */
80
+ export const formatError = (errorMessage, additionalContext = []) => {
81
+ const diagnostics = getInstallationDiagnostics();
82
+ const lines = [];
83
+
84
+ lines.push(`⚠️ ${errorMessage}`);
85
+ lines.push('');
86
+
87
+ // Add any additional context first
88
+ if (additionalContext.length > 0) {
89
+ lines.push(...additionalContext);
90
+ lines.push('');
91
+ }
92
+
93
+ // Diagnostic information
94
+ lines.push('Installation diagnostics:');
95
+ lines.push(` Current directory: ${diagnostics.currentDir}`);
96
+ if (diagnostics.repoRoot) {
97
+ lines.push(` Repository root: ${diagnostics.repoRoot}`);
98
+ lines.push(` .claude/ exists: ${diagnostics.claudeDirExists ? '✓' : '✗'}`);
99
+ lines.push(` presets/ exists: ${diagnostics.presetsDirExists ? '✓' : '✗'}`);
100
+ } else {
101
+ lines.push(` Repository root: [Not in a git repository]`);
102
+ }
103
+ lines.push('');
104
+
105
+ // Remediation steps based on detected issues
106
+ lines.push('Recommended solution:');
107
+ if (!diagnostics.repoRoot) {
108
+ lines.push(' Not in a git repository');
109
+ lines.push(' → Navigate to your repository and try again');
110
+ } else if (!diagnostics.claudeDirExists) {
111
+ lines.push(' claude-hooks not installed');
112
+ if (!diagnostics.isInRepoRoot) {
113
+ lines.push(` → cd ${diagnostics.repoRoot}`);
114
+ lines.push(' → claude-hooks install');
115
+ } else {
116
+ lines.push(' → claude-hooks install');
117
+ }
118
+ } else if (!diagnostics.isInRepoRoot) {
119
+ lines.push(' Running from subdirectory (may cause path issues)');
120
+ lines.push(` → cd ${diagnostics.repoRoot}`);
121
+ lines.push(' → claude-hooks uninstall');
122
+ lines.push(' → claude-hooks install');
123
+ } else if (!diagnostics.presetsDirExists) {
124
+ lines.push(' Incomplete installation (presets missing)');
125
+ lines.push(' → claude-hooks install --force');
126
+ } else {
127
+ lines.push(' Unknown issue detected');
128
+ lines.push(' → claude-hooks install --force');
129
+ }
130
+
131
+ return lines.join('\n');
132
+ };
133
+
134
+ /**
135
+ * Checks if installation appears healthy
136
+ * Why: Quick validation before operations that require full installation
137
+ *
138
+ * @returns {boolean} True if installation looks healthy
139
+ */
140
+ export const isInstallationHealthy = () => {
141
+ const diagnostics = getInstallationDiagnostics();
142
+ return diagnostics.claudeDirExists &&
143
+ diagnostics.presetsDirExists &&
144
+ diagnostics.gitHooksExists;
145
+ };
@@ -13,12 +13,14 @@
13
13
  * - path: Cross-platform path handling
14
14
  * - git-operations: For getRepoRoot()
15
15
  * - logger: Debug and error logging
16
+ * - installation-diagnostics: Error formatting with remediation steps
16
17
  */
17
18
 
18
19
  import fs from 'fs/promises';
19
20
  import path from 'path';
20
21
  import { getRepoRoot } from './git-operations.js';
21
22
  import logger from './logger.js';
23
+ import { formatError } from './installation-diagnostics.js';
22
24
 
23
25
  /**
24
26
  * Custom error for preset loading failures
@@ -189,7 +191,10 @@ export async function listPresets() {
189
191
  }
190
192
  }
191
193
  } catch (error) {
192
- logger.warning('No presets directory found. Run "claude-hooks install" first.');
194
+ const errorMsg = formatError('No presets directory found', [
195
+ `Expected location: ${presetsDir}`
196
+ ]);
197
+ logger.warning(errorMsg);
193
198
  logger.debug(
194
199
  'preset-loader - listPresets',
195
200
  'Failed to read presets directory',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
5
5
  "type": "module",
6
6
  "bin": {