claude-git-hooks 2.44.0 → 2.51.2

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.
@@ -23,10 +23,11 @@ import path from 'path';
23
23
  import { executeClaudeWithRetry, extractJSON } from './claude-client.js';
24
24
  import { loadPrompt } from './prompt-builder.js';
25
25
  import logger from './logger.js';
26
+ import { getDefaultSection, resolveSection } from './config-registry.js';
26
27
 
27
- // Orchestration model Opus for semantic grouping quality, not user-configurable
28
- const ORCHESTRATOR_MODEL = 'opus';
29
- const ORCHESTRATOR_TIMEOUT = 60000; // 60s — lightweight call, just file overview
28
+ // Orchestration config from lib/defaults.json (sync fallback)
29
+ const { model: ORCHESTRATOR_MODEL, timeout: ORCHESTRATOR_TIMEOUT } =
30
+ getDefaultSection('orchestrator');
30
31
 
31
32
  /**
32
33
  * Counts added/removed lines in a unified diff string
@@ -184,6 +185,10 @@ const _buildCommonContext = (filesData, batchGroups) => {
184
185
  * @returns {Promise<{batches: Array<{files: FileData[], rationale: string, model: string}>, commonContext: string}>}
185
186
  */
186
187
  export const orchestrateBatches = async (filesData, { headless = false } = {}) => {
188
+ // Resolve orchestrator config: remote settings.json > local defaults.json
189
+ const orchConfig = await resolveSection('orchestrator');
190
+ const orchModel = orchConfig?.model || ORCHESTRATOR_MODEL;
191
+ const orchTimeout = orchConfig?.timeout || ORCHESTRATOR_TIMEOUT;
187
192
  logger.debug('diff-analysis-orchestrator - orchestrateBatches', 'Building file overview', {
188
193
  fileCount: filesData.length
189
194
  });
@@ -214,20 +219,20 @@ export const orchestrateBatches = async (filesData, { headless = false } = {}) =
214
219
  }
215
220
 
216
221
  logger.debug('diff-analysis-orchestrator - orchestrateBatches', 'Calling Opus orchestrator', {
217
- model: ORCHESTRATOR_MODEL,
218
- timeout: ORCHESTRATOR_TIMEOUT
222
+ model: orchModel,
223
+ timeout: orchTimeout
219
224
  });
220
225
 
221
226
  let rawResponse;
222
227
  try {
223
228
  rawResponse = await executeClaudeWithRetry(prompt, {
224
- model: ORCHESTRATOR_MODEL,
225
- timeout: ORCHESTRATOR_TIMEOUT,
229
+ model: orchModel,
230
+ timeout: orchTimeout,
226
231
  headless,
227
232
  telemetryContext: {
228
233
  hook: 'orchestrator',
229
234
  fileCount: filesData.length,
230
- model: ORCHESTRATOR_MODEL
235
+ model: orchModel
231
236
  }
232
237
  });
233
238
  } catch (err) {
@@ -26,9 +26,9 @@ import { formatBlockingIssues, getAffectedFiles, formatFileContents } from './re
26
26
  import { getRepoRoot, getRepoName, getCurrentBranch } from './git-operations.js';
27
27
  import logger from './logger.js';
28
28
  import { recordMetric } from './metrics.js';
29
+ import { getDefaultSection, resolveSection } from './config-registry.js';
29
30
 
30
- const JUDGE_DEFAULT_MODEL = 'sonnet';
31
- const JUDGE_TIMEOUT = 360000;
31
+ const { model: JUDGE_DEFAULT_MODEL, timeout: JUDGE_TIMEOUT } = getDefaultSection('judge');
32
32
 
33
33
  /**
34
34
  * Applies a single search/replace fix to a file and stages it
@@ -103,7 +103,10 @@ const applyFix = async (fix, repoRoot) => {
103
103
  * @returns {Promise<{fixedCount: number, falsePositiveCount: number, remainingIssues: Array, verdicts: Array}>}
104
104
  */
105
105
  const judgeAndFix = async (analysisResult, filesData, config, { headless = false } = {}) => {
106
- const model = config.judge?.model || JUDGE_DEFAULT_MODEL;
106
+ // Resolve judge config: remote settings.json > local defaults.json
107
+ const judgeConfig = await resolveSection('judge');
108
+ const model = config.judge?.model ?? judgeConfig?.model ?? JUDGE_DEFAULT_MODEL;
109
+ const judgeTimeout = judgeConfig?.timeout ?? JUDGE_TIMEOUT;
107
110
  const repoRoot = getRepoRoot();
108
111
 
109
112
  // Get all issues: prefer details (all severities), fallback to blockingIssues
@@ -135,7 +138,7 @@ const judgeAndFix = async (analysisResult, filesData, config, { headless = false
135
138
  // Call LLM
136
139
  const response = await executeClaudeWithRetry(prompt, {
137
140
  model,
138
- timeout: JUDGE_TIMEOUT,
141
+ timeout: judgeTimeout,
139
142
  headless
140
143
  });
141
144
 
@@ -19,11 +19,13 @@
19
19
  import https from 'https';
20
20
  import logger from './logger.js';
21
21
  import { loadToken } from './token-store.js';
22
+ import { getDefaultSection } from './config-registry.js';
22
23
 
23
- const LINEAR_HOSTNAME = 'api.linear.app';
24
- const LINEAR_PATH = '/graphql';
25
- const LINEAR_TIMEOUT = 10000;
26
- const MAX_RETRIES = 2;
24
+ const linearDefaults = getDefaultSection('linear') || {};
25
+ const LINEAR_HOSTNAME = linearDefaults.hostname || 'api.linear.app';
26
+ const LINEAR_PATH = linearDefaults.path || '/graphql';
27
+ const LINEAR_TIMEOUT = linearDefaults.timeout || 10000;
28
+ const MAX_RETRIES = linearDefaults.maxRetries || 2;
27
29
 
28
30
  /**
29
31
  * Custom error for Linear connector failures
@@ -27,6 +27,7 @@ import {
27
27
  import { getRepoRoot } from './git-operations.js';
28
28
  import { which } from './which-command.js';
29
29
  import { fetchRemoteConfig } from './remote-config.js';
30
+ import { resolveSection } from './config-registry.js';
30
31
  import logger from './logger.js';
31
32
 
32
33
  /**
@@ -422,6 +423,9 @@ export async function runLinters(files, config, presetName = 'default') {
422
423
  const timeout = lintConfig.timeout || 30000;
423
424
 
424
425
  const tools = await getLinterToolsForPreset(presetName);
426
+
427
+ // Merge remote tools.json overrides (timeout, perFile, enabled) into tool definitions
428
+ const remoteToolsConfig = await resolveSection('tools');
425
429
  const results = [];
426
430
 
427
431
  logger.debug('linter-runner - runLinters', 'Starting linters', {
@@ -431,7 +435,18 @@ export async function runLinters(files, config, presetName = 'default') {
431
435
  autoFix
432
436
  });
433
437
 
434
- for (const toolDef of tools) {
438
+ for (let toolDef of tools) {
439
+ // Apply remote tool config overrides (remote > local)
440
+ const remoteToolOverride = remoteToolsConfig?.[toolDef.name];
441
+ if (remoteToolOverride) {
442
+ toolDef = { ...toolDef };
443
+ if (remoteToolOverride.timeout !== undefined) toolDef.timeout = remoteToolOverride.timeout;
444
+ if (remoteToolOverride.perFile !== undefined) toolDef.perFile = remoteToolOverride.perFile;
445
+ if (remoteToolOverride.enabled === false) {
446
+ logger.info(`Skipping ${toolDef.name} (disabled by remote config)`);
447
+ continue;
448
+ }
449
+ }
435
450
  // Filter files by tool's extensions
436
451
  const matchingFiles = filterFilesByTool(files, toolDef);
437
452
 
@@ -32,6 +32,7 @@ import { executeClaudeWithRetry, extractJSON } from './claude-client.js';
32
32
  import { loadPrompt } from './prompt-builder.js';
33
33
  import { getConfig } from '../config.js';
34
34
  import logger from './logger.js';
35
+ import { getDefaultSection, resolveSection } from './config-registry.js';
35
36
 
36
37
  /**
37
38
  * @typedef {Object} BranchContext
@@ -58,13 +59,11 @@ import logger from './logger.js';
58
59
  */
59
60
 
60
61
  /**
61
- * Internal defaults (not documented in config.example.json)
62
+ * Internal defaults loaded from lib/defaults.json
62
63
  * Why: Convention over configuration - sensible defaults for 95% of cases
64
+ * Fallbacks: timeout=180000 (3 min), maxDiffSize=51200 (50KB)
63
65
  */
64
- const DEFAULTS = {
65
- timeout: 180000, // 3 minutes
66
- maxDiffSize: 50000 // 50KB
67
- };
66
+ const DEFAULTS = getDefaultSection('prMetadata') || { timeout: 180000, maxDiffSize: 50000 };
68
67
 
69
68
  /**
70
69
  * Builds diff payload with tiered reduction strategy
@@ -310,12 +309,14 @@ export const getBranchContext = async (targetBranch) => {
310
309
 
311
310
  logger.debug('pr-metadata-engine - getBranchContext', 'Commits retrieved');
312
311
 
313
- // Build diff payload with tiered reduction
312
+ // Build diff payload with tiered reduction (remote settings.json > local defaults)
313
+ const resolvedPrMeta = await resolveSection('prMetadata');
314
+ const maxDiffSize = resolvedPrMeta?.maxDiffSize || DEFAULTS.maxDiffSize;
314
315
  const {
315
316
  payload: diff,
316
317
  isTruncated,
317
318
  truncationDetails
318
- } = buildDiffPayload(baseBranch, 'HEAD', files, DEFAULTS.maxDiffSize);
319
+ } = buildDiffPayload(baseBranch, 'HEAD', files, maxDiffSize);
319
320
 
320
321
  logger.debug('pr-metadata-engine - getBranchContext', 'Diff payload built', {
321
322
  diffSize: diff.length,
@@ -371,7 +372,9 @@ export const getBranchContext = async (targetBranch) => {
371
372
  export const generatePRMetadata = async (context, options = {}) => {
372
373
  const config = await getConfig();
373
374
  const configTimeout = config.analysis?.timeout;
374
- const { timeout = configTimeout || DEFAULTS.timeout, hook = 'pr-metadata', headless = false, costTracker = null } = options;
375
+ const resolvedPrMetaGen = await resolveSection('prMetadata');
376
+ const defaultTimeout = resolvedPrMetaGen?.timeout || DEFAULTS.timeout;
377
+ const { timeout = configTimeout || defaultTimeout, hook = 'pr-metadata', headless = false, costTracker = null } = options;
375
378
 
376
379
  logger.debug('pr-metadata-engine - generatePRMetadata', 'Generating PR metadata', {
377
380
  filesCount: context.filesCount,
@@ -377,11 +377,13 @@ export function writeVersionToFile(filePath, type, newVersion) {
377
377
  }
378
378
 
379
379
  /**
380
- * Updates version in selected files
381
- * Why: Applies version update to specific VersionFileDescriptor[] subset
380
+ * Updates version in the given files (resolved mutations from the selector)
381
+ * Why: Applies version update to the concrete list of files the caller provides.
382
+ * Every item in the array is a mutation to apply — the caller is responsible
383
+ * for filtering before calling this function.
382
384
  *
383
- * @param {Array} files - Array of VersionFileDescriptor objects with selected=true
384
- * @param {string} newVersion - New version string
385
+ * @param {Array} files - Array of VersionFileDescriptor objects to update
386
+ * @param {string} newVersion - New version string (used when file has no targetVersion)
385
387
  */
386
388
  export function updateVersionFiles(files, newVersion) {
387
389
  logger.debug('version-manager - updateVersionFiles', 'Updating version files', {
@@ -402,10 +404,6 @@ export function updateVersionFiles(files, newVersion) {
402
404
  const errors = [];
403
405
 
404
406
  for (const file of files) {
405
- if (!file.selected) {
406
- continue; // Skip unselected files
407
- }
408
-
409
407
  try {
410
408
  // Verify file still exists
411
409
  if (!fs.existsSync(file.path)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-git-hooks",
3
- "version": "2.44.0",
3
+ "version": "2.51.2",
4
4
  "description": "Git hooks with Claude CLI for code analysis and automatic commit messages",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,8 +21,14 @@
21
21
  "format": "prettier --write \"lib/**/*.js\" \"bin/**\" \"test/**/*.js\"",
22
22
  "precommit": "npm run lint && npm run test:smoke",
23
23
  "prepublishOnly": "npm run test:all",
24
- "library:tokens": "node .library/tools/measure-tokens.js",
25
- "library:graph": "node .library/tools/generate-graph.js"
24
+ "library:check": "node .library/bin/library check",
25
+ "library:regenerate": "node .library/bin/library regenerate",
26
+ "library:extract": "node .library/bin/library extract",
27
+ "library:tokens": "node .library/bin/library tokens",
28
+ "library:graph": "node .library/bin/library graph",
29
+ "library:inject": "node .library/bin/library inject",
30
+ "library:validate": "node .library/bin/library validate",
31
+ "library:report": "node .library/bin/library report"
26
32
  },
27
33
  "keywords": [
28
34
  "git",
@@ -70,6 +76,8 @@
70
76
  "jest": "^29.7.0",
71
77
  "js-tiktoken": "^1.0.18",
72
78
  "madge": "^8.0.0",
73
- "prettier": "^3.2.0"
79
+ "prettier": "^3.2.0",
80
+ "tree-sitter-wasms": "^0.1.13",
81
+ "web-tree-sitter": "^0.24.7"
74
82
  }
75
83
  }