claude-git-hooks 2.18.0 → 2.19.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 +38 -0
- package/CLAUDE.md +12 -8
- package/README.md +2 -1
- package/bin/claude-hooks +75 -89
- package/lib/cli-metadata.js +301 -0
- package/lib/commands/analyze-diff.js +12 -10
- package/lib/commands/analyze.js +9 -5
- package/lib/commands/bump-version.js +66 -43
- package/lib/commands/create-pr.js +71 -34
- package/lib/commands/debug.js +4 -7
- package/lib/commands/generate-changelog.js +11 -4
- package/lib/commands/help.js +47 -27
- package/lib/commands/helpers.js +66 -43
- package/lib/commands/hooks.js +15 -13
- package/lib/commands/install.js +546 -39
- package/lib/commands/migrate-config.js +8 -11
- package/lib/commands/presets.js +6 -13
- package/lib/commands/setup-github.js +12 -3
- package/lib/commands/telemetry-cmd.js +8 -6
- package/lib/commands/update.js +1 -2
- package/lib/config.js +36 -31
- package/lib/hooks/pre-commit.js +34 -54
- package/lib/hooks/prepare-commit-msg.js +39 -58
- package/lib/utils/analysis-engine.js +28 -21
- package/lib/utils/changelog-generator.js +162 -34
- package/lib/utils/claude-client.js +438 -377
- package/lib/utils/claude-diagnostics.js +20 -10
- package/lib/utils/file-operations.js +51 -79
- package/lib/utils/file-utils.js +46 -9
- package/lib/utils/git-operations.js +140 -123
- package/lib/utils/git-tag-manager.js +24 -23
- package/lib/utils/github-api.js +85 -61
- package/lib/utils/github-client.js +12 -14
- package/lib/utils/installation-diagnostics.js +4 -4
- package/lib/utils/interactive-ui.js +29 -17
- package/lib/utils/logger.js +4 -1
- package/lib/utils/pr-metadata-engine.js +67 -33
- package/lib/utils/preset-loader.js +20 -62
- package/lib/utils/prompt-builder.js +50 -55
- package/lib/utils/resolution-prompt.js +33 -44
- package/lib/utils/sanitize.js +20 -19
- package/lib/utils/task-id.js +27 -40
- package/lib/utils/telemetry.js +29 -17
- package/lib/utils/version-manager.js +173 -126
- package/lib/utils/which-command.js +23 -12
- package/package.json +69 -69
package/lib/commands/help.js
CHANGED
|
@@ -14,11 +14,7 @@ import path from 'path';
|
|
|
14
14
|
import { getPackageJson } from './helpers.js';
|
|
15
15
|
import { executeClaudeWithRetry } from '../utils/claude-client.js';
|
|
16
16
|
import { loadPrompt } from '../utils/prompt-builder.js';
|
|
17
|
-
import {
|
|
18
|
-
fetchFileContent,
|
|
19
|
-
fetchDirectoryListing,
|
|
20
|
-
createIssue
|
|
21
|
-
} from '../utils/github-api.js';
|
|
17
|
+
import { fetchFileContent, fetchDirectoryListing, createIssue } from '../utils/github-api.js';
|
|
22
18
|
import { promptMenu, promptEditField, promptConfirmation } from '../utils/interactive-ui.js';
|
|
23
19
|
import logger from '../utils/logger.js';
|
|
24
20
|
|
|
@@ -147,7 +143,10 @@ const readClaudeMd = async () => {
|
|
|
147
143
|
logger.debug('help - readClaudeMd', 'CLAUDE.md loaded', { length: content.length });
|
|
148
144
|
return content;
|
|
149
145
|
} catch (error) {
|
|
150
|
-
logger.debug('help - readClaudeMd', 'CLAUDE.md not found', {
|
|
146
|
+
logger.debug('help - readClaudeMd', 'CLAUDE.md not found', {
|
|
147
|
+
path: claudeMdPath,
|
|
148
|
+
error: error.message
|
|
149
|
+
});
|
|
151
150
|
return null;
|
|
152
151
|
}
|
|
153
152
|
};
|
|
@@ -170,7 +169,10 @@ async function runAiHelp(question) {
|
|
|
170
169
|
try {
|
|
171
170
|
const claudeMdContent = await readClaudeMd();
|
|
172
171
|
if (!claudeMdContent) {
|
|
173
|
-
logger.debug(
|
|
172
|
+
logger.debug(
|
|
173
|
+
'help - runAiHelp',
|
|
174
|
+
'CLAUDE.md not available, falling back to static help'
|
|
175
|
+
);
|
|
174
176
|
showStaticHelp();
|
|
175
177
|
return;
|
|
176
178
|
}
|
|
@@ -190,9 +192,15 @@ async function runAiHelp(question) {
|
|
|
190
192
|
const trimmedResponse = response.trim();
|
|
191
193
|
|
|
192
194
|
// Check for NEED_MORE_CONTEXT second pass
|
|
193
|
-
const needMoreLine = trimmedResponse
|
|
195
|
+
const needMoreLine = trimmedResponse
|
|
196
|
+
.split('\n')
|
|
197
|
+
.find((l) => l.includes('NEED_MORE_CONTEXT'));
|
|
194
198
|
if (needMoreLine) {
|
|
195
|
-
const enrichedResponse = await handleNeedMoreContext(
|
|
199
|
+
const enrichedResponse = await handleNeedMoreContext(
|
|
200
|
+
needMoreLine,
|
|
201
|
+
question,
|
|
202
|
+
claudeMdContent
|
|
203
|
+
);
|
|
196
204
|
if (enrichedResponse) {
|
|
197
205
|
printAiResponse(enrichedResponse, `${localVersion} + source`);
|
|
198
206
|
return;
|
|
@@ -200,7 +208,7 @@ async function runAiHelp(question) {
|
|
|
200
208
|
// Enrichment failed: show first-pass answer with marker line stripped
|
|
201
209
|
const cleanResponse = trimmedResponse
|
|
202
210
|
.split('\n')
|
|
203
|
-
.filter(l => !l.includes('NEED_MORE_CONTEXT'))
|
|
211
|
+
.filter((l) => !l.includes('NEED_MORE_CONTEXT'))
|
|
204
212
|
.join('\n')
|
|
205
213
|
.trim();
|
|
206
214
|
if (cleanResponse) {
|
|
@@ -211,7 +219,9 @@ async function runAiHelp(question) {
|
|
|
211
219
|
|
|
212
220
|
printAiResponse(trimmedResponse, localVersion);
|
|
213
221
|
} catch (error) {
|
|
214
|
-
logger.debug('help - runAiHelp', 'AI help failed, falling back to static help', {
|
|
222
|
+
logger.debug('help - runAiHelp', 'AI help failed, falling back to static help', {
|
|
223
|
+
error: error.message
|
|
224
|
+
});
|
|
215
225
|
showStaticHelp();
|
|
216
226
|
}
|
|
217
227
|
}
|
|
@@ -228,7 +238,10 @@ async function handleNeedMoreContext(needMoreLine, question, claudeMdContent) {
|
|
|
228
238
|
try {
|
|
229
239
|
// Parse file paths from line: "NEED_MORE_CONTEXT: file1.js, file2.js"
|
|
230
240
|
const pathsPart = needMoreLine.replace('NEED_MORE_CONTEXT', '').replace(':', '').trim();
|
|
231
|
-
const filePaths = pathsPart
|
|
241
|
+
const filePaths = pathsPart
|
|
242
|
+
.split(',')
|
|
243
|
+
.map((p) => p.trim())
|
|
244
|
+
.filter(Boolean);
|
|
232
245
|
|
|
233
246
|
if (filePaths.length === 0) {
|
|
234
247
|
logger.debug('help - handleNeedMoreContext', 'No file paths in NEED_MORE_CONTEXT');
|
|
@@ -249,8 +262,8 @@ async function handleNeedMoreContext(needMoreLine, question, claudeMdContent) {
|
|
|
249
262
|
|
|
250
263
|
// Build enriched documentation
|
|
251
264
|
const additionalContent = fetchResults
|
|
252
|
-
.filter(r => r.content !== null)
|
|
253
|
-
.map(r => `\n--- Source: ${r.filePath} ---\n${r.content}`)
|
|
265
|
+
.filter((r) => r.content !== null)
|
|
266
|
+
.map((r) => `\n--- Source: ${r.filePath} ---\n${r.content}`)
|
|
254
267
|
.join('\n');
|
|
255
268
|
|
|
256
269
|
if (!additionalContent) {
|
|
@@ -295,13 +308,15 @@ async function runReportIssue() {
|
|
|
295
308
|
if (!templates || templates.length === 0) {
|
|
296
309
|
logger.debug('help - runReportIssue', 'No issue templates found');
|
|
297
310
|
console.log('\nNo issue templates found in .github/ISSUE_TEMPLATE/');
|
|
298
|
-
console.log(
|
|
311
|
+
console.log(
|
|
312
|
+
`Create issues directly at: https://github.com/${owner}/${repo}/issues/new`
|
|
313
|
+
);
|
|
299
314
|
return;
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
// Filter to markdown/yaml template files
|
|
303
|
-
const templateFiles = templates.filter(
|
|
304
|
-
t.name.endsWith('.md') || t.name.endsWith('.yml') || t.name.endsWith('.yaml')
|
|
318
|
+
const templateFiles = templates.filter(
|
|
319
|
+
(t) => t.name.endsWith('.md') || t.name.endsWith('.yml') || t.name.endsWith('.yaml')
|
|
305
320
|
);
|
|
306
321
|
|
|
307
322
|
if (templateFiles.length === 0) {
|
|
@@ -345,12 +360,16 @@ async function runReportIssue() {
|
|
|
345
360
|
const jsonMatch = questionsResponse.match(/\[[\s\S]*\]/);
|
|
346
361
|
questions = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
|
|
347
362
|
} catch (parseError) {
|
|
348
|
-
logger.debug('help - runReportIssue', 'Failed to parse questions JSON', {
|
|
363
|
+
logger.debug('help - runReportIssue', 'Failed to parse questions JSON', {
|
|
364
|
+
error: parseError.message
|
|
365
|
+
});
|
|
349
366
|
questions = null;
|
|
350
367
|
}
|
|
351
368
|
|
|
352
369
|
if (!questions || !Array.isArray(questions) || questions.length === 0) {
|
|
353
|
-
console.log(
|
|
370
|
+
console.log(
|
|
371
|
+
'\nCould not generate questions from template. Please create the issue manually:'
|
|
372
|
+
);
|
|
354
373
|
console.log(`https://github.com/${owner}/${repo}/issues/new`);
|
|
355
374
|
return;
|
|
356
375
|
}
|
|
@@ -359,10 +378,7 @@ async function runReportIssue() {
|
|
|
359
378
|
console.log('\nPlease answer the following questions:\n');
|
|
360
379
|
const answers = [];
|
|
361
380
|
for (const q of questions) {
|
|
362
|
-
const answer = await promptEditField(
|
|
363
|
-
q.section,
|
|
364
|
-
q.question
|
|
365
|
-
);
|
|
381
|
+
const answer = await promptEditField(q.section, q.question);
|
|
366
382
|
answers.push({ section: q.section, answer });
|
|
367
383
|
}
|
|
368
384
|
|
|
@@ -370,7 +386,7 @@ async function runReportIssue() {
|
|
|
370
386
|
const composePrompt = await loadPrompt('HELP_COMPOSE_ISSUE.md', {
|
|
371
387
|
TEMPLATE_NAME: selectedTemplate.name,
|
|
372
388
|
TEMPLATE_CONTENT: templateContent,
|
|
373
|
-
USER_ANSWERS: answers.map(a => `${a.section}: ${a.answer}`).join('\n')
|
|
389
|
+
USER_ANSWERS: answers.map((a) => `${a.section}: ${a.answer}`).join('\n')
|
|
374
390
|
});
|
|
375
391
|
|
|
376
392
|
console.log('\nComposing issue...\n');
|
|
@@ -381,7 +397,9 @@ async function runReportIssue() {
|
|
|
381
397
|
const jsonMatch = composeResponse.match(/\{[\s\S]*\}/);
|
|
382
398
|
issueData = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
|
|
383
399
|
} catch (parseError) {
|
|
384
|
-
logger.debug('help - runReportIssue', 'Failed to parse issue JSON', {
|
|
400
|
+
logger.debug('help - runReportIssue', 'Failed to parse issue JSON', {
|
|
401
|
+
error: parseError.message
|
|
402
|
+
});
|
|
385
403
|
issueData = null;
|
|
386
404
|
}
|
|
387
405
|
|
|
@@ -413,7 +431,9 @@ async function runReportIssue() {
|
|
|
413
431
|
} catch (error) {
|
|
414
432
|
logger.debug('help - runReportIssue', 'Report issue failed', { error: error.message });
|
|
415
433
|
console.log(`\nCould not create issue: ${error.message}`);
|
|
416
|
-
console.log(
|
|
434
|
+
console.log(
|
|
435
|
+
'You can create issues manually at: https://github.com/mscope-S-L/git-hooks/issues/new'
|
|
436
|
+
);
|
|
417
437
|
}
|
|
418
438
|
}
|
|
419
439
|
|
|
@@ -434,6 +454,6 @@ function extractLabelsFromTemplate(templateContent) {
|
|
|
434
454
|
|
|
435
455
|
return labelsMatch[1]
|
|
436
456
|
.split(',')
|
|
437
|
-
.map(l => l.trim().replace(/^["']|["']$/g, ''))
|
|
457
|
+
.map((l) => l.trim().replace(/^["']|["']$/g, ''))
|
|
438
458
|
.filter(Boolean);
|
|
439
459
|
}
|
package/lib/commands/helpers.js
CHANGED
|
@@ -71,7 +71,10 @@ export function checkGitRepo() {
|
|
|
71
71
|
let gitdir = gitContent.substring(8).trim();
|
|
72
72
|
// Convert Windows path to WSL if needed (C:\ -> /mnt/c/)
|
|
73
73
|
if (/^[A-Za-z]:/.test(gitdir)) {
|
|
74
|
-
gitdir = gitdir.replace(
|
|
74
|
+
gitdir = gitdir.replace(
|
|
75
|
+
/^([A-Za-z]):/,
|
|
76
|
+
(_, drive) => `/mnt/${drive.toLowerCase()}`
|
|
77
|
+
);
|
|
75
78
|
gitdir = gitdir.replace(/\\/g, '/');
|
|
76
79
|
}
|
|
77
80
|
// Verify the gitdir exists
|
|
@@ -106,7 +109,10 @@ export function getGitHooksPath() {
|
|
|
106
109
|
let gitCommonDir = execSync('git rev-parse --git-common-dir', { encoding: 'utf8' }).trim();
|
|
107
110
|
// Handle Windows paths when running under WSL (e.g. C:\... -> /mnt/c/...)
|
|
108
111
|
if (/^[A-Za-z]:/.test(gitCommonDir)) {
|
|
109
|
-
gitCommonDir = gitCommonDir.replace(
|
|
112
|
+
gitCommonDir = gitCommonDir.replace(
|
|
113
|
+
/^([A-Za-z]):/,
|
|
114
|
+
(_, drive) => `/mnt/${drive.toLowerCase()}`
|
|
115
|
+
);
|
|
110
116
|
gitCommonDir = gitCommonDir.replace(/\\/g, '/');
|
|
111
117
|
}
|
|
112
118
|
return path.join(gitCommonDir, 'hooks').replace(/\\/g, '/');
|
|
@@ -257,29 +263,33 @@ export async function updateConfig(propertyPath, value, options = {}) {
|
|
|
257
263
|
export function getLatestVersion(packageName) {
|
|
258
264
|
return new Promise((resolve, reject) => {
|
|
259
265
|
// Use the main NPM API, not /latest
|
|
260
|
-
https
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const json = JSON.parse(data);
|
|
266
|
-
// Get the version from the 'latest' tag
|
|
267
|
-
if (json['dist-tags'] && json['dist-tags'].latest) {
|
|
268
|
-
resolve(json['dist-tags'].latest);
|
|
269
|
-
} else {
|
|
270
|
-
reject(new Error('Could not get the version'));
|
|
271
|
-
}
|
|
272
|
-
} catch (e) {
|
|
273
|
-
// If it fails, try with npm view
|
|
266
|
+
https
|
|
267
|
+
.get(`https://registry.npmjs.org/${packageName}`, (res) => {
|
|
268
|
+
let data = '';
|
|
269
|
+
res.on('data', (chunk) => (data += chunk));
|
|
270
|
+
res.on('end', () => {
|
|
274
271
|
try {
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
272
|
+
const json = JSON.parse(data);
|
|
273
|
+
// Get the version from the 'latest' tag
|
|
274
|
+
if (json['dist-tags'] && json['dist-tags'].latest) {
|
|
275
|
+
resolve(json['dist-tags'].latest);
|
|
276
|
+
} else {
|
|
277
|
+
reject(new Error('Could not get the version'));
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
// If it fails, try with npm view
|
|
281
|
+
try {
|
|
282
|
+
const version = execSync(`npm view ${packageName} version`, {
|
|
283
|
+
encoding: 'utf8'
|
|
284
|
+
}).trim();
|
|
285
|
+
resolve(version);
|
|
286
|
+
} catch (npmError) {
|
|
287
|
+
reject(e);
|
|
288
|
+
}
|
|
279
289
|
}
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
|
|
290
|
+
});
|
|
291
|
+
})
|
|
292
|
+
.on('error', reject);
|
|
283
293
|
});
|
|
284
294
|
}
|
|
285
295
|
|
|
@@ -301,14 +311,18 @@ export class Entertainment {
|
|
|
301
311
|
static async getJoke() {
|
|
302
312
|
return new Promise((resolve) => {
|
|
303
313
|
// Try to get joke from API
|
|
304
|
-
const req = https.get(
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
res
|
|
311
|
-
|
|
314
|
+
const req = https.get(
|
|
315
|
+
'https://icanhazdadjoke.com/',
|
|
316
|
+
{
|
|
317
|
+
headers: { Accept: 'text/plain' },
|
|
318
|
+
timeout: 3000
|
|
319
|
+
},
|
|
320
|
+
(res) => {
|
|
321
|
+
let data = '';
|
|
322
|
+
res.on('data', (chunk) => (data += chunk));
|
|
323
|
+
res.on('end', () => resolve(data.trim()));
|
|
324
|
+
}
|
|
325
|
+
);
|
|
312
326
|
|
|
313
327
|
req.on('error', () => {
|
|
314
328
|
// If it fails, use local joke
|
|
@@ -332,9 +346,11 @@ export class Entertainment {
|
|
|
332
346
|
let isFinished = false;
|
|
333
347
|
|
|
334
348
|
// Get first joke from API without blocking
|
|
335
|
-
this.getJoke()
|
|
336
|
-
|
|
337
|
-
|
|
349
|
+
this.getJoke()
|
|
350
|
+
.then((joke) => {
|
|
351
|
+
if (!isFinished) currentJoke = joke;
|
|
352
|
+
})
|
|
353
|
+
.catch(() => {}); // If it fails, keep the local one
|
|
338
354
|
|
|
339
355
|
// Hide cursor
|
|
340
356
|
process.stdout.write('\x1B[?25l');
|
|
@@ -356,13 +372,16 @@ export class Entertainment {
|
|
|
356
372
|
|
|
357
373
|
// Refresh joke every 10 seconds
|
|
358
374
|
if (jokeCountdown <= 0) {
|
|
359
|
-
this.getJoke()
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
375
|
+
this.getJoke()
|
|
376
|
+
.then((joke) => {
|
|
377
|
+
if (!isFinished) currentJoke = joke;
|
|
378
|
+
})
|
|
379
|
+
.catch(() => {
|
|
380
|
+
if (!isFinished) {
|
|
381
|
+
currentJoke =
|
|
382
|
+
this.jokes[Math.floor(Math.random() * this.jokes.length)];
|
|
383
|
+
}
|
|
384
|
+
});
|
|
366
385
|
jokeCountdown = 10;
|
|
367
386
|
}
|
|
368
387
|
}
|
|
@@ -374,13 +393,17 @@ export class Entertainment {
|
|
|
374
393
|
const spinner = spinners[spinnerIndex % spinners.length];
|
|
375
394
|
|
|
376
395
|
// Line 1: Spinner
|
|
377
|
-
process.stdout.write(
|
|
396
|
+
process.stdout.write(
|
|
397
|
+
'\r\x1B[2K' + `${colors.yellow}${spinner} ${message}${colors.reset}\n`
|
|
398
|
+
);
|
|
378
399
|
|
|
379
400
|
// Line 2: Joke
|
|
380
401
|
process.stdout.write('\r\x1B[2K' + `${colors.green}🎭 ${currentJoke}${colors.reset}\n`);
|
|
381
402
|
|
|
382
403
|
// Line 3: Countdown
|
|
383
|
-
process.stdout.write(
|
|
404
|
+
process.stdout.write(
|
|
405
|
+
'\r\x1B[2K' + `${colors.yellow}⏱️ Next joke in: ${jokeCountdown}s${colors.reset}\n`
|
|
406
|
+
);
|
|
384
407
|
}, 100);
|
|
385
408
|
|
|
386
409
|
try {
|
package/lib/commands/hooks.js
CHANGED
|
@@ -5,14 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
success,
|
|
11
|
-
info,
|
|
12
|
-
warning,
|
|
13
|
-
checkGitRepo,
|
|
14
|
-
getGitHooksPath
|
|
15
|
-
} from './helpers.js';
|
|
8
|
+
import { error, success, info, warning, checkGitRepo, getGitHooksPath } from './helpers.js';
|
|
9
|
+
import { removeCompletions } from './install.js';
|
|
16
10
|
|
|
17
11
|
/**
|
|
18
12
|
* Enable command
|
|
@@ -26,7 +20,7 @@ export function runEnable(hookName) {
|
|
|
26
20
|
const hooksDir = getGitHooksPath();
|
|
27
21
|
const hooks = hookName ? [hookName] : ['pre-commit', 'prepare-commit-msg'];
|
|
28
22
|
|
|
29
|
-
hooks.forEach(hook => {
|
|
23
|
+
hooks.forEach((hook) => {
|
|
30
24
|
const disabledPath = `${hooksDir}/${hook}.disabled`;
|
|
31
25
|
const enabledPath = `${hooksDir}/${hook}`;
|
|
32
26
|
|
|
@@ -53,7 +47,7 @@ export function runDisable(hookName) {
|
|
|
53
47
|
const hooksDir = getGitHooksPath();
|
|
54
48
|
const hooks = hookName ? [hookName] : ['pre-commit', 'prepare-commit-msg'];
|
|
55
49
|
|
|
56
|
-
hooks.forEach(hook => {
|
|
50
|
+
hooks.forEach((hook) => {
|
|
57
51
|
const enabledPath = `${hooksDir}/${hook}`;
|
|
58
52
|
const disabledPath = `${hooksDir}/${hook}.disabled`;
|
|
59
53
|
|
|
@@ -80,7 +74,7 @@ export function runStatus() {
|
|
|
80
74
|
|
|
81
75
|
const hooksDir = getGitHooksPath();
|
|
82
76
|
const hooks = ['pre-commit', 'prepare-commit-msg'];
|
|
83
|
-
hooks.forEach(hook => {
|
|
77
|
+
hooks.forEach((hook) => {
|
|
84
78
|
const enabledPath = `${hooksDir}/${hook}`;
|
|
85
79
|
const disabledPath = `${hooksDir}/${hook}.disabled`;
|
|
86
80
|
|
|
@@ -96,7 +90,7 @@ export function runStatus() {
|
|
|
96
90
|
// Check guidelines files
|
|
97
91
|
console.log('\nGuidelines files:');
|
|
98
92
|
const guidelines = ['CLAUDE_PRE_COMMIT.md'];
|
|
99
|
-
guidelines.forEach(guideline => {
|
|
93
|
+
guidelines.forEach((guideline) => {
|
|
100
94
|
const promptsPath = path.join('.claude', 'prompts', guideline);
|
|
101
95
|
const legacyPath = path.join('.claude', guideline);
|
|
102
96
|
if (fs.existsSync(promptsPath)) {
|
|
@@ -142,7 +136,7 @@ export function runUninstall() {
|
|
|
142
136
|
const hooksPath = getGitHooksPath();
|
|
143
137
|
const hooks = ['pre-commit', 'prepare-commit-msg'];
|
|
144
138
|
|
|
145
|
-
hooks.forEach(hook => {
|
|
139
|
+
hooks.forEach((hook) => {
|
|
146
140
|
const hookPath = `${hooksPath}/${hook}`;
|
|
147
141
|
if (fs.existsSync(hookPath)) {
|
|
148
142
|
fs.unlinkSync(hookPath);
|
|
@@ -150,5 +144,13 @@ export function runUninstall() {
|
|
|
150
144
|
}
|
|
151
145
|
});
|
|
152
146
|
|
|
147
|
+
// Remove shell completion scripts and rc modifications
|
|
148
|
+
try {
|
|
149
|
+
removeCompletions();
|
|
150
|
+
success('Shell completions removed');
|
|
151
|
+
} catch (e) {
|
|
152
|
+
warning(`Could not remove shell completions: ${e.message}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
153
155
|
success('Claude Git Hooks uninstalled');
|
|
154
156
|
}
|