claude-git-hooks 2.67.3 → 2.68.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 +21 -0
- package/bin/claude-hooks +19 -0
- package/lib/commands/install.js +9 -19
- package/lib/commands/update.js +14 -28
- package/lib/defaults.json +4 -0
- package/lib/utils/auto-update.js +198 -0
- package/package.json +84 -84
- package/templates/config.advanced.example.json +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ 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.68.0] - 2026-06-12
|
|
9
|
+
|
|
10
|
+
### ✨ Added
|
|
11
|
+
- Silent, throttled pre-command auto-update guard that checks for newer versions before running commands and self-updates automatically (#196)
|
|
12
|
+
- New `autoUpdate` configuration section with `enabled` (default: true) and `intervalHours` (default: 24) settings to control automatic update behavior
|
|
13
|
+
- Centralized `lib/utils/auto-update.js` module providing shared update logic (`getUpdateStatus`, `performUpdate`, `shouldAutoCheck`, `maybeAutoUpdate`) for all update paths
|
|
14
|
+
|
|
15
|
+
### 🔧 Changed
|
|
16
|
+
- Refactored `update` command into a thin wrapper over the shared auto-update module, eliminating duplicated version-check and npm-install logic
|
|
17
|
+
- Refactored install-time update prompt to use shared `getUpdateStatus()` and `performUpdate()` instead of inline implementation
|
|
18
|
+
- Auto-update is fully fail-open and skipped in `--headless` mode — never blocks the real command on failure
|
|
19
|
+
- Excluded fast/offline commands (`update`, `install`, `uninstall`, `help`, `version`, `migrate-config`) from the pre-command auto-update check to avoid recursion and unnecessary latency
|
|
20
|
+
|
|
21
|
+
### 🐛 Fixed
|
|
22
|
+
- Fixed install-time update prompt incorrectly offering to "update" dev builds that are ahead of the latest published npm version
|
|
23
|
+
|
|
24
|
+
### 🗑️ Removed
|
|
25
|
+
- Removed `check-version.sh` template installation from the install command (replaced by the integrated auto-update guard)
|
|
26
|
+
- Removed inline `npm install -g` and `runInstall(['--force'])` calls from `update.js` and `install.js` in favor of centralized `performUpdate()`
|
|
27
|
+
|
|
28
|
+
|
|
8
29
|
## [2.67.3] - 2026-06-12
|
|
9
30
|
|
|
10
31
|
### ✨ Added
|
package/bin/claude-hooks
CHANGED
|
@@ -131,6 +131,25 @@ async function main() {
|
|
|
131
131
|
logger.debug('bin - main', 'Authorization guard bypassed (--headless)', { command: entry.name });
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// --- Pre-command auto-update guard (silent, throttled) ---
|
|
135
|
+
// Single choke-point: every command flows through here, so auto-update is added
|
|
136
|
+
// once instead of in each command file. Mirrors the authorization guard above.
|
|
137
|
+
// Fully fail-open — never blocks the real command.
|
|
138
|
+
if (!isHeadless) {
|
|
139
|
+
try {
|
|
140
|
+
const { maybeAutoUpdate } = await import('../lib/utils/auto-update.js');
|
|
141
|
+
const updated = await maybeAutoUpdate(entry.name);
|
|
142
|
+
if (updated) {
|
|
143
|
+
// The running binary was just replaced; user must re-run their command.
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
logger.debug('bin - main', 'Pre-command auto-update skipped (non-fatal)', {
|
|
148
|
+
error: err.message
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
134
153
|
// --- Commands with special argument handling ---
|
|
135
154
|
|
|
136
155
|
// analyze: translate flags to options object
|
package/lib/commands/install.js
CHANGED
|
@@ -27,10 +27,9 @@ import {
|
|
|
27
27
|
getGitHooksPath,
|
|
28
28
|
isWindows,
|
|
29
29
|
getClaudeCommand,
|
|
30
|
-
getPackageJson,
|
|
31
|
-
getLatestVersion,
|
|
32
30
|
Entertainment
|
|
33
31
|
} from './helpers.js';
|
|
32
|
+
import { getUpdateStatus, performUpdate } from '../utils/auto-update.js';
|
|
34
33
|
import { runSetupGitHub } from './setup-github.js';
|
|
35
34
|
import { generateCompletionData } from '../cli-metadata.js';
|
|
36
35
|
import { getConfig } from '../config.js';
|
|
@@ -41,11 +40,12 @@ import { getConfig } from '../config.js';
|
|
|
41
40
|
*/
|
|
42
41
|
async function checkVersionAndPromptUpdate() {
|
|
43
42
|
try {
|
|
44
|
-
const currentVersion =
|
|
45
|
-
const latestVersion = await getLatestVersion('claude-git-hooks');
|
|
43
|
+
const { currentVersion, latestVersion, isNewer } = await getUpdateStatus();
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
// isNewer guards against the `===` downgrade bug: a dev build ahead of npm
|
|
46
|
+
// (current > latest) must NOT prompt to "update" to an older published version.
|
|
47
|
+
if (!isNewer) {
|
|
48
|
+
return true; // Already on latest (or a dev build ahead of npm)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
console.log('');
|
|
@@ -58,13 +58,13 @@ async function checkVersionAndPromptUpdate() {
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
return new Promise((resolve) => {
|
|
61
|
-
rl.question('Do you want to update now? (y/n): ', (answer) => {
|
|
61
|
+
rl.question('Do you want to update now? (y/n): ', async (answer) => {
|
|
62
62
|
rl.close();
|
|
63
63
|
|
|
64
64
|
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
65
|
-
info('Updating claude-git-hooks...');
|
|
66
65
|
try {
|
|
67
|
-
|
|
66
|
+
// install is about to run, so skip the hook reinstall here
|
|
67
|
+
await performUpdate({ reinstallHooks: false, silent: false });
|
|
68
68
|
success('Update completed. Please run your command again.');
|
|
69
69
|
process.exit(0); // Exit so user restarts the process
|
|
70
70
|
} catch (e) {
|
|
@@ -463,16 +463,6 @@ export async function runInstall(args) {
|
|
|
463
463
|
success(`${hook} installed`);
|
|
464
464
|
});
|
|
465
465
|
|
|
466
|
-
// Copy version verification script with LF line endings
|
|
467
|
-
const checkVersionSource = path.join(templatesPath, 'check-version.sh');
|
|
468
|
-
const checkVersionDest = path.join(hooksPath, 'check-version.sh');
|
|
469
|
-
|
|
470
|
-
if (fs.existsSync(checkVersionSource)) {
|
|
471
|
-
copyWithLF(checkVersionSource, checkVersionDest);
|
|
472
|
-
fs.chmodSync(checkVersionDest, '755');
|
|
473
|
-
success('Version verification script installed');
|
|
474
|
-
}
|
|
475
|
-
|
|
476
466
|
// Create .claude directory if it doesn't exist
|
|
477
467
|
const claudeDir = '.claude';
|
|
478
468
|
if (!fs.existsSync(claudeDir)) {
|
package/lib/commands/update.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* File: update.js
|
|
3
3
|
* Purpose: Update command - update to the latest version
|
|
4
|
+
*
|
|
5
|
+
* Thin wrapper over the shared auto-update logic in lib/utils/auto-update.js.
|
|
6
|
+
* Runs verbosely (silent: false) — this is a user-initiated update.
|
|
4
7
|
*/
|
|
5
8
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
error,
|
|
9
|
-
success,
|
|
10
|
-
info,
|
|
11
|
-
warning,
|
|
12
|
-
getPackageJson,
|
|
13
|
-
getLatestVersion,
|
|
14
|
-
compareVersions
|
|
15
|
-
} from './helpers.js';
|
|
16
|
-
import { runInstall } from './install.js';
|
|
9
|
+
import { error, success, info, warning } from './helpers.js';
|
|
10
|
+
import { getUpdateStatus, performUpdate } from '../utils/auto-update.js';
|
|
17
11
|
|
|
18
12
|
/**
|
|
19
13
|
* Update command - update to the latest version
|
|
@@ -22,33 +16,26 @@ export async function runUpdate() {
|
|
|
22
16
|
info('Checking latest available version...');
|
|
23
17
|
|
|
24
18
|
try {
|
|
25
|
-
const currentVersion =
|
|
26
|
-
const latestVersion = await getLatestVersion('claude-git-hooks');
|
|
19
|
+
const { currentVersion, latestVersion, isNewer, isDev } = await getUpdateStatus();
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (comparison === 0) {
|
|
31
|
-
success(`You already have the latest version installed (${currentVersion})`);
|
|
32
|
-
return;
|
|
33
|
-
} else if (comparison > 0) {
|
|
21
|
+
if (isDev) {
|
|
34
22
|
info(`You are using a development version (${currentVersion})`);
|
|
35
23
|
info(`Latest published version: ${latestVersion}`);
|
|
36
24
|
success(`You already have the latest version installed (${currentVersion})`);
|
|
37
25
|
return;
|
|
38
26
|
}
|
|
39
27
|
|
|
28
|
+
if (!isNewer) {
|
|
29
|
+
success(`You already have the latest version installed (${currentVersion})`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
40
33
|
info(`Current version: ${currentVersion}`);
|
|
41
34
|
info(`Available version: ${latestVersion}`);
|
|
42
35
|
|
|
43
|
-
// Update the package
|
|
44
|
-
info('Updating claude-git-hooks...');
|
|
45
36
|
try {
|
|
46
|
-
|
|
37
|
+
await performUpdate({ reinstallHooks: true, silent: false });
|
|
47
38
|
success(`Successfully updated to version ${latestVersion}`);
|
|
48
|
-
|
|
49
|
-
// Reinstall hooks with the new version
|
|
50
|
-
info('Reinstalling hooks with the new version...');
|
|
51
|
-
await runInstall(['--force']);
|
|
52
39
|
} catch (updateError) {
|
|
53
40
|
error('Error updating. Try running: npm install -g claude-git-hooks@latest');
|
|
54
41
|
}
|
|
@@ -56,9 +43,8 @@ export async function runUpdate() {
|
|
|
56
43
|
warning('Could not check the latest available version');
|
|
57
44
|
warning('Trying to update anyway...');
|
|
58
45
|
try {
|
|
59
|
-
|
|
46
|
+
await performUpdate({ reinstallHooks: true, silent: false });
|
|
60
47
|
success('Update completed');
|
|
61
|
-
await runInstall(['--force']);
|
|
62
48
|
} catch (updateError) {
|
|
63
49
|
error(`Error updating: ${updateError.message}`);
|
|
64
50
|
}
|
package/lib/defaults.json
CHANGED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: auto-update.js
|
|
3
|
+
* Purpose: Centralized self-update logic shared by the manual `update` command,
|
|
4
|
+
* the install-time prompt, and the pre-command guard in bin/claude-hooks.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for: resolving current-vs-latest version, performing the
|
|
7
|
+
* `npm install -g` update + hook reinstall, throttling automatic checks, and the
|
|
8
|
+
* silent pre-command auto-update guard.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import {
|
|
15
|
+
info,
|
|
16
|
+
warning,
|
|
17
|
+
success,
|
|
18
|
+
getPackageJson,
|
|
19
|
+
getLatestVersion,
|
|
20
|
+
compareVersions
|
|
21
|
+
} from '../commands/helpers.js';
|
|
22
|
+
import { getRepoRoot } from './git-operations.js';
|
|
23
|
+
import { getConfig } from '../config.js';
|
|
24
|
+
import logger from './logger.js';
|
|
25
|
+
|
|
26
|
+
const PACKAGE_NAME = 'claude-git-hooks';
|
|
27
|
+
const CHECK_FILE = '.last-update-check';
|
|
28
|
+
const DEFAULT_INTERVAL_HOURS = 24;
|
|
29
|
+
|
|
30
|
+
// Commands that must NOT trigger the pre-command auto-update guard.
|
|
31
|
+
// Why: avoids recursion (update → install --force), and skips fast/offline
|
|
32
|
+
// commands where a network round-trip would only add latency.
|
|
33
|
+
const EXCLUDED_COMMANDS = new Set([
|
|
34
|
+
'update',
|
|
35
|
+
'install',
|
|
36
|
+
'uninstall',
|
|
37
|
+
'help',
|
|
38
|
+
'version',
|
|
39
|
+
'migrate-config'
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve current vs latest version and derived flags.
|
|
44
|
+
* @returns {Promise<{currentVersion:string, latestVersion:string, comparison:number, isNewer:boolean, isDev:boolean}>}
|
|
45
|
+
*/
|
|
46
|
+
export async function getUpdateStatus() {
|
|
47
|
+
const currentVersion = getPackageJson().version;
|
|
48
|
+
const latestVersion = await getLatestVersion(PACKAGE_NAME);
|
|
49
|
+
const comparison = compareVersions(currentVersion, latestVersion);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
currentVersion,
|
|
53
|
+
latestVersion,
|
|
54
|
+
comparison,
|
|
55
|
+
isNewer: comparison < 0,
|
|
56
|
+
isDev: comparison > 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Update the globally-installed package and (optionally) reinstall hooks in the
|
|
62
|
+
* current repo. Implements the approved flow:
|
|
63
|
+
* npm install -g claude-git-hooks@latest
|
|
64
|
+
* → locate repo root; if none, warn and stop; else reinstall hooks (--force)
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} [opts]
|
|
67
|
+
* @param {boolean} [opts.reinstallHooks=true] - reinstall hooks at repo root after update
|
|
68
|
+
* @param {boolean} [opts.silent=false] - suppress sub-process output and progress chatter
|
|
69
|
+
* @returns {Promise<{updated:boolean, hooksReinstalled:boolean}>}
|
|
70
|
+
*/
|
|
71
|
+
export async function performUpdate({ reinstallHooks = true, silent = false } = {}) {
|
|
72
|
+
const stdio = silent ? 'ignore' : 'inherit';
|
|
73
|
+
|
|
74
|
+
if (!silent) {
|
|
75
|
+
info('Updating claude-git-hooks...');
|
|
76
|
+
}
|
|
77
|
+
execSync(`npm install -g ${PACKAGE_NAME}@latest`, { stdio });
|
|
78
|
+
|
|
79
|
+
if (!reinstallHooks) {
|
|
80
|
+
return { updated: true, hooksReinstalled: false };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Locate the repo root: this is the "look for root" step. getRepoRoot()
|
|
84
|
+
// throws (GitError) outside a git repo — treat that as "no root → warn & stop".
|
|
85
|
+
try {
|
|
86
|
+
const root = getRepoRoot();
|
|
87
|
+
logger.debug('auto-update - performUpdate', 'Repo root located for hook reinstall', { root });
|
|
88
|
+
} catch {
|
|
89
|
+
warning(
|
|
90
|
+
'Package updated, but you are not inside a git repository — hooks were not reinstalled.\n' +
|
|
91
|
+
' cd into your repo and run: claude-hooks install --force'
|
|
92
|
+
);
|
|
93
|
+
return { updated: true, hooksReinstalled: false };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!silent) {
|
|
97
|
+
info('Reinstalling hooks with the new version...');
|
|
98
|
+
}
|
|
99
|
+
// Dynamic import breaks the static cycle (install.js imports this module).
|
|
100
|
+
const { runInstall } = await import('../commands/install.js');
|
|
101
|
+
await runInstall(silent ? ['--force', '--headless'] : ['--force']);
|
|
102
|
+
|
|
103
|
+
return { updated: true, hooksReinstalled: true };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Whether enough time has elapsed since the last auto-update check.
|
|
108
|
+
* Throttle state lives in `<repoRoot>/.claude/.last-update-check` (.claude is gitignored).
|
|
109
|
+
* Fail-open: any FS/parse error returns true (allow the check).
|
|
110
|
+
*
|
|
111
|
+
* @param {number} [intervalHours=24]
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
export function shouldAutoCheck(intervalHours = DEFAULT_INTERVAL_HOURS) {
|
|
115
|
+
try {
|
|
116
|
+
const file = path.join(getRepoRoot(), '.claude', CHECK_FILE);
|
|
117
|
+
if (!fs.existsSync(file)) return true;
|
|
118
|
+
|
|
119
|
+
const last = Number(fs.readFileSync(file, 'utf8').trim());
|
|
120
|
+
if (!Number.isFinite(last)) return true;
|
|
121
|
+
|
|
122
|
+
const elapsedHours = (Date.now() - last) / (1000 * 60 * 60);
|
|
123
|
+
return elapsedHours >= intervalHours;
|
|
124
|
+
} catch {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Record the current time as the last auto-update check. Best-effort (fail-open).
|
|
131
|
+
*/
|
|
132
|
+
export function recordCheckTimestamp() {
|
|
133
|
+
try {
|
|
134
|
+
const dir = path.join(getRepoRoot(), '.claude');
|
|
135
|
+
if (!fs.existsSync(dir)) return;
|
|
136
|
+
fs.writeFileSync(path.join(dir, CHECK_FILE), String(Date.now()), 'utf8');
|
|
137
|
+
} catch {
|
|
138
|
+
// best effort — throttling is an optimization, not a correctness requirement
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Pre-command auto-update guard. Runs SILENTLY before a command in bin/claude-hooks.
|
|
144
|
+
* Returns true when an update was performed (the running binary changed, so the
|
|
145
|
+
* caller should exit and ask the user to re-run their command).
|
|
146
|
+
*
|
|
147
|
+
* Fully fail-open: any error is swallowed (debug-logged) and returns false so the
|
|
148
|
+
* real command always proceeds.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} command - the resolved command name (entry.name)
|
|
151
|
+
* @returns {Promise<boolean>}
|
|
152
|
+
*/
|
|
153
|
+
export async function maybeAutoUpdate(command) {
|
|
154
|
+
// Defensive recursion guard (in-process runInstall does not re-enter the router,
|
|
155
|
+
// but this is cheap insurance against any future re-spawn).
|
|
156
|
+
if (process.env.CLAUDE_HOOKS_UPDATE_IN_PROGRESS) return false;
|
|
157
|
+
if (EXCLUDED_COMMANDS.has(command)) return false;
|
|
158
|
+
|
|
159
|
+
let autoUpdate = { enabled: true, intervalHours: DEFAULT_INTERVAL_HOURS };
|
|
160
|
+
try {
|
|
161
|
+
const config = await getConfig();
|
|
162
|
+
autoUpdate = { ...autoUpdate, ...(config.autoUpdate || {}) };
|
|
163
|
+
} catch {
|
|
164
|
+
// Config load failure → use defaults (enabled)
|
|
165
|
+
}
|
|
166
|
+
if (autoUpdate.enabled === false) return false;
|
|
167
|
+
|
|
168
|
+
// Must be inside a repo: throttle state + hook reinstall target live there.
|
|
169
|
+
try {
|
|
170
|
+
getRepoRoot();
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!shouldAutoCheck(autoUpdate.intervalHours)) return false;
|
|
176
|
+
// Throttle the next attempt regardless of network outcome (avoids offline spam).
|
|
177
|
+
recordCheckTimestamp();
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
process.env.CLAUDE_HOOKS_UPDATE_IN_PROGRESS = '1';
|
|
181
|
+
|
|
182
|
+
const status = await getUpdateStatus();
|
|
183
|
+
if (!status.isNewer) return false;
|
|
184
|
+
|
|
185
|
+
await performUpdate({ reinstallHooks: true, silent: true });
|
|
186
|
+
success(
|
|
187
|
+
`⬆️ Updated claude-git-hooks to v${status.latestVersion} — please re-run your command.`
|
|
188
|
+
);
|
|
189
|
+
return true;
|
|
190
|
+
} catch (e) {
|
|
191
|
+
logger.debug('auto-update - maybeAutoUpdate', 'Auto-update check skipped (non-fatal)', {
|
|
192
|
+
error: e.message
|
|
193
|
+
});
|
|
194
|
+
return false;
|
|
195
|
+
} finally {
|
|
196
|
+
delete process.env.CLAUDE_HOOKS_UPDATE_IN_PROGRESS;
|
|
197
|
+
}
|
|
198
|
+
}
|
package/package.json
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-git-hooks",
|
|
3
|
-
"version": "2.
|
|
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
|
-
"test:e2e": "bash test/manual/sdlc-stability-check.sh",
|
|
20
|
-
"lint": "eslint lib/ bin/claude-hooks .library/librarian/",
|
|
21
|
-
"lint:fix": "eslint lib/ bin/claude-hooks .library/librarian/ --fix",
|
|
22
|
-
"format": "prettier --write \"lib/**/*.js\" \"bin/**\" \"test/**/*.js\"",
|
|
23
|
-
"precommit": "npm run lint && npm run test:smoke",
|
|
24
|
-
"prepublishOnly": "npm run test:all",
|
|
25
|
-
"library:check": "node .library/bin/library check",
|
|
26
|
-
"library:regenerate": "node .library/bin/library regenerate",
|
|
27
|
-
"library:extract": "node .library/bin/library extract",
|
|
28
|
-
"library:tokens": "node .library/bin/library tokens",
|
|
29
|
-
"library:graph": "node .library/bin/library graph",
|
|
30
|
-
"library:inject": "node .library/bin/library inject",
|
|
31
|
-
"library:validate": "node .library/bin/library validate",
|
|
32
|
-
"library:report": "node .library/bin/library report"
|
|
33
|
-
},
|
|
34
|
-
"keywords": [
|
|
35
|
-
"git",
|
|
36
|
-
"hooks",
|
|
37
|
-
"claude",
|
|
38
|
-
"ai",
|
|
39
|
-
"code-review",
|
|
40
|
-
"commit-messages",
|
|
41
|
-
"pre-commit",
|
|
42
|
-
"automation"
|
|
43
|
-
],
|
|
44
|
-
"author": "Pablo Rovito",
|
|
45
|
-
"license": "MIT",
|
|
46
|
-
"repository": {
|
|
47
|
-
"type": "git",
|
|
48
|
-
"url": "https://github.com/mscope-S-L/git-hooks.git"
|
|
49
|
-
},
|
|
50
|
-
"engines": {
|
|
51
|
-
"node": ">=16.9.0"
|
|
52
|
-
},
|
|
53
|
-
"engineStrict": false,
|
|
54
|
-
"os": [
|
|
55
|
-
"darwin",
|
|
56
|
-
"linux",
|
|
57
|
-
"win32"
|
|
58
|
-
],
|
|
59
|
-
"preferGlobal": true,
|
|
60
|
-
"files": [
|
|
61
|
-
"bin/",
|
|
62
|
-
"lib/",
|
|
63
|
-
"templates/",
|
|
64
|
-
"README.md",
|
|
65
|
-
"CHANGELOG.md",
|
|
66
|
-
"CLAUDE.md",
|
|
67
|
-
"LICENSE"
|
|
68
|
-
],
|
|
69
|
-
"dependencies": {
|
|
70
|
-
"@anthropic-ai/sdk": "^0.91.0",
|
|
71
|
-
"@octokit/rest": "^21.0.0",
|
|
72
|
-
"langfuse": "^3.38.20"
|
|
73
|
-
},
|
|
74
|
-
"devDependencies": {
|
|
75
|
-
"@types/jest": "^29.5.0",
|
|
76
|
-
"eslint": "^8.57.1",
|
|
77
|
-
"jest": "^29.7.0",
|
|
78
|
-
"js-tiktoken": "^1.0.18",
|
|
79
|
-
"madge": "^8.0.0",
|
|
80
|
-
"prettier": "^3.2.0",
|
|
81
|
-
"tree-sitter-wasms": "^0.1.13",
|
|
82
|
-
"web-tree-sitter": "^0.24.7"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-git-hooks",
|
|
3
|
+
"version": "2.68.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
|
+
"test:e2e": "bash test/manual/sdlc-stability-check.sh",
|
|
20
|
+
"lint": "eslint lib/ bin/claude-hooks .library/librarian/",
|
|
21
|
+
"lint:fix": "eslint lib/ bin/claude-hooks .library/librarian/ --fix",
|
|
22
|
+
"format": "prettier --write \"lib/**/*.js\" \"bin/**\" \"test/**/*.js\"",
|
|
23
|
+
"precommit": "npm run lint && npm run test:smoke",
|
|
24
|
+
"prepublishOnly": "npm run test:all",
|
|
25
|
+
"library:check": "node .library/bin/library check",
|
|
26
|
+
"library:regenerate": "node .library/bin/library regenerate",
|
|
27
|
+
"library:extract": "node .library/bin/library extract",
|
|
28
|
+
"library:tokens": "node .library/bin/library tokens",
|
|
29
|
+
"library:graph": "node .library/bin/library graph",
|
|
30
|
+
"library:inject": "node .library/bin/library inject",
|
|
31
|
+
"library:validate": "node .library/bin/library validate",
|
|
32
|
+
"library:report": "node .library/bin/library report"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"git",
|
|
36
|
+
"hooks",
|
|
37
|
+
"claude",
|
|
38
|
+
"ai",
|
|
39
|
+
"code-review",
|
|
40
|
+
"commit-messages",
|
|
41
|
+
"pre-commit",
|
|
42
|
+
"automation"
|
|
43
|
+
],
|
|
44
|
+
"author": "Pablo Rovito",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/mscope-S-L/git-hooks.git"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=16.9.0"
|
|
52
|
+
},
|
|
53
|
+
"engineStrict": false,
|
|
54
|
+
"os": [
|
|
55
|
+
"darwin",
|
|
56
|
+
"linux",
|
|
57
|
+
"win32"
|
|
58
|
+
],
|
|
59
|
+
"preferGlobal": true,
|
|
60
|
+
"files": [
|
|
61
|
+
"bin/",
|
|
62
|
+
"lib/",
|
|
63
|
+
"templates/",
|
|
64
|
+
"README.md",
|
|
65
|
+
"CHANGELOG.md",
|
|
66
|
+
"CLAUDE.md",
|
|
67
|
+
"LICENSE"
|
|
68
|
+
],
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@anthropic-ai/sdk": "^0.91.0",
|
|
71
|
+
"@octokit/rest": "^21.0.0",
|
|
72
|
+
"langfuse": "^3.38.20"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/jest": "^29.5.0",
|
|
76
|
+
"eslint": "^8.57.1",
|
|
77
|
+
"jest": "^29.7.0",
|
|
78
|
+
"js-tiktoken": "^1.0.18",
|
|
79
|
+
"madge": "^8.0.0",
|
|
80
|
+
"prettier": "^3.2.0",
|
|
81
|
+
"tree-sitter-wasms": "^0.1.13",
|
|
82
|
+
"web-tree-sitter": "^0.24.7"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -61,6 +61,11 @@
|
|
|
61
61
|
"failOnError": true,
|
|
62
62
|
"failOnWarning": false,
|
|
63
63
|
"timeout": 30000
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
"autoUpdate": {
|
|
67
|
+
"enabled": true,
|
|
68
|
+
"intervalHours": 24
|
|
64
69
|
}
|
|
65
70
|
},
|
|
66
71
|
|
|
@@ -168,6 +173,18 @@
|
|
|
168
173
|
"description": "Timeout in milliseconds for each linter execution",
|
|
169
174
|
"default": "30000",
|
|
170
175
|
"use_case": "Increase for large projects where linters take longer"
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
"autoUpdate.enabled": {
|
|
179
|
+
"description": "Enable the silent, throttled pre-command auto-update check. When a newer published version is detected before running a command, claude-git-hooks updates itself globally, reinstalls hooks (--force), and asks you to re-run your command. Excludes update/install/uninstall/help/version/migrate-config, and is skipped in --headless mode. The manual `claude-hooks update` command runs verbosely regardless of this setting.",
|
|
180
|
+
"default": "true",
|
|
181
|
+
"use_case": "Set to false to disable automatic background updates and rely only on `claude-hooks update`"
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
"autoUpdate.intervalHours": {
|
|
185
|
+
"description": "Minimum hours between pre-command auto-update checks. Throttle state is stored in .claude/.last-update-check (gitignored).",
|
|
186
|
+
"default": "24",
|
|
187
|
+
"use_case": "Lower it for faster propagation of releases, or raise it to reduce network checks"
|
|
171
188
|
}
|
|
172
189
|
},
|
|
173
190
|
|