claude-git-hooks 2.35.3 → 2.43.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 +135 -0
- package/CLAUDE.md +24 -1389
- package/README.md +113 -0
- package/bin/claude-hooks +11 -7
- package/lib/cli-metadata.js +17 -3
- package/lib/commands/analyze-pr.js +270 -145
- package/lib/commands/analyze.js +151 -3
- package/lib/commands/create-pr.js +345 -134
- package/lib/commands/helpers.js +9 -4
- package/lib/commands/hooks.js +5 -5
- package/lib/commands/install.js +77 -28
- package/lib/commands/lint.js +120 -4
- package/lib/config.js +3 -0
- package/lib/hooks/pre-commit.js +26 -5
- package/lib/hooks/prepare-commit-msg.js +78 -4
- package/lib/utils/analysis-engine.js +12 -6
- package/lib/utils/claude-client.js +222 -12
- package/lib/utils/claude-diagnostics.js +5 -4
- package/lib/utils/cost-tracker.js +128 -0
- package/lib/utils/diff-analysis-orchestrator.js +2 -1
- package/lib/utils/git-operations.js +105 -2
- package/lib/utils/hooks-verified-marker.js +121 -0
- package/lib/utils/interactive-ui.js +4 -4
- package/lib/utils/judge.js +3 -2
- package/lib/utils/langfuse-tracer.js +156 -0
- package/lib/utils/logger.js +30 -5
- package/lib/utils/pr-metadata-engine.js +4 -2
- package/package.json +4 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: hooks-verified-marker.js
|
|
3
|
+
* Purpose: Manages the .git/.hooks-verified marker file used to coordinate
|
|
4
|
+
* between pre-commit.js (writes marker on success) and prepare-commit-msg.js
|
|
5
|
+
* (reads marker, appends Hooks-Verified trailer).
|
|
6
|
+
*
|
|
7
|
+
* The marker contains the tree-SHA of the staged content at the time pre-commit
|
|
8
|
+
* passed. prepare-commit-msg compares it against the current tree-SHA to detect
|
|
9
|
+
* staging changes between hooks (e.g., user ran `git add` after pre-commit).
|
|
10
|
+
*
|
|
11
|
+
* Design:
|
|
12
|
+
* - No git operations — standalone testable
|
|
13
|
+
* - Marker file lives inside .git/ (not tracked, per-worktree)
|
|
14
|
+
* - Permissions 0o600 (defensive, even though .git/ is user-scoped)
|
|
15
|
+
*
|
|
16
|
+
* Dependencies:
|
|
17
|
+
* - fs (sync operations for simplicity — marker is tiny)
|
|
18
|
+
* - path
|
|
19
|
+
* - logger
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import fs from 'fs';
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import logger from './logger.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the path to the hooks-verified marker file
|
|
28
|
+
*
|
|
29
|
+
* @param {string} repoRoot - Absolute path to repository root
|
|
30
|
+
* @returns {string} Absolute path to .git/.hooks-verified
|
|
31
|
+
*/
|
|
32
|
+
const getMarkerPath = (repoRoot) => path.join(repoRoot, '.git', '.hooks-verified');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Writes the hooks-verified marker file
|
|
36
|
+
*
|
|
37
|
+
* @param {string} repoRoot - Absolute path to repository root
|
|
38
|
+
* @param {string} treeSha - 40-char hex SHA from git write-tree
|
|
39
|
+
* @param {string} version - Package version (e.g., '2.41.0')
|
|
40
|
+
* @throws {Error} If .git/ directory doesn't exist or write fails
|
|
41
|
+
*/
|
|
42
|
+
const writeMarker = (repoRoot, treeSha, version) => {
|
|
43
|
+
const markerPath = getMarkerPath(repoRoot);
|
|
44
|
+
|
|
45
|
+
const payload = JSON.stringify({
|
|
46
|
+
tree: treeSha,
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
version
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
fs.writeFileSync(markerPath, payload, { mode: 0o600 });
|
|
52
|
+
|
|
53
|
+
logger.debug('hooks-verified-marker - writeMarker', 'Marker written', {
|
|
54
|
+
path: markerPath,
|
|
55
|
+
treeSha
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Reads the hooks-verified marker file
|
|
61
|
+
*
|
|
62
|
+
* @param {string} repoRoot - Absolute path to repository root
|
|
63
|
+
* @returns {Object|null} Parsed marker object, or null if absent/malformed
|
|
64
|
+
*/
|
|
65
|
+
const readMarker = (repoRoot) => {
|
|
66
|
+
const markerPath = getMarkerPath(repoRoot);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const content = fs.readFileSync(markerPath, 'utf8');
|
|
70
|
+
const parsed = JSON.parse(content);
|
|
71
|
+
|
|
72
|
+
logger.debug('hooks-verified-marker - readMarker', 'Marker read', {
|
|
73
|
+
tree: parsed.tree
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return parsed;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (err.code === 'ENOENT') {
|
|
79
|
+
logger.debug('hooks-verified-marker - readMarker', 'No marker file found');
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Malformed JSON or other read error
|
|
84
|
+
logger.warning(`Hooks-verified marker is malformed: ${err.message}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Deletes the hooks-verified marker file (best-effort)
|
|
91
|
+
*
|
|
92
|
+
* @param {string} repoRoot - Absolute path to repository root
|
|
93
|
+
*/
|
|
94
|
+
const consumeMarker = (repoRoot) => {
|
|
95
|
+
const markerPath = getMarkerPath(repoRoot);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
fs.unlinkSync(markerPath);
|
|
99
|
+
logger.debug('hooks-verified-marker - consumeMarker', 'Marker consumed');
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (err.code === 'ENOENT') {
|
|
102
|
+
// Already gone — nothing to do
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
logger.debug('hooks-verified-marker - consumeMarker', 'Could not delete marker', {
|
|
106
|
+
error: err.message
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validates a marker against the current staged tree SHA
|
|
113
|
+
*
|
|
114
|
+
* @param {Object|null} marker - Parsed marker from readMarker()
|
|
115
|
+
* @param {string} currentTreeSha - Current tree SHA from getStagedTreeSha()
|
|
116
|
+
* @returns {boolean} True if marker is valid and tree SHAs match
|
|
117
|
+
*/
|
|
118
|
+
const validateMarker = (marker, currentTreeSha) =>
|
|
119
|
+
marker !== null && marker.tree === currentTreeSha;
|
|
120
|
+
|
|
121
|
+
export { getMarkerPath, writeMarker, readMarker, consumeMarker, validateMarker };
|
|
@@ -267,7 +267,7 @@ export const showSpinner = (message) => {
|
|
|
267
267
|
* @param {string} message - Success message
|
|
268
268
|
*/
|
|
269
269
|
export const showSuccess = (message) => {
|
|
270
|
-
|
|
270
|
+
logger.success(message);
|
|
271
271
|
};
|
|
272
272
|
|
|
273
273
|
/**
|
|
@@ -275,7 +275,7 @@ export const showSuccess = (message) => {
|
|
|
275
275
|
* @param {string} message - Error message
|
|
276
276
|
*/
|
|
277
277
|
export const showError = (message) => {
|
|
278
|
-
|
|
278
|
+
logger.error('interactive-ui', message);
|
|
279
279
|
};
|
|
280
280
|
|
|
281
281
|
/**
|
|
@@ -283,7 +283,7 @@ export const showError = (message) => {
|
|
|
283
283
|
* @param {string} message - Warning message
|
|
284
284
|
*/
|
|
285
285
|
export const showWarning = (message) => {
|
|
286
|
-
|
|
286
|
+
logger.warning(message);
|
|
287
287
|
};
|
|
288
288
|
|
|
289
289
|
/**
|
|
@@ -291,7 +291,7 @@ export const showWarning = (message) => {
|
|
|
291
291
|
* @param {string} message - Info message
|
|
292
292
|
*/
|
|
293
293
|
export const showInfo = (message) => {
|
|
294
|
-
|
|
294
|
+
logger.info(message);
|
|
295
295
|
};
|
|
296
296
|
|
|
297
297
|
/**
|
package/lib/utils/judge.js
CHANGED
|
@@ -102,7 +102,7 @@ const applyFix = async (fix, repoRoot) => {
|
|
|
102
102
|
* @param {Object} config - Application config
|
|
103
103
|
* @returns {Promise<{fixedCount: number, falsePositiveCount: number, remainingIssues: Array, verdicts: Array}>}
|
|
104
104
|
*/
|
|
105
|
-
const judgeAndFix = async (analysisResult, filesData, config) => {
|
|
105
|
+
const judgeAndFix = async (analysisResult, filesData, config, { headless = false } = {}) => {
|
|
106
106
|
const model = config.judge?.model || JUDGE_DEFAULT_MODEL;
|
|
107
107
|
const repoRoot = getRepoRoot();
|
|
108
108
|
|
|
@@ -135,7 +135,8 @@ const judgeAndFix = async (analysisResult, filesData, config) => {
|
|
|
135
135
|
// Call LLM
|
|
136
136
|
const response = await executeClaudeWithRetry(prompt, {
|
|
137
137
|
model,
|
|
138
|
-
timeout: JUDGE_TIMEOUT
|
|
138
|
+
timeout: JUDGE_TIMEOUT,
|
|
139
|
+
headless
|
|
139
140
|
});
|
|
140
141
|
|
|
141
142
|
const parsed = extractJSON(response);
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File: langfuse-tracer.js
|
|
3
|
+
* Purpose: Optional Langfuse integration for headless SDK calls.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors Lumiere's observability/langfuse_tracer.py — same field names,
|
|
6
|
+
* same metadata conventions, same graceful degradation pattern.
|
|
7
|
+
*
|
|
8
|
+
* Disabled by default. Activate via env vars:
|
|
9
|
+
* LANGFUSE_ENABLED=true
|
|
10
|
+
* LANGFUSE_SECRET_KEY=sk-lf-...
|
|
11
|
+
* LANGFUSE_PUBLIC_KEY=pk-lf-...
|
|
12
|
+
* LANGFUSE_HOST=https://cloud.langfuse.com (optional, default shown)
|
|
13
|
+
*
|
|
14
|
+
* The `langfuse` npm package is lazy-loaded (same pattern as @anthropic-ai/sdk).
|
|
15
|
+
* It is NOT listed in package.json — expected to be installed in the ECS/CI
|
|
16
|
+
* environment where headless mode runs.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import logger from './logger.js';
|
|
20
|
+
import { calculateCost } from './cost-tracker.js';
|
|
21
|
+
|
|
22
|
+
let _client = null;
|
|
23
|
+
let _enabled = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check whether Langfuse tracing is enabled and credentials are present.
|
|
27
|
+
* Result is cached after first evaluation.
|
|
28
|
+
*
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
function isEnabled() {
|
|
32
|
+
if (_enabled !== null) return _enabled;
|
|
33
|
+
_enabled =
|
|
34
|
+
process.env.LANGFUSE_ENABLED === 'true' &&
|
|
35
|
+
!!process.env.LANGFUSE_SECRET_KEY &&
|
|
36
|
+
!!process.env.LANGFUSE_PUBLIC_KEY;
|
|
37
|
+
return _enabled;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Lazily initialize the Langfuse client. Returns null when disabled or on any failure.
|
|
42
|
+
*
|
|
43
|
+
* @returns {Promise<Object|null>}
|
|
44
|
+
*/
|
|
45
|
+
async function _getClient() {
|
|
46
|
+
if (_client) return _client;
|
|
47
|
+
if (!isEnabled()) return null;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const { Langfuse } = await import('langfuse');
|
|
51
|
+
_client = new Langfuse({
|
|
52
|
+
publicKey: process.env.LANGFUSE_PUBLIC_KEY,
|
|
53
|
+
secretKey: process.env.LANGFUSE_SECRET_KEY,
|
|
54
|
+
baseUrl: process.env.LANGFUSE_HOST || 'https://cloud.langfuse.com'
|
|
55
|
+
});
|
|
56
|
+
logger.debug('langfuse-tracer', 'Langfuse client initialized');
|
|
57
|
+
return _client;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.warning(`langfuse-tracer: failed to initialize — ${err.message}`);
|
|
60
|
+
_enabled = false;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Record a single LLM generation in Langfuse.
|
|
67
|
+
*
|
|
68
|
+
* Creates a trace (named `claude_hooks`) with one nested generation.
|
|
69
|
+
* Metadata field names match Lumiere's orchestrator.py for cross-repo
|
|
70
|
+
* dashboard consistency. All metadata values are strings (Langfuse requirement).
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} params
|
|
73
|
+
* @param {string} params.name - Generation name (e.g. 'executeClaude')
|
|
74
|
+
* @param {string} params.model - Full Anthropic model ID
|
|
75
|
+
* @param {string} [params.input] - Prompt text (optional, can be large)
|
|
76
|
+
* @param {string} [params.output] - Response text (optional)
|
|
77
|
+
* @param {Object} params.usage - Token counts from SDK response
|
|
78
|
+
* @param {number} params.usage.input_tokens
|
|
79
|
+
* @param {number} params.usage.output_tokens
|
|
80
|
+
* @param {number} [params.usage.cache_creation_input_tokens=0]
|
|
81
|
+
* @param {number} [params.usage.cache_read_input_tokens=0]
|
|
82
|
+
* @param {number} [params.durationMs=0] - Wall-clock milliseconds
|
|
83
|
+
*/
|
|
84
|
+
async function traceGeneration({ name, model, input, output, usage, durationMs = 0 }) {
|
|
85
|
+
const client = await _getClient();
|
|
86
|
+
if (!client) return;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const inputTokens = usage.input_tokens || 0;
|
|
90
|
+
const outputTokens = usage.output_tokens || 0;
|
|
91
|
+
const cacheCreation = usage.cache_creation_input_tokens || 0;
|
|
92
|
+
const cacheRead = usage.cache_read_input_tokens || 0;
|
|
93
|
+
|
|
94
|
+
const totalCost = calculateCost({
|
|
95
|
+
tokens_input: inputTokens,
|
|
96
|
+
tokens_output: outputTokens,
|
|
97
|
+
model,
|
|
98
|
+
cache_creation: cacheCreation,
|
|
99
|
+
cache_read: cacheRead
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const trace = client.trace({
|
|
103
|
+
name: 'claude_hooks',
|
|
104
|
+
tags: ['claude-hooks'],
|
|
105
|
+
metadata: { source: 'claude-hooks' }
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
trace.generation({
|
|
109
|
+
name,
|
|
110
|
+
model,
|
|
111
|
+
input: input !== undefined ? input : undefined,
|
|
112
|
+
output: output !== undefined ? output : undefined,
|
|
113
|
+
usage: {
|
|
114
|
+
input: inputTokens + cacheCreation + cacheRead,
|
|
115
|
+
output: outputTokens,
|
|
116
|
+
total: inputTokens + cacheCreation + cacheRead + outputTokens
|
|
117
|
+
},
|
|
118
|
+
metadata: {
|
|
119
|
+
tokens_input_regular: String(inputTokens),
|
|
120
|
+
cache_creation_tokens: String(cacheCreation),
|
|
121
|
+
cache_read_tokens: String(cacheRead),
|
|
122
|
+
total_cost_usd: String(totalCost),
|
|
123
|
+
duration_ms: String(durationMs),
|
|
124
|
+
source: 'claude-hooks'
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
logger.debug('langfuse-tracer', 'Generation recorded', { name, model, totalCost });
|
|
129
|
+
} catch (err) {
|
|
130
|
+
logger.warning(`langfuse-tracer: failed to record generation — ${err.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Flush pending Langfuse events. Call at the end of a command/analysis
|
|
136
|
+
* to ensure data is sent before the process exits.
|
|
137
|
+
*/
|
|
138
|
+
async function flush() {
|
|
139
|
+
if (!_client) return;
|
|
140
|
+
try {
|
|
141
|
+
await _client.flushAsync();
|
|
142
|
+
logger.debug('langfuse-tracer', 'Flushed');
|
|
143
|
+
} catch (err) {
|
|
144
|
+
logger.warning(`langfuse-tracer: flush failed — ${err.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Reset client state — for testing only.
|
|
150
|
+
*/
|
|
151
|
+
function _resetClient() {
|
|
152
|
+
_client = null;
|
|
153
|
+
_enabled = null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { isEnabled, traceGeneration, flush, _getClient, _resetClient };
|
package/lib/utils/logger.js
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
class Logger {
|
|
18
18
|
constructor({ debugMode = false } = {}) {
|
|
19
19
|
this.debugMode = debugMode;
|
|
20
|
+
this.jsonMode = false;
|
|
20
21
|
this.colors = {
|
|
21
22
|
reset: '\x1b[0m',
|
|
22
23
|
red: '\x1b[31m',
|
|
@@ -34,7 +35,8 @@ class Logger {
|
|
|
34
35
|
* @param {string} message - Simple message for end users
|
|
35
36
|
*/
|
|
36
37
|
info(message) {
|
|
37
|
-
|
|
38
|
+
const out = this.jsonMode ? console.error : console.log;
|
|
39
|
+
out(`${this.colors.blue}ℹ️ ${message}${this.colors.reset}`);
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -44,7 +46,8 @@ class Logger {
|
|
|
44
46
|
* @param {string} message - Success message
|
|
45
47
|
*/
|
|
46
48
|
success(message) {
|
|
47
|
-
|
|
49
|
+
const out = this.jsonMode ? console.error : console.log;
|
|
50
|
+
out(`${this.colors.green}✅ ${message}${this.colors.reset}`);
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
/**
|
|
@@ -54,7 +57,8 @@ class Logger {
|
|
|
54
57
|
* @param {string} message - Warning message
|
|
55
58
|
*/
|
|
56
59
|
warning(message) {
|
|
57
|
-
|
|
60
|
+
const out = this.jsonMode ? console.error : console.log;
|
|
61
|
+
out(`${this.colors.yellow}⚠️ ${message}${this.colors.reset}`);
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
/**
|
|
@@ -71,13 +75,14 @@ class Logger {
|
|
|
71
75
|
debug(context, message, data = {}) {
|
|
72
76
|
if (!this.debugMode) return;
|
|
73
77
|
|
|
78
|
+
const out = this.jsonMode ? console.error : console.log;
|
|
74
79
|
const timestamp = new Date().toISOString();
|
|
75
|
-
|
|
80
|
+
out(
|
|
76
81
|
`${this.colors.gray}[DEBUG ${timestamp}] [${context}] ${message}${this.colors.reset}`
|
|
77
82
|
);
|
|
78
83
|
|
|
79
84
|
if (Object.keys(data).length > 0) {
|
|
80
|
-
|
|
85
|
+
out(this.colors.gray, JSON.stringify(data, null, 2), this.colors.reset);
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -137,6 +142,26 @@ class Logger {
|
|
|
137
142
|
isDebugMode() {
|
|
138
143
|
return this.debugMode;
|
|
139
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Set JSON mode dynamically
|
|
148
|
+
* Why: When active, info/success/warning/debug write to stderr so stdout
|
|
149
|
+
* is reserved exclusively for structured JSON output.
|
|
150
|
+
*
|
|
151
|
+
* @param {boolean} enabled - Whether to enable JSON mode
|
|
152
|
+
*/
|
|
153
|
+
setJSONMode(enabled) {
|
|
154
|
+
this.jsonMode = !!enabled;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if JSON mode is enabled
|
|
159
|
+
*
|
|
160
|
+
* @returns {boolean} True if JSON mode is active
|
|
161
|
+
*/
|
|
162
|
+
isJSONMode() {
|
|
163
|
+
return this.jsonMode;
|
|
164
|
+
}
|
|
140
165
|
}
|
|
141
166
|
|
|
142
167
|
// Export singleton instance
|
|
@@ -371,7 +371,7 @@ export const getBranchContext = async (targetBranch) => {
|
|
|
371
371
|
export const generatePRMetadata = async (context, options = {}) => {
|
|
372
372
|
const config = await getConfig();
|
|
373
373
|
const configTimeout = config.analysis?.timeout;
|
|
374
|
-
const { timeout = configTimeout || DEFAULTS.timeout, hook = 'pr-metadata' } = options;
|
|
374
|
+
const { timeout = configTimeout || DEFAULTS.timeout, hook = 'pr-metadata', headless = false, costTracker = null } = options;
|
|
375
375
|
|
|
376
376
|
logger.debug('pr-metadata-engine - generatePRMetadata', 'Generating PR metadata', {
|
|
377
377
|
filesCount: context.filesCount,
|
|
@@ -411,7 +411,9 @@ export const generatePRMetadata = async (context, options = {}) => {
|
|
|
411
411
|
// Call Claude with retry logic
|
|
412
412
|
const response = await executeClaudeWithRetry(prompt, {
|
|
413
413
|
timeout,
|
|
414
|
-
telemetryContext
|
|
414
|
+
telemetryContext,
|
|
415
|
+
headless,
|
|
416
|
+
costTracker
|
|
415
417
|
});
|
|
416
418
|
|
|
417
419
|
logger.debug('pr-metadata-engine - generatePRMetadata', 'Claude response received', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-git-hooks",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.43.0",
|
|
4
4
|
"description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -60,7 +60,9 @@
|
|
|
60
60
|
"LICENSE"
|
|
61
61
|
],
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@
|
|
63
|
+
"@anthropic-ai/sdk": "^0.91.0",
|
|
64
|
+
"@octokit/rest": "^21.0.0",
|
|
65
|
+
"langfuse": "^3.38.20"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
68
|
"@types/jest": "^29.5.0",
|