sales-frontend-gemini-cli 0.1.0 → 0.2.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 +1 @@
1
- {"version":3,"sources":["../../src/pr-review/helper.ts","../../src/pr-review/commander.ts"],"names":["__dirname","path","fileURLToPath","reviewFormPath","fs"],"mappings":";;;;;;;;;;;;;AAKA,IAAMA,cAAYC,qBAAK,CAAA,OAAA,CAAQC,iBAAc,CAAA,+PAAe,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAAD,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,2CAA2C,CAAA;AACrF,IAAM,eAAkB,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,0CAA0C,CAAA;AAC1F,IAAM,yBAA4B,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,gDAAgD,CAAA;AACnFC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,yCAAyC;AACzDC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,oDAAoD;;;ACVlH,IAAM,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AAkBpB,IAAA,aAAA,GAAgB,CAAC,YAAA,EAAsBG,eAA2B,KAAA;AAE3E,EAAA,IAAI,WAAc,GAAA,EAAA;AAElB,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,UAAU,CAAG,EAAA;AAG3B,IAAc,WAAA,GAAA,aAAA;AAAA,GACP,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,SAAS,CAAG,EAAA;AAGjC,IAAc,WAAA,GAAA,eAAA;AAAA,GACX,MAAA;AAEH,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,SAAS,CAAA;AAGzC,IAAA,IAAI,UAAe,KAAA,EAAA,IAAM,IAAK,CAAA,UAAA,GAAa,CAAC,CAAG,EAAA;AAC3C,MAAA,OAAA,CAAQ,KAAK,2JAAmC,CAAA;AAChD,MAAA,WAAA,GAAc,CAAW,QAAA,EAAA,IAAA,CAAK,UAAa,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,KAC1C,MAAA;AACH,MAAA,OAAA,CAAQ,KAAK,6KAAgD,CAAA;AAC7D,MAAc,WAAA,GAAA,eAAA;AAAA;AAClB;AAIJ,EAAA,MAAM,KAAQ,GAAA;AAAA,IACV,EAAE,IAAA,EAAM,SAAW,EAAA,OAAA,EAAS,cAAK,EAAA;AAAA,IACjC,EAAE,IAAA,EAAM,eAAiB,EAAA,OAAA,EAAS,iCAAS,EAAA;AAAA,IAC3C,EAAE,IAAA,EAAM,yBAA2B,EAAA,OAAA,EAAS,iCAAS;AAAA,GACzD;AAEA,EAAA,MAAM,aAAa,KACd,CAAA,MAAA,CAAO,UAAQC,mBAAG,CAAA,UAAA,CAAW,KAAK,IAAI,CAAC,CACvC,CAAA,GAAA,CAAI,UAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAC3B,KAAK,IAAI,CAAA;AAKd,EAAM,MAAA,OAAA,GAAU,UAAU,WAAW,CAAA,oEAAA,EAAqB,UAAU,CAAa,eAAA,EAAA,YAAY,qEAAmBD,eAAc,CAAA,sDAAA,CAAA;AAG9H,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA;AAE/C,IAAO,OAAA,CAAA;;AAAA;AAAA,EAA+D,WAAW,CAAA,CAAA,CAAA;AAAA;AAGrF,EAAO,OAAA,OAAA;AACX","file":"commander.cjs","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}"]}
1
+ {"version":3,"sources":["../../src/pr-review/helper.ts","../../src/pr-review/commander.ts"],"names":["__dirname","path","fileURLToPath","reviewFormPath","fs"],"mappings":";;;;;;;;;;;;;AAKA,IAAMA,cAAYC,qBAAK,CAAA,OAAA,CAAQC,iBAAc,CAAA,+PAAe,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAAD,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,2CAA2C,CAAA;AACrF,IAAM,eAAkB,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,0CAA0C,CAAA;AAC1F,IAAM,yBAA4B,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,gDAAgD,CAAA;AACnFC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,yCAAyC;AACzDC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,oDAAoD;;;ACVlH,IAAM,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AAkBpB,IAAA,aAAA,GAAgB,CAAC,YAAA,EAAsBG,eAA2B,KAAA;AAE3E,EAAA,IAAI,WAAc,GAAA,EAAA;AAElB,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,UAAU,CAAG,EAAA;AAG3B,IAAc,WAAA,GAAA,aAAA;AAAA,GACP,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,SAAS,CAAG,EAAA;AAGjC,IAAc,WAAA,GAAA,eAAA;AAAA,GACX,MAAA;AAEH,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,SAAS,CAAA;AAGzC,IAAA,IAAI,UAAe,KAAA,EAAA,IAAM,IAAK,CAAA,UAAA,GAAa,CAAC,CAAG,EAAA;AAC3C,MAAA,OAAA,CAAQ,KAAK,2JAAmC,CAAA;AAChD,MAAA,WAAA,GAAc,CAAW,QAAA,EAAA,IAAA,CAAK,UAAa,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,KAC1C,MAAA;AACH,MAAA,OAAA,CAAQ,KAAK,6KAAgD,CAAA;AAC7D,MAAc,WAAA,GAAA,eAAA;AAAA;AAClB;AAIJ,EAAA,MAAM,KAAQ,GAAA;AAAA,IACV,EAAE,IAAA,EAAM,SAAW,EAAA,OAAA,EAAS,cAAK,EAAA;AAAA,IACjC,EAAE,IAAA,EAAM,eAAiB,EAAA,OAAA,EAAS,iCAAS,EAAA;AAAA,IAC3C,EAAE,IAAA,EAAM,yBAA2B,EAAA,OAAA,EAAS,iCAAS;AAAA,GACzD;AAEA,EAAA,MAAM,aAAa,KACd,CAAA,MAAA,CAAO,UAAQC,mBAAG,CAAA,UAAA,CAAW,KAAK,IAAI,CAAC,CACvC,CAAA,GAAA,CAAI,UAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAC3B,KAAK,IAAI,CAAA;AAKd,EAAM,MAAA,OAAA,GAAU,UAAU,WAAW,CAAA,oEAAA,EAAqB,UAAU,CAAa,eAAA,EAAA,YAAY,qEAAmBD,eAAc,CAAA,sDAAA,CAAA;AAG9H,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA;AAE/C,IAAO,OAAA,CAAA;;AAAA;AAAA,EAA+D,WAAW,CAAA,CAAA,CAAA;AAAA;AAGrF,EAAO,OAAA,OAAA;AACX","file":"commander.cjs","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pr-review/helper.ts","../../src/pr-review/commander.ts"],"names":["reviewFormPath"],"mappings":";;;;;AAKA,IAAM,YAAY,IAAK,CAAA,OAAA,CAAQ,aAAc,CAAA,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,2CAA2C,CAAA;AACrF,IAAM,eAAkB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,0CAA0C,CAAA;AAC1F,IAAM,yBAA4B,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,gDAAgD,CAAA;AACnF,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,yCAAyC;AACzD,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,oDAAoD;;;ACVlH,IAAM,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AAkBpB,IAAA,aAAA,GAAgB,CAAC,YAAA,EAAsBA,eAA2B,KAAA;AAE3E,EAAA,IAAI,WAAc,GAAA,EAAA;AAElB,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,UAAU,CAAG,EAAA;AAG3B,IAAc,WAAA,GAAA,aAAA;AAAA,GACP,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,SAAS,CAAG,EAAA;AAGjC,IAAc,WAAA,GAAA,eAAA;AAAA,GACX,MAAA;AAEH,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,SAAS,CAAA;AAGzC,IAAA,IAAI,UAAe,KAAA,EAAA,IAAM,IAAK,CAAA,UAAA,GAAa,CAAC,CAAG,EAAA;AAC3C,MAAA,OAAA,CAAQ,KAAK,2JAAmC,CAAA;AAChD,MAAA,WAAA,GAAc,CAAW,QAAA,EAAA,IAAA,CAAK,UAAa,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,KAC1C,MAAA;AACH,MAAA,OAAA,CAAQ,KAAK,6KAAgD,CAAA;AAC7D,MAAc,WAAA,GAAA,eAAA;AAAA;AAClB;AAIJ,EAAA,MAAM,KAAQ,GAAA;AAAA,IACV,EAAE,IAAA,EAAM,SAAW,EAAA,OAAA,EAAS,cAAK,EAAA;AAAA,IACjC,EAAE,IAAA,EAAM,eAAiB,EAAA,OAAA,EAAS,iCAAS,EAAA;AAAA,IAC3C,EAAE,IAAA,EAAM,yBAA2B,EAAA,OAAA,EAAS,iCAAS;AAAA,GACzD;AAEA,EAAA,MAAM,aAAa,KACd,CAAA,MAAA,CAAO,UAAQ,EAAG,CAAA,UAAA,CAAW,KAAK,IAAI,CAAC,CACvC,CAAA,GAAA,CAAI,UAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAC3B,KAAK,IAAI,CAAA;AAKd,EAAM,MAAA,OAAA,GAAU,UAAU,WAAW,CAAA,oEAAA,EAAqB,UAAU,CAAa,eAAA,EAAA,YAAY,qEAAmBA,eAAc,CAAA,sDAAA,CAAA;AAG9H,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA;AAE/C,IAAO,OAAA,CAAA;;AAAA;AAAA,EAA+D,WAAW,CAAA,CAAA,CAAA;AAAA;AAGrF,EAAO,OAAA,OAAA;AACX","file":"commander.js","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}"]}
1
+ {"version":3,"sources":["../../src/pr-review/helper.ts","../../src/pr-review/commander.ts"],"names":["reviewFormPath"],"mappings":";;;;;AAKA,IAAM,YAAY,IAAK,CAAA,OAAA,CAAQ,aAAc,CAAA,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,2CAA2C,CAAA;AACrF,IAAM,eAAkB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,0CAA0C,CAAA;AAC1F,IAAM,yBAA4B,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,gDAAgD,CAAA;AACnF,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,yCAAyC;AACzD,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,oDAAoD;;;ACVlH,IAAM,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AAkBpB,IAAA,aAAA,GAAgB,CAAC,YAAA,EAAsBA,eAA2B,KAAA;AAE3E,EAAA,IAAI,WAAc,GAAA,EAAA;AAElB,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,UAAU,CAAG,EAAA;AAG3B,IAAc,WAAA,GAAA,aAAA;AAAA,GACP,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,SAAS,CAAG,EAAA;AAGjC,IAAc,WAAA,GAAA,eAAA;AAAA,GACX,MAAA;AAEH,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,SAAS,CAAA;AAGzC,IAAA,IAAI,UAAe,KAAA,EAAA,IAAM,IAAK,CAAA,UAAA,GAAa,CAAC,CAAG,EAAA;AAC3C,MAAA,OAAA,CAAQ,KAAK,2JAAmC,CAAA;AAChD,MAAA,WAAA,GAAc,CAAW,QAAA,EAAA,IAAA,CAAK,UAAa,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,KAC1C,MAAA;AACH,MAAA,OAAA,CAAQ,KAAK,6KAAgD,CAAA;AAC7D,MAAc,WAAA,GAAA,eAAA;AAAA;AAClB;AAIJ,EAAA,MAAM,KAAQ,GAAA;AAAA,IACV,EAAE,IAAA,EAAM,SAAW,EAAA,OAAA,EAAS,cAAK,EAAA;AAAA,IACjC,EAAE,IAAA,EAAM,eAAiB,EAAA,OAAA,EAAS,iCAAS,EAAA;AAAA,IAC3C,EAAE,IAAA,EAAM,yBAA2B,EAAA,OAAA,EAAS,iCAAS;AAAA,GACzD;AAEA,EAAA,MAAM,aAAa,KACd,CAAA,MAAA,CAAO,UAAQ,EAAG,CAAA,UAAA,CAAW,KAAK,IAAI,CAAC,CACvC,CAAA,GAAA,CAAI,UAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAC3B,KAAK,IAAI,CAAA;AAKd,EAAM,MAAA,OAAA,GAAU,UAAU,WAAW,CAAA,oEAAA,EAAqB,UAAU,CAAa,eAAA,EAAA,YAAY,qEAAmBA,eAAc,CAAA,sDAAA,CAAA;AAG9H,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA;AAE/C,IAAO,OAAA,CAAA;;AAAA;AAAA,EAA+D,WAAW,CAAA,CAAA,CAAA;AAAA;AAGrF,EAAO,OAAA,OAAA;AACX","file":"commander.js","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}"]}
@@ -83,12 +83,46 @@ function openReport(reportPath) {
83
83
  console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
84
84
  }
