claude-git-hooks 2.17.0 → 2.17.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.
package/CHANGELOG.md CHANGED
@@ -5,23 +5,37 @@ Todos los cambios notables en este proyecto se documentarán en este archivo.
5
5
  El formato está basado en [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.17.1] - 2026-03-02
9
+
10
+ ### 🐛 Fixed
11
+
12
+ - Installer no longer crashes with `ENOTDIR` when running inside a Git worktree where `.git` is a file pointer instead of a directory
13
+ - Hook management commands (`enable`, `disable`, `status`, `uninstall`) now correctly resolve the hooks path in worktree repos
14
+
15
+ ### 🔧 Changed
16
+
17
+ - Extracted `getGitHooksPath()` to `helpers.js` — uses `git rev-parse --git-common-dir` to find the shared hooks directory in both regular repos and worktrees
18
+
8
19
  ## [2.17.0] - 2026-02-16
9
20
 
10
21
  ### ✨ Added
22
+
11
23
  - Automatic GitHub token setup now runs after installation
12
24
 
13
25
  ### 🔧 Changed
26
+
14
27
  - Simplified CLI help output to show concise command reference instead of verbose documentation
15
28
  - Commands in help output are now listed in alphabetical order for easier navigation
16
29
  - Installation success message now shows single help reference instead of detailed usage examples
17
30
 
18
31
  ### 🗑️ Removed
19
- - Removed extensive inline documentation from help output (use README for detailed docs)
20
32
 
33
+ - Removed extensive inline documentation from help output (use README for detailed docs)
21
34
 
22
35
  ## [2.16.0] - 2026-02-15
23
36
 
24
37
  ### ✨ Added
38
+
25
39
  - Multi-file version discovery with recursive scanning up to 3 levels deep
26
40
  - Per-file version editing with `--interactive` flag for advanced control
27
41
  - Support for additional project types: build.gradle, build.gradle.kts, pyproject.toml, Cargo.toml, version.sbt
@@ -32,22 +46,25 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
32
46
  - New UI components: `promptToggleList()` and `promptEditField()` in interactive-ui.js
33
47
 
34
48
  ### 🔧 Changed
49
+
35
50
  - Refactored version-manager.js to use new discovery-based API with `discoverVersionFiles()`, `updateVersionFiles()`, and `modifySuffix()`
36
51
  - Bump-version workflow now supports selecting specific files to update
37
52
  - Each file can have an independent target version when using per-file editing mode
38
53
  - Updated CLAUDE.md documentation with new version bump flow and API changes
39
54
 
40
55
  ### ⚠️ Deprecated
41
- - Legacy version-manager functions `detectProjectType()`, `getCurrentVersion()`, `updateVersion()`, and `getDiscoveredPaths()` replaced by new discovery API
42
56
 
57
+ - Legacy version-manager functions `detectProjectType()`, `getCurrentVersion()`, `updateVersion()`, and `getDiscoveredPaths()` replaced by new discovery API
43
58
 
44
59
  ## [2.15.5] - 2026-02-12
45
60
 
46
61
  ### ✨ Added
62
+
47
63
  - Added comprehensive CI-safe integration test suite that runs without Claude CLI dependency
48
64
  - Added new test scripts: `test:integration:ci` for CI environments and `test:changed` for testing only modified files
49
65
 
50
66
  ### 🔧 Changed
67
+
51
68
  - Optimized CI matrix to run full OS/Node.js combinations only on push events, reducing PR build times
52
69
  - Refactored integration tests to verify installation workflow, hook lifecycle, config persistence, and CLI routing
53
70
  - Improved timeout handling in Claude CLI client by clearing timeouts consistently on all exit paths
@@ -55,35 +72,37 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
55
72
  - Reduced Jest verbosity and coverage reporters for cleaner CI output
56
73
 
57
74
  ### 🐛 Fixed
75
+
58
76
  - Fixed potential timeout leak in claude-client.js where timeouts were not cleared on error or stdin failures
59
77
 
60
78
  ### 🗑️ Removed
61
- - Removed unused variables and parameters across multiple modules (analyze-diff, create-pr, install, config, changelog-generator, claude-client, claude-diagnostics, file-utils, git-operations, prompt-builder)
62
79
 
