count_lines
Analyze code line counts in projects by classifying lines as code, comments, or blanks. Specify directory path and file extensions to measure project size and structure.
Instructions
프로젝트의 코드 라인 수를 분석합니다 (코드/주석/빈줄 분류).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | 분석할 디렉토리 경로 | |
| extensions | No | 분석할 확장자 목록 (예: ["ts", "js"]). 기본값: 모든 지원 확장자 |
Implementation Reference
- src/index.ts:525-564 (registration)Registers the 'count_lines' tool with the MCP server, defines input schema using Zod, and provides a thin handler that formats the response from the core countLines function.server.tool( "count_lines", "프로젝트의 코드 라인 수를 분석합니다 (코드/주석/빈줄 분류).", { path: z.string().describe("분석할 디렉토리 경로"), extensions: z .array(z.string()) .optional() .describe("분석할 확장자 목록 (예: [\"ts\", \"js\"]). 기본값: 모든 지원 확장자"), }, async ({ path: targetPath, extensions }) => { const result = countLines(targetPath, { extensions }); if (!result.success) { return { content: [{ type: "text", text: `오류: ${result.error}` }], isError: true, }; } let text = `📊 코드 라인 통계\n\n`; text += `📁 총 파일: ${result.totalFiles}개\n`; text += `📝 총 라인: ${result.totalLines}줄\n`; text += ` - 코드: ${result.codeLines}줄\n`; text += ` - 주석: ${result.commentLines}줄\n`; text += ` - 빈줄: ${result.blankLines}줄\n`; if (result.byExtension && Object.keys(result.byExtension).length > 0) { text += `\n📈 확장자별 통계:\n`; Object.entries(result.byExtension) .sort((a, b) => b[1].lines - a[1].lines) .forEach(([ext, stats]) => { text += ` .${ext}: ${stats.files}개 파일, ${stats.lines}줄 (코드: ${stats.codeLines}, 주석: ${stats.commentLines})\n`; }); } return { content: [{ type: "text", text }], }; }
- src/index.ts:528-534 (schema)Zod input schema for the count_lines tool: path (directory) and optional extensions array.{ path: z.string().describe("분석할 디렉토리 경로"), extensions: z .array(z.string()) .optional() .describe("분석할 확장자 목록 (예: [\"ts\", \"js\"]). 기본값: 모든 지원 확장자"), },
- src/project-analyzer.ts:279-312 (handler)Main handler function implementing the core logic of counting lines in project files, initializing stats and invoking recursive traversal.export function countLines( targetPath: string, options: LineCountOptions = {} ): LineCountResult { const { extensions } = options; try { if (!fs.existsSync(targetPath)) { return { success: false, error: `경로를 찾을 수 없습니다: ${targetPath}`, }; } const stats: LineCountResult = { success: true, totalLines: 0, totalFiles: 0, codeLines: 0, commentLines: 0, blankLines: 0, byExtension: {}, }; countLinesRecursive(targetPath, extensions, stats); return stats; } catch (err) { return { success: false, error: err instanceof Error ? err.message : String(err), }; } }
- src/project-analyzer.ts:317-382 (helper)Recursive directory traversal helper that processes files, applies filters, reads content, analyzes lines, and updates statistics.function countLinesRecursive( dirPath: string, extensions: string[] | undefined, stats: LineCountResult ): void { const entries = fs.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { // 숨김 디렉토리와 node_modules 제외 if (!entry.name.startsWith(".") && entry.name !== "node_modules") { countLinesRecursive(fullPath, extensions, stats); } } else if (entry.isFile()) { const ext = path.extname(entry.name).slice(1); // .ts -> ts // 확장자 필터 if (extensions && extensions.length > 0 && !extensions.includes(ext)) { continue; } // 숨김 파일 제외 if (entry.name.startsWith(".")) { continue; } // 지원하는 텍스트 파일만 분석 const textExtensions = ["ts", "js", "tsx", "jsx", "json", "md", "txt", "css", "scss", "html", "py", "go", "rs", "java", "c", "cpp", "h"]; if (!textExtensions.includes(ext)) { continue; } try { const content = fs.readFileSync(fullPath, "utf-8"); const fileStats = analyzeFileLines(content, ext); stats.totalFiles! += 1; stats.totalLines! += fileStats.total; stats.codeLines! += fileStats.code; stats.commentLines! += fileStats.comment; stats.blankLines! += fileStats.blank; // 확장자별 통계 if (!stats.byExtension![ext]) { stats.byExtension![ext] = { files: 0, lines: 0, codeLines: 0, commentLines: 0, blankLines: 0, }; } stats.byExtension![ext].files += 1; stats.byExtension![ext].lines += fileStats.total; stats.byExtension![ext].codeLines += fileStats.code; stats.byExtension![ext].commentLines += fileStats.comment; stats.byExtension![ext].blankLines += fileStats.blank; } catch { // 바이너리 파일 등 읽기 실패 시 무시 } } } }
- src/project-analyzer.ts:387-437 (helper)Per-file line analyzer that classifies lines into code, comments (single/multi-line), and blanks using language-specific patterns.function analyzeFileLines( content: string, ext: string ): { total: number; code: number; comment: number; blank: number } { const lines = content.split("\n"); let code = 0; let comment = 0; let blank = 0; let inMultiLineComment = false; // 주석 패턴 (언어별) const singleLineComment = getSingleLineCommentPattern(ext); const multiLineStart = getMultiLineCommentStart(ext); const multiLineEnd = getMultiLineCommentEnd(ext); for (const line of lines) { const trimmed = line.trim(); if (trimmed === "") { blank++; continue; } // 멀티라인 주석 처리 if (inMultiLineComment) { comment++; if (multiLineEnd && trimmed.includes(multiLineEnd)) { inMultiLineComment = false; } continue; } if (multiLineStart && trimmed.startsWith(multiLineStart)) { comment++; if (!multiLineEnd || !trimmed.includes(multiLineEnd)) { inMultiLineComment = true; } continue; } // 단일 라인 주석 if (singleLineComment && trimmed.startsWith(singleLineComment)) { comment++; continue; } code++; } return { total: lines.length, code, comment, blank }; }