85
85
  }
86
+ function getDiffArgs() {
87
+ const args = process.argv.slice(2);
88
+ const commitIndex = args.indexOf("--commit");
89
+ const { includeParams, excludeParams } = getGitDiffFilter();
90
+ let diffArgs = "";
91
+ if (commitIndex !== -1) {
92
+ const commitHash = args[commitIndex + 1];
93
+ if (!commitHash) {
94
+ console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
95
+ process.exit(1);
96
+ }
97
+ const nextArg = args[commitIndex + 2];
98
+ let n = 0;
99
+ if (nextArg && !nextArg.startsWith("--")) {
100
+ n = parseInt(nextArg, 10);
101
+ if (isNaN(n)) {
102
+ n = 0;
103
+ }
104
+ }
105
+ console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
106
+ diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
107
+ } else {
108
+ try {
109
+ const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
110
+ if (!check.trim()) {
111
+ console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
112
+ diffArgs = "HEAD~1 HEAD";
113
+ }
114
+ } catch {
115
+ }
116
+ }
117
+ return diffArgs;
118
+ }
86
119
 
87
120
  exports.REPORT_DIR = REPORT_DIR;
88
121
  exports.codingConventionRulesPath = codingConventionRulesPath;
89
122
  exports.createReportDirectory = createReportDirectory;
90
123
  exports.deleteFile = deleteFile;
91
124
  exports.deleteTempDiff = deleteTempDiff;
125
+ exports.getDiffArgs = getDiffArgs;
92
126
  exports.getGitDiffFilter = getGitDiffFilter;
93
127
  exports.getNextFilePath = getNextFilePath;
94
128
  exports.getNowString = getNowString;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pr-review/helper.ts"],"names":["__dirname","path","fileURLToPath","fs","execSync"],"mappings":"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","file":"helper.cjs","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}"]}
1
+ {"version":3,"sources":["../../src/pr-review/helper.ts"],"names":["__dirname","path","fileURLToPath","fs","execSync"],"mappings":"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","file":"helper.cjs","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}"]}
@@ -25,5 +25,6 @@ declare function getGitDiffFilter(): {
25
25
  excludeParams: string;
26
26
  };
27
27
  declare function openReport(reportPath: string): void;
28
+ declare function getDiffArgs(): string;
28
29
 
29
- export { REPORT_DIR, codingConventionRulesPath, createReportDirectory, deleteFile, deleteTempDiff, getGitDiffFilter, getNextFilePath, getNowString, ignoreList, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, tempDiffPath };
30
+ export { REPORT_DIR, codingConventionRulesPath, createReportDirectory, deleteFile, deleteTempDiff, getDiffArgs, getGitDiffFilter, getNextFilePath, getNowString, ignoreList, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, tempDiffPath };
@@ -25,5 +25,6 @@ declare function getGitDiffFilter(): {
25
25
  excludeParams: string;
26
26
  };
27
27
  declare function openReport(reportPath: string): void;
28
+ declare function getDiffArgs(): string;
28
29
 
29
- export { REPORT_DIR, codingConventionRulesPath, createReportDirectory, deleteFile, deleteTempDiff, getGitDiffFilter, getNextFilePath, getNowString, ignoreList, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, tempDiffPath };
30
+ export { REPORT_DIR, codingConventionRulesPath, createReportDirectory, deleteFile, deleteTempDiff, getDiffArgs, getGitDiffFilter, getNextFilePath, getNowString, ignoreList, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, tempDiffPath };
@@ -75,7 +75,40 @@ function openReport(reportPath) {
75
75
  console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
76
76
  }
77
77
  }
78
+ function getDiffArgs() {
79
+ const args = process.argv.slice(2);
80
+ const commitIndex = args.indexOf("--commit");
81
+ const { includeParams, excludeParams } = getGitDiffFilter();
82
+ let diffArgs = "";
83
+ if (commitIndex !== -1) {
84
+ const commitHash = args[commitIndex + 1];
85
+ if (!commitHash) {
86
+ console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
87
+ process.exit(1);
88
+ }
89
+ const nextArg = args[commitIndex + 2];
90
+ let n = 0;
91
+ if (nextArg && !nextArg.startsWith("--")) {
92
+ n = parseInt(nextArg, 10);
93
+ if (isNaN(n)) {
94
+ n = 0;
95
+ }
96
+ }
97
+ console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
98
+ diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
99
+ } else {
100
+ try {
101
+ const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
102
+ if (!check.trim()) {
103
+ console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
104
+ diffArgs = "HEAD~1 HEAD";
105
+ }
106
+ } catch {
107
+ }
108
+ }
109
+ return diffArgs;
110
+ }
78
111
 
79
- export { REPORT_DIR, codingConventionRulesPath, createReportDirectory, deleteFile, deleteTempDiff, getGitDiffFilter, getNextFilePath, getNowString, ignoreList, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, tempDiffPath };
112
+ export { REPORT_DIR, codingConventionRulesPath, createReportDirectory, deleteFile, deleteTempDiff, getDiffArgs, getGitDiffFilter, getNextFilePath, getNowString, ignoreList, namingRulesPath, openReport, reviewFormOneByOnePath, reviewFormPath, rulesPath, tempDiffPath };
80
113
  //# sourceMappingURL=helper.js.map
81
114
  //# sourceMappingURL=helper.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pr-review/helper.ts"],"names":[],"mappings":";;;;;;AAKA,IAAM,YAAY,IAAK,CAAA,OAAA,CAAQ,aAAc,CAAA,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,2CAA2C;AACrF,IAAM,eAAkB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,0CAA0C;AAC1F,IAAM,yBAA4B,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,gDAAgD;AAC1G,IAAM,cAAiB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,yCAAyC;AACxF,IAAM,sBAAyB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,oDAAoD;AAC3G,IAAM,UAAa,GAAA;AACnB,IAAM,YAAe,GAAA;AAErB,IAAM,UAAa,GAAA;AAAA,EACtB,cAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AAAA;AACJ;AAGO,SAAS,eAAA,CAAgB,GAAa,EAAA,QAAA,EAAkB,SAAmB,EAAA;AAC9E,EAAA,IAAI,OAAU,GAAA,CAAA;AAEd,EAAA,OAAO,IAAM,EAAA;AACT,IAAM,MAAA,QAAA,GAAW,IAAK,CAAA,IAAA,CAAK,GAAK,EAAA,CAAA,EAAG,QAAQ,CAAI,CAAA,EAAA,OAAO,CAAG,EAAA,SAAS,CAAE,CAAA,CAAA;AACpE,IAAA,IAAI,CAAC,EAAA,CAAG,UAAW,CAAA,QAAQ,CAAG,EAAA;AAC1B,MAAO,OAAA,QAAA;AAAA;AAEX,IAAA,OAAA,EAAA;AAAA;AAER;AAEO,SAAS,WAAW,QAAkB,EAAA;AACzC,EAAI,IAAA,EAAA,CAAG,UAAW,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,EAAA,CAAG,WAAW,QAAQ,CAAA;AAAA;AAE9B;AAMO,SAAS,cAAiB,GAAA;AAC7B,EAAA,UAAA,CAAW,YAAY,CAAA;AAC3B;AAMO,SAAS,qBAAwB,GAAA;AACpC,EAAA,IAAI,CAAC,EAAA,CAAG,UAAW,CAAA,UAAU,CAAG,EAAA;AAC5B,IAAA,EAAA,CAAG,SAAU,CAAA,UAAA,EAAY,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA;AAEpD;AAMO,SAAS,YAAe,GAAA;AAC3B,EAAM,MAAA,GAAA,uBAAU,IAAK,EAAA;AACrB,EAAM,MAAA,IAAA,GAAO,IAAI,WAAY,EAAA;AAC7B,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,QAAA,KAAa,CAAC,CAAA,CAAE,QAAS,CAAA,CAAA,EAAG,GAAG,CAAA;AACrD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,OAAA,EAAS,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AAChD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,QAAA,EAAU,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACjD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,UAAA,EAAY,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACnD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,UAAA,EAAY,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AAEnD,EAAO,OAAA,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAChD;AAEO,SAAS,gBAAmB,GAAA;AAG/B,EAAA,MAAM,iBAAoB,GAAA,CAAC,MAAQ,EAAA,OAAA,EAAS,QAAQ,OAAO,CAAA;AAG3D,EAAA,MAAM,kBAAkB,UAAW,CAAA,GAAA,CAAI,CAAQ,IAAA,KAAA,CAAA,UAAA,EAAa,IAAI,CAAE,CAAA,CAAA;AAKlE,EAAA,MAAM,KAAQ,GAAA,CAAC,OAAoB,KAAA,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,CAAA;AAC9C,EAAA,MAAM,gBAAgB,iBAAkB,CAAA,GAAA,CAAI,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAC3D,EAAA,MAAM,gBAAgB,eAAgB,CAAA,GAAA,CAAI,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEzD,EAAO,OAAA,EAAE,eAAe,aAAc,EAAA;AAG1C;AAEO,SAAS,WAAW,UAAoB,EAAA;AAE3C,EAAI,IAAA;AACA,IAAA,QAAA,CAAS,CAA4B,yBAAA,EAAA,IAAA,CAAK,OAAQ,CAAA,UAAU,CAAC,CAAG,CAAA,CAAA,CAAA;AAChE,IAAA,OAAA,CAAQ,IAAI,CAAuB,uGAAA,CAAA,CAAA;AAAA,WAC9B,CAAG,EAAA;AACR,IAAQ,OAAA,CAAA,KAAA,CAAM,oEAAkB,CAAC,CAAA;AAAA;AAEzC","file":"helper.js","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}"]}
1
+ {"version":3,"sources":["../../src/pr-review/helper.ts"],"names":[],"mappings":"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","file":"helper.js","sourcesContent":["import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}"]}
@@ -103,6 +103,39 @@ function openReport(reportPath) {
103
103
  console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
104
104
  }
105
105
  }
106
+ function getDiffArgs() {
107
+ const args2 = process.argv.slice(2);
108
+ const commitIndex = args2.indexOf("--commit");
109
+ const { includeParams, excludeParams } = getGitDiffFilter();
110
+ let diffArgs = "";
111
+ if (commitIndex !== -1) {
112
+ const commitHash = args2[commitIndex + 1];
113
+ if (!commitHash) {
114
+ console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
115
+ process.exit(1);
116
+ }
117
+ const nextArg = args2[commitIndex + 2];
118
+ let n = 0;
119
+ if (nextArg && !nextArg.startsWith("--")) {
120
+ n = parseInt(nextArg, 10);
121
+ if (isNaN(n)) {
122
+ n = 0;
123
+ }
124
+ }
125
+ console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
126
+ diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
127
+ } else {
128
+ try {
129
+ const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
130
+ if (!check.trim()) {
131
+ console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
132
+ diffArgs = "HEAD~1 HEAD";
133
+ }
134
+ } catch {
135
+ }
136
+ }
137
+ return diffArgs;
138
+ }
106
139
 
107
140
  // src/pr-review/commander.ts
108
141
  var args = process.argv.slice(2);