80
+ - Removed unused variables and parameters across multiple modules (analyze-diff, create-pr, install, config, changelog-generator, claude-client, claude-diagnostics, file-utils, git-operations, prompt-builder)
63
81
 
64
82
  ## [2.15.4] - 2026-02-11
65
83
 
66
84
  ### ✨ Added
85
+
67
86
  - Added comprehensive unit tests for claude-diagnostics.js covering error detection, formatting, and recovery classification
68
87
  - Added unit tests for file-utils.js covering directory creation and file writing operations
69
88
 
70
-
71
89
  ## [2.15.3] - 2026-02-10
72
90
 
73
91
  ### ✨ Added
92
+
74
93
  - Unit tests for `generate-changelog.js` command covering CHANGELOG generation, version detection, and error handling
75
94
  - Unit tests for `github-client.js` covering CODEOWNERS parsing, reviewer detection, and GitHub repo parsing
76
95
  - Unit tests for `hooks.js` command covering enable, disable, status, and uninstall operations
77
96
 
78
-
79
97
  ## [2.15.2] - 2026-02-10
80
98
 
81
99
  ### 🔧 Changed
100
+
82
101
  - The bump-version command now uses the configured default branch (from github.pr.defaultBase) in the 'Next steps' output instead of hardcoded 'main'
83
102
 
84
103
  ### 🐛 Fixed
85
- - Fixed cross-platform path assertions in unit tests to use partial matching instead of exact paths, improving Windows compatibility
86
104
 
105
+ - Fixed cross-platform path assertions in unit tests to use partial matching instead of exact paths, improving Windows compatibility
87
106
 
88
107
  ## [2.15.1] - 2026-02-10
89
108
 
@@ -95,6 +95,26 @@ export function getTemplatesPath() {
95
95
  return path.join(__dirname, '..', '..', 'templates');
96
96
  }
97
97
 
