UNPKG

rltgjqm

Version:

Google Gemini API를 사용하여 자연어로 Git 명령어를 생성하는 CLI 도구 - 인터랙티브 메뉴와 자동 API 키 설정 지원

1,016 lines (885 loc) 48.1 kB
#!/usr/bin/env node const { program } = require('commander'); const chalk = require('chalk'); const inquirer = require('inquirer'); const path = require('path'); // 한글 입력 최적화 설정 process.stdin.setEncoding('utf8'); if (process.stdout.isTTY) { process.stdout.setEncoding('utf8'); } // 버전 정보 const packageJson = require('../package.json'); // 모듈 import const aiService = require('../lib/aiService'); const promptTemplate = require('../lib/promptTemplate'); const executor = require('../lib/executor'); const config = require('../lib/config'); const usageTracker = require('../lib/usageTracker'); /** * 메인 Git 명령어 생성 및 실행 함수 */ async function executeGitCommand(promptArg, options) { try { // 출력 모드 확인 const outputMode = config.getOutputMode(); if (!promptArg) { console.clear(); } if (outputMode === 'detail') { console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('🚀 rltgjqm CLI 시작')); console.log(chalk.white('─────────────────────────────────────────────')); // 현재 Git 상태 확인 및 표시 const gitStatus = await executor.getGitStatus(); executor.displayGitStatus(gitStatus); console.log(chalk.white('─────────────────────────────────────────────')); } // API 키 자동 확인 및 설정 const apiKey = await config.ensureApiKey(); if (!apiKey) { console.log(chalk.yellow('⚠️ API 키 설정이 완료되지 않았습니다. 프로그램을 종료합니다.')); return false; } // 기본 실행 모드 확인 및 설정 const defaultMode = await config.ensureDefaultExecutionMode(); // 실행 모드 설정 (옵션이 있으면 기본값 오버라이드) let mode = defaultMode; if (options.auto) mode = 'auto'; if (options.interactive) mode = 'interactive'; if (options.dryRun) mode = 'dry'; // 프롬프트 입력 받기 let userPrompt; if (promptArg) { userPrompt = promptArg; } else { const answers = await inquirer.prompt([ { type: 'input', name: 'userInput', message: '어떤 Git 작업을 하고 싶으신가요?', validate: (input) => { // 한글 입력 최적화: 입력 완료 후에만 검증 if (input === '') { return '프롬프트를 입력해주세요.'; } const trimmed = input.trim(); if (trimmed.length === 0) { return '프롬프트를 입력해주세요.'; } return true; }, prefix: '', suffix: '', // 한글 입력 최적화 설정 transformer: (input) => input, filter: (input) => input.trim() } ]); userPrompt = answers.userInput; } if (outputMode === 'detail') { console.log(chalk.white('\n📝 명령어 생성 중...')); console.log(chalk.white(`모드: ${mode}`)); } else { console.log(chalk.white('📝 생성 중...')); } // 현재 Git 상태 확인 (simple 모드에서도 필요하지만 표시하지 않음) const gitStatus = await executor.getGitStatus(); // 프롬프트 템플릿 생성 const fullPrompt = promptTemplate.buildPrompt(userPrompt, mode, gitStatus); // AI API 호출 const result = await aiService.generateCommand(fullPrompt); const response = result.response; const usageInfo = result.usageInfo; // 응답에서 명령어 추출 const commands = promptTemplate.parseCommands(response); if (commands.length === 0) { console.log(chalk.yellow('⚠️ 명령어를 생성할 수 없습니다.')); console.log(chalk.white('다른 방식으로 설명해보세요.')); return false; } // 명령어 출력 if (outputMode === 'simple') { console.log(chalk.green('✅ 생성된 명령어:')); } else { console.log(chalk.green('\n✅ 생성된 명령어:')); } commands.forEach((cmd, index) => { console.log(chalk.cyan(`${index + 1}. ${cmd}`)); }); // 실행 모드에 따른 처리 if (mode === 'dry') { if (outputMode === 'simple') { console.log(chalk.yellow('🧪 미리보기 모드 (실행되지 않음)')); } else { console.log(chalk.yellow('\n🧪 드라이런 모드: 명령어를 실행하지 않습니다.')); console.log(chalk.white('실행하려면 --auto 또는 --interactive 옵션을 사용하세요.')); } } else if (mode === 'auto') { if (outputMode === 'detail') { console.log(chalk.white('\n🔄 자동 실행 모드: 모든 명령어를 순서대로 실행합니다.')); } const results = await executor.executeMultipleCommands(commands, { mode: 'auto' }); executor.printExecutionSummary(results); } else if (mode === 'interactive') { if (outputMode === 'detail') { console.log(chalk.white('\n🔍 인터랙티브 모드: 각 명령어를 개별적으로 확인합니다.')); } const results = await executor.executeMultipleCommands(commands, { mode: 'interactive' }); executor.printExecutionSummary(results); } if (outputMode === 'detail') { console.log(chalk.white('\n─────────────────────────────────────────────')); console.log(chalk.green('🎉 완료!')); console.log(chalk.white('─────────────────────────────────────────────')); } return true; } catch (error) { console.log(chalk.white('\n─────────────────────────────────────────────')); console.error(chalk.red('❌ 오류 발생:'), error.message); if (error.response) { console.error(chalk.red('API 응답:'), error.response.data); } console.log(chalk.white('─────────────────────────────────────────────')); return false; } } /** * 인터랙티브 메인 메뉴 */ async function showMainMenu() { // 설정 상태 먼저 확인 const configStatus = config.checkConfigStatus(); if (!configStatus.hasApiKey) { // AI 플랫폼이 설정되지 않았으면 바로 설정 메뉴로 const apiKey = await config.ensureApiKey(); if (!apiKey) { return; // 종료 선택시 메인 메뉴 종료 } // AI 설정 후 메뉴 새로고침 return await showMainMenu(); } const platformNames = { 'chatgpt': 'ChatGPT', 'gemini': 'Gemini' }; while (true) { // 메뉴 표시 전 화면 클리어 및 상태 재표시 console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('🚀 rltgjqm - Git 명령어 생성 도구')); console.log(chalk.white('─────────────────────────────────────────────')); // 현재 Git 상태 확인 및 표시 const gitStatus = await executor.getGitStatus(); executor.displayGitStatus(gitStatus); console.log(chalk.white('─────────────────────────────────────────────')); // 현재 AI 플랫폼 정보 표시 const currentConfigStatus = config.checkConfigStatus(); console.log(chalk.green(`✅ AI 플랫폼: ${platformNames[currentConfigStatus.provider] || currentConfigStatus.provider}`)); // 사용량 정보 표시 const currentUsageBrief = usageTracker.displayUsageBrief(currentConfigStatus.provider); if (currentUsageBrief) { console.log(chalk.white(`${currentUsageBrief}`)); } const choices = [ { name: '⚙️ Run 방식 설정하기', value: 'executionMode' }, { name: '🔧 설정 관리', value: 'config' }, { name: '📖 도움말', value: 'help' }, { name: '❌ 종료', value: 'exit' } ]; const { selection: action } = await inquirer.prompt([ { type: 'list', name: 'selection', message: '', choices, prefix: '', suffix: '', pageSize: 10, loop: false } ]); switch (action) { case 'executionMode': await config.showExecutionModeMenu(); break; case 'config': await showConfigMenuWrapper(); break; case 'help': await showHelp(); break; case 'exit': console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('👋 안녕히 가세요!')); console.log(chalk.white('─────────────────────────────────────────────')); process.exit(0); } } } /** * 명령어 생성 메뉴 */ async function showGenerateMenu() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('🚀 Git 명령어 생성 모드')); console.log(chalk.white('─────────────────────────────────────────────')); // 현재 Git 상태 확인 및 표시 const gitStatus = await executor.getGitStatus(); executor.displayGitStatus(gitStatus); console.log(chalk.white('─────────────────────────────────────────────')); const choices = [ { name: '🚀 바로 실행 모드', value: 'auto' }, { name: '🔍 단계별 확인 모드', value: 'interactive' }, { name: '👀 미리보기 모드 (실행 안함)', value: 'dry' }, { name: '↩️ 메인 메뉴로', value: 'back' } ]; const { selection: mode } = await inquirer.prompt([ { type: 'list', name: 'selection', message: '실행 모드를 선택하세요:', choices, prefix: '', suffix: '' } ]); if (mode === 'back') { return; } const options = {}; if (mode === 'auto') options.auto = true; if (mode === 'interactive') options.interactive = true; if (mode === 'dry') options.dryRun = true; console.log(chalk.white('─────────────────────────────────────────────')); await executeGitCommand(null, options); } /** * 설정 메뉴 래퍼 */ async function showConfigMenuWrapper() { while (true) { const continueConfig = await config.showConfigMenu(); if (!continueConfig) { break; // 메인 메뉴로 돌아가기 } } } /** * 도움말 표시 */ async function showHelp() { // 완전히 새로운 화면으로 도움말 표시 console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('📖 rltgjqm 사용법')); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.yellow('\n명령행에서 직접 사용:')); console.log(chalk.cyan(' rltgjqm "새 브랜치 만들어줘" # 기본 모드로 실행')); console.log(chalk.cyan(' rltgjqm "커밋하고 푸시해줘" --auto # 이번만 자동 실행')); console.log(chalk.cyan(' rltgjqm "변경사항 되돌려줘" -i # 이번만 단계별 확인')); console.log(chalk.cyan(' 깃허브 "새 브랜치 만들어줘" # 한글 명령어')); console.log(chalk.yellow('\n설정 관리:')); console.log(chalk.cyan(' rltgjqm config # 설정 메뉴')); console.log(chalk.cyan(' rltgjqm -a # 기본 모드: 자동 실행')); console.log(chalk.cyan(' rltgjqm -i # 기본 모드: 단계별 확인')); console.log(chalk.cyan(' rltgjqm --dry # 기본 모드: 미리보기')); console.log(chalk.cyan(' rltgjqm -d # 출력 모드: 상세')); console.log(chalk.cyan(' rltgjqm -s # 출력 모드: 간단')); console.log(chalk.cyan(' rltgjqm --debug # 프롬프트 디버그 모드 전환')); console.log(chalk.yellow('\n옵션 (일회성):')); console.log(chalk.white(' -a, --auto 이번만 자동으로 실행')); console.log(chalk.cyan(' 예: rltgjqm "커밋하고 푸시" --auto')); console.log(chalk.white(' -i, --interactive 이번만 단계별 확인')); console.log(chalk.cyan(' 예: rltgjqm "브랜치 삭제" -i')); console.log(chalk.white(' --dry-run 이번만 미리보기')); console.log(chalk.cyan(' 예: rltgjqm "새 브랜치 만들어" --dry-run')); console.log(chalk.white(' -h, --help 도움말 출력')); console.log(chalk.yellow('\n💡 설정된 기본 모드가 사용되며, 옵션으로 일회성 변경 가능')); console.log(chalk.white('지원되는 AI 플랫폼: ChatGPT, Gemini')); console.log(chalk.white('\n─────────────────────────────────────────────')); // 메인 메뉴로 돌아가기 위한 입력 대기 await inquirer.prompt([ { type: 'input', name: 'continue', message: '메인 메뉴로 돌아가려면 Enter를 누르세요...', prefix: '', suffix: '' } ]); } /** * UI/UX 색상 테스트 */ async function testColors() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('🎨 UI/UX 색상 테스트 - 실제 사용 중인 모든 UI')); console.log(chalk.white('─────────────────────────────────────────────')); // 구분선들 console.log(chalk.white('\n📏 구분선 스타일:')); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.white('─────────────────────────────────────────────')); // 헤더/타이틀 메시지들 console.log(chalk.white('\n📋 헤더 및 타이틀:')); console.log(chalk.white('🚀 rltgjqm CLI 시작')); console.log(chalk.white('🚀 rltgjqm - Git 명령어 생성 도구')); console.log(chalk.white('🚀 Git 명령어 생성 모드')); console.log(chalk.white('📖 rltgjqm 사용법')); console.log(chalk.white(' 🤖 AI 플랫폼 선택 및 설정')); console.log(chalk.white(' 🔑 ChatGPT (OpenAI) API 키 설정')); console.log(chalk.white(' 🔑 Gemini API 키 설정')); console.log(chalk.magenta('🐛 디버깅 메뉴 (개발용)')); console.log(chalk.magenta('📊 시스템 상태 확인')); // 진행 상태 메시지들 console.log(chalk.white('\n⏳ 진행 상태 메시지:')); console.log(chalk.white('🤖 AI 명령어 생성 중...')); console.log(chalk.white('🤖 Gemini API 호출 중...')); console.log(chalk.white('🔑 ChatGPT (OpenAI) API 키 유효성 검사 중...')); console.log(chalk.white('🔑 Gemini (Google) API 키 유효성 검사 중...')); console.log(chalk.white('\n🔍 API 키 유효성 검사 중...')); console.log(chalk.white('📝 명령어 생성 중...')); console.log(chalk.white('🔄 명령어 실행 중...')); console.log(chalk.white('\n🚀 자동 실행 모드 시작')); console.log(chalk.white('\n🔍 인터랙티브 모드 시작')); console.log(chalk.white('🔍 연결 테스트 중...')); // 성공 메시지들 console.log(chalk.white('\n✅ 성공 메시지:')); console.log(chalk.green('✅ ChatGPT (OpenAI) API 키가 유효합니다.')); console.log(chalk.green('✅ Gemini (Google) API 키가 유효합니다.')); console.log(chalk.green('✅ API 응답 받음')); console.log(chalk.green('✅ ChatGPT 응답 받음')); console.log(chalk.green('✅ Gemini 응답 받음')); console.log(chalk.green('✅ 명령어가 성공적으로 실행되었습니다.')); console.log(chalk.green('✅ AI 설정이 저장되었습니다: ~/.rltgjqm/config.json')); console.log(chalk.green('✅ API 키가 저장되었습니다: ~/.rltgjqm/.env')); console.log(chalk.green('✅ ChatGPT (OpenAI) 설정이 완료되었습니다!')); console.log(chalk.green('✅ Git 레포지토리: rltgjqm')); console.log(chalk.green('✅ AI 플랫폼: ChatGPT')); console.log(chalk.green('\n✅ 생성된 명령어:')); console.log(chalk.green('🎉 완료!')); // 오류 메시지들 console.log(chalk.white('\n❌ 오류 메시지:')); console.log(chalk.red('❌ ChatGPT (OpenAI) API 키가 유효하지 않습니다.')); console.log(chalk.red('❌ Gemini (Google) API 키가 유효하지 않습니다.')); console.log(chalk.red('\n❌ API 키 유효성 검사에 실패했습니다.')); console.log(chalk.red('❌ 명령어 실행 실패. 남은 명령어 3개를 건너뜁니다.')); console.log(chalk.red('❌ 사용자가 실행을 중단했습니다.')); console.log(chalk.red('❌ Git 레포지토리가 아닙니다')); console.log(chalk.red('❌ 실패: 2개')); console.log(chalk.red('❌ API 키 설정 저장 실패: permission denied')); // 경고 메시지들 console.log(chalk.white('\n⚠️ 경고 메시지:')); console.log(chalk.yellow('⚠️ 이 메뉴는 개발 및 테스트 목적으로만 사용됩니다.')); console.log(chalk.yellow('⚠️ API 키 설정이 완료되지 않았습니다. 프로그램을 종료합니다.')); console.log(chalk.yellow('⚠️ 명령어를 생성할 수 없습니다.')); console.log(chalk.yellow('⚠️ 실행이 취소되었습니다.')); console.log(chalk.yellow('⚠️ 명령어 실행이 취소되었습니다. 남은 명령어 2개를 건너뜁니다.')); console.log(chalk.yellow('⚠️ Git 저장소가 아닙니다.')); console.log(chalk.yellow('⚠️ 설정된 API 키가 없습니다.')); console.log(chalk.yellow('⚠️ 현재 위치: /Users/user/project')); console.log(chalk.yellow('\n⚠️ 주의: 이 명령어는 위험할 수 있습니다.')); console.log(chalk.yellow('🧪 드라이런 모드: 명령어를 실행하지 않습니다.')); console.log(chalk.yellow('🧪 드라이런 모드: 실제로 실행되지 않습니다.')); console.log(chalk.yellow('🧪 드라이런 모드가 활성화되었습니다.')); console.log(chalk.yellow('🛑 사용자가 실행을 중단했습니다.')); console.log(chalk.yellow('⏭️ 명령어를 건너뜁니다.')); console.log(chalk.yellow('📋 실행된 명령어가 없습니다.')); console.log(chalk.yellow('다시 시도하시겠습니까?')); console.log(chalk.yellow('설정이 취소되었습니다.')); console.log(chalk.yellow('취소되었습니다.')); console.log(chalk.yellow('�� 설정을 완료한 후 다시 시도해주세요.')); // 정보/안내 메시지들 console.log(chalk.white('\n📄 정보 및 안내 메시지:')); console.log(chalk.white('플랫폼: ChatGPT (OpenAI)')); console.log(chalk.white('모델: gpt-4o-mini')); console.log(chalk.white('모델: gemini-1.5-flash')); console.log(chalk.white('모드: interactive')); console.log(chalk.white('다른 방식으로 설명해보세요.')); console.log(chalk.white('실행하려면 --auto 또는 --interactive 옵션을 사용하세요.')); console.log(chalk.white('📍 현재 브랜치: main')); console.log(chalk.white('📝 커밋되지 않은 변경사항이 있습니다.')); console.log(chalk.white('📤 푸시되지 않은 커밋이 있습니다.')); console.log(chalk.white('📊 총 커밋 수: 25개')); console.log(chalk.white('📋 상태: 2 staged, 3 modified')); console.log(chalk.white('🔗 원격 저장소: 로컬 전용')); console.log(chalk.white('📁 레포지토리 루트: /Users/user/project')); console.log(chalk.white(' 키 소스: 환경변수')); console.log(chalk.white(' OPENAI_API_KEY: ✅ 설정됨')); console.log(chalk.white(' GEMINI_API_KEY: ❌ 없음')); console.log(chalk.white('💡 "git init"으로 레포지토리를 초기화하거나')); console.log(chalk.white(' Git 레포지토리 폴더에서 명령어를 실행하세요')); // 사용자 상호작용 메시지들 console.log(chalk.white('\n💬 사용자 상호작용:')); console.log(chalk.white('rltgjqm은 다음 AI 플랫폼을 지원합니다:')); console.log(chalk.white('API 키는 https://platform.openai.com/api-keys 에서 발급받을 수 있습니다.')); console.log(chalk.white('API 키는 https://ai.google.dev/ 에서 발급받을 수 있습니다.')); console.log(chalk.white('저장 위치:'), chalk.greenBright('~/.rltgjqm/config.json')); console.log(chalk.white('취소하려면 "cancel" 또는 "exit"를 입력하세요.')); // 플랫폼 정보 console.log(chalk.white('\n📌 플랫폼 정보:')); console.log(chalk.cyan('📌 ChatGPT (OpenAI)')); console.log(chalk.white(' • 모델: gpt-4o-mini')); console.log(chalk.white(' • API 키: https://platform.openai.com/api-keys')); console.log(chalk.cyan('📌 Gemini (Google)')); console.log(chalk.white(' • 모델: gemini-1.5-flash')); console.log(chalk.white(' • API 키: https://ai.google.dev/')); // 명령어 관련 메시지들 console.log(chalk.white('\n💻 명령어 관련:')); console.log(chalk.cyan('💻 명령어: git add .')); console.log(chalk.cyan('💻 git status')); console.log(chalk.cyan('1. git add .')); console.log(chalk.cyan('2. git commit -m "initial commit"')); console.log(chalk.cyan('3. git push origin main')); console.log(chalk.cyan('🔗 링크/명령어')); console.log(chalk.cyan('📋 1/3: git add .')); console.log(chalk.red('🔥 명령어: rm -rf /')); console.log(chalk.white('👀 명령어 미리보기:')); console.log(chalk.white('🔄 실제 실행 모드로 변경되었습니다.')); console.log(chalk.white('📍 현재 위치 정보:')); // 브랜치 및 Git 상태 console.log(chalk.white('\n🌿 Git 상태:')); console.log(chalk.cyan('🌿 현재 브랜치: main')); console.log(chalk.green('🌿 현재 브랜치: develop')); console.log(chalk.white('🔗 원격 저장소: github.com/user/repo')); // 카테고리 섹션들 console.log(chalk.white('\n📊 카테고리 표시:')); console.log(chalk.cyan('\n🔧 Node.js 환경:')); console.log(chalk.cyan('\n📦 패키지 정보:')); console.log(chalk.cyan('\n🌍 환경변수:')); console.log(chalk.cyan('\n💾 메모리 사용량:')); console.log(chalk.cyan('\n⚙️ 설정 상태:')); console.log(chalk.cyan('\n📋 지원되는 AI 플랫폼:')); console.log(chalk.cyan('\n📁 설정 디렉터리 정보:')); console.log(chalk.cyan('\n📄 설정 파일들:')); console.log(chalk.cyan('\n⚙️ 현재 활성 설정:')); console.log(chalk.cyan('\n📊 현재 Git 저장소 상태:')); console.log(chalk.cyan('\n🎨 포맷된 Git 상태 표시:')); console.log(chalk.cyan('\n🔍 엔드포인트 연결 테스트:')); console.log(chalk.cyan('\n🔍 DNS 해석 테스트:')); console.log(chalk.cyan('\n📊 임시 디렉터리:')); console.log(chalk.cyan('\n🏠 사용자 홈 디렉터리:')); console.log(chalk.cyan('\n💾 rltgjqm 관련 파일들:')); console.log(chalk.cyan('\n📈 프로세스 정보:')); console.log(chalk.cyan('\n🔧 환경 정보:')); // 도움말 스타일 console.log(chalk.white('\n📖 도움말 스타일:')); console.log(chalk.yellow('\n명령행에서 직접 사용:')); console.log(chalk.cyan(' rltgjqm "새 브랜치 만들어줘"')); console.log(chalk.cyan(' rltgjqm "커밋하고 푸시해줘" --auto')); console.log(chalk.cyan(' rltgjqm "변경사항 되돌려줘" --interactive')); console.log(chalk.yellow('\n설정 관리:')); console.log(chalk.cyan(' rltgjqm config # 설정 메뉴')); console.log(chalk.yellow('\n옵션:')); console.log(chalk.white(' -a, --auto 생성된 명령어를 자동으로 실행')); console.log(chalk.white(' -i, --interactive 각 명령어마다 실행 여부를 확인')); console.log(chalk.white(' --dry-run 명령어만 출력 (기본값)')); console.log(chalk.white(' -h, --help 도움말 출력')); console.log(chalk.white('\n지원되는 AI 플랫폼: ChatGPT, Gemini')); console.log(chalk.white('자연어로 Git 명령어를 생성하고 실행합니다.')); console.log(chalk.white('\n─────────────────────────────────────────────')); console.log(chalk.magenta('위의 모든 메시지들은 현재 코드에서 실제로 사용되는 UI들입니다!')); console.log(chalk.white('─────────────────────────────────────────────')); } /** * 시스템 상태 확인 */ async function showSystemStatus() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('📊 시스템 상태 확인')); console.log(chalk.white('─────────────────────────────────────────────')); // Node.js 정보 console.log(chalk.cyan('\n🔧 Node.js 환경:')); console.log(chalk.white(` 버전: ${process.version}`)); console.log(chalk.white(` 플랫폼: ${process.platform}`)); console.log(chalk.white(` 아키텍처: ${process.arch}`)); console.log(chalk.white(` 작업 디렉터리: ${process.cwd()}`)); // 패키지 정보 console.log(chalk.cyan('\n📦 패키지 정보:')); console.log(chalk.white(` rltgjqm 버전: ${packageJson.version}`)); console.log(chalk.white(` 설명: ${packageJson.description}`)); // 환경변수 확인 console.log(chalk.cyan('\n🌍 환경변수:')); console.log(chalk.white(` OPENAI_API_KEY: ${process.env.OPENAI_API_KEY ? '✅ 설정됨' : '❌ 없음'}`)); console.log(chalk.white(` GEMINI_API_KEY: ${process.env.GEMINI_API_KEY ? '✅ 설정됨' : '❌ 없음'}`)); console.log(chalk.white(` NODE_ENV: ${process.env.NODE_ENV || '설정 안됨'}`)); // 메모리 사용량 const memUsage = process.memoryUsage(); console.log(chalk.cyan('\n💾 메모리 사용량:')); console.log(chalk.white(` RSS: ${(memUsage.rss / 1024 / 1024).toFixed(2)} MB`)); console.log(chalk.white(` Heap Used: ${(memUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`)); console.log(chalk.white(` Heap Total: ${(memUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`)); console.log(chalk.white(` External: ${(memUsage.external / 1024 / 1024).toFixed(2)} MB`)); // 설정 상태 const configStatus = config.checkConfigStatus(); console.log(chalk.cyan('\n⚙️ 설정 상태:')); console.log(chalk.white(` API 키: ${configStatus.hasApiKey ? '✅ 설정됨' : '❌ 없음'}`)); console.log(chalk.white(` AI 플랫폼: ${configStatus.provider || '설정 안됨'}`)); console.log(chalk.white(` 키 소스: ${configStatus.keySource || '없음'}`)); console.log(chalk.white(` 설정 파일: ${configStatus.configExists ? '✅ 존재' : '❌ 없음'}`)); // API 사용량 정보 if (configStatus.hasApiKey && configStatus.provider) { console.log(chalk.cyan('\n📊 오늘의 API 사용량:')); const usage = usageTracker.getCurrentUsage(); const usageBrief = usageTracker.displayUsageBrief(configStatus.provider); if (configStatus.provider === 'chatgpt') { console.log(chalk.white(` 총 토큰: ${usage.chatgpt.totalTokens.toLocaleString()}`)); console.log(chalk.white(` 요청 수: ${usage.chatgpt.requests}회`)); } else if (configStatus.provider === 'gemini') { console.log(chalk.white(` 추정 토큰: ${usage.gemini.estimatedTokens.toLocaleString()}`)); console.log(chalk.white(` 요청 수: ${usage.gemini.requests}회`)); } if (usageBrief) { console.log(chalk.white(` ${usageBrief}`)); } } } /** * AI 서비스 연결 테스트 */ async function testAIConnection() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('🔗 AI 서비스 연결 테스트')); console.log(chalk.white('─────────────────────────────────────────────')); const supportedProviders = aiService.getSupportedProviders(); console.log(chalk.cyan('\n📋 지원되는 AI 플랫폼:')); for (const [key, provider] of Object.entries(supportedProviders)) { console.log(chalk.white(` ${provider.name}: ${provider.modelName}`)); console.log(chalk.white(` 엔드포인트: ${provider.endpoint}`)); } const { provider, apiKey } = aiService.getConfig(); if (!provider || !apiKey) { console.log(chalk.red('\n❌ AI 플랫폼이 설정되지 않았습니다.')); return; } console.log(chalk.cyan(`\n🤖 현재 설정: ${supportedProviders[provider].name}`)); console.log(chalk.white('🔍 연결 테스트 중...')); try { const testResult = await aiService.validateApiKey(provider, apiKey); if (testResult) { console.log(chalk.green('✅ AI 서비스 연결 성공!')); } else { console.log(chalk.red('❌ AI 서비스 연결 실패!')); } } catch (error) { console.log(chalk.red(`❌ 연결 테스트 오류: ${error.message}`)); } } /** * 설정 파일 디버깅 */ async function debugConfig() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('📄 설정 파일 디버깅')); console.log(chalk.white('─────────────────────────────────────────────')); const fs = require('fs'); const os = require('os'); const path = require('path'); const configDir = path.join(os.homedir(), '.rltgjqm'); const configFile = path.join(configDir, 'config.json'); const envFile = path.join(configDir, '.env'); console.log(chalk.cyan('\n📁 설정 디렉터리 정보:')); console.log(chalk.white(` 디렉터리: ${configDir}`)); console.log(chalk.white(` 존재 여부: ${fs.existsSync(configDir) ? '✅' : '❌'}`)); console.log(chalk.cyan('\n📄 설정 파일들:')); // JSON 설정 파일 console.log(chalk.white(` config.json: ${fs.existsSync(configFile) ? '✅ 존재' : '❌ 없음'}`)); if (fs.existsSync(configFile)) { try { const content = fs.readFileSync(configFile, 'utf-8'); const config = JSON.parse(content); console.log(chalk.white(' 내용:')); console.log(chalk.white(` AI 플랫폼: ${config.aiProvider || '없음'}`)); console.log(chalk.white(` API 키: ${config.apiKey ? '설정됨 (숨김)' : '없음'}`)); console.log(chalk.white(` 마지막 업데이트: ${config.lastUpdated || '없음'}`)); } catch (error) { console.log(chalk.red(` 오류: ${error.message}`)); } } // .env 파일 (레거시) console.log(chalk.white(` .env: ${fs.existsSync(envFile) ? '✅ 존재 (레거시)' : '❌ 없음'}`)); if (fs.existsSync(envFile)) { try { const content = fs.readFileSync(envFile, 'utf-8'); console.log(chalk.white(' 내용 (레거시):')); console.log(chalk.white(` GEMINI_API_KEY: ${content.includes('GEMINI_API_KEY') ? '설정됨' : '없음'}`)); } catch (error) { console.log(chalk.red(` 오류: ${error.message}`)); } } // 현재 설정 상태 const currentConfig = config.getAIConfig(); console.log(chalk.cyan('\n⚙️ 현재 활성 설정:')); console.log(chalk.white(` AI 플랫폼: ${currentConfig.provider || '없음'}`)); console.log(chalk.white(` API 키: ${currentConfig.apiKey ? '설정됨' : '없음'}`)); } /** * Git 상태 디스플레이 테스트 */ async function testGitDisplay() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('🏗️ Git 상태 디스플레이 테스트')); console.log(chalk.white('─────────────────────────────────────────────')); try { console.log(chalk.cyan('\n📊 현재 Git 저장소 상태:')); const gitStatus = await executor.getGitStatus(); console.log(chalk.white('\n원시 Git 상태 데이터:')); console.log(chalk.white(JSON.stringify(gitStatus, null, 2))); console.log(chalk.cyan('\n🎨 포맷된 Git 상태 표시:')); console.log(chalk.white('─────────────────────────────────────────────')); executor.displayGitStatus(gitStatus); console.log(chalk.white('─────────────────────────────────────────────')); } catch (error) { console.log(chalk.red(`❌ Git 상태 확인 오류: ${error.message}`)); } } /** * 네트워크 연결 테스트 */ async function testNetwork() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('🌐 네트워크 연결 테스트')); console.log(chalk.white('─────────────────────────────────────────────')); const axios = require('axios'); const endpoints = [ { name: 'Google', url: 'https://www.google.com' }, { name: 'OpenAI API', url: 'https://api.openai.com' }, { name: 'Google AI API', url: 'https://generativelanguage.googleapis.com' }, { name: 'GitHub', url: 'https://api.github.com' } ]; console.log(chalk.cyan('\n🔍 엔드포인트 연결 테스트:')); for (const endpoint of endpoints) { try { console.log(chalk.white(` ${endpoint.name} 테스트 중...`)); const startTime = Date.now(); await axios.get(endpoint.url, { timeout: 5000 }); const responseTime = Date.now() - startTime; console.log(chalk.green(` ✅ ${endpoint.name}: ${responseTime}ms`)); } catch (error) { if (error.code === 'ECONNABORTED') { console.log(chalk.red(` ❌ ${endpoint.name}: 연결 시간 초과`)); } else if (error.response) { console.log(chalk.yellow(` ⚠️ ${endpoint.name}: HTTP ${error.response.status}`)); } else { console.log(chalk.red(` ❌ ${endpoint.name}: ${error.message}`)); } } } // DNS 해석 테스트 console.log(chalk.cyan('\n🔍 DNS 해석 테스트:')); const dns = require('dns').promises; const domains = ['google.com', 'openai.com', 'github.com']; for (const domain of domains) { try { const addresses = await dns.resolve4(domain); console.log(chalk.green(` ✅ ${domain}: ${addresses[0]}`)); } catch (error) { console.log(chalk.red(` ❌ ${domain}: ${error.message}`)); } } } /** * 로그 및 캐시 정보 */ async function showLogsInfo() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('💾 로그 및 캐시 정보')); console.log(chalk.white('─────────────────────────────────────────────')); const fs = require('fs'); const os = require('os'); const path = require('path'); console.log(chalk.cyan('\n📊 임시 디렉터리:')); console.log(chalk.white(` 시스템 임시 디렉터리: ${os.tmpdir()}`)); console.log(chalk.cyan('\n🏠 사용자 홈 디렉터리:')); console.log(chalk.white(` 홈 디렉터리: ${os.homedir()}`)); console.log(chalk.cyan('\n💾 rltgjqm 관련 파일들:')); const configDir = path.join(os.homedir(), '.rltgjqm'); if (fs.existsSync(configDir)) { try { const files = fs.readdirSync(configDir); files.forEach(file => { const filePath = path.join(configDir, file); const stats = fs.statSync(filePath); console.log(chalk.white(` ${file}: ${stats.size} bytes, 수정: ${stats.mtime.toLocaleString()}`)); }); } catch (error) { console.log(chalk.red(` 디렉터리 읽기 오류: ${error.message}`)); } } else { console.log(chalk.white(' rltgjqm 설정 디렉터리가 없습니다.')); } console.log(chalk.cyan('\n📈 프로세스 정보:')); console.log(chalk.white(` 프로세스 ID: ${process.pid}`)); console.log(chalk.white(` 실행 시간: ${Math.floor(process.uptime())}초`)); console.log(chalk.white(` 명령줄 인수: ${process.argv.join(' ')}`)); console.log(chalk.cyan('\n🔧 환경 정보:')); console.log(chalk.white(` 셸: ${process.env.SHELL || '알 수 없음'}`)); console.log(chalk.white(` 터미널: ${process.env.TERM || '알 수 없음'}`)); console.log(chalk.white(` 사용자: ${process.env.USER || process.env.USERNAME || '알 수 없음'}`)); } /** * API 사용량 통계 표시 */ async function showUsageStats() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('📈 API 사용량 통계')); console.log(chalk.white('─────────────────────────────────────────────')); const stats = usageTracker.getUsageStats(); const currentUsage = usageTracker.getCurrentUsage(); console.log(chalk.cyan(`\n📅 날짜: ${stats.date}`)); // ChatGPT 통계 console.log(chalk.cyan('\n🤖 ChatGPT (OpenAI) 통계:')); if (stats.chatgpt.requests > 0) { console.log(chalk.white(` 총 사용 토큰: ${stats.chatgpt.totalTokens.toLocaleString()}`)); console.log(chalk.white(` 총 요청 수: ${stats.chatgpt.requests.toLocaleString()}회`)); console.log(chalk.white(` 평균 토큰/요청: ${stats.chatgpt.averageTokensPerRequest.toLocaleString()}`)); console.log(chalk.white(` 프롬프트 토큰: ${currentUsage.chatgpt.promptTokens.toLocaleString()}`)); console.log(chalk.white(` 응답 토큰: ${currentUsage.chatgpt.completionTokens.toLocaleString()}`)); usageTracker.displayUsageInfo('chatgpt'); } else { console.log(chalk.white(' 오늘 사용 기록이 없습니다.')); } // Gemini 통계 console.log(chalk.cyan('\n🧠 Gemini (Google) 통계:')); if (stats.gemini.requests > 0) { console.log(chalk.white(` 총 추정 토큰: ${stats.gemini.estimatedTokens.toLocaleString()}`)); console.log(chalk.white(` 총 요청 수: ${stats.gemini.requests.toLocaleString()}회`)); console.log(chalk.white(` 평균 토큰/요청: ${stats.gemini.averageTokensPerRequest.toLocaleString()}`)); usageTracker.displayUsageInfo('gemini'); } else { console.log(chalk.white(' 오늘 사용 기록이 없습니다.')); } // 전체 통계 const totalRequests = stats.chatgpt.requests + stats.gemini.requests; console.log(chalk.cyan('\n📊 전체 통계:')); console.log(chalk.white(` 총 요청 수: ${totalRequests.toLocaleString()}회`)); if (totalRequests > 0) { const chatgptPercentage = ((stats.chatgpt.requests / totalRequests) * 100).toFixed(1); const geminiPercentage = ((stats.gemini.requests / totalRequests) * 100).toFixed(1); console.log(chalk.white(` ChatGPT 사용 비율: ${chatgptPercentage}%`)); console.log(chalk.white(` Gemini 사용 비율: ${geminiPercentage}%`)); } // 사용량 파일 정보 console.log(chalk.cyan('\n📄 사용량 파일:')); console.log(chalk.white(` 경로: ${usageTracker.usageFile}`)); try { const fs = require('fs'); const stats = fs.statSync(usageTracker.usageFile); console.log(chalk.white(` 크기: ${stats.size} bytes`)); console.log(chalk.white(` 수정일: ${stats.mtime.toLocaleString()}`)); } catch (error) { console.log(chalk.red(' 파일 정보를 읽을 수 없습니다.')); } } /** * 사용량 초기화 */ async function resetUsageStats() { console.clear(); console.log(chalk.white('─────────────────────────────────────────────')); console.log(chalk.magenta('🔄 사용량 초기화')); console.log(chalk.white('─────────────────────────────────────────────')); const stats = usageTracker.getUsageStats(); console.log(chalk.yellow('\n⚠️ 현재 사용량:')); console.log(chalk.white(` ChatGPT: ${stats.chatgpt.totalTokens.toLocaleString()} 토큰, ${stats.chatgpt.requests}회`)); console.log(chalk.white(` Gemini: ${stats.gemini.estimatedTokens.toLocaleString()} 토큰 (추정), ${stats.gemini.requests}회`)); const { confirm } = await inquirer.prompt([ { type: 'confirm', name: 'confirm', message: '정말로 모든 사용량 데이터를 초기화하시겠습니까?', default: false, prefix: '', suffix: '' } ]); if (confirm) { usageTracker.resetUsage(); console.log(chalk.white('\n─────────────────────────────────────────────')); console.log(chalk.green('✅ 사용량 데이터가 초기화되었습니다!')); console.log(chalk.white('─────────────────────────────────────────────')); } else { console.log(chalk.white('\n─────────────────────────────────────────────')); console.log(chalk.yellow('취소되었습니다.')); console.log(chalk.white('─────────────────────────────────────────────')); } } // CLI 프로그램 설정 program .name('rltgjqm') .description('Gemini API를 사용한 Git 명령어 생성 CLI') .version(packageJson.version); // 메인 명령어 (프롬프트 직접 입력) program .argument('[prompt]', '자연어로 Git 작업을 설명하세요') .option('-a, --auto', '생성된 명령어를 자동으로 실행') .option('-i, --interactive', '각 명령어마다 실행 여부를 확인') .option('--dry-run', '명령어만 출력 (실행하지 않음)') .option('--dry', '기본 실행 모드를 미리보기로 변경') .option('-d, --detail', '상세 출력 모드로 변경') .option('-s, --simple', '간단 출력 모드로 변경') .option('--debug', '프롬프트 디버그 모드 전환') .action(async (promptArg, options) => { // 프롬프트 없이 옵션만 주어진 경우 설정 변경 if (!promptArg && (options.auto || options.interactive || options.dryRun || options.dry || options.detail || options.simple || options.debug)) { if (options.auto) { await config.setDefaultExecutionMode('auto'); return; } if (options.interactive) { await config.setDefaultExecutionMode('interactive'); return; } if (options.dryRun || options.dry) { await config.setDefaultExecutionMode('dry'); return; } if (options.detail) { await config.setOutputMode('detail'); return; } if (options.simple) { await config.setOutputMode('simple'); return; } if (options.debug) { await config.setDebugMode(!config.getDebugMode()); return; } } if (promptArg) { // 프롬프트가 주어진 경우 바로 실행 await executeGitCommand(promptArg, options); } else { // 프롬프트가 없으면 인터랙티브 메뉴 await showMainMenu(); } }); // 설정 관리 명령어 program .command('config') .description('설정 관리 (API 키, 기본 동작 등)') .action(async () => { await showConfigMenuWrapper(); }); // 프로그램 실행 if (process.argv.length <= 2) { // 명령어가 없을 경우 인터랙티브 메뉴 표시 showMainMenu().catch(error => { console.error(chalk.red('❌ 오류 발생:'), error.message); process.exit(1); }); } else { program.parse(process.argv); }