claude-git-hooks 2.18.0 → 2.18.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 +14 -0
- package/CLAUDE.md +2 -1
- package/README.md +2 -1
- package/lib/commands/bump-version.js +11 -4
- package/lib/commands/generate-changelog.js +8 -2
- package/lib/utils/changelog-generator.js +108 -6
- package/lib/utils/file-utils.js +41 -3
- package/lib/utils/version-manager.js +26 -56
- package/package.json +69 -69
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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.18.1] - 2026-03-04
|
|
9
|
+
|
|
10
|
+
### ✨ Added
|
|
11
|
+
- Monorepo support for CHANGELOG discovery - discovers all CHANGELOG.md files and prompts to select when multiple are found (#77)
|
|
12
|
+
- AI-powered help and issue creation system (#79)
|
|
13
|
+
|
|
14
|
+
### 🔧 Changed
|
|
15
|
+
- Enhanced `walkDirectoryTree` utility function in file-utils.js for reusable directory traversal
|
|
16
|
+
- Updated documentation to reflect monorepo CHANGELOG discovery capabilities
|
|
17
|
+
|
|
18
|
+
### 🐛 Fixed
|
|
19
|
+
- Cross-platform path handling in git hooks directory resolution (normalized path separators)
|
|
20
|
+
|
|
21
|
+
|
|
8
22
|
## [2.18.0] - 2026-03-03
|
|
9
23
|
|
|
10
24
|
### ✨ Added
|
package/CLAUDE.md
CHANGED
|
@@ -240,10 +240,11 @@ Wait for both → consolidate results
|
|
|
240
240
|
| `claude-client.js` | Claude CLI wrapper | `analyzeCode()`, `analyzeCodeParallel()`, `executeClaudeWithRetry()` |
|
|
241
241
|
| `prompt-builder.js` | Prompt construction | `buildAnalysisPrompt()`, `loadPrompt()` |
|
|
242
242
|
| `git-operations.js` | Git abstractions | `getStagedFiles()`, `getUnstagedFiles()`, `getAllTrackedFiles()`, `getDiff()`, `getRepoRoot()`, `getBranchPushStatus()`, `pushBranch()`, `createCommit()`, `fetchRemote()`, `branchExists()`, `getRemoteBranches()`, `resolveBaseBranch()`, `getChangedFilesBetweenRefs()`, `getDiffBetweenRefs()`, `getCommitsBetweenRefs()` |
|
|
243
|
+
| `file-utils.js` | File operations | `ensureDir()`, `ensureOutputDir()`, `writeOutputFile()`, `walkDirectoryTree()` |
|
|
243
244
|
| `pr-metadata-engine.js` | PR metadata generation | `getBranchContext()`, `buildDiffPayload()`, `generatePRMetadata()`, `analyzeBranchForPR()` (v2.14.0) |
|
|
244
245
|
| `git-tag-manager.js` | Git tag operations | `createTag()`, `pushTags()`, `getLocalTags()`, `getRemoteTags()`, `compareLocalAndRemoteTags()`, `tagExists()` (v2.12.0) |
|
|
245
246
|
| `version-manager.js` | Version management | `discoverVersionFiles()`, `getDiscoveryResult()`, `readVersionFromFile()`, `writeVersionToFile()`, `updateVersionFiles()`, `modifySuffix()`, `incrementVersion()`, `parseVersion()`, `validateVersionFormat()`, `compareVersions()`, `validateVersionAlignment()` (v2.15.5) |
|
|
246
|
-
| `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()` (v2.12.0)
|
|
247
|
+
| `changelog-generator.js` | CHANGELOG generation | `generateChangelogEntry()`, `updateChangelogFile()`, `getLastFinalVersionTag()`, `getCommitsSinceTag()`, `discoverChangelogFiles()`, `selectChangelogFile()` (v2.12.0) |
|
|
247
248
|
| `github-api.js` | Octokit integration | `createPullRequest()`, `validateToken()`, `saveGitHubToken()`, `fetchFileContent()`, `fetchDirectoryListing()`, `createIssue()` |
|
|
248
249
|
| `github-client.js` | GitHub helpers | `getReviewersForFiles()`, `parseGitHubRepo()` |
|
|
249
250
|
| `preset-loader.js` | Preset system | `loadPreset()`, `listPresets()` |
|
package/README.md
CHANGED
|
@@ -157,7 +157,8 @@ claude-hooks generate-changelog --base-branch develop
|
|
|
157
157
|
- Analyzes commits since last tag using Claude
|
|
158
158
|
- Categorizes by Conventional Commits types (feat, fix, refactor, etc.)
|
|
159
159
|
- Generates Keep a Changelog format entries
|
|
160
|
-
-
|
|
160
|
+
- Discovers all CHANGELOG.md files (monorepo aware); prompts to select when multiple found
|
|
161
|
+
- Updates the selected CHANGELOG.md automatically
|
|
161
162
|
- Useful when `bump-version --update-changelog` fails
|
|
162
163
|
|
|
163
164
|
### Disable/Enable Hooks
|
|
@@ -35,7 +35,8 @@ import {
|
|
|
35
35
|
} from '../utils/git-tag-manager.js';
|
|
36
36
|
import {
|
|
37
37
|
generateChangelogEntry,
|
|
38
|
-
updateChangelogFile
|
|
38
|
+
updateChangelogFile,
|
|
39
|
+
selectChangelogFile
|
|
39
40
|
} from '../utils/changelog-generator.js';
|
|
40
41
|
import {
|
|
41
42
|
getRepoRoot,
|
|
@@ -582,6 +583,10 @@ export async function runBumpVersion(args) {
|
|
|
582
583
|
process.exit(1);
|
|
583
584
|
}
|
|
584
585
|
|
|
586
|
+
// selectedChangelogPath: set in Step 9 when multiple CHANGELOGs found;
|
|
587
|
+
// used in Step 10 to stage the correct file.
|
|
588
|
+
let selectedChangelogPath = null;
|
|
589
|
+
|
|
585
590
|
// Step 9: Generate CHANGELOG (if requested)
|
|
586
591
|
if (options.updateChangelog) {
|
|
587
592
|
logger.debug('bump-version', 'Step 9: Generating CHANGELOG');
|
|
@@ -597,7 +602,9 @@ export async function runBumpVersion(args) {
|
|
|
597
602
|
});
|
|
598
603
|
|
|
599
604
|
if (changelogResult.content) {
|
|
600
|
-
|
|
605
|
+
selectedChangelogPath = await selectChangelogFile();
|
|
606
|
+
|
|
607
|
+
const updated = updateChangelogFile(changelogResult.content, selectedChangelogPath);
|
|
601
608
|
if (updated) {
|
|
602
609
|
showSuccess('✓ CHANGELOG.md updated');
|
|
603
610
|
} else {
|
|
@@ -620,9 +627,9 @@ export async function runBumpVersion(args) {
|
|
|
620
627
|
// Collect files to stage
|
|
621
628
|
const filesToStage = selectedFiles.map(f => f.path);
|
|
622
629
|
|
|
623
|
-
// Add CHANGELOG if it was updated
|
|
630
|
+
// Add CHANGELOG if it was updated (use selected path from Step 9, or default root)
|
|
624
631
|
if (options.updateChangelog) {
|
|
625
|
-
const changelogPath = path.join(getRepoRoot(), 'CHANGELOG.md');
|
|
632
|
+
const changelogPath = selectedChangelogPath || path.join(getRepoRoot(), 'CHANGELOG.md');
|
|
626
633
|
if (fs.existsSync(changelogPath)) {
|
|
627
634
|
filesToStage.push(changelogPath);
|
|
628
635
|
}
|
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { discoverVersionFiles } from '../utils/version-manager.js';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
generateChangelogEntry,
|
|
12
|
+
updateChangelogFile,
|
|
13
|
+
selectChangelogFile
|
|
14
|
+
} from '../utils/changelog-generator.js';
|
|
11
15
|
import { getConfig } from '../config.js';
|
|
12
16
|
import { showInfo, showSuccess, showWarning, showError } from '../utils/interactive-ui.js';
|
|
13
17
|
import logger from '../utils/logger.js';
|
|
@@ -116,7 +120,9 @@ export async function runGenerateChangelog(args) {
|
|
|
116
120
|
});
|
|
117
121
|
|
|
118
122
|
if (changelogResult.content) {
|
|
119
|
-
const
|
|
123
|
+
const selectedPath = await selectChangelogFile();
|
|
124
|
+
|
|
125
|
+
const updated = updateChangelogFile(changelogResult.content, selectedPath);
|
|
120
126
|
if (updated) {
|
|
121
127
|
showSuccess('✓ CHANGELOG.md updated');
|
|
122
128
|
} else {
|
|
@@ -13,6 +13,8 @@ import { getRepoRoot } from './git-operations.js';
|
|
|
13
13
|
import { executeClaudeWithRetry, extractJSON } from './claude-client.js';
|
|
14
14
|
import { loadPrompt } from './prompt-builder.js';
|
|
15
15
|
import logger from './logger.js';
|
|
16
|
+
import { walkDirectoryTree } from './file-utils.js';
|
|
17
|
+
import { showInfo, promptMenu } from './interactive-ui.js';
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* Gets the last final version tag (without suffix)
|
|
@@ -256,13 +258,30 @@ export async function generateChangelogEntry(options) {
|
|
|
256
258
|
.map(c => `${c.sha.substring(0, 7)} - ${c.message} (${c.author}, ${c.date})`)
|
|
257
259
|
.join('\n');
|
|
258
260
|
|
|
261
|
+
if (!fromTag) {
|
|
262
|
+
logger.debug(
|
|
263
|
+
'changelog-generator - generateChangelogEntry',
|
|
264
|
+
'No version tags found — using full commit history for first CHANGELOG entry'
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
259
268
|
// Get diff for context
|
|
260
269
|
const compareWith = fromTag ? `${fromTag}..HEAD` : `origin/${baseBranch}...HEAD`;
|
|
261
270
|
let fullDiff;
|
|
262
271
|
try {
|
|
263
272
|
fullDiff = execSync(`git diff ${compareWith}`, { encoding: 'utf8' });
|
|
264
273
|
} catch (error) {
|
|
265
|
-
|
|
274
|
+
if (fromTag) {
|
|
275
|
+
// Unexpected: a tag exists but diff failed
|
|
276
|
+
logger.warning('changelog-generator - generateChangelogEntry', 'Could not get diff', error);
|
|
277
|
+
} else {
|
|
278
|
+
// Expected: no remote or remote branch not yet pushed — commit messages are primary context
|
|
279
|
+
logger.debug(
|
|
280
|
+
'changelog-generator - generateChangelogEntry',
|
|
281
|
+
'Diff not available — no remote or no version tags yet, using commit messages as primary context',
|
|
282
|
+
{ error: error.message }
|
|
283
|
+
);
|
|
284
|
+
}
|
|
266
285
|
fullDiff = '(diff not available)';
|
|
267
286
|
}
|
|
268
287
|
|
|
@@ -322,18 +341,20 @@ export async function generateChangelogEntry(options) {
|
|
|
322
341
|
* Why: Inserts new version section at the top
|
|
323
342
|
*
|
|
324
343
|
* @param {string} newContent - New CHANGELOG section
|
|
344
|
+
* @param {string|null} changelogPath - Absolute path to target CHANGELOG file.
|
|
345
|
+
* If null, defaults to CHANGELOG.md at repo root (backward compatible).
|
|
325
346
|
* @returns {boolean} True if updated successfully
|
|
326
347
|
*/
|
|
327
|
-
export function updateChangelogFile(newContent) {
|
|
348
|
+
export function updateChangelogFile(newContent, changelogPath = null) {
|
|
328
349
|
logger.debug('changelog-generator - updateChangelogFile', 'Updating CHANGELOG.md');
|
|
329
350
|
|
|
330
351
|
try {
|
|
331
352
|
const repoRoot = getRepoRoot();
|
|
332
|
-
const
|
|
353
|
+
const targetPath = changelogPath || path.join(repoRoot, 'CHANGELOG.md');
|
|
333
354
|
|
|
334
355
|
let existingContent = '';
|
|
335
|
-
if (fs.existsSync(
|
|
336
|
-
existingContent = fs.readFileSync(
|
|
356
|
+
if (fs.existsSync(targetPath)) {
|
|
357
|
+
existingContent = fs.readFileSync(targetPath, 'utf8');
|
|
337
358
|
} else {
|
|
338
359
|
// Create new CHANGELOG with header
|
|
339
360
|
existingContent = `# Changelog
|
|
@@ -367,7 +388,7 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|
|
367
388
|
updatedContent = `${newContent }\n${ existingContent}`;
|
|
368
389
|
}
|
|
369
390
|
|
|
370
|
-
fs.writeFileSync(
|
|
391
|
+
fs.writeFileSync(targetPath, updatedContent, 'utf8');
|
|
371
392
|
|
|
372
393
|
logger.debug('changelog-generator - updateChangelogFile', 'CHANGELOG.md updated successfully');
|
|
373
394
|
|
|
@@ -378,3 +399,84 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/spec/v2.0.0.h
|
|
|
378
399
|
return false;
|
|
379
400
|
}
|
|
380
401
|
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Discovers all CHANGELOG.md files in the repository
|
|
405
|
+
* Why: Support monorepos where each module has its own changelog
|
|
406
|
+
*
|
|
407
|
+
* Reuses the same maxDepth and ignoreSet patterns as discoverVersionFiles()
|
|
408
|
+
* in version-manager.js for consistency.
|
|
409
|
+
*
|
|
410
|
+
* @param {Object} options - Discovery options
|
|
411
|
+
* @param {number} options.maxDepth - Maximum directory depth to search (default: 3)
|
|
412
|
+
* @param {string[]} options.ignoreDirs - Additional directories to ignore
|
|
413
|
+
* @returns {string[]} Absolute paths to discovered CHANGELOG.md files, root-first
|
|
414
|
+
*/
|
|
415
|
+
export function discoverChangelogFiles(options = {}) {
|
|
416
|
+
const { maxDepth = 3, ignoreDirs = [] } = options;
|
|
417
|
+
|
|
418
|
+
logger.debug('changelog-generator - discoverChangelogFiles', 'Starting discovery', { maxDepth });
|
|
419
|
+
|
|
420
|
+
const repoRoot = getRepoRoot();
|
|
421
|
+
|
|
422
|
+
const defaultIgnore = [
|
|
423
|
+
'node_modules', 'dist', 'build', 'target', 'vendor',
|
|
424
|
+
'.next', '.nuxt', 'coverage'
|
|
425
|
+
];
|
|
426
|
+
const ignoreSet = new Set([...defaultIgnore, ...ignoreDirs]);
|
|
427
|
+
|
|
428
|
+
const found = [];
|
|
429
|
+
|
|
430
|
+
walkDirectoryTree(repoRoot, {
|
|
431
|
+
maxDepth,
|
|
432
|
+
ignoreSet,
|
|
433
|
+
onFile: (entry, fullPath) => {
|
|
434
|
+
if (entry.name.toLowerCase() === 'changelog.md') found.push(fullPath);
|
|
435
|
+
},
|
|
436
|
+
onError: (dir, error) => {
|
|
437
|
+
logger.debug('changelog-generator - discoverChangelogFiles', 'Error reading directory', {
|
|
438
|
+
dir,
|
|
439
|
+
error: error.message
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Sort: root-level first, then by depth, then alphabetically
|
|
445
|
+
found.sort((a, b) => {
|
|
446
|
+
const depthA = path.relative(repoRoot, a).split(path.sep).length;
|
|
447
|
+
const depthB = path.relative(repoRoot, b).split(path.sep).length;
|
|
448
|
+
if (depthA !== depthB) return depthA - depthB;
|
|
449
|
+
return a.localeCompare(b);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
logger.debug('changelog-generator - discoverChangelogFiles', 'Discovery complete', {
|
|
453
|
+
count: found.length,
|
|
454
|
+
files: found.map(f => path.relative(repoRoot, f))
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
return found;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Discovers CHANGELOG files and prompts the user to select one if multiple exist
|
|
462
|
+
* Why: Shared interactive selection used by both generate-changelog and bump-version
|
|
463
|
+
*
|
|
464
|
+
* @returns {Promise<string|null>} Absolute path to selected CHANGELOG, or null if none found
|
|
465
|
+
* (null → updateChangelogFile falls back to repo-root CHANGELOG.md)
|
|
466
|
+
*/
|
|
467
|
+
export async function selectChangelogFile() {
|
|
468
|
+
const files = discoverChangelogFiles();
|
|
469
|
+
|
|
470
|
+
if (files.length > 1) {
|
|
471
|
+
const repoRoot = getRepoRoot();
|
|
472
|
+
const menuOptions = files.map((f, i) => ({
|
|
473
|
+
key: String(i + 1),
|
|
474
|
+
label: path.relative(repoRoot, f)
|
|
475
|
+
}));
|
|
476
|
+
showInfo('Multiple CHANGELOG files detected:');
|
|
477
|
+
const selected = await promptMenu('Select CHANGELOG to update:', menuOptions, '1');
|
|
478
|
+
return files[parseInt(selected) - 1];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return files[0] || null;
|
|
482
|
+
}
|
package/lib/utils/file-utils.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Purpose: Utility functions for file system operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import fsPromises from 'fs/promises';
|
|
7
|
+
import fs from 'fs';
|
|
7
8
|
import path from 'path';
|
|
8
9
|
import { getRepoRoot } from './git-operations.js';
|
|
9
10
|
import logger from './logger.js';
|
|
@@ -20,7 +21,7 @@ export const ensureDir = async (dirPath) => {
|
|
|
20
21
|
: path.join(getRepoRoot(), dirPath);
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
|
-
await
|
|
24
|
+
await fsPromises.mkdir(absolutePath, { recursive: true });
|
|
24
25
|
logger.debug('file-utils - ensureDir', 'Directory ensured', { path: absolutePath });
|
|
25
26
|
} catch (error) {
|
|
26
27
|
logger.error('file-utils - ensureDir', 'Failed to create directory', error);
|
|
@@ -55,10 +56,47 @@ export const writeOutputFile = async (filePath, content, config) => {
|
|
|
55
56
|
? filePath
|
|
56
57
|
: path.join(getRepoRoot(), filePath);
|
|
57
58
|
|
|
58
|
-
await
|
|
59
|
+
await fsPromises.writeFile(absolutePath, content, 'utf8');
|
|
59
60
|
|
|
60
61
|
logger.debug('file-utils - writeOutputFile', 'File written', {
|
|
61
62
|
path: absolutePath,
|
|
62
63
|
size: content.length
|
|
63
64
|
});
|
|
64
65
|
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Walks a directory tree recursively, invoking callbacks for each file
|
|
69
|
+
* Why: Shared traversal logic for discoverVersionFiles and discoverChangelogFiles
|
|
70
|
+
*
|
|
71
|
+
* @param {string} rootDir - Starting directory path
|
|
72
|
+
* @param {Object} options
|
|
73
|
+
* @param {number} options.maxDepth - Maximum recursion depth (default: 3)
|
|
74
|
+
* @param {Set<string>} options.ignoreSet - Directory names to skip
|
|
75
|
+
* @param {Function} options.onFile - Called as onFile(entry, fullPath) for each file entry
|
|
76
|
+
* @param {Function} options.onError - Called as onError(dir, error) on readdirSync failures
|
|
77
|
+
*/
|
|
78
|
+
export function walkDirectoryTree(rootDir, { maxDepth = 3, ignoreSet = new Set(), onFile, onError } = {}) {
|
|
79
|
+
function walk(dir, depth) {
|
|
80
|
+
if (depth > maxDepth) return;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
84
|
+
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.name.startsWith('.') || ignoreSet.has(entry.name)) continue;
|
|
87
|
+
|
|
88
|
+
const fullPath = path.join(dir, entry.name);
|
|
89
|
+
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
walk(fullPath, depth + 1);
|
|
92
|
+
} else if (entry.isFile()) {
|
|
93
|
+
onFile?.(entry, fullPath);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
onError?.(dir, error);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
walk(rootDir, 0);
|
|
102
|
+
}
|
|
@@ -14,6 +14,7 @@ import fs from 'fs';
|
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { getRepoRoot } from './git-operations.js';
|
|
16
16
|
import logger from './logger.js';
|
|
17
|
+
import { walkDirectoryTree } from './file-utils.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Cache for discovery result
|
|
@@ -232,69 +233,38 @@ export function discoverVersionFiles(options = {}) {
|
|
|
232
233
|
];
|
|
233
234
|
const ignoreSet = new Set([...defaultIgnore, ...ignoreDirs]);
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// Recurse into subdirectory
|
|
258
|
-
walkDirectory(fullPath, depth + 1);
|
|
259
|
-
} else if (entry.isFile()) {
|
|
260
|
-
// Check if this file matches any registered type
|
|
261
|
-
for (const fileType of fileTypes) {
|
|
262
|
-
const registry = VERSION_FILE_TYPES[fileType];
|
|
263
|
-
if (registry && entry.name === registry.filename) {
|
|
264
|
-
// Read version from file
|
|
265
|
-
const version = registry.readVersion(fullPath);
|
|
266
|
-
|
|
267
|
-
// Create descriptor
|
|
268
|
-
const descriptor = {
|
|
269
|
-
path: fullPath,
|
|
270
|
-
relativePath: path.relative(repoRoot, fullPath),
|
|
271
|
-
type: fileType,
|
|
272
|
-
projectLabel: registry.projectLabel,
|
|
273
|
-
version,
|
|
274
|
-
selected: true // Default: all files selected
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
discoveredFiles.push(descriptor);
|
|
278
|
-
|
|
279
|
-
logger.debug('version-manager - discoverVersionFiles', 'Found version file', {
|
|
280
|
-
relativePath: descriptor.relativePath,
|
|
281
|
-
type: fileType,
|
|
282
|
-
version
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
236
|
+
walkDirectoryTree(repoRoot, {
|
|
237
|
+
maxDepth,
|
|
238
|
+
ignoreSet,
|
|
239
|
+
onFile: (entry, fullPath) => {
|
|
240
|
+
for (const fileType of fileTypes) {
|
|
241
|
+
const registry = VERSION_FILE_TYPES[fileType];
|
|
242
|
+
if (registry && entry.name === registry.filename) {
|
|
243
|
+
const version = registry.readVersion(fullPath);
|
|
244
|
+
const descriptor = {
|
|
245
|
+
path: fullPath,
|
|
246
|
+
relativePath: path.relative(repoRoot, fullPath),
|
|
247
|
+
type: fileType,
|
|
248
|
+
projectLabel: registry.projectLabel,
|
|
249
|
+
version,
|
|
250
|
+
selected: true
|
|
251
|
+
};
|
|
252
|
+
discoveredFiles.push(descriptor);
|
|
253
|
+
logger.debug('version-manager - discoverVersionFiles', 'Found version file', {
|
|
254
|
+
relativePath: descriptor.relativePath,
|
|
255
|
+
type: fileType,
|
|
256
|
+
version
|
|
257
|
+
});
|
|
286
258
|
}
|
|
287
259
|
}
|
|
288
|
-
}
|
|
260
|
+
},
|
|
261
|
+
onError: (dir, error) => {
|
|
289
262
|
logger.debug('version-manager - discoverVersionFiles', 'Error reading directory', {
|
|
290
263
|
dir,
|
|
291
264
|
error: error.message
|
|
292
265
|
});
|
|
293
266
|
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Start walking from repo root at depth 0
|
|
297
|
-
walkDirectory(repoRoot, 0);
|
|
267
|
+
});
|
|
298
268
|
|
|
299
269
|
// Sort results: root files first, then by depth, then alphabetically
|
|
300
270
|
discoveredFiles.sort((a, b) => {
|
package/package.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-git-hooks",
|
|
3
|
-
"version": "2.18.
|
|
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/mscope-S-L/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
|
-
"CLAUDE.md",
|
|
58
|
-
"LICENSE"
|
|
59
|
-
],
|
|
60
|
-
"dependencies": {
|
|
61
|
-
"@octokit/rest": "^21.0.0"
|
|
62
|
-
},
|
|
63
|
-
"devDependencies": {
|
|
64
|
-
"@types/jest": "^29.5.0",
|
|
65
|
-
"eslint": "^8.57.0",
|
|
66
|
-
"jest": "^29.7.0",
|
|
67
|
-
"prettier": "^3.2.0"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-git-hooks",
|
|
3
|
+
"version": "2.18.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/mscope-S-L/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
|
+
"CLAUDE.md",
|
|
58
|
+
"LICENSE"
|
|
59
|
+
],
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@octokit/rest": "^21.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/jest": "^29.5.0",
|
|
65
|
+
"eslint": "^8.57.0",
|
|
66
|
+
"jest": "^29.7.0",
|
|
67
|
+
"prettier": "^3.2.0"
|
|
68
|
+
}
|
|
69
|
+
}
|