98
+ /**
99
+ * Get the git hooks directory, correctly handling worktrees.
100
+ * In a worktree, hooks live in the main repo's .git/hooks, not the worktree's .git file.
101
+ * Uses `git rev-parse --git-common-dir` to find the shared git directory.
102
+ * @returns {string} Path to the git hooks directory
103
+ */
104
+ export function getGitHooksPath() {
105
+ try {
106
+ let gitCommonDir = execSync('git rev-parse --git-common-dir', { encoding: 'utf8' }).trim();
107
+ // Handle Windows paths when running under WSL (e.g. C:\... -> /mnt/c/...)
108
+ if (/^[A-Za-z]:/.test(gitCommonDir)) {
109
+ gitCommonDir = gitCommonDir.replace(/^([A-Za-z]):/, (_, drive) => `/mnt/${drive.toLowerCase()}`);
110
+ gitCommonDir = gitCommonDir.replace(/\\/g, '/');
111
+ }
112
+ return path.join(gitCommonDir, 'hooks');
113
+ } catch (e) {
114
+ return path.join('.git', 'hooks');
115
+ }
116
+ }
117
+
98
118
  /**
99
119
  * Detect if running on Windows
100
120
  * Why: Need to use 'wsl claude' instead of 'claude' on Windows
@@ -10,7 +10,8 @@ import {
10
10
  success,
11
11
  info,
12
12
  warning,
13
- checkGitRepo
13
+ checkGitRepo,
14
+ getGitHooksPath
14
15
  } from './helpers.js';
15
16
 
16
17
  /**
@@ -22,11 +23,12 @@ export function runEnable(hookName) {
22
23
  error('You are not in a Git repository.');
23
24
  }
24
25
 
26
+ const hooksDir = getGitHooksPath();
25
27
  const hooks = hookName ? [hookName] : ['pre-commit', 'prepare-commit-msg'];
26
28
 
27
29
  hooks.forEach(hook => {
28
- const disabledPath = `.git/hooks/${hook}.disabled`;
29
- const enabledPath = `.git/hooks/${hook}`;
30
+ const disabledPath = path.join(hooksDir, `${hook}.disabled`);
31
+ const enabledPath = path.join(hooksDir, hook);
30
32
 
31
33
  if (fs.existsSync(disabledPath)) {
32
34
  fs.renameSync(disabledPath, enabledPath);
@@ -48,11 +50,12 @@ export function runDisable(hookName) {
48
50
  error('You are not in a Git repository.');
49
51
  }
50
52
 
53
+ const hooksDir = getGitHooksPath();
51
54
  const hooks = hookName ? [hookName] : ['pre-commit', 'prepare-commit-msg'];
52
55
 
53
56
  hooks.forEach(hook => {
54
- const enabledPath = `.git/hooks/${hook}`;
55
- const disabledPath = `.git/hooks/${hook}.disabled`;
57
+ const enabledPath = path.join(hooksDir, hook);
58
+ const disabledPath = path.join(hooksDir, `${hook}.disabled`);
56
59
 
57
60
  if (fs.existsSync(enabledPath)) {
58
61
  fs.renameSync(enabledPath, disabledPath);
@@ -75,10 +78,11 @@ export function runStatus() {
75
78
 
76
79
  info('Claude Git Hooks status:\n');
77
80
 
81
+ const hooksDir = getGitHooksPath();
78
82
  const hooks = ['pre-commit', 'prepare-commit-msg'];
79
83
  hooks.forEach(hook => {
80
- const enabledPath = `.git/hooks/${hook}`;
81
- const disabledPath = `.git/hooks/${hook}.disabled`;
84
+ const enabledPath = path.join(hooksDir, hook);
85
+ const disabledPath = path.join(hooksDir, `${hook}.disabled`);
82
86
 
83
87
  if (fs.existsSync(enabledPath)) {
84
88
  success(`${hook}: enabled`);
@@ -135,7 +139,7 @@ export function runUninstall() {
135
139
 
136
140
  info('Uninstalling Claude Git Hooks...');
137
141
 
138
- const hooksPath = '.git/hooks';
142
+ const hooksPath = getGitHooksPath();
139
143
  const hooks = ['pre-commit', 'prepare-commit-msg'];
140
144
 
141
145
  hooks.forEach(hook => {
@@ -23,6 +23,7 @@ import {
23
23
  warning,
24
24
  checkGitRepo,
25
25
  getTemplatesPath,
26
+ getGitHooksPath,
26
27
  isWindows,
27
28
  getClaudeCommand,
28
29
  getPackageJson,
@@ -402,7 +403,7 @@ export async function runInstall(args) {
402
403
  await checkAndInstallDependencies(skipAuth);
403
404
 
404
405
  const templatesPath = getTemplatesPath();
405
- const hooksPath = '.git/hooks';
406
+ const hooksPath = getGitHooksPath();
406
407
 
407
408
  // Create hooks directory if it doesn't exist
408
409
  if (!fs.existsSync(hooksPath)) {
@@ -22,6 +22,7 @@
22
22
  import fs from 'fs';
23
23
  import path from 'path';
24
24
  import { getRepoRoot } from './git-operations.js';
25
+ import { getGitHooksPath } from '../commands/helpers.js';
25
26
 
26
27
  /**
27
28
  * Gets installation diagnostics
@@ -60,7 +61,7 @@ export const getInstallationDiagnostics = () => {
60
61
  diagnostics.presetsDirPath = path.join(diagnostics.claudeDirPath, 'presets');
61
62
  diagnostics.presetsDirExists = fs.existsSync(diagnostics.presetsDirPath);
62
63
 
63
- const gitHooksPath = path.join(diagnostics.repoRoot, '.git', 'hooks');
64
+ const gitHooksPath = getGitHooksPath();
64
65
  diagnostics.gitHooksExists = fs.existsSync(gitHooksPath);
65
66
  } catch (error) {
66
67
  // Not in a git repository - diagnostics.repoRoot will be null
package/package.json CHANGED
@@ -1,68 +1,68 @@
1
- {
2
- "name": "claude-git-hooks",
3
- "version": "2.17.0",
4
- "description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
5
- "type": "module",
6
- "bin": {
7
- "claude-hooks": "./bin/claude-hooks"
8
- },
9
- "scripts": {
10
- "test": "npm run test:all",
11
- "test:all": "npm run lint && npm run test:smoke && npm run test:unit && npm run test:integration",
12
- "test:smoke": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/smoke --maxWorkers=1",
13
- "test:unit": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/unit --forceExit",
14
- "test:integration": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/integration --maxWorkers=1 --testTimeout=30000 --forceExit",
15
- "test:integration:ci": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/integration/ci-safe.test.js --maxWorkers=1 --testTimeout=30000 --forceExit",
16
- "test:changed": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/unit --changedSince=main --forceExit",
17
- "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
18
- "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
19
- "lint": "eslint lib/ bin/claude-hooks",
20
- "lint:fix": "eslint lib/ bin/claude-hooks --fix",
21
- "format": "prettier --write \"lib/**/*.js\" \"bin/**\" \"test/**/*.js\"",
22
- "precommit": "npm run lint && npm run test:smoke",
23
- "prepublishOnly": "npm run test:all"
24
- },
25
- "keywords": [
26
- "git",
27
- "hooks",
28
- "claude",
29
- "ai",
30
- "code-review",
31
- "commit-messages",
32
- "pre-commit",
33
- "automation"
34
- ],
35
- "author": "Pablo Rovito",
36
- "license": "MIT",
37
- "repository": {
38
- "type": "git",
39
- "url": "https://github.com/pablorovito/claude-git-hooks.git"
40
- },
41
- "engines": {
42
- "node": ">=16.9.0"
43
- },
44
- "engineStrict": false,
45
- "os": [
46
- "darwin",
47
- "linux",
48
- "win32"
49
- ],
50
- "preferGlobal": true,
51
- "files": [
52
- "bin/",
53
- "lib/",
54
- "templates/",
55
- "README.md",
56
- "CHANGELOG.md",
57
- "LICENSE"
58
- ],
59
- "dependencies": {
60
- "@octokit/rest": "^21.0.0"
61
- },
62
- "devDependencies": {
63
- "@types/jest": "^29.5.0",
64
- "eslint": "^8.57.0",
65
- "jest": "^29.7.0",
66
- "prettier": "^3.2.0"
67
- }
68
- }
1
+ {
2
+ "name": "claude-git-hooks",
3
+ "version": "2.17.1",
4
+ "description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
5
+ "type": "module",
6
+ "bin": {
7
+ "claude-hooks": "./bin/claude-hooks"
8
+ },
9
+ "scripts": {
10
+ "test": "npm run test:all",
11
+ "test:all": "npm run lint && npm run test:smoke && npm run test:unit && npm run test:integration",
12
+ "test:smoke": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/smoke --maxWorkers=1",
13
+ "test:unit": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/unit --forceExit",
14
+ "test:integration": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/integration --maxWorkers=1 --testTimeout=30000 --forceExit",
15
+ "test:integration:ci": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/integration/ci-safe.test.js --maxWorkers=1 --testTimeout=30000 --forceExit",
16
+ "test:changed": "node --experimental-vm-modules node_modules/jest/bin/jest.js test/unit --changedSince=main --forceExit",
17
+ "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch",
18
+ "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
19
+ "lint": "eslint lib/ bin/claude-hooks",
20
+ "lint:fix": "eslint lib/ bin/claude-hooks --fix",
21
+ "format": "prettier --write \"lib/**/*.js\" \"bin/**\" \"test/**/*.js\"",
22
+ "precommit": "npm run lint && npm run test:smoke",
23
+ "prepublishOnly": "npm run test:all"
24
+ },
25
+ "keywords": [
26
+ "git",
27
+ "hooks",
28
+ "claude",
29
+ "ai",
30
+ "code-review",
31
+ "commit-messages",
32
+ "pre-commit",
33
+ "automation"
34
+ ],
35
+ "author": "Pablo Rovito",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/pablorovito/claude-git-hooks.git"
40
+ },
41
+ "engines": {
42
+ "node": ">=16.9.0"
43
+ },
44
+ "engineStrict": false,
45
+ "os": [
46
+ "darwin",
47
+ "linux",
48
+ "win32"
49
+ ],
50
+ "preferGlobal": true,
51
+ "files": [
52
+ "bin/",
53
+ "lib/",
54
+ "templates/",
55
+ "README.md",
56
+ "CHANGELOG.md",
57
+ "LICENSE"
58
+ ],
59
+ "dependencies": {
60
+ "@octokit/rest": "^21.0.0"
61
+ },
62
+ "devDependencies": {
63
+ "@types/jest": "^29.5.0",
64
+ "eslint": "^8.57.0",
65
+ "jest": "^29.7.0",
66
+ "prettier": "^3.2.0"
67
+ }
68
+ }