claude-git-hooks 2.11.0 → 2.13.0

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,28 +5,95 @@ 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.13.0] - 2026-02-05
9
+
10
+ ### ✨ Added
11
+ - Interactive analysis command `claude-hooks analyze` - review all issues (INFO to BLOCKER) interactively before committing with auto-commit option
12
+ - Shared analysis engine module (`analysis-engine.js`) - centralized file data building, analysis orchestration, and results display for both pre-commit hooks and interactive analysis
13
+ - Support for version files in subdirectories - automatically searches parent directories when version files not found in repo root
14
+ - Auto-commit functionality after interactive analysis - creates commit with auto-generated message when user confirms
15
+
16
+ ### 🔧 Changed
17
+ - Improved version detection logic for pom.xml files - enhanced reliability of version extraction in Maven projects
18
+
19
+ ### 🐛 Fixed
20
+ - Fixed version search logic for pom.xml files - corrected parsing issues in Maven version detection
21
+
22
+
23
+ ## [2.12.0] - 2026-02-03
24
+
25
+ ### ✨ Added
26
+
27
+ - **Automatic version management with `bump-version` command**
28
+ - Detects project type (Node.js, Maven, or monorepo with both)
29
+ - Increments version following semantic versioning (major/minor/patch)
30
+ - Supports version suffixes (SNAPSHOT, RC, custom)
31
+ - Updates package.json and/or pom.xml automatically
32
+ - Generates CHANGELOG entries using Claude AI analysis of commits
33
+ - Creates annotated Git tags with `v` prefix (v2.7.0, v2.7.0-SNAPSHOT)
34
+ - Automatically pushes tags to remote
35
+ - CLI options: `--suffix`, `--update-changelog`, `--dry-run`, `--no-tag`, `--no-push`
36
+
37
+ - **CHANGELOG generation with Claude integration**
38
+ - Standalone `generate-changelog` command for independent CHANGELOG generation without version bumping
39
+ - Support for manual version specification in CHANGELOG generation via command argument
40
+ - Base branch selection for CHANGELOG comparison via `--base-branch` flag
41
+ - Release marking option via `--release` flag to control [Unreleased] header behavior
42
+ - Analyzes commits since last final version tag
43
+ - Categorizes changes by Conventional Commits types
44
+ - Generates human-readable entries in Keep a Changelog format
45
+ - Sections: Added, Changed, Fixed, Security, Deprecated, Removed
46
+ - Places entries in [Unreleased] for suffixed versions, [X.Y.Z] for final versions
47
+
48
+ - **Version alignment validation in `create-pr`** (#44)
49
+ - Checks consistency across package.json, pom.xml, CHANGELOG.md, and git tags
50
+ - Compares local version vs remote version
51
+ - Shows helpful prompts with fix commands if misalignment detected
52
+ - Allows user to continue or abort PR creation based on validation results
53
+
54
+ - **Unpushed tags detection in `create-pr`** (#44)
55
+ - Detects local tags not pushed to remote
56
+ - Prompts user to push tags before creating PR
57
+ - Prevents version inconsistencies between local and remote
58
+
59
+ - **New utility modules**:
60
+ - `lib/utils/version-manager.js` - Version detection, parsing, incrementing, validation
61
+ - `lib/utils/git-tag-manager.js` - Git tag operations (create, list, compare, push, isSemverTag)
62
+ - `lib/utils/changelog-generator.js` - CHANGELOG generation with Claude
63
+ - `templates/GENERATE_CHANGELOG.md` - Claude prompt for changelog analysis
64
+
65
+ ### 🔧 Changed
66
+
67
+ - Improved Windows compatibility by quoting git log format string in changelog-generator.js
68
+
69
+ ### 📚 Documentation
70
+
71
+ - Updated help.js with bump-version command documentation and examples
72
+ - Added version workflow examples (development → RC → release)
73
+ - Updated CLAUDE.md with new utility modules and exports
74
+
8
75
  ## [2.11.0] - 2026-02-03
9
76
 
10
77
  ### ✨ Added
11
78
 
12
79
  - **Auto-push functionality in `create-pr` command** - Automatically detects and pushes unpublished branches before creating PR (#59, #34)
13
- - Handles 3 scenarios: unpublished branch, unpushed commits, up-to-date branch
14
- - Interactive confirmation prompt before pushing (configurable)
15
- - Commit preview showing what will be pushed
16
- - Diverged branch detection with helpful error message and resolution steps
17
- - Never uses `git push --force` for security
80
+ - Handles 3 scenarios: unpublished branch, unpushed commits, up-to-date branch
81
+ - Interactive confirmation prompt before pushing (configurable)
82
+ - Commit preview showing what will be pushed
83
+ - Diverged branch detection with helpful error message and resolution steps
84
+ - Never uses `git push --force` for security
18
85
 
19
86
  - **New configuration options** under `github.pr`:
20
- - `autoPush: true` - Enable/disable auto-push (enabled by default)
21
- - `pushConfirm: true` - Prompt for confirmation before push
22
- - `showCommits: true` - Show commit preview before push
23
- - `verifyRemote: true` - Verify remote exists before push
87
+ - `autoPush: true` - Enable/disable auto-push (enabled by default)
88
+ - `pushConfirm: true` - Prompt for confirmation before push
89
+ - `showCommits: true` - Show commit preview before push
90
+ - `verifyRemote: true` - Verify remote exists before push
24
91
 
25
92
  - **New git-operations functions**:
26
- - `getRemoteName()` - Get configured remote name (usually 'origin')
27
- - `verifyRemoteExists()` - Check if remote is configured
28
- - `getBranchPushStatus()` - Detect branch publish status and unpushed commits
29
- - `pushBranch()` - Execute git push with upstream tracking
93
+ - `getRemoteName()` - Get configured remote name (usually 'origin')
94
+ - `verifyRemoteExists()` - Check if remote is configured
95
+ - `getBranchPushStatus()` - Detect branch publish status and unpushed commits
96
+ - `pushBranch()` - Execute git push with upstream tracking
30
97
 
31
98
  ### 🔧 Fixed
32
99
 
@@ -76,13 +143,13 @@ This release implements a **modular, decoupled, reusable** architecture for bett
76
143
 
77
144
  ### 🎯 Benefits
78
145
 
79
- | Aspect | Before | After |
80
- |--------|--------|-------|
81
- | `bin/claude-hooks` size | 2,277 lines | 111 lines |
82
- | Command modules | 1 (`setup-github.js`) | 13 files |
83
- | Testability | Hard (monolithic) | Easy (modular) |
84
- | Finding code | Search entire file | Match filename to command |
85
- | Adding commands | Edit large file | Create new module |
146
+ | Aspect | Before | After |
147
+ | ----------------------- | --------------------- | ------------------------- |
148
+ | `bin/claude-hooks` size | 2,277 lines | 111 lines |
149
+ | Command modules | 1 (`setup-github.js`) | 13 files |
150
+ | Testability | Hard (monolithic) | Easy (modular) |
151
+ | Finding code | Search entire file | Match filename to command |
152
+ | Adding commands | Edit large file | Create new module |
86
153
 
87
154
  ### 📚 Documentation
88
155
 
@@ -94,6 +161,7 @@ This release implements a **modular, decoupled, reusable** architecture for bett
94
161
  ### 🔧 Technical Details
95
162
 
96
163
  **New module structure:**
164
+
97
165
  ```
98
166
  lib/commands/
99
167
  ├── helpers.js (389 lines) - Shared utilities
@@ -111,6 +179,7 @@ lib/commands/
111
179
  ```
112
180
 
113
181
  **Key patterns implemented:**
182
+
114
183
  - **Command Pattern**: Each CLI command is a self-contained module
115
184
  - **Thin Controller**: `bin/claude-hooks` only routes, doesn't implement
116
185
  - **Separation of Concerns**: UI helpers in `helpers.js`, business logic in specific modules
package/README.md CHANGED
@@ -60,6 +60,33 @@ export GITHUB_TOKEN="ghp_..."
60
60
 
61
61
  Create token at https://github.com/settings/tokens with scopes: `repo`, `read:org`
62
62
 
63
+ ### Analyze Code (Interactive Review)
64
+
65
+ Run interactive code analysis before committing:
66
+
67
+ ```bash
68
+ # Analyze staged changes (default)
69
+ claude-hooks analyze
70
+
71
+ # Analyze unstaged changes
72
+ claude-hooks analyze --unstaged
73
+
74
+ # Analyze all tracked files
75
+ claude-hooks analyze --all
76
+ ```
77
+
78
+ **What it does:**
79
+ - Analyzes selected file scope (staged, unstaged, or all)
80
+ - Shows all issues (INFO, MINOR, MAJOR, CRITICAL, BLOCKER)
81
+ - Interactive prompt with options:
82
+ - **Continue**: Creates commit automatically with auto-generated message
83
+ - **Abort**: Generate resolution prompt and fix issues
84
+ - **View**: Show detailed issue list
85
+ - Executes `git commit -m "auto" --no-verify` on confirmation
86
+ - Works outside git hooks (no stdin limitations)
87
+
88
+ **Use case:** Complete analysis-to-commit workflow in one command.
89
+
63
90
  ### Analyze Diff (without creating PR)
64
91
 
65
92
  ```bash
@@ -67,6 +94,65 @@ claude-hooks analyze-diff develop
67
94
  # Generates PR metadata without creating
68
95
  ```
69
96
 
97
+ ### Bump Version
98
+
99
+ Automatic version management with CHANGELOG generation and Git tagging:
100
+
101
+ ```bash
102
+ # Bump patch version (1.0.0 → 1.0.1)
103
+ claude-hooks bump-version patch
104
+
105
+ # Bump with suffix
106
+ claude-hooks bump-version minor --suffix SNAPSHOT # → 1.1.0-SNAPSHOT
107
+
108
+ # Bump and generate CHANGELOG
109
+ claude-hooks bump-version major --update-changelog
110
+
111
+ # Preview without applying
112
+ claude-hooks bump-version patch --dry-run
113
+ ```
114
+
115
+ **What it does:**
116
+ - Detects project type (Node.js, Maven, or monorepo with both)
117
+ - Updates `package.json` and/or `pom.xml`
118
+ - Generates CHANGELOG entry with Claude (analyzes commits)
119
+ - Creates annotated Git tag with `v` prefix (e.g., `v2.7.0`)
120
+ - Pushes tag to remote automatically
121
+
122
+ **Version workflow:**
123
+ ```
124
+ 2.7.0 → 2.8.0-SNAPSHOT # Start development
125
+ 2.8.0-SNAPSHOT → 2.8.0-RC # Release candidate
126
+ 2.8.0-RC → 2.8.0 # Final release
127
+ ```
128
+
129
+ **Integration with create-pr:**
130
+ - Validates version alignment (package.json, pom.xml, CHANGELOG, tags)
131
+ - Detects and prompts to push unpushed tags
132
+ - Warns if local version ≤ remote version
133
+
134
+ ### Generate CHANGELOG
135
+
136
+ Standalone CHANGELOG generation (without version bump):
137
+
138
+ ```bash
139
+ # Auto-detect version from package.json/pom.xml
140
+ claude-hooks generate-changelog
141
+
142
+ # Specific version with release marking
143
+ claude-hooks generate-changelog 2.7.0 --release
144
+
145
+ # Compare against different base branch
146
+ claude-hooks generate-changelog --base-branch develop
147
+ ```
148
+
149
+ **What it does:**
150
+ - Analyzes commits since last tag using Claude
151
+ - Categorizes by Conventional Commits types (feat, fix, refactor, etc.)
152
+ - Generates Keep a Changelog format entries
153
+ - Updates CHANGELOG.md automatically
154
+ - Useful when `bump-version --update-changelog` fails
155
+
70
156
  ### Disable/Enable Hooks
71
157
 
72
158
  ```bash
@@ -164,6 +250,7 @@ claude-hooks --help # Full command reference
164
250
 
165
251
  | Module | Purpose | Key Exports |
166
252
  |--------|---------|-------------|
253
+ | `analysis-engine.js` | **Shared analysis logic** - file data, orchestration, results (v2.13.0) | `buildFilesData()`, `runAnalysis()`, `consolidateResults()`, `displayResults()` |
167
254
  | `claude-client.js` | **Claude CLI wrapper** - spawn, retry, parallel execution | `analyzeCode()`, `analyzeCodeParallel()`, `executeClaudeWithRetry()` |
168
255
  | `prompt-builder.js` | **Prompt construction** - load templates, replace variables | `buildAnalysisPrompt()`, `loadPrompt()` |
169
256
  | `git-operations.js` | **Git abstractions** - staged files, diff, repo root | `getStagedFiles()`, `getDiff()`, `getRepoRoot()` |
@@ -183,12 +270,16 @@ claude-hooks --help # Full command reference
183
270
  ```
184
271
  git commit → templates/pre-commit (bash wrapper)
185
272
  → lib/hooks/pre-commit.js
186
- → getStagedFiles() → filter by preset extensions
187
- buildAnalysisPrompt() → analyzeCode() or analyzeCodeParallel()
188
- parse JSON response exit 0 (pass) or exit 1 (block)
189
- → if blocked: generates claude_resolution_prompt.md
273
+ → getStagedFiles() → filterFiles() by preset extensions + size
274
+ buildFilesData() → runAnalysis() (via analysis-engine.js)
275
+ displayResults()show quality gate status
276
+ → if blocking issues (critical/blocker):
277
+ generates claude_resolution_prompt.md → exit 1 (block)
278
+ → if non-blocking or no issues: exit 0 (pass)
190
279
  ```
191
280
 
281
+ **Note:** For interactive review of non-blocking issues, use `claude-hooks analyze` before committing.
282
+
192
283
  #### Commit Message Generation
193
284
 
194
285
  ```
package/bin/claude-hooks CHANGED
@@ -13,6 +13,7 @@ import { error } from '../lib/commands/helpers.js';
13
13
  // Import commands
14
14
  import { runInstall } from '../lib/commands/install.js';
15
15
  import { runEnable, runDisable, runStatus, runUninstall } from '../lib/commands/hooks.js';
16
+ import { runAnalyze } from '../lib/commands/analyze.js';
16
17
  import { runAnalyzeDiff } from '../lib/commands/analyze-diff.js';
17
18
  import { runCreatePr } from '../lib/commands/create-pr.js';
18
19
  import { runSetupGitHub } from '../lib/commands/setup-github.js';
@@ -22,6 +23,8 @@ import { runMigrateConfig } from '../lib/commands/migrate-config.js';
22
23
  import { runSetDebug } from '../lib/commands/debug.js';
23
24
  import { runShowTelemetry, runClearTelemetry } from '../lib/commands/telemetry-cmd.js';
24
25
  import { runShowHelp, runShowVersion } from '../lib/commands/help.js';
26
+ import { runBumpVersion } from '../lib/commands/bump-version.js';
27
+ import { runGenerateChangelog } from '../lib/commands/generate-changelog.js';
25
28
 
26
29
  /**
27
30
  * Main CLI router
@@ -49,6 +52,13 @@ async function main() {
49
52
  case 'status':
50
53
  runStatus();
51
54
  break;
55
+ case 'analyze':
56
+ await runAnalyze({
57
+ staged: !args.includes('--unstaged') && !args.includes('--all'),
58
+ unstaged: args.includes('--unstaged'),
59
+ all: args.includes('--all')
60
+ });
61
+ break;
52
62
  case 'analyze-diff':
53
63
  await runAnalyzeDiff(args.slice(1));
54
64
  break;
@@ -75,6 +85,12 @@ async function main() {
75
85
  case 'migrate-config':
76
86
  await runMigrateConfig();
77
87
  break;
88
+ case 'bump-version':
89
+ await runBumpVersion(args.slice(1));
90
+ break;
91
+ case 'generate-changelog':
92
+ await runGenerateChangelog(args.slice(1));
93
+ break;
78
94
  case 'telemetry':
79
95
  // Handle subcommands: telemetry show, telemetry clear
80
96
  if (args[1] === 'show' || args[1] === undefined) {
@@ -0,0 +1,217 @@
1
+ /**
2
+ * File: analyze.js
3
+ * Purpose: On-demand code analysis command (interactive, runs outside git hooks)
4
+ *
5
+ * Why this exists: Git hooks cannot reliably read stdin for interactive prompts
6
+ * (stdin is redirected from /dev/null). This command provides interactive analysis
7
+ * before committing, allowing developers to review all issues and decide whether
8
+ * to proceed or fix them first.
9
+ *
10
+ * Key features:
11
+ * - Runs outside git hook context (stdin works normally)
12
+ * - Analyzes staged, unstaged, or all tracked files
13
+ * - Interactive confirmation with detailed issue view
14
+ * - Generates resolution prompt on abort
15
+ *
16
+ * Usage:
17
+ * claude-hooks analyze # Analyze staged files (default)
18
+ * claude-hooks analyze --unstaged # Analyze unstaged changes
19
+ * claude-hooks analyze --all # Analyze all tracked files
20
+ */
21
+
22
+ import { getStagedFiles, getUnstagedFiles, getAllTrackedFiles, createCommit } from '../utils/git-operations.js';
23
+ import { filterFiles } from '../utils/file-operations.js';
24
+ import {
25
+ buildFilesData,
26
+ runAnalysis,
27
+ hasAnyIssues,
28
+ displayIssueSummary
29
+ } from '../utils/analysis-engine.js';
30
+ import { promptUserConfirmation, promptConfirmation } from '../utils/interactive-ui.js';
31
+ import { generateResolutionPrompt } from '../utils/resolution-prompt.js';
32
+ import { getConfig } from '../config.js';
33
+ import { loadPreset } from '../utils/preset-loader.js';
34
+ import logger from '../utils/logger.js';
35
+ import { error, success, info } from './helpers.js';
36
+
37
+ /**
38
+ * Main analyze command
39
+ * Why: Provides interactive analysis before committing
40
+ *
41
+ * @param {Object} options - Command options
42
+ * @param {boolean} options.staged - Analyze staged files (default: true)
43
+ * @param {boolean} options.unstaged - Analyze unstaged files
44
+ * @param {boolean} options.all - Analyze all tracked files
45
+ */
46
+ export const runAnalyze = async (options = {}) => {
47
+ const { unstaged = false, all = false } = options;
48
+
49
+ try {
50
+ // Load configuration
51
+ const config = await getConfig();
52
+
53
+ // Enable debug mode from config
54
+ if (config.system?.debug) {
55
+ logger.setDebugMode(true);
56
+ }
57
+
58
+ // Load active preset for file extensions
59
+ const presetName = config.preset || 'default';
60
+ const { metadata } = await loadPreset(presetName);
61
+ const allowedExtensions = metadata.fileExtensions;
62
+
63
+ // Determine scope
64
+ let scopeLabel = 'staged changes';
65
+ if (all) {
66
+ scopeLabel = 'all tracked files';
67
+ } else if (unstaged) {
68
+ scopeLabel = 'unstaged changes';
69
+ }
70
+
71
+ info(`Analyzing ${scopeLabel} with '${metadata.displayName}' preset...`);
72
+
73
+ // Get files based on scope
74
+ let files = [];
75
+ if (all) {
76
+ files = getAllTrackedFiles({ extensions: allowedExtensions });
77
+ } else if (unstaged) {
78
+ files = getUnstagedFiles({ extensions: allowedExtensions });
79
+ } else {
80
+ files = getStagedFiles({ extensions: allowedExtensions });
81
+ }
82
+
83
+ if (files.length === 0) {
84
+ info(`No files to analyze in ${scopeLabel}.`);
85
+ process.exit(0);
86
+ }
87
+
88
+ logger.debug('analyze', 'Files found', {
89
+ scope: scopeLabel,
90
+ count: files.length,
91
+ extensions: allowedExtensions
92
+ });
93
+
94
+ // Filter files by size
95
+ const filteredFiles = await filterFiles(files, {
96
+ maxSize: config.analysis?.maxFileSize || 1048576,
97
+ extensions: allowedExtensions
98
+ });
99
+
100
+ const validFiles = filteredFiles.filter(f => f.valid);
101
+ const invalidFiles = filteredFiles.filter(f => !f.valid);
102
+
103
+ // Show warnings for skipped files
104
+ if (invalidFiles.length > 0) {
105
+ invalidFiles.forEach(file => {
106
+ logger.warning(`Skipping ${file.path}: ${file.reason}`);
107
+ });
108
+ }
109
+
110
+ if (validFiles.length === 0) {
111
+ info(`No valid files found to analyze in ${scopeLabel}.`);
112
+ process.exit(0);
113
+ }
114
+
115
+ info(`Sending ${validFiles.length} file(s) for analysis...`);
116
+
117
+ // Build file data (diff/content) using shared engine
118
+ const filesData = buildFilesData(validFiles, { staged: !unstaged && !all });
119
+
120
+ if (filesData.length === 0) {
121
+ info('No file data could be extracted.');
122
+ process.exit(0);
123
+ }
124
+
125
+ // Run analysis using shared engine
126
+ const result = await runAnalysis(filesData, config, { hook: 'analyze' });
127
+
128
+ // Check results
129
+ if (!hasAnyIssues(result)) {
130
+ console.log('');
131
+ success('No issues found. Code is ready to commit!');
132
+ console.log('');
133
+
134
+ // Prompt user to commit or cancel
135
+ const shouldCommit = await promptConfirmation('Create commit now?', true);
136
+
137
+ if (shouldCommit) {
138
+ info('Creating commit with auto-generated message...');
139
+ console.log('');
140
+
141
+ const commitResult = createCommit('auto', { noVerify: true });
142
+
143
+ if (commitResult.success) {
144
+ success('Commit created successfully!');
145
+ if (commitResult.output) {
146
+ console.log(commitResult.output);
147
+ }
148
+ console.log('');
149
+ process.exit(0);
150
+ } else {
151
+ error(`Commit failed: ${commitResult.error}`);
152
+ console.log('');
153
+ process.exit(1);
154
+ }
155
+ } else {
156
+ info('Commit cancelled. Staged files remain unchanged.');
157
+ console.log('');
158
+ process.exit(0);
159
+ }
160
+ }
161
+
162
+ // Display summary
163
+ console.log('');
164
+ console.log('Analysis complete:');
165
+ displayIssueSummary(result);
166
+ console.log('');
167
+
168
+ // Interactive confirmation (works outside git hook)
169
+ const userChoice = await promptUserConfirmation(result);
170
+
171
+ if (userChoice === 'abort') {
172
+ // Generate resolution prompt
173
+ await generateResolutionPrompt(result, {
174
+ fileCount: filesData.length
175
+ });
176
+ console.log('');
177
+ success('Resolution prompt generated: claude_resolution_prompt.md');
178
+ info('Fix issues and run `claude-hooks analyze` again.');
179
+ console.log('');
180
+ process.exit(1);
181
+ } else {
182
+ // User chose to continue - execute commit automatically
183
+ console.log('');
184
+
185
+ // Safeguard: verify staged files still exist
186
+ const currentStagedFiles = getStagedFiles({ extensions: allowedExtensions });
187
+ if (currentStagedFiles.length === 0) {
188
+ error('No staged files found. Did you unstage changes?');
189
+ process.exit(1);
190
+ }
191
+
192
+ info('Creating commit with auto-generated message...');
193
+ console.log('');
194
+
195
+ // Execute commit with --no-verify (skip hooks - we already analyzed)
196
+ const commitResult = createCommit('auto', { noVerify: true });
197
+
198
+ if (commitResult.success) {
199
+ success('Commit created successfully!');
200
+ if (commitResult.output) {
201
+ console.log(commitResult.output);
202
+ }
203
+ console.log('');
204
+ process.exit(0);
205
+ } else {
206
+ error(`Commit failed: ${commitResult.error}`);
207
+ console.log('');
208
+ process.exit(1);
209
+ }
210
+ }
211
+
212
+ } catch (err) {
213
+ logger.error('analyze', 'Analysis failed', err);
214
+ error(`Analysis failed: ${err.message}`);
215
+ process.exit(1);
216
+ }
217
+ };