@@ -147,14 +180,15 @@ async function main() {
147
180
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
148
181
  createReportDirectory();
149
182
  const { includeParams, excludeParams } = getGitDiffFilter();
150
- const filesCommand = `git diff --name-only -- ${includeParams} ${excludeParams}`;
183
+ const diffArgs = getDiffArgs();
184
+ const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;
151
185
  const fileList = child_process.execSync(filesCommand).toString().split("\n").filter(Boolean);
152
186
  if (fileList.length === 0) {
153
187
  console.log("\u2139\uFE0F \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
154
188
  deleteTempDiff();
155
189
  process.exit(0);
156
190
  }
157
- const fullDiffCommand = `git diff -- ${includeParams} ${excludeParams}`;
191
+ const fullDiffCommand = `git diff ${diffArgs} -- ${includeParams} ${excludeParams}`;
158
192
  const fullDiff = child_process.execSync(fullDiffCommand).toString();
159
193
  const nowStr = getNowString();
160
194
  fs3__default.default.writeFileSync(tempDiffPath, fullDiff);
@@ -164,7 +198,7 @@ async function main() {
164
198
  const promises = fileList.map(async (file) => {
165
199
  try {
166
200
  console.log(`\u{1F50D} Reviewing: ${file}...`);
167
- const fileDiff = child_process.execSync(`git diff -- "${file}"`).toString();
201
+ const fileDiff = child_process.execSync(`git diff ${diffArgs} -- "${file}"`).toString();
168
202
  const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
169
203
  fs3__default.default.writeFileSync(tempOneFileDiffPath, fileDiff);
170
204
  const command = createCommand(tempOneFileDiffPath, reviewFormOneByOnePath);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review-one-by-one.ts"],"names":["execSync","__dirname","path","fileURLToPath","fs","tempDiffPath","reviewFormPath","util","exec"],"mappings":";;;;;;;;;;;;;;;;AAGO,SAAS,uBAA0B,GAAA;AACtC,EAAI,IAAA;AACA,IAAAA,sBAAA,CAAS,kBAAoB,EAAA,EAAE,KAAO,EAAA,QAAA,EAAU,CAAA;AAAA,GAC5C,CAAA,MAAA;AACJ,IAAA,OAAA,CAAQ,IAAI,sLAA6E,CAAA;AACzF,IAAI,IAAA;AACA,MAAAA,sBAAA,CAAS,mCAAqC,EAAA,EAAE,KAAO,EAAA,SAAA,EAAW,CAAA;AAClE,MAAA,OAAA,CAAQ,IAAI,kFAA2B,CAAA;AACvC,MAAA,OAAA,CAAQ,IAAI,6GAAkC,CAAA;AAC9C,MAAA,OAAA,CAAQ,IAAI,4MAAsD,CAAA;AAClE,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,aACT,YAAc,EAAA;AACnB,MAAA,OAAA,CAAQ,MAAM,qLAAwD,CAAA;AACtE,MAAA,OAAA,CAAQ,MAAM,YAAY,CAAA;AAC1B,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAClB;AAER;ACfA,IAAMC,cAAYC,qBAAK,CAAA,OAAA,CAAQC,iBAAc,CAAA,uQAAe,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAAD,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,2CAA2C,CAAA;AACrF,IAAM,eAAkB,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,0CAA0C,CAAA;AAC1F,IAAM,yBAA4B,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,gDAAgD,CAAA;AACnFC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,yCAAyC;AACxF,IAAM,sBAAyB,GAAAC,qBAAA,CAAK,OAAQ,CAAAD,WAAA,EAAW,oDAAoD,CAAA;AAC3G,IAAM,UAAa,GAAA,gBAAA;AACnB,IAAM,YAAe,GAAA,eAAA;AAErB,IAAM,UAAa,GAAA;AAAA,EACtB,cAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AAAA;AACJ,CAAA;AAGO,SAAS,eAAA,CAAgB,GAAa,EAAA,QAAA,EAAkB,SAAmB,EAAA;AAC9E,EAAA,IAAI,OAAU,GAAA,CAAA;AAEd,EAAA,OAAO,IAAM,EAAA;AACT,IAAM,MAAA,QAAA,GAAWC,qBAAK,CAAA,IAAA,CAAK,GAAK,EAAA,CAAA,EAAG,QAAQ,CAAI,CAAA,EAAA,OAAO,CAAG,EAAA,SAAS,CAAE,CAAA,CAAA;AACpE,IAAA,IAAI,CAACE,oBAAA,CAAG,UAAW,CAAA,QAAQ,CAAG,EAAA;AAC1B,MAAO,OAAA,QAAA;AAAA;AAEX,IAAA,OAAA,EAAA;AAAA;AAER;AAEO,SAAS,WAAW,QAAkB,EAAA;AACzC,EAAI,IAAAA,oBAAA,CAAG,UAAW,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAAA,oBAAA,CAAG,WAAW,QAAQ,CAAA;AAAA;AAE9B;AAMO,SAAS,cAAiB,GAAA;AAC7B,EAAA,UAAA,CAAW,YAAY,CAAA;AAC3B;AAMO,SAAS,qBAAwB,GAAA;AACpC,EAAA,IAAI,CAACA,oBAAA,CAAG,UAAW,CAAA,UAAU,CAAG,EAAA;AAC5B,IAAAA,oBAAA,CAAG,SAAU,CAAA,UAAA,EAAY,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA;AAEpD;AAMO,SAAS,YAAe,GAAA;AAC3B,EAAM,MAAA,GAAA,uBAAU,IAAK,EAAA;AACrB,EAAM,MAAA,IAAA,GAAO,IAAI,WAAY,EAAA;AAC7B,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,QAAA,KAAa,CAAC,CAAA,CAAE,QAAS,CAAA,CAAA,EAAG,GAAG,CAAA;AACrD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,OAAA,EAAS,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AAChD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,QAAA,EAAU,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACjD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,UAAA,EAAY,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACnD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,UAAA,EAAY,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AAEnD,EAAO,OAAA,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAChD;AAEO,SAAS,gBAAmB,GAAA;AAG/B,EAAA,MAAM,iBAAoB,GAAA,CAAC,MAAQ,EAAA,OAAA,EAAS,QAAQ,OAAO,CAAA;AAG3D,EAAA,MAAM,kBAAkB,UAAW,CAAA,GAAA,CAAI,CAAQ,IAAA,KAAA,CAAA,UAAA,EAAa,IAAI,CAAE,CAAA,CAAA;AAKlE,EAAA,MAAM,KAAQ,GAAA,CAAC,OAAoB,KAAA,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,CAAA;AAC9C,EAAA,MAAM,gBAAgB,iBAAkB,CAAA,GAAA,CAAI,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAC3D,EAAA,MAAM,gBAAgB,eAAgB,CAAA,GAAA,CAAI,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEzD,EAAO,OAAA,EAAE,eAAe,aAAc,EAAA;AAG1C;AAEO,SAAS,WAAW,UAAoB,EAAA;AAE3C,EAAI,IAAA;AACA,IAAAJ,uBAAS,CAA4B,yBAAA,EAAAE,qBAAA,CAAK,OAAQ,CAAA,UAAU,CAAC,CAAG,CAAA,CAAA,CAAA;AAChE,IAAA,OAAA,CAAQ,IAAI,CAAuB,uGAAA,CAAA,CAAA;AAAA,WAC9B,CAAG,EAAA;AACR,IAAQ,OAAA,CAAA,KAAA,CAAM,oEAAkB,CAAC,CAAA;AAAA;AAEzC;;;AC7GA,IAAM,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AAkB1B,IAAM,aAAA,GAAgB,CAACG,aAAAA,EAAsBC,eAA2B,KAAA;AAE3E,EAAA,IAAI,WAAc,GAAA,EAAA;AAElB,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,UAAU,CAAG,EAAA;AAG3B,IAAc,WAAA,GAAA,aAAA;AAAA,GACP,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,SAAS,CAAG,EAAA;AAGjC,IAAc,WAAA,GAAA,eAAA;AAAA,GACX,MAAA;AAEH,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,SAAS,CAAA;AAGzC,IAAA,IAAI,UAAe,KAAA,EAAA,IAAM,IAAK,CAAA,UAAA,GAAa,CAAC,CAAG,EAAA;AAC3C,MAAA,OAAA,CAAQ,KAAK,2JAAmC,CAAA;AAChD,MAAA,WAAA,GAAc,CAAW,QAAA,EAAA,IAAA,CAAK,UAAa,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,KAC1C,MAAA;AACH,MAAA,OAAA,CAAQ,KAAK,6KAAgD,CAAA;AAC7D,MAAc,WAAA,GAAA,eAAA;AAAA;AAClB;AAIJ,EAAA,MAAM,KAAQ,GAAA;AAAA,IACV,EAAE,IAAA,EAAM,SAAW,EAAA,OAAA,EAAS,cAAK,EAAA;AAAA,IACjC,EAAE,IAAA,EAAM,eAAiB,EAAA,OAAA,EAAS,iCAAS,EAAA;AAAA,IAC3C,EAAE,IAAA,EAAM,yBAA2B,EAAA,OAAA,EAAS,iCAAS;AAAA,GACzD;AAEA,EAAA,MAAM,aAAa,KACd,CAAA,MAAA,CAAO,UAAQF,oBAAG,CAAA,UAAA,CAAW,KAAK,IAAI,CAAC,CACvC,CAAA,GAAA,CAAI,UAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAC3B,KAAK,IAAI,CAAA;AAKd,EAAM,MAAA,OAAA,GAAU,UAAU,WAAW,CAAA,oEAAA,EAAqB,UAAU,CAAaC,eAAAA,EAAAA,aAAY,qEAAmBC,eAAc,CAAA,sDAAA,CAAA;AAG9H,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA;AAE/C,IAAO,OAAA,CAAA;;AAAA;AAAA,EAA+D,WAAW,CAAA,CAAA,CAAA;AAAA;AAGrF,EAAO,OAAA,OAAA;AACX,CAAA;;;ACpEA,IAAM,SAAA,GAAYC,qBAAK,CAAA,SAAA,CAAUC,kBAAI,CAAA;AAoBrC,eAAe,IAAO,GAAA;AAGlB,EAAwB,uBAAA,EAAA;AAExB,EAAI,IAAA;AACA,IAAA,OAAA,CAAQ,IAAI,kEAA6B,CAAA;AAGzC,IAAsB,qBAAA,EAAA;AAEtB,IAAA,MAAM,EAAE,aAAA,EAAe,aAAc,EAAA,GAAI,gBAAiB,EAAA;AAC1D,IAAA,MAAM,YAAe,GAAA,CAAA,wBAAA,EAA2B,aAAa,CAAA,CAAA,EAAI,aAAa,CAAA,CAAA;AAC9E,IAAM,MAAA,QAAA,GAAWR,sBAAS,CAAA,YAAY,CAAE,CAAA,QAAA,GAAW,KAAM,CAAA,IAAI,CAAE,CAAA,MAAA,CAAO,OAAO,CAAA;AAE7E,IAAI,IAAA,QAAA,CAAS,WAAW,CAAG,EAAA;AACvB,MAAA,OAAA,CAAQ,IAAI,wEAAiB,CAAA;AAC7B,MAAe,cAAA,EAAA;AAEf,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAGlB,IAAA,MAAM,eAAkB,GAAA,CAAA,YAAA,EAAe,aAAa,CAAA,CAAA,EAAI,aAAa,CAAA,CAAA;AACrE,IAAA,MAAM,QAAWA,GAAAA,sBAAAA,CAAS,eAAe,CAAA,CAAE,QAAS,EAAA;AACpD,IAAA,MAAM,SAAS,YAAa,EAAA;AAE5B,IAAAI,oBAAAA,CAAG,aAAc,CAAA,YAAA,EAAc,QAAQ,CAAA;AAGvC,IAAA,MAAM,gBAAgB,eAAgB,CAAA,UAAA,EAAY,CAAG,EAAA,MAAM,SAAS,MAAM,CAAA;AAC1E,IAAAA,oBAAAA,CAAG,YAAa,CAAA,YAAA,EAAc,aAAa,CAAA;AAG3C,IAAA,MAAM,eAAkB,GAAA,eAAA,CAAgB,UAAY,EAAA,MAAA,EAAQ,KAAK,CAAA;AAIjE,IAAA,MAAM,QAAW,GAAA,QAAA,CAAS,GAAI,CAAA,OAAO,IAAS,KAAA;AAC1C,MAAI,IAAA;AACA,QAAQ,OAAA,CAAA,GAAA,CAAI,CAAiB,qBAAA,EAAA,IAAI,CAAK,GAAA,CAAA,CAAA;AAGtC,QAAA,MAAM,WAAWJ,sBAAS,CAAA,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,CAAG,EAAE,QAAS,EAAA;AAE5D,QAAA,MAAM,sBAAsB,CAAa,UAAA,EAAA,IAAA,CAAK,OAAQ,CAAA,KAAA,EAAO,GAAG,CAAC,CAAA,IAAA,CAAA;AACjE,QAAAI,oBAAAA,CAAG,aAAc,CAAA,mBAAA,EAAqB,QAAQ,CAAA;AAI9C,QAAM,MAAA,OAAA,GAAU,aAAc,CAAA,mBAAA,EAAqB,sBAAsB,CAAA;AAIzE,QAAM,MAAA,EAAE,MAAO,EAAA,GAAI,MAAM,SAAA,CAAU,OAAS,EAAA,EAAE,SAAW,EAAA,IAAA,GAAO,IAAO,GAAA,EAAA,EAAI,CAAA;AAC3E,QAAM,MAAA,MAAA,GAAS,OAAO,QAAS,EAAA;AAG/B,QAAA,UAAA,CAAW,mBAAmB,CAAA;AAE9B,QAAM,MAAA,UAAA,GAAa,aAAa,IAAI;AAAA,EAAK,MAAM;;AAAA,CAAA;AAG/C,QAAA,OAAA,CAAQ,IAAI,UAAU,CAAA;AAEtB,QAAAA,oBAAAA,CAAG,cAAe,CAAA,eAAA,EAAiB,UAAU,CAAA;AAG7C,QAAAA,oBAAAA,CAAG,eAAe,eAAiB,EAAA;;AAAA;;AAAA,EAAqB,OAAO,CAAE,CAAA,CAAA;AAAA,eAC5D,GAAK,EAAA;AACV,QAAA,OAAA,CAAQ,KAAM,CAAA,CAAA,4BAAA,EAA0B,IAAI,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA;AAEpD,QAAM,MAAA,WAAA,GAAc,aAAa,IAAI;AAAA;AAAA;AAAA,EAA8B,GAAG;AAAA;;AAAA,CAAA;AACtE,QAAAA,oBAAAA,CAAG,cAAe,CAAA,eAAA,EAAiB,WAAW,CAAA;AAG9C,QAAI,IAAA;AACA,UAAA,MAAM,sBAAsB,CAAa,UAAA,EAAA,IAAA,CAAK,OAAQ,CAAA,KAAA,EAAO,GAAG,CAAC,CAAA,IAAA,CAAA;AACjE,UAAIA,IAAAA,oBAAAA,CAAG,UAAW,CAAA,mBAAmB,CAAG,EAAA;AACpC,YAAA,UAAA,CAAW,mBAAmB,CAAA;AAAA;AAClC,iBACK,CAAG,EAAA;AACR,UAAA,OAAA,CAAQ,KAAM,CAAA,CAAA,oCAAA,EAAkC,IAAI,CAAA,CAAA,CAAA,EAAK,CAAC,CAAA;AAAA;AAC9D;AACJ,KACH,CAAA;AAED,IAAM,MAAA,OAAA,CAAQ,IAAI,QAAQ,CAAA;AAE1B,IAAA,OAAA,CAAQ,GAAI,CAAA;AAAA,qEAAkB,CAAA,CAAA;AAC9B,IAAQ,OAAA,CAAA,GAAA,CAAI,CAAiB,wDAAA,EAAA,eAAe,CAAE,CAAA,CAAA;AAC9C,IAAQ,OAAA,CAAA,GAAA,CAAI,CAAe,gCAAA,EAAA,aAAa,CAAE,CAAA,CAAA;AAG1C,IAAA,UAAA,CAAW,eAAe,CAAA;AAE1B,IAAe,cAAA,EAAA;AAAA,WAGV,KAAO,EAAA;AACZ,IAAA,OAAA,CAAQ,MAAM,2FAAqB,CAAA;AACnC,IAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAGnB,IAAe,cAAA,EAAA;AAEf,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAEtB;AAEA,IAAK,EAAA","file":"review-one-by-one.cjs","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync, exec } from 'child_process';\nimport fs from 'fs';\nimport util from 'util';\n\nconst execAsync = util.promisify(exec);\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n tempDiffPath,\n getNextFilePath,\n deleteTempDiff,\n deleteFile,\n createReportDirectory,\n REPORT_DIR,\n getNowString,\n getGitDiffFilter,\n reviewFormOneByOnePath,\n openReport\n} from './helper';\n\n\n\nasync function main() {\n\n // gemini-cli 설치 확인 및 설치\n checkGeminiCliInstalled();\n\n try {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n const { includeParams, excludeParams } = getGitDiffFilter();\n const filesCommand = `git diff --name-only -- ${includeParams} ${excludeParams}`;\n const fileList = execSync(filesCommand).toString().split('\\n').filter(Boolean);\n\n if (fileList.length === 0) {\n console.log('ℹ️ 변경 사항이 없습니다.');\n deleteTempDiff();\n\n process.exit(0);\n }\n\n const fullDiffCommand = `git diff -- ${includeParams} ${excludeParams}`;\n const fullDiff = execSync(fullDiffCommand).toString()\n const nowStr = getNowString();\n\n fs.writeFileSync(tempDiffPath, fullDiff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n\n // let fullReport = '';\n // 병렬 처리를 위해 Promise.all 사용\n const promises = fileList.map(async (file) => {\n try {\n console.log(`🔍 Reviewing: ${file}...`);\n\n // 3. 개별 파일에 대한 diff 생성\n const fileDiff = execSync(`git diff -- \"${file}\"`).toString();\n\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n fs.writeFileSync(tempOneFileDiffPath, fileDiff);\n\n\n // 4. 명령어 생성\n const command = createCommand(tempOneFileDiffPath, reviewFormOneByOnePath);\n\n // 5. AI에게 해당 파일만 리뷰 요청 (비동기 실행)\n // execSync 대신 execAsync 사용. maxBuffer를 넉넉하게 설정\n const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });\n const result = stdout.toString();\n\n // 6. 리뷰 마친후 해당 파일 삭제\n deleteFile(tempOneFileDiffPath);\n\n const tempReport = `### File: ${file}\\n${result}\\n\\n`;\n\n // 콘솔 출력\n console.log(tempReport);\n\n fs.appendFileSync(savedReportPath, tempReport);\n\n // 사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n } catch (err) {\n console.error(`❌ Error reviewing file ${file}:`, err);\n // 에러 내용도 리포트에 포함\n const errorReport = `### File: ${file}\\n❌ Review Failed\\n\\`\\`\\`\\n${err}\\n\\`\\`\\`\\n\\n`;\n fs.appendFileSync(savedReportPath, errorReport);\n\n // 임시 파일 정리 시도\n try {\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n if (fs.existsSync(tempOneFileDiffPath)) {\n deleteFile(tempOneFileDiffPath);\n }\n } catch (e) {\n console.error(`❌ Error deleting temp file for ${file}:`, e);\n }\n }\n });\n\n await Promise.all(promises);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n deleteTempDiff();\n\n\n } catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n process.exit(1);\n }\n}\n\nmain();\n\n"]}
1
+ {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review-one-by-one.ts"],"names":["execSync","__dirname","path","fileURLToPath","fs","args","tempDiffPath","reviewFormPath","util","exec"],"mappings":"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","file":"review-one-by-one.cjs","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync, exec } from 'child_process';\nimport fs from 'fs';\nimport util from 'util';\n\nconst execAsync = util.promisify(exec);\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n tempDiffPath,\n getNextFilePath,\n deleteTempDiff,\n deleteFile,\n createReportDirectory,\n REPORT_DIR,\n getNowString,\n getGitDiffFilter,\n reviewFormOneByOnePath,\n getDiffArgs,\n openReport\n} from './helper';\n\n\n\nasync function main() {\n\n // gemini-cli 설치 확인 및 설치\n checkGeminiCliInstalled();\n\n try {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n const diffArgs = getDiffArgs();\n\n const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;\n const fileList = execSync(filesCommand).toString().split('\\n').filter(Boolean);\n\n if (fileList.length === 0) {\n console.log('ℹ️ 변경 사항이 없습니다.');\n deleteTempDiff();\n\n process.exit(0);\n }\n\n const fullDiffCommand = `git diff ${diffArgs} -- ${includeParams} ${excludeParams}`;\n const fullDiff = execSync(fullDiffCommand).toString()\n const nowStr = getNowString();\n\n fs.writeFileSync(tempDiffPath, fullDiff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n\n // let fullReport = '';\n // 병렬 처리를 위해 Promise.all 사용\n const promises = fileList.map(async (file) => {\n try {\n console.log(`🔍 Reviewing: ${file}...`);\n\n // 3. 개별 파일에 대한 diff 생성\n const fileDiff = execSync(`git diff ${diffArgs} -- \"${file}\"`).toString();\n\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n fs.writeFileSync(tempOneFileDiffPath, fileDiff);\n\n\n // 4. 명령어 생성\n const command = createCommand(tempOneFileDiffPath, reviewFormOneByOnePath);\n\n // 5. AI에게 해당 파일만 리뷰 요청 (비동기 실행)\n // execSync 대신 execAsync 사용. maxBuffer를 넉넉하게 설정\n const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });\n const result = stdout.toString();\n\n // 6. 리뷰 마친후 해당 파일 삭제\n deleteFile(tempOneFileDiffPath);\n\n const tempReport = `### File: ${file}\\n${result}\\n\\n`;\n\n // 콘솔 출력\n console.log(tempReport);\n\n fs.appendFileSync(savedReportPath, tempReport);\n\n // 사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n } catch (err) {\n console.error(`❌ Error reviewing file ${file}:`, err);\n // 에러 내용도 리포트에 포함\n const errorReport = `### File: ${file}\\n❌ Review Failed\\n\\`\\`\\`\\n${err}\\n\\`\\`\\`\\n\\n`;\n fs.appendFileSync(savedReportPath, errorReport);\n\n // 임시 파일 정리 시도\n try {\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n if (fs.existsSync(tempOneFileDiffPath)) {\n deleteFile(tempOneFileDiffPath);\n }\n } catch (e) {\n console.error(`❌ Error deleting temp file for ${file}:`, e);\n }\n }\n });\n\n await Promise.all(promises);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n deleteTempDiff();\n\n\n } catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n process.exit(1);\n }\n}\n\nmain();\n\n"]}
@@ -94,6 +94,39 @@ function openReport(reportPath) {
94
94
  console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
95
95
  }
96
96
  }
97
+ function getDiffArgs() {
98
+ const args2 = process.argv.slice(2);
99
+ const commitIndex = args2.indexOf("--commit");
100
+ const { includeParams, excludeParams } = getGitDiffFilter();
101
+ let diffArgs = "";
102
+ if (commitIndex !== -1) {
103
+ const commitHash = args2[commitIndex + 1];
104
+ if (!commitHash) {
105
+ console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
106
+ process.exit(1);
107
+ }
108
+ const nextArg = args2[commitIndex + 2];
109
+ let n = 0;
110
+ if (nextArg && !nextArg.startsWith("--")) {
111
+ n = parseInt(nextArg, 10);
112
+ if (isNaN(n)) {
113
+ n = 0;
114
+ }
115
+ }
116
+ console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
117
+ diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
118
+ } else {
119
+ try {
120
+ const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
121
+ if (!check.trim()) {
122
+ console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
123
+ diffArgs = "HEAD~1 HEAD";
124
+ }
125
+ } catch {
126
+ }
127
+ }
128
+ return diffArgs;
129
+ }
97
130
 
98
131
  // src/pr-review/commander.ts
99
132
  var args = process.argv.slice(2);
@@ -138,14 +171,15 @@ async function main() {
138
171
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
139
172
  createReportDirectory();
140
173
  const { includeParams, excludeParams } = getGitDiffFilter();
141
- const filesCommand = `git diff --name-only -- ${includeParams} ${excludeParams}`;
174
+ const diffArgs = getDiffArgs();
175
+ const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;
142
176
  const fileList = execSync(filesCommand).toString().split("\n").filter(Boolean);
143
177
  if (fileList.length === 0) {
144
178
  console.log("\u2139\uFE0F \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
145
179
  deleteTempDiff();
146
180
  process.exit(0);
147
181
  }
148
- const fullDiffCommand = `git diff -- ${includeParams} ${excludeParams}`;
182
+ const fullDiffCommand = `git diff ${diffArgs} -- ${includeParams} ${excludeParams}`;
149
183
  const fullDiff = execSync(fullDiffCommand).toString();
150
184
  const nowStr = getNowString();
151
185
  fs3.writeFileSync(tempDiffPath, fullDiff);
@@ -155,7 +189,7 @@ async function main() {
155
189
  const promises = fileList.map(async (file) => {
156
190
  try {
157
191
  console.log(`\u{1F50D} Reviewing: ${file}...`);
158
- const fileDiff = execSync(`git diff -- "${file}"`).toString();
192
+ const fileDiff = execSync(`git diff ${diffArgs} -- "${file}"`).toString();
159
193
  const tempOneFileDiffPath = `temp_diff_${file.replace(/\//g, "_")}.txt`;
160
194
  fs3.writeFileSync(tempOneFileDiffPath, fileDiff);
161
195
  const command = createCommand(tempOneFileDiffPath, reviewFormOneByOnePath);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review-one-by-one.ts"],"names":["fs","execSync","tempDiffPath","reviewFormPath"],"mappings":"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","file":"review-one-by-one.js","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync, exec } from 'child_process';\nimport fs from 'fs';\nimport util from 'util';\n\nconst execAsync = util.promisify(exec);\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n tempDiffPath,\n getNextFilePath,\n deleteTempDiff,\n deleteFile,\n createReportDirectory,\n REPORT_DIR,\n getNowString,\n getGitDiffFilter,\n reviewFormOneByOnePath,\n openReport\n} from './helper';\n\n\n\nasync function main() {\n\n // gemini-cli 설치 확인 및 설치\n checkGeminiCliInstalled();\n\n try {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n const { includeParams, excludeParams } = getGitDiffFilter();\n const filesCommand = `git diff --name-only -- ${includeParams} ${excludeParams}`;\n const fileList = execSync(filesCommand).toString().split('\\n').filter(Boolean);\n\n if (fileList.length === 0) {\n console.log('ℹ️ 변경 사항이 없습니다.');\n deleteTempDiff();\n\n process.exit(0);\n }\n\n const fullDiffCommand = `git diff -- ${includeParams} ${excludeParams}`;\n const fullDiff = execSync(fullDiffCommand).toString()\n const nowStr = getNowString();\n\n fs.writeFileSync(tempDiffPath, fullDiff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n\n // let fullReport = '';\n // 병렬 처리를 위해 Promise.all 사용\n const promises = fileList.map(async (file) => {\n try {\n console.log(`🔍 Reviewing: ${file}...`);\n\n // 3. 개별 파일에 대한 diff 생성\n const fileDiff = execSync(`git diff -- \"${file}\"`).toString();\n\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n fs.writeFileSync(tempOneFileDiffPath, fileDiff);\n\n\n // 4. 명령어 생성\n const command = createCommand(tempOneFileDiffPath, reviewFormOneByOnePath);\n\n // 5. AI에게 해당 파일만 리뷰 요청 (비동기 실행)\n // execSync 대신 execAsync 사용. maxBuffer를 넉넉하게 설정\n const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });\n const result = stdout.toString();\n\n // 6. 리뷰 마친후 해당 파일 삭제\n deleteFile(tempOneFileDiffPath);\n\n const tempReport = `### File: ${file}\\n${result}\\n\\n`;\n\n // 콘솔 출력\n console.log(tempReport);\n\n fs.appendFileSync(savedReportPath, tempReport);\n\n // 사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n } catch (err) {\n console.error(`❌ Error reviewing file ${file}:`, err);\n // 에러 내용도 리포트에 포함\n const errorReport = `### File: ${file}\\n❌ Review Failed\\n\\`\\`\\`\\n${err}\\n\\`\\`\\`\\n\\n`;\n fs.appendFileSync(savedReportPath, errorReport);\n\n // 임시 파일 정리 시도\n try {\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n if (fs.existsSync(tempOneFileDiffPath)) {\n deleteFile(tempOneFileDiffPath);\n }\n } catch (e) {\n console.error(`❌ Error deleting temp file for ${file}:`, e);\n }\n }\n });\n\n await Promise.all(promises);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n deleteTempDiff();\n\n\n } catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n process.exit(1);\n }\n}\n\nmain();\n\n"]}
1
+ {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review-one-by-one.ts"],"names":["fs","execSync","args","tempDiffPath","reviewFormPath"],"mappings":"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","file":"review-one-by-one.js","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync, exec } from 'child_process';\nimport fs from 'fs';\nimport util from 'util';\n\nconst execAsync = util.promisify(exec);\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n tempDiffPath,\n getNextFilePath,\n deleteTempDiff,\n deleteFile,\n createReportDirectory,\n REPORT_DIR,\n getNowString,\n getGitDiffFilter,\n reviewFormOneByOnePath,\n getDiffArgs,\n openReport\n} from './helper';\n\n\n\nasync function main() {\n\n // gemini-cli 설치 확인 및 설치\n checkGeminiCliInstalled();\n\n try {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n const diffArgs = getDiffArgs();\n\n const filesCommand = `git diff --name-only ${diffArgs} -- ${includeParams} ${excludeParams}`;\n const fileList = execSync(filesCommand).toString().split('\\n').filter(Boolean);\n\n if (fileList.length === 0) {\n console.log('ℹ️ 변경 사항이 없습니다.');\n deleteTempDiff();\n\n process.exit(0);\n }\n\n const fullDiffCommand = `git diff ${diffArgs} -- ${includeParams} ${excludeParams}`;\n const fullDiff = execSync(fullDiffCommand).toString()\n const nowStr = getNowString();\n\n fs.writeFileSync(tempDiffPath, fullDiff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n\n // let fullReport = '';\n // 병렬 처리를 위해 Promise.all 사용\n const promises = fileList.map(async (file) => {\n try {\n console.log(`🔍 Reviewing: ${file}...`);\n\n // 3. 개별 파일에 대한 diff 생성\n const fileDiff = execSync(`git diff ${diffArgs} -- \"${file}\"`).toString();\n\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n fs.writeFileSync(tempOneFileDiffPath, fileDiff);\n\n\n // 4. 명령어 생성\n const command = createCommand(tempOneFileDiffPath, reviewFormOneByOnePath);\n\n // 5. AI에게 해당 파일만 리뷰 요청 (비동기 실행)\n // execSync 대신 execAsync 사용. maxBuffer를 넉넉하게 설정\n const { stdout } = await execAsync(command, { maxBuffer: 1024 * 1024 * 20 });\n const result = stdout.toString();\n\n // 6. 리뷰 마친후 해당 파일 삭제\n deleteFile(tempOneFileDiffPath);\n\n const tempReport = `### File: ${file}\\n${result}\\n\\n`;\n\n // 콘솔 출력\n console.log(tempReport);\n\n fs.appendFileSync(savedReportPath, tempReport);\n\n // 사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n } catch (err) {\n console.error(`❌ Error reviewing file ${file}:`, err);\n // 에러 내용도 리포트에 포함\n const errorReport = `### File: ${file}\\n❌ Review Failed\\n\\`\\`\\`\\n${err}\\n\\`\\`\\`\\n\\n`;\n fs.appendFileSync(savedReportPath, errorReport);\n\n // 임시 파일 정리 시도\n try {\n const tempOneFileDiffPath = `temp_diff_${file.replace(/\\//g, '_')}.txt`;\n if (fs.existsSync(tempOneFileDiffPath)) {\n deleteFile(tempOneFileDiffPath);\n }\n } catch (e) {\n console.error(`❌ Error deleting temp file for ${file}:`, e);\n }\n }\n });\n\n await Promise.all(promises);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n deleteTempDiff();\n\n\n } catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n process.exit(1);\n }\n}\n\nmain();\n\n"]}
@@ -101,6 +101,39 @@ function openReport(reportPath) {
101
101
  console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
102
102
  }
103
103
  }
104
+ function getDiffArgs() {
105
+ const args3 = process.argv.slice(2);
106
+ const commitIndex = args3.indexOf("--commit");
107
+ const { includeParams, excludeParams } = getGitDiffFilter();
108
+ let diffArgs = "";
109
+ if (commitIndex !== -1) {
110
+ const commitHash = args3[commitIndex + 1];
111
+ if (!commitHash) {
112
+ console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
113
+ process.exit(1);
114
+ }
115
+ const nextArg = args3[commitIndex + 2];
116
+ let n = 0;
117
+ if (nextArg && !nextArg.startsWith("--")) {
118
+ n = parseInt(nextArg, 10);
119
+ if (isNaN(n)) {
120
+ n = 0;
121
+ }
122
+ }
123
+ console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
124
+ diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
125
+ } else {
126
+ try {
127
+ const check = child_process.execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
128
+ if (!check.trim()) {
129
+ console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
130
+ diffArgs = "HEAD~1 HEAD";
131
+ }
132
+ } catch {
133
+ }
134
+ }
135
+ return diffArgs;
136
+ }
104
137
 
105
138
  // src/pr-review/commander.ts
106
139
  var args = process.argv.slice(2);
@@ -143,12 +176,17 @@ var isTest = args2.includes("--test");
143
176
  checkGeminiCliInstalled();
144
177
  try {
145
178
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
146
- createReportDirectory();
147
179
  const nowStr = getNowString();
180
+ createReportDirectory();
181
+ const diffArgs = getDiffArgs();
182
+ let diff = "";
148
183
  const { includeParams, excludeParams } = getGitDiffFilter();
149
- const diff = child_process.execSync(`git diff -- ${includeParams} ${excludeParams}`).toString();
184
+ try {
185
+ diff = child_process.execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();
186
+ } catch {
187
+ }
150
188
  if (!diff.trim() && !isTest) {
151
- console.log("\u2139\uFE0F \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
189
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (Unstaged Empty & HEAD Empty).");
152
190
  deleteTempDiff();
153
191
  process.exit(0);
154
192
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review.ts"],"names":["execSync","__dirname","path","fileURLToPath","fs","tempDiffPath","reviewFormPath","args"],"mappings":"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","file":"review.cjs","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync } from 'child_process';\nimport fs from 'fs';\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n createReportDirectory,\n deleteTempDiff,\n getGitDiffFilter,\n getNextFilePath,\n getNowString,\n openReport,\n REPORT_DIR,\n reviewFormPath,\n tempDiffPath\n} from './helper';\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\nconst isTest = args.includes('--test');\n\n// gemini-cli 설치 확인 및 설치\ncheckGeminiCliInstalled();\n\ntry {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n const nowStr = getNowString();\n const { includeParams, excludeParams } = getGitDiffFilter();\n // git diff 생성\n const diff = execSync(`git diff -- ${includeParams} ${excludeParams}`).toString();\n\n if (!diff.trim() && !isTest) {\n console.log('ℹ️ 변경 사항이 없습니다.');\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(0);\n }\n\n\n fs.writeFileSync(tempDiffPath, diff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 명령어 생성\n const command = createCommand(tempDiffPath, reviewFormPath);\n\n // 결과가 길 수 있으므로 buffer를 넉넉하게 잡거나 순차적으로 받음. 여기서는 간단히 toString()\n const result = execSync(command).toString();\n\n // 콘솔 출력\n console.log(result);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n fs.writeFileSync(savedReportPath, result);\n\n //사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n} catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(1);\n}\n\n"]}
1
+ {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review.ts"],"names":["execSync","__dirname","path","fileURLToPath","fs","args","tempDiffPath","reviewFormPath"],"mappings":"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","file":"review.cjs","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync } from 'child_process';\nimport fs from 'fs';\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n createReportDirectory,\n deleteTempDiff,\n getGitDiffFilter,\n getNextFilePath,\n getNowString,\n getDiffArgs,\n openReport,\n REPORT_DIR,\n reviewFormPath,\n tempDiffPath\n} from './helper';\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\nconst isTest = args.includes('--test');\n\n// gemini-cli 설치 확인 및 설치\ncheckGeminiCliInstalled();\n\ntry {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n const nowStr = getNowString();\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n // diff 가져오기\n const diffArgs = getDiffArgs();\n let diff = '';\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n try {\n diff = execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();\n } catch {\n // ignore\n }\n\n if (!diff.trim() && !isTest) {\n console.log('ℹ️ 리뷰할 변경 사항이 없습니다 (Unstaged Empty & HEAD Empty).');\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(0);\n }\n\n\n fs.writeFileSync(tempDiffPath, diff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 명령어 생성\n const command = createCommand(tempDiffPath, reviewFormPath);\n\n // 결과가 길 수 있으므로 buffer를 넉넉하게 잡거나 순차적으로 받음. 여기서는 간단히 toString()\n const result = execSync(command).toString();\n\n // 콘솔 출력\n console.log(result);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n fs.writeFileSync(savedReportPath, result);\n\n //사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n} catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(1);\n}\n\n"]}
@@ -93,6 +93,39 @@ function openReport(reportPath) {
93
93
  console.error("\u26A0\uFE0F \uBE0C\uB77C\uC6B0\uC800 \uC5F4\uAE30 \uC2E4\uD328:", e);
94
94
  }
95
95
  }
96
+ function getDiffArgs() {
97
+ const args3 = process.argv.slice(2);
98
+ const commitIndex = args3.indexOf("--commit");
99
+ const { includeParams, excludeParams } = getGitDiffFilter();
100
+ let diffArgs = "";
101
+ if (commitIndex !== -1) {
102
+ const commitHash = args3[commitIndex + 1];
103
+ if (!commitHash) {
104
+ console.error("\u274C \uCEE4\uBC0B \uD574\uC2DC\uAC00 \uC81C\uACF5\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
105
+ process.exit(1);
106
+ }
107
+ const nextArg = args3[commitIndex + 2];
108
+ let n = 0;
109
+ if (nextArg && !nextArg.startsWith("--")) {
110
+ n = parseInt(nextArg, 10);
111
+ if (isNaN(n)) {
112
+ n = 0;
113
+ }
114
+ }
115
+ console.log(`\u2139\uFE0F \uCEE4\uBC0B '${commitHash}' ${n > 0 ? ` \uD3EC\uD568 \uCD1D ${n + 1}\uAC1C\uC758 \uCEE4\uBC0B` : ""}\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...`);
116
+ diffArgs = `${commitHash}~${n + 1} ${commitHash}`;
117
+ } else {
118
+ try {
119
+ const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();
120
+ if (!check.trim()) {
121
+ console.log("\u2139\uFE0F Unstaged \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uB9C8\uC9C0\uB9C9 \uCEE4\uBC0B(HEAD)\uC744 \uB9AC\uBDF0\uD569\uB2C8\uB2E4...");
122
+ diffArgs = "HEAD~1 HEAD";
123
+ }
124
+ } catch {
125
+ }
126
+ }
127
+ return diffArgs;
128
+ }
96
129
 
97
130
  // src/pr-review/commander.ts
98
131
  var args = process.argv.slice(2);
@@ -135,12 +168,17 @@ var isTest = args2.includes("--test");
135
168
  checkGeminiCliInstalled();
136
169
  try {
137
170
  console.log("\u{1F680} AI Code Review\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4...");
138
- createReportDirectory();
139
171
  const nowStr = getNowString();
172
+ createReportDirectory();
173
+ const diffArgs = getDiffArgs();
174
+ let diff = "";
140
175
  const { includeParams, excludeParams } = getGitDiffFilter();
141
- const diff = execSync(`git diff -- ${includeParams} ${excludeParams}`).toString();
176
+ try {
177
+ diff = execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();
178
+ } catch {
179
+ }
142
180
  if (!diff.trim() && !isTest) {
143
- console.log("\u2139\uFE0F \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
181
+ console.log("\u2139\uFE0F \uB9AC\uBDF0\uD560 \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 (Unstaged Empty & HEAD Empty).");
144
182
  deleteTempDiff();
145
183
  process.exit(0);
146
184
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review.ts"],"names":["execSync","tempDiffPath","reviewFormPath","fs","args"],"mappings":"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","file":"review.js","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync } from 'child_process';\nimport fs from 'fs';\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n createReportDirectory,\n deleteTempDiff,\n getGitDiffFilter,\n getNextFilePath,\n getNowString,\n openReport,\n REPORT_DIR,\n reviewFormPath,\n tempDiffPath\n} from './helper';\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\nconst isTest = args.includes('--test');\n\n// gemini-cli 설치 확인 및 설치\ncheckGeminiCliInstalled();\n\ntry {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n const nowStr = getNowString();\n const { includeParams, excludeParams } = getGitDiffFilter();\n // git diff 생성\n const diff = execSync(`git diff -- ${includeParams} ${excludeParams}`).toString();\n\n if (!diff.trim() && !isTest) {\n console.log('ℹ️ 변경 사항이 없습니다.');\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(0);\n }\n\n\n fs.writeFileSync(tempDiffPath, diff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 명령어 생성\n const command = createCommand(tempDiffPath, reviewFormPath);\n\n // 결과가 길 수 있으므로 buffer를 넉넉하게 잡거나 순차적으로 받음. 여기서는 간단히 toString()\n const result = execSync(command).toString();\n\n // 콘솔 출력\n console.log(result);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n fs.writeFileSync(savedReportPath, result);\n\n //사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n} catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(1);\n}\n\n"]}
1
+ {"version":3,"sources":["../../src/common/installation-gemini.ts","../../src/pr-review/helper.ts","../../src/pr-review/commander.ts","../../src/pr-review/review.ts"],"names":["execSync","args","tempDiffPath","reviewFormPath","fs"],"mappings":";;;;;;AAGO,SAAS,uBAA0B,GAAA;AACtC,EAAI,IAAA;AACA,IAAA,QAAA,CAAS,kBAAoB,EAAA,EAAE,KAAO,EAAA,QAAA,EAAU,CAAA;AAAA,GAC5C,CAAA,MAAA;AACJ,IAAA,OAAA,CAAQ,IAAI,sLAA6E,CAAA;AACzF,IAAI,IAAA;AACA,MAAA,QAAA,CAAS,mCAAqC,EAAA,EAAE,KAAO,EAAA,SAAA,EAAW,CAAA;AAClE,MAAA,OAAA,CAAQ,IAAI,kFAA2B,CAAA;AACvC,MAAA,OAAA,CAAQ,IAAI,6GAAkC,CAAA;AAC9C,MAAA,OAAA,CAAQ,IAAI,4MAAsD,CAAA;AAClE,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,aACT,YAAc,EAAA;AACnB,MAAA,OAAA,CAAQ,MAAM,qLAAwD,CAAA;AACtE,MAAA,OAAA,CAAQ,MAAM,YAAY,CAAA;AAC1B,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAClB;AAER;ACfA,IAAM,YAAY,IAAK,CAAA,OAAA,CAAQ,aAAc,CAAA,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAKtD,IAAM,SAAY,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,2CAA2C,CAAA;AACrF,IAAM,eAAkB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,0CAA0C,CAAA;AAC1F,IAAM,yBAA4B,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,gDAAgD,CAAA;AAC1G,IAAM,cAAiB,GAAA,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,yCAAyC,CAAA;AACzD,IAAA,CAAK,OAAQ,CAAA,SAAA,EAAW,oDAAoD;AAC3G,IAAM,UAAa,GAAA,gBAAA;AACnB,IAAM,YAAe,GAAA,eAAA;AAErB,IAAM,UAAa,GAAA;AAAA,EACtB,cAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,eAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA;AAAA;AACJ,CAAA;AAGO,SAAS,eAAA,CAAgB,GAAa,EAAA,QAAA,EAAkB,SAAmB,EAAA;AAC9E,EAAA,IAAI,OAAU,GAAA,CAAA;AAEd,EAAA,OAAO,IAAM,EAAA;AACT,IAAM,MAAA,QAAA,GAAW,IAAK,CAAA,IAAA,CAAK,GAAK,EAAA,CAAA,EAAG,QAAQ,CAAI,CAAA,EAAA,OAAO,CAAG,EAAA,SAAS,CAAE,CAAA,CAAA;AACpE,IAAA,IAAI,CAAC,EAAA,CAAG,UAAW,CAAA,QAAQ,CAAG,EAAA;AAC1B,MAAO,OAAA,QAAA;AAAA;AAEX,IAAA,OAAA,EAAA;AAAA;AAER;AAEO,SAAS,WAAW,QAAkB,EAAA;AACzC,EAAI,IAAA,EAAA,CAAG,UAAW,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,EAAA,CAAG,WAAW,QAAQ,CAAA;AAAA;AAE9B;AAMO,SAAS,cAAiB,GAAA;AAC7B,EAAA,UAAA,CAAW,YAAY,CAAA;AAC3B;AAMO,SAAS,qBAAwB,GAAA;AACpC,EAAA,IAAI,CAAC,EAAA,CAAG,UAAW,CAAA,UAAU,CAAG,EAAA;AAC5B,IAAA,EAAA,CAAG,SAAU,CAAA,UAAA,EAAY,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA;AAEpD;AAMO,SAAS,YAAe,GAAA;AAC3B,EAAM,MAAA,GAAA,uBAAU,IAAK,EAAA;AACrB,EAAM,MAAA,IAAA,GAAO,IAAI,WAAY,EAAA;AAC7B,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,QAAA,KAAa,CAAC,CAAA,CAAE,QAAS,CAAA,CAAA,EAAG,GAAG,CAAA;AACrD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,OAAA,EAAS,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AAChD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,QAAA,EAAU,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACjD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,UAAA,EAAY,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AACnD,EAAM,MAAA,EAAA,GAAK,OAAO,GAAI,CAAA,UAAA,EAAY,CAAE,CAAA,QAAA,CAAS,GAAG,GAAG,CAAA;AAEnD,EAAO,OAAA,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAChD;AAEO,SAAS,gBAAmB,GAAA;AAG/B,EAAA,MAAM,iBAAoB,GAAA,CAAC,MAAQ,EAAA,OAAA,EAAS,QAAQ,OAAO,CAAA;AAG3D,EAAA,MAAM,kBAAkB,UAAW,CAAA,GAAA,CAAI,CAAQ,IAAA,KAAA,CAAA,UAAA,EAAa,IAAI,CAAE,CAAA,CAAA;AAKlE,EAAA,MAAM,KAAQ,GAAA,CAAC,OAAoB,KAAA,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA,CAAA;AAC9C,EAAA,MAAM,gBAAgB,iBAAkB,CAAA,GAAA,CAAI,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAC3D,EAAA,MAAM,gBAAgB,eAAgB,CAAA,GAAA,CAAI,KAAK,CAAA,CAAE,KAAK,GAAG,CAAA;AAEzD,EAAO,OAAA,EAAE,eAAe,aAAc,EAAA;AAG1C;AAEO,SAAS,WAAW,UAAoB,EAAA;AAE3C,EAAI,IAAA;AACA,IAAAA,SAAS,CAA4B,yBAAA,EAAA,IAAA,CAAK,OAAQ,CAAA,UAAU,CAAC,CAAG,CAAA,CAAA,CAAA;AAChE,IAAA,OAAA,CAAQ,IAAI,CAAuB,uGAAA,CAAA,CAAA;AAAA,WAC9B,CAAG,EAAA;AACR,IAAQ,OAAA,CAAA,KAAA,CAAM,oEAAkB,CAAC,CAAA;AAAA;AAEzC;AAGO,SAAS,WAAc,GAAA;AAC1B,EAAA,MAAMC,KAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AACjC,EAAM,MAAA,WAAA,GAAcA,KAAK,CAAA,OAAA,CAAQ,UAAU,CAAA;AAC3C,EAAA,MAAM,EAAE,aAAA,EAAe,aAAc,EAAA,GAAI,gBAAiB,EAAA;AAE1D,EAAA,IAAI,QAAW,GAAA,EAAA;AAEf,EAAA,IAAI,gBAAgB,EAAI,EAAA;AAEpB,IAAM,MAAA,UAAA,GAAaA,KAAK,CAAA,WAAA,GAAc,CAAC,CAAA;AACvC,IAAA,IAAI,CAAC,UAAY,EAAA;AACb,MAAA,OAAA,CAAQ,MAAM,iGAAsB,CAAA;AACpC,MAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAIlB,IAAM,MAAA,OAAA,GAAUA,KAAK,CAAA,WAAA,GAAc,CAAC,CAAA;AACpC,IAAA,IAAI,CAAI,GAAA,CAAA;AACR,IAAA,IAAI,OAAW,IAAA,CAAC,OAAQ,CAAA,UAAA,CAAW,IAAI,CAAG,EAAA;AACtC,MAAI,CAAA,GAAA,QAAA,CAAS,SAAS,EAAE,CAAA;AACxB,MAAI,IAAA,KAAA,CAAM,CAAC,CAAG,EAAA;AACV,QAAI,CAAA,GAAA,CAAA;AAAA;AACR;AAGJ,IAAQ,OAAA,CAAA,GAAA,CAAI,CAAU,2BAAA,EAAA,UAAU,CAAK,EAAA,EAAA,CAAA,GAAI,CAAI,GAAA,CAAA,qBAAA,EAAS,CAAI,GAAA,CAAC,CAAU,yBAAA,CAAA,GAAA,EAAE,CAAY,wCAAA,CAAA,CAAA;AACnF,IAAA,QAAA,GAAW,GAAG,UAAU,CAAA,CAAA,EAAI,CAAI,GAAA,CAAC,IAAI,UAAU,CAAA,CAAA;AAAA,GAC5C,MAAA;AAGH,IAAI,IAAA;AACA,MAAM,MAAA,KAAA,GAAQD,SAAS,CAA2B,wBAAA,EAAA,aAAa,IAAI,aAAa,CAAA,CAAE,EAAE,QAAS,EAAA;AAC7F,MAAI,IAAA,CAAC,KAAM,CAAA,IAAA,EAAQ,EAAA;AACf,QAAA,OAAA,CAAQ,IAAI,8JAAgD,CAAA;AAC5D,QAAW,QAAA,GAAA,aAAA;AAAA;AACf,KACI,CAAA,MAAA;AAAA;AAER;AAGJ,EAAO,OAAA,QAAA;AACX;;;AC1JA,IAAM,IAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AAkB1B,IAAM,aAAA,GAAgB,CAACE,aAAAA,EAAsBC,eAA2B,KAAA;AAE3E,EAAA,IAAI,WAAc,GAAA,EAAA;AAElB,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,UAAU,CAAG,EAAA;AAG3B,IAAc,WAAA,GAAA,aAAA;AAAA,GACP,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,SAAS,CAAG,EAAA;AAGjC,IAAc,WAAA,GAAA,eAAA;AAAA,GACX,MAAA;AAEH,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,OAAA,CAAQ,SAAS,CAAA;AAGzC,IAAA,IAAI,UAAe,KAAA,EAAA,IAAM,IAAK,CAAA,UAAA,GAAa,CAAC,CAAG,EAAA;AAC3C,MAAA,OAAA,CAAQ,KAAK,2JAAmC,CAAA;AAChD,MAAA,WAAA,GAAc,CAAW,QAAA,EAAA,IAAA,CAAK,UAAa,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,KAC1C,MAAA;AACH,MAAA,OAAA,CAAQ,KAAK,6KAAgD,CAAA;AAC7D,MAAc,WAAA,GAAA,eAAA;AAAA;AAClB;AAIJ,EAAA,MAAM,KAAQ,GAAA;AAAA,IACV,EAAE,IAAA,EAAM,SAAW,EAAA,OAAA,EAAS,cAAK,EAAA;AAAA,IACjC,EAAE,IAAA,EAAM,eAAiB,EAAA,OAAA,EAAS,iCAAS,EAAA;AAAA,IAC3C,EAAE,IAAA,EAAM,yBAA2B,EAAA,OAAA,EAAS,iCAAS;AAAA,GACzD;AAEA,EAAA,MAAM,aAAa,KACd,CAAA,MAAA,CAAO,UAAQC,EAAG,CAAA,UAAA,CAAW,KAAK,IAAI,CAAC,CACvC,CAAA,GAAA,CAAI,UAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAC3B,KAAK,IAAI,CAAA;AAKd,EAAM,MAAA,OAAA,GAAU,UAAU,WAAW,CAAA,oEAAA,EAAqB,UAAU,CAAaF,eAAAA,EAAAA,aAAY,qEAAmBC,eAAc,CAAA,sDAAA,CAAA;AAG9H,EAAI,IAAA,IAAA,CAAK,QAAS,CAAA,QAAQ,CAAG,EAAA;AACzB,IAAA,MAAM,WAAc,GAAA,OAAA,CAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA;AAE/C,IAAO,OAAA,CAAA;;AAAA;AAAA,EAA+D,WAAW,CAAA,CAAA,CAAA;AAAA;AAGrF,EAAO,OAAA,OAAA;AACX,CAAA;;;ACrDA,IAAMF,KAAO,GAAA,OAAA,CAAQ,IAAK,CAAA,KAAA,CAAM,CAAC,CAAA;AACjC,IAAM,MAAA,GAASA,KAAK,CAAA,QAAA,CAAS,QAAQ,CAAA;AAGrC,uBAAwB,EAAA;AAExB,IAAI;AACA,EAAA,OAAA,CAAQ,IAAI,kEAA6B,CAAA;AAEzC,EAAA,MAAM,SAAS,YAAa,EAAA;AAG5B,EAAsB,qBAAA,EAAA;AAGtB,EAAA,MAAM,WAAW,WAAY,EAAA;AAC7B,EAAA,IAAI,IAAO,GAAA,EAAA;AACX,EAAA,MAAM,EAAE,aAAA,EAAe,aAAc,EAAA,GAAI,gBAAiB,EAAA;AAE1D,EAAI,IAAA;AACA,IAAOD,IAAAA,GAAAA,QAAAA,CAAS,YAAY,QAAQ,CAAA,IAAA,EAAO,aAAa,CAAI,CAAA,EAAA,aAAa,CAAE,CAAA,CAAA,CAAE,QAAS,EAAA;AAAA,GAClF,CAAA,MAAA;AAAA;AAIR,EAAA,IAAI,CAAC,IAAA,CAAK,IAAK,EAAA,IAAK,CAAC,MAAQ,EAAA;AACzB,IAAA,OAAA,CAAQ,IAAI,yHAAmD,CAAA;AAE/D,IAAe,cAAA,EAAA;AACf,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA;AAIlB,EAAAI,EAAAA,CAAG,aAAc,CAAA,YAAA,EAAc,IAAI,CAAA;AAGnC,EAAA,MAAM,gBAAgB,eAAgB,CAAA,UAAA,EAAY,CAAG,EAAA,MAAM,SAAS,MAAM,CAAA;AAC1E,EAAAA,EAAAA,CAAG,YAAa,CAAA,YAAA,EAAc,aAAa,CAAA;AAG3C,EAAM,MAAA,OAAA,GAAU,aAAc,CAAA,YAAA,EAAc,cAAc,CAAA;AAG1D,EAAA,MAAM,MAASJ,GAAAA,QAAAA,CAAS,OAAO,CAAA,CAAE,QAAS,EAAA;AAG1C,EAAA,OAAA,CAAQ,IAAI,MAAM,CAAA;AAGlB,EAAA,MAAM,eAAkB,GAAA,eAAA,CAAgB,UAAY,EAAA,MAAA,EAAQ,KAAK,CAAA;AACjE,EAAAI,EAAAA,CAAG,aAAc,CAAA,eAAA,EAAiB,MAAM,CAAA;AAGxC,EAAAA,EAAAA,CAAG,eAAe,eAAiB,EAAA;;AAAA;;AAAA,EAAqB,OAAO,CAAE,CAAA,CAAA;AAEjE,EAAA,OAAA,CAAQ,GAAI,CAAA;AAAA,qEAAkB,CAAA,CAAA;AAC9B,EAAQ,OAAA,CAAA,GAAA,CAAI,CAAiB,wDAAA,EAAA,eAAe,CAAE,CAAA,CAAA;AAC9C,EAAQ,OAAA,CAAA,GAAA,CAAI,CAAe,gCAAA,EAAA,aAAa,CAAE,CAAA,CAAA;AAG1C,EAAA,UAAA,CAAW,eAAe,CAAA;AAG1B,EAAe,cAAA,EAAA;AAEnB,CAAA,CAAA,OAAS,KAAO,EAAA;AACZ,EAAA,OAAA,CAAQ,MAAM,2FAAqB,CAAA;AACnC,EAAA,OAAA,CAAQ,MAAM,KAAK,CAAA;AAGnB,EAAe,cAAA,EAAA;AACf,EAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAClB","file":"review.js","sourcesContent":["import { execSync } from \"child_process\";\n\n// gemini-cli 설치 확인 및 설치\nexport function checkGeminiCliInstalled() {\n try {\n execSync('gemini --version', { stdio: 'ignore' });\n } catch {\n console.log('ℹ️ gemini-cli가 설치되어 있지 않습니다. 설치를 진행합니다... npm install -g @google/gemini-cli');\n try {\n execSync('npm install -g @google/gemini-cli', { stdio: 'inherit' });\n console.log('✅ gemini-cli 설치가 완료되었습니다.');\n console.log('⚠️ Gemini API 사용을 위해 인증이 필요합니다.');\n console.log(' 터미널에서 \"gemini\" 를 입력하여 브라우저 로그인을 완료한 후, 다시 시도해주세요.');\n process.exit(1);\n } catch (installError) {\n console.error('❌ gemini-cli 설치 중 오류가 발생했습니다. 권한 문제일 수 있습니다 (sudo 필요).');\n console.error(installError);\n process.exit(1);\n }\n }\n}","import { execSync } from 'child_process';\nimport fs from 'fs'\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 설치된 위치에 맞게 review-rules.md 경로를 계산 (dist에서 src로 이동 등 고려)\n// dist/common/helper.js 가 실행되므로 __dirname은 .../dist/common 입니다.\n// 따라서 ../../src/pr-review/rules 로 이동해야 원본 소스의 규칙 파일을 찾을 수 있습니다.\nexport const rulesPath = path.resolve(__dirname, '../../src/pr-review/rules/review-rules.md');\nexport const namingRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/naming-rule.md');\nexport const codingConventionRulesPath = path.resolve(__dirname, '../../src/pr-review/rules/coding-convention.md');\nexport const reviewFormPath = path.resolve(__dirname, '../../src/pr-review/form/review-form.md');\nexport const reviewFormOneByOnePath = path.resolve(__dirname, '../../src/pr-review/form/review-form-one-by-one.md');\nexport const REPORT_DIR = '.review-report';\nexport const tempDiffPath = 'temp_diff.txt';\n\nexport const ignoreList = [\n 'package.json',\n '*.yml',\n '*.md',\n '*.lock',\n 'dist/',\n 'node_modules/',\n 'assets/',\n 'public/',\n '*.json',\n '*.yaml',\n '.review-report/' // 생성되는 리포트 폴더도 제외\n];\n\n\nexport function getNextFilePath(dir: string, baseName: string, extension: string) {\n let counter = 1;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const filePath = path.join(dir, `${baseName}-${counter}${extension}`);\n if (!fs.existsSync(filePath)) {\n return filePath;\n }\n counter++;\n }\n}\n\nexport function deleteFile(filePath: string) {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n}\n\n\n/**\n * 임시파일 삭제\n */\nexport function deleteTempDiff() {\n deleteFile(tempDiffPath);\n}\n\n\n/**\n * 리뷰 결과 폴더 생성\n */\nexport function createReportDirectory() {\n if (!fs.existsSync(REPORT_DIR)) {\n fs.mkdirSync(REPORT_DIR, { recursive: true });\n }\n}\n\n\n/**\n * 현재 시간 문자열 생성\n */\nexport function getNowString() {\n const now = new Date();\n const YYYY = now.getFullYear();\n const MM = String(now.getMonth() + 1).padStart(2, '0');\n const DD = String(now.getDate()).padStart(2, '0');\n const HH = String(now.getHours()).padStart(2, '0');\n const mm = String(now.getMinutes()).padStart(2, '0');\n const ss = String(now.getSeconds()).padStart(2, '0');\n\n return `${YYYY}-${MM}-${DD}_${HH}-${mm}-${ss}`;\n}\n\nexport function getGitDiffFilter() {\n\n // 1. 리뷰 대상 파일 확장자 정의\n const includeExtensions = ['*.ts', '*.tsx', '*.js', '*.jsx'];\n\n // ignoreList 를 import 하여 재사용하여 작성한다.\n const excludePatterns = ignoreList.map(item => `:(exclude)${item}`);\n\n // const excludePatterns = [':(exclude)*.lock', ':(exclude)dist/', ':(exclude)*.md'];\n\n // 2. 변경된 파일 목록 가져오기 (각 패턴을 따옴표로 감싸서 쉘 에러 방지)\n const quote = (pattern: string) => `\"${pattern}\"`;\n const includeParams = includeExtensions.map(quote).join(' ');\n const excludeParams = excludePatterns.map(quote).join(' ');\n\n return { includeParams, excludeParams };\n\n\n}\n\nexport function openReport(reportPath: string) {\n // 브라우저 열기\n try {\n execSync(`open -a \"Google Chrome\" \"${path.resolve(reportPath)}\"`);\n console.log(`🚀 브라우저에서 리포트를 열었습니다.`);\n } catch (e) {\n console.error('⚠️ 브라우저 열기 실패:', e);\n }\n}\n\n\nexport function getDiffArgs() {\n const args = process.argv.slice(2);\n const commitIndex = args.indexOf('--commit');\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n let diffArgs = '';\n\n if (commitIndex !== -1) {\n // 특정 커밋 (및 이전 n개) 리뷰\n const commitHash = args[commitIndex + 1];\n if (!commitHash) {\n console.error('❌ 커밋 해시가 제공되지 않았습니다.');\n process.exit(1);\n }\n\n // n값 확인 (optional)\n const nextArg = args[commitIndex + 2];\n let n = 0;\n if (nextArg && !nextArg.startsWith('--')) {\n n = parseInt(nextArg, 10);\n if (isNaN(n)) {\n n = 0;\n }\n }\n\n console.log(`ℹ️ 커밋 '${commitHash}' ${n > 0 ? ` 포함 총 ${n + 1}개의 커밋` : ''}을 리뷰합니다...`);\n diffArgs = `${commitHash}~${n + 1} ${commitHash}`;\n } else {\n // 기본 모드:\n // 1. Unstaged 변경사항 확인\n try {\n const check = execSync(`git diff --name-only -- ${includeParams} ${excludeParams}`).toString();\n if (!check.trim()) {\n console.log('ℹ️ Unstaged 변경사항이 없습니다. 마지막 커밋(HEAD)을 리뷰합니다...');\n diffArgs = 'HEAD~1 HEAD';\n }\n } catch {\n // git diff 실패시 무시\n }\n }\n\n return diffArgs;\n}","import fs from 'fs';\n\nimport { codingConventionRulesPath, namingRulesPath, rulesPath } from \"./helper\";\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\n\n/**\n * @description\n * Google AI Studio / Vertex AI에서 제공하는 추상적 모델 식별자 (Alias) 목록입니다. 이들은 특정 버전에 고정되지 않고, \n * 구글이 최신이라고 판단하는 모델로 자동 연결됩니다.\n * 1. Pro 계열 (고성능, 밸런스)\n * gemini-pro: 현재 기준 가장 안정적인 최신 Pro 모델 (보통 1.0 Pro 또는 1.5 Pro 중 설정된 기본값)\n * 2. Flash 계열 (빠른 속도, 가성비)\n * gemini-flash: 현재 기준 가장 최신 Flash 모델 (보통 1.5 Flash)\n * 3. Experimental (실험적 기능, 최신 기술)\n * gemini-exp: 가장 실험적인 최신 모델 (가변적)\n * gemini-flash-thinking-exp: 최신의 \"Thinking\" 모델 (사고력 강화)\n * 주의사항\n * gemini-ultra (가장 강력한 모델)는 현재 CLI에서 바로 접근 가능한 공개 Alias가 제한적일 수 있습니다. (주로 pro와 flash 위주로 제공)\n * 추상적인 이름(gemini-pro)을 쓰면 구글 마음대로 모델이 업데이트될 수 있어서, \n * 운영 환경에서는 gemini-1.5-pro-001 처럼 고정된 버전을 쓰는 것이 안전하지만, 개발/테스트용으로는 위 Alias들이 편리합니다.\n */\nexport const createCommand = (tempDiffPath: string, reviewFormPath: string) => {\n\n let modelOption = '';\n\n if (args.includes('--review')) {\n // 안정적인 고성능 모델\n // 긴 컨텍스트 처리에 강점\n modelOption = '--model pro';\n } else if (args.includes('--flash')) {\n // 속도 우선\n // 오타 수정, 간단한 리뷰에 적합\n modelOption = '--model flash';\n } else {\n // 사용자 직접 지정\n const modelIndex = args.indexOf('--model');\n // args[modelIndex + 1] 에는 사용자가 지정한 모델명이 들어갑니다.\n // 예: gemini-pro, gemini-1.5-pro-latest, gemini-1.5-flash, gemini-2.0-flash-exp 등\n if (modelIndex !== -1 && args[modelIndex + 1]) {\n console.warn('⚠️ 지정한 모델이 없는 경우, 에러가 발생하니 주의하세요.')\n modelOption = `--model ${args[modelIndex + 1]}`;\n } else {\n console.warn('⚠️ 모델이 지정되지 않았습니다. 기본 모델인 gemini-flash를 사용합니다.')\n modelOption = '--model flash';\n }\n }\n\n // 존재하지 않는 파일은 제외하여 명령어 생성\n const rules = [\n { path: rulesPath, display: '룰셋' },\n { path: namingRulesPath, display: '네이밍 규칙' },\n { path: codingConventionRulesPath, display: '코딩 컨벤션' }\n ];\n\n const validRules = rules\n .filter(rule => fs.existsSync(rule.path))\n .map(rule => `@${rule.path}`)\n .join(', ');\n\n // gemini 실행 및 결과 캡처\n // 주의: gemini CLI가 non-interactive 모드로 실행되려면 프롬프트 전달 방식이 중요합니다.\n // 보통 \"query\" 형태로 전달하면 동작하지만, 확실히 하기 위해 필요한 경우 -p 옵션 사용 고려\n const command = `gemini ${modelOption} -p \"다음 규칙들을 참고해서(${validRules}) 이 diff(@${tempDiffPath})를 리뷰해줘. 리뷰양식은 @${reviewFormPath} 에 맞춰서 작성해줘. \"`;\n\n // test mode\n if (args.includes('--test')) {\n const safeCommand = command.replace(/\"/g, '\\\\\"');\n\n return `echo \"[TEST MODE] Gemini 명령어가 실행되지 않았습니다.\\n\\n생성될 명령어 미리보기:\\n${safeCommand}\"`;\n }\n\n return command;\n}","#!/usr/bin/env node\nimport { execSync } from 'child_process';\nimport fs from 'fs';\n\nimport { checkGeminiCliInstalled } from '../common/installation-gemini';\n\nimport { createCommand } from './commander';\nimport {\n createReportDirectory,\n deleteTempDiff,\n getGitDiffFilter,\n getNextFilePath,\n getNowString,\n getDiffArgs,\n openReport,\n REPORT_DIR,\n reviewFormPath,\n tempDiffPath\n} from './helper';\n// gemini 실행 및 결과 캡처\nconst args = process.argv.slice(2);\nconst isTest = args.includes('--test');\n\n// gemini-cli 설치 확인 및 설치\ncheckGeminiCliInstalled();\n\ntry {\n console.log('🚀 AI Code Review를 시작합니다...');\n\n const nowStr = getNowString();\n\n // 리뷰 결과 폴더 생성\n createReportDirectory();\n\n // diff 가져오기\n const diffArgs = getDiffArgs();\n let diff = '';\n const { includeParams, excludeParams } = getGitDiffFilter();\n\n try {\n diff = execSync(`git diff ${diffArgs} -- ${includeParams} ${excludeParams}`).toString();\n } catch {\n // ignore\n }\n\n if (!diff.trim() && !isTest) {\n console.log('ℹ️ 리뷰할 변경 사항이 없습니다 (Unstaged Empty & HEAD Empty).');\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(0);\n }\n\n\n fs.writeFileSync(tempDiffPath, diff);\n\n // diff 파일 저장 (.review-report 안으로 복사)\n const savedDiffPath = getNextFilePath(REPORT_DIR, `${nowStr}-diff`, '.txt');\n fs.copyFileSync(tempDiffPath, savedDiffPath);\n\n // 명령어 생성\n const command = createCommand(tempDiffPath, reviewFormPath);\n\n // 결과가 길 수 있으므로 buffer를 넉넉하게 잡거나 순차적으로 받음. 여기서는 간단히 toString()\n const result = execSync(command).toString();\n\n // 콘솔 출력\n console.log(result);\n\n // 리뷰 결과 저장\n const savedReportPath = getNextFilePath(REPORT_DIR, nowStr, '.md');\n fs.writeFileSync(savedReportPath, result);\n\n //사용한 명령어도 저장\n fs.appendFileSync(savedReportPath, `\\n\\n## 사용된 명령어\\n\\n${command}`);\n\n console.log(`\\n✅ 리뷰가 완료되었습니다.`);\n console.log(`📄 리포트 저장 위치: ${savedReportPath}`);\n console.log(`diff 저장 위치: ${savedDiffPath}`);\n\n // 브라우저 열기\n openReport(savedReportPath);\n\n // 임시 파일 삭제\n deleteTempDiff();\n\n} catch (error) {\n console.error('❌ 리뷰 도중 오류가 발생했습니다.');\n console.error(error);\n\n // 임시 파일 삭제\n deleteTempDiff();\n process.exit(1);\n}\n\n"]}
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "src/pr-review/form/review-form.md",
13
13
  "src/pr-review/form/review-form-one-by-one.md"
14
14
  ],
15
- "version": "0.1.0",
15
+ "version": "0.2.0",
16
16
  "private": false,
17
17
  "type": "module",
18
18
  "author": "",