claude-git-hooks 2.9.1 → 2.10.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.
@@ -1,342 +0,0 @@
1
- /**
2
- * File: mcp-setup.js
3
- * Purpose: Automated GitHub MCP setup for Claude CLI
4
- *
5
- * Features:
6
- * - Auto-detect existing configuration
7
- * - Read token from Claude Desktop config
8
- * - Interactive token prompt if needed
9
- * - Configure Claude CLI MCP
10
- * - Set environment variables
11
- * - Verify configuration
12
- */
13
-
14
- import { execSync } from 'child_process';
15
- import fs from 'fs';
16
- import path from 'path';
17
- import os from 'os';
18
- import logger from './logger.js';
19
- import { promptConfirmation, promptEditField, showSuccess, showError, showInfo, showWarning } from './interactive-ui.js';
20
- import { getClaudeCommand } from './claude-client.js';
21
- import { approveGitHubMcpPermissions, executeMcpCommand } from './github-client.js';
22
-
23
- /**
24
- * Check if GitHub MCP is already configured in Claude CLI
25
- * Why: Avoid duplicate configuration
26
- *
27
- * @returns {boolean} - True if configured
28
- */
29
- export const isGitHubMCPConfigured = () => {
30
- try {
31
- const { command, args } = getClaudeCommand();
32
- const fullCommand = `${command} ${args.join(' ')} mcp list`;
33
- const mcpList = execSync(fullCommand, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
34
- return mcpList.toLowerCase().includes('github');
35
- } catch (error) {
36
- logger.debug('mcp-setup - isGitHubMCPConfigured', 'Failed to check MCP list', { error: error.message });
37
- return false;
38
- }
39
- };
40
-
41
- /**
42
- * Find GitHub token in Claude Desktop config
43
- * Why: Reuse existing token instead of asking user
44
- *
45
- * @returns {Object|null} - { token, configPath } or null if not found
46
- */
47
- export const findGitHubTokenInDesktopConfig = () => {
48
- const possibleConfigPaths = [
49
- // Linux/macOS standard path
50
- path.join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json'),
51
- // Windows native path
52
- path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'),
53
- // WSL accessing Windows path - try multiple user names
54
- '/mnt/c/Users/' + os.userInfo().username + '/AppData/Roaming/Claude/claude_desktop_config.json',
55
- ];
56
-
57
- // Add additional Windows paths by checking if we're in WSL
58
- if (process.platform === 'linux' && fs.existsSync('/mnt/c')) {
59
- // We're in WSL, try to find all Windows user directories
60
- try {
61
- const usersDir = '/mnt/c/Users';
62
- if (fs.existsSync(usersDir)) {
63
- const users = fs.readdirSync(usersDir);
64
- for (const user of users) {
65
- const configPath = `/mnt/c/Users/${user}/AppData/Roaming/Claude/claude_desktop_config.json`;
66
- if (!possibleConfigPaths.includes(configPath)) {
67
- possibleConfigPaths.push(configPath);
68
- }
69
- }
70
- }
71
- } catch (error) {
72
- logger.debug('mcp-setup - findGitHubTokenInDesktopConfig', 'Failed to scan /mnt/c/Users', {
73
- error: error.message
74
- });
75
- }
76
- }
77
-
78
- for (const configPath of possibleConfigPaths) {
79
- try {
80
- if (fs.existsSync(configPath)) {
81
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
82
- const token = config.mcpServers?.github?.env?.GITHUB_PERSONAL_ACCESS_TOKEN;
83
-
84
- if (token) {
85
- logger.debug('mcp-setup - findGitHubTokenInDesktopConfig', 'Found token', { configPath });
86
- return { token, configPath };
87
- }
88
- }
89
- } catch (error) {
90
- logger.debug('mcp-setup - findGitHubTokenInDesktopConfig', 'Failed to read config', {
91
- configPath,
92
- error: error.message
93
- });
94
- }
95
- }
96
-
97
- return null;
98
- };
99
-
100
- /**
101
- * Add GitHub MCP to Claude CLI
102
- * Why: Configure MCP server for PR creation
103
- *
104
- * @param {boolean} replace - Replace existing configuration
105
- * @returns {Promise<boolean>} - True if successful
106
- */
107
- export const addGitHubMCP = async (replace = false) => {
108
- try {
109
- const { command, args } = getClaudeCommand();
110
-
111
- // Remove existing if replacing
112
- if (replace) {
113
- try {
114
- const removeCmd = `${command} ${args.join(' ')} mcp remove github`;
115
- execSync(removeCmd, { stdio: 'ignore' });
116
- logger.debug('mcp-setup - addGitHubMCP', 'Removed existing GitHub MCP');
117
- } catch (error) {
118
- // Ignore errors, may not have remove command
119
- logger.debug('mcp-setup - addGitHubMCP', 'Failed to remove (may not exist)', { error: error.message });
120
- }
121
- }
122
-
123
- // Add GitHub MCP
124
- // Use -- to separate claude options from npx arguments
125
- const mcpCommand = `${command} ${args.join(' ')} mcp add github npx -- -y @modelcontextprotocol/server-github`;
126
- logger.debug('mcp-setup - addGitHubMCP', 'Running MCP add command', { mcpCommand });
127
-
128
- execSync(mcpCommand, { stdio: 'inherit' });
129
-
130
- logger.debug('mcp-setup - addGitHubMCP', 'Successfully added GitHub MCP');
131
- return true;
132
-
133
- } catch (error) {
134
- logger.error('mcp-setup - addGitHubMCP', 'Failed to add GitHub MCP', error);
135
- return false;
136
- }
137
- };
138
-
139
- /**
140
- * Set environment variable in shell RC files
141
- * Why: Persist GitHub token across sessions
142
- *
143
- * @param {string} token - GitHub Personal Access Token
144
- * @returns {boolean} - True if set in at least one file
145
- */
146
- export const setEnvironmentVariable = (token) => {
147
- const envVarLine = `export GITHUB_PERSONAL_ACCESS_TOKEN="${token}"`;
148
- const shellRcFiles = [
149
- path.join(os.homedir(), '.bashrc'),
150
- path.join(os.homedir(), '.bash_profile'),
151
- path.join(os.homedir(), '.zshrc')
152
- ];
153
-
154
- let envVarSet = false;
155
-
156
- for (const rcFile of shellRcFiles) {
157
- try {
158
- if (fs.existsSync(rcFile)) {
159
- const content = fs.readFileSync(rcFile, 'utf8');
160
-
161
- // Check if already present
162
- if (content.includes('GITHUB_PERSONAL_ACCESS_TOKEN')) {
163
- logger.debug('mcp-setup - setEnvironmentVariable', 'Already present', { rcFile });
164
- showInfo(`Environment variable already in ${path.basename(rcFile)}`);
165
- envVarSet = true;
166
- continue;
167
- }
168
-
169
- // Add to file
170
- fs.appendFileSync(rcFile, `\n# GitHub MCP token for Claude CLI\n${envVarLine}\n`);
171
- showSuccess(`Added environment variable to ${path.basename(rcFile)}`);
172
- logger.debug('mcp-setup - setEnvironmentVariable', 'Added to file', { rcFile });
173
- envVarSet = true;
174
- }
175
- } catch (error) {
176
- logger.debug('mcp-setup - setEnvironmentVariable', 'Failed to update file', {
177
- rcFile,
178
- error: error.message
179
- });
180
- }
181
- }
182
-
183
- if (!envVarSet) {
184
- showWarning('Could not find shell RC file (.bashrc, .bash_profile, .zshrc)');
185
- console.log('');
186
- console.log('Please add this line to your shell configuration manually:');
187
- console.log(` ${envVarLine}`);
188
- }
189
-
190
- // Set for current session
191
- process.env.GITHUB_PERSONAL_ACCESS_TOKEN = token;
192
-
193
- return envVarSet;
194
- };
195
-
196
- /**
197
- * Setup GitHub MCP for Claude CLI
198
- * Why: Automate MCP configuration to enable create-pr functionality
199
- *
200
- * Interactive setup process:
201
- * 1. Check if GitHub MCP already configured
202
- * 2. Try to read GitHub token from Claude Desktop config
203
- * 3. If not found, prompt user for token
204
- * 4. Run: claude mcp add github npx -y @modelcontextprotocol/server-github
205
- * 5. Configure environment variable for token
206
- * 6. Verify configuration
207
- *
208
- * @returns {Promise<void>}
209
- */
210
- export const setupGitHubMCP = async () => {
211
- try {
212
- console.log('');
213
- showInfo('GitHub MCP Setup for Claude CLI');
214
- console.log('');
215
-
216
- // Step 1: Check if already configured
217
- console.log('🔍 Checking current MCP configuration...');
218
-
219
- let mcpList = '';
220
- try {
221
- const { command, args } = getClaudeCommand();
222
- const fullCommand = `${command} ${args.join(' ')} mcp list`;
223
- mcpList = execSync(fullCommand, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
224
- } catch (error) {
225
- showError('Failed to run "claude mcp list". Is Claude CLI installed?');
226
- console.error('Make sure Claude CLI is installed: npm install -g @anthropic-ai/claude-cli');
227
- console.error('Error:', error.message);
228
- process.exit(1);
229
- }
230
-
231
- const hasGitHub = isGitHubMCPConfigured();
232
- if (hasGitHub) {
233
- showSuccess('GitHub MCP is already configured!');
234
- console.log('');
235
- console.log(mcpList);
236
-
237
- const reconfigure = await promptConfirmation('Do you want to reconfigure it?', false);
238
- if (!reconfigure) {
239
- showInfo('Setup cancelled. Your existing configuration is unchanged.');
240
- return;
241
- }
242
- }
243
-
244
- // Step 2: Try to read token from Claude Desktop config
245
- console.log('');
246
- console.log('🔑 Looking for GitHub token...');
247
-
248
- let githubToken = null;
249
- const tokenInfo = findGitHubTokenInDesktopConfig();
250
-
251
- if (tokenInfo) {
252
- githubToken = tokenInfo.token;
253
- showSuccess('Found GitHub token in Claude Desktop config');
254
- console.log(` Location: ${tokenInfo.configPath}`);
255
- }
256
-
257
- // Step 3: If not found, prompt user
258
- if (!githubToken) {
259
- showWarning('No GitHub token found in Claude Desktop config');
260
- console.log('');
261
- console.log('You need a GitHub Personal Access Token with these permissions:');
262
- console.log(' - repo (Full control of private repositories)');
263
- console.log(' - read:org (Read org and team membership)');
264
- console.log('');
265
- console.log('Create one at: https://github.com/settings/tokens/new');
266
- console.log('');
267
-
268
- githubToken = await promptEditField('GitHub Personal Access Token', '');
269
-
270
- if (!githubToken || githubToken.trim() === '') {
271
- showError('GitHub token is required. Setup cancelled.');
272
- process.exit(1);
273
- }
274
- }
275
-
276
- // Step 4: Add GitHub MCP to Claude CLI
277
- console.log('');
278
- console.log('⚙️ Configuring GitHub MCP for Claude CLI...');
279
-
280
- const added = await addGitHubMCP(hasGitHub);
281
- if (!added) {
282
- showError('Failed to add GitHub MCP');
283
- process.exit(1);
284
- }
285
-
286
- showSuccess('GitHub MCP added to Claude CLI');
287
-
288
- // Step 5: Configure environment variable
289
- console.log('');
290
- console.log('🔧 Setting up environment variable...');
291
-
292
- setEnvironmentVariable(githubToken);
293
-
294
- // Step 6: Approve MCP permissions
295
- console.log('');
296
- console.log('🔐 Approving MCP permissions...');
297
-
298
- const permResult = await approveGitHubMcpPermissions();
299
- if (permResult.success) {
300
- showSuccess('MCP permissions approved');
301
- } else {
302
- showWarning('Some permissions may need manual approval');
303
- console.log(' Run: claude mcp approve github --all');
304
- }
305
-
306
- // Step 7: Verify configuration
307
- console.log('');
308
- console.log('✅ Verifying configuration...');
309
-
310
- try {
311
- const { command, args } = getClaudeCommand();
312
- const fullCommand = `${command} ${args.join(' ')} mcp list`;
313
- const verifyList = execSync(fullCommand, { encoding: 'utf8' });
314
- if (verifyList.toLowerCase().includes('github')) {
315
- showSuccess('GitHub MCP is configured and ready!');
316
- console.log('');
317
- console.log(verifyList);
318
- } else {
319
- showWarning('Configuration completed but GitHub MCP not showing in list');
320
- console.log('You may need to restart your terminal');
321
- }
322
- } catch (error) {
323
- showWarning('Could not verify configuration');
324
- logger.debug('mcp-setup - setupGitHubMCP', 'Verification failed', { error: error.message });
325
- }
326
-
327
- // Final instructions
328
- console.log('');
329
- showSuccess('Setup complete!');
330
- console.log('');
331
- console.log('Next steps:');
332
- console.log(' 1. Restart your terminal (or run: source ~/.bashrc)');
333
- console.log(' 2. Verify with: claude mcp list');
334
- console.log(' 3. Try creating a PR: claude-hooks create-pr develop');
335
- console.log('');
336
-
337
- } catch (error) {
338
- showError('Error during MCP setup: ' + error.message);
339
- console.error(error);
340
- throw error;
341
- }
342
- };