Skip to content

Commit 95b2c30

Browse files
authored
Merge pull request #966 from seocylucky/vibe
FEAT(vibe): 작성자별 커밋 및 코드 변경 패턴 분석 툴 작업
2 parents 0121d58 + 8d16f80 commit 95b2c30

7 files changed

Lines changed: 545 additions & 14 deletions

File tree

packages/mcp/src/common/types.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,29 @@ export interface DataDrivenComponentResult {
150150
components: DataDrivenComponentDefinition[];
151151
testQuestions: string[];
152152
}
153+
154+
export interface AuthorWorkPatternArgs {
155+
repoPath: string;
156+
author: string;
157+
branch?: string;
158+
since?: string;
159+
until?: string;
160+
githubToken: string;
161+
locale?: "en" | "ko";
162+
chart?: boolean;
163+
}
164+
165+
export interface AuthorWorkPatternPayload {
166+
repo: string;
167+
author: string;
168+
period: { from: string | null; to: string | null };
169+
branch: string;
170+
metrics: {
171+
commits: number;
172+
insertions: number;
173+
deletions: number;
174+
churn: number;
175+
};
176+
typeMix: Array<{ label: string; value: number }>;
177+
locale?: "en" | "ko";
178+
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
<!DOCTYPE html>
2+
<html lang="ko">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>{{TITLE}}</title>
6+
<meta
7+
name="viewport"
8+
content="width=device-width, initial-scale=1.0"
9+
/>
10+
<style>
11+
:root {
12+
--card-bg: #fafafa;
13+
--card-border: #ddd;
14+
--muted: #666;
15+
--thead: #f5f5f5;
16+
}
17+
body {
18+
font-family: Arial, sans-serif;
19+
margin: 20px;
20+
}
21+
h1 {
22+
margin: 0 0 4px 0;
23+
}
24+
.sub {
25+
color: var(--muted);
26+
font-size: 14px;
27+
margin-bottom: 16px;
28+
}
29+
30+
.cards {
31+
display: flex;
32+
flex-wrap: wrap;
33+
gap: 8px;
34+
margin: 8px 0 16px;
35+
}
36+
.card {
37+
border: 1px solid var(--card-border);
38+
border-radius: 6px;
39+
padding: 10px 14px;
40+
background: var(--card-bg);
41+
min-width: 140px;
42+
}
43+
.card h3 {
44+
margin: 0 0 4px 0;
45+
font-size: 13px;
46+
color: #444;
47+
}
48+
.card .val {
49+
font-size: 18px;
50+
font-weight: 700;
51+
}
52+
53+
.grid {
54+
display: grid;
55+
gap: 18px;
56+
grid-template-columns: 1fr;
57+
}
58+
@media (min-width: 900px) {
59+
.grid {
60+
grid-template-columns: 1.2fr 0.8fr;
61+
}
62+
}
63+
.panel h2 {
64+
margin: 8px 0;
65+
}
66+
67+
table {
68+
border-collapse: collapse;
69+
width: 100%;
70+
}
71+
th,
72+
td {
73+
border: 1px solid #ddd;
74+
padding: 6px 8px;
75+
}
76+
th {
77+
background: var(--thead);
78+
}
79+
td.val-right {
80+
text-align: right;
81+
}
82+
</style>
83+
</head>
84+
<body>
85+
<h1>{{TITLE}}</h1>
86+
<div class="sub">{{NOTES}}</div>
87+
88+
<div class="cards">
89+
<div class="card">
90+
<h3>커밋 수</h3>
91+
<div class="val">{{COMMITS}}</div>
92+
</div>
93+
<div class="card">
94+
<h3>삽입 LOC</h3>
95+
<div class="val">{{INSERTIONS}}</div>
96+
</div>
97+
<div class="card">
98+
<h3>삭제 LOC</h3>
99+
<div class="val">{{DELETIONS}}</div>
100+
</div>
101+
<div class="card">
102+
<h3>Churn(±)</h3>
103+
<div class="val">{{CHURN}}</div>
104+
</div>
105+
<div class="card">
106+
<h3>브랜치</h3>
107+
<div class="val">{{BRANCH}}</div>
108+
</div>
109+
</div>
110+
111+
<div class="grid">
112+
<div class="panel">
113+
<h2>Commits vs Churn</h2>
114+
<canvas
115+
id="barChart"
116+
height="160"
117+
></canvas>
118+
</div>
119+
<div class="panel">
120+
<h2>Commit Type Mix</h2>
121+
<canvas
122+
id="donutChart"
123+
height="160"
124+
></canvas>
125+
</div>
126+
</div>
127+
128+
<div style="margin-top: 22px">
129+
<h2>타입별 건수</h2>
130+
<table>
131+
<thead>
132+
<tr>
133+
<th style="width: 50%">Type</th>
134+
<th class="val-right">Count</th>
135+
</tr>
136+
</thead>
137+
<tbody>
138+
{{TYPE_TABLE_ROWS}}
139+
</tbody>
140+
</table>
141+
</div>
142+
143+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
144+
<script>
145+
const tooltipStyle = {
146+
backgroundColor: "#1e293b",
147+
borderColor: "#475569",
148+
borderWidth: 2,
149+
cornerRadius: 8,
150+
padding: 12,
151+
titleColor: "#f8fafc",
152+
bodyColor: "#f1f5f9",
153+
titleFont: { weight: "700" },
154+
bodyFont: { weight: "600" },
155+
};
156+
157+
const ctxBar = document.getElementById("barChart").getContext("2d");
158+
new Chart(ctxBar, {
159+
type: "bar",
160+
data: {
161+
labels: JSON.parse("{{BAR_LABELS_JSON}}"),
162+
datasets: [
163+
{
164+
label: "값",
165+
data: JSON.parse("{{BAR_VALUES_JSON}}"),
166+
backgroundColor: ["rgba(54, 162, 235, 0.85)", "rgba(255, 99, 132, 0.85)"],
167+
borderColor: ["rgba(54, 162, 235, 1)", "rgba(255, 99, 132, 1)"],
168+
borderWidth: 1,
169+
},
170+
],
171+
},
172+
options: {
173+
responsive: true,
174+
plugins: {
175+
legend: { display: false },
176+
title: { display: true, text: "Commits & Churn" },
177+
tooltip: tooltipStyle,
178+
},
179+
scales: { y: { beginAtZero: true } },
180+
},
181+
});
182+
183+
const ctxDonut = document.getElementById("donutChart").getContext("2d");
184+
new Chart(ctxDonut, {
185+
type: "doughnut",
186+
data: {
187+
labels: JSON.parse("{{DONUT_LABELS_JSON}}"),
188+
datasets: [
189+
{
190+
data: JSON.parse("{{DONUT_VALUES_JSON}}"),
191+
backgroundColor: JSON.parse("{{DONUT_COLORS_JSON}}"),
192+
borderWidth: 0,
193+
},
194+
],
195+
},
196+
options: {
197+
responsive: true,
198+
plugins: {
199+
legend: { position: "right" },
200+
tooltip: tooltipStyle,
201+
},
202+
cutout: "55%",
203+
},
204+
});
205+
</script>
206+
</body>
207+
</html>

packages/mcp/src/resources/locales/en.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
"branch_analysis": "Branch analysis error:",
66
"file_analysis": "Error analyzing file '{file}':",
77
"feature_impact_analysis": "Feature impact analysis error:",
8-
"github_api_error": "GitHub API error:"
8+
"github_api_error": "GitHub API error:",
9+
"author_work_analysis": "Error occurred during Author Work Pattern analysis:",
10+
"commit_detail_fetch": "Error fetching commit details:",
11+
"commit_filter": "Error filtering commits:"
912
},
1013
"notes": {
1114
"pr_recommendation": "PR #{pr} based recommendation",
@@ -16,13 +19,17 @@
1619
"feature_dispersion": "Dispersion: {dispersion}",
1720
"feature_chaos": "Chaos: {chaos}",
1821
"feature_isolation": "Isolation: {isolation}",
19-
"feature_coupling": "Coupling: {coupling}"
22+
"feature_coupling": "Coupling: {coupling}",
23+
"author": "Author: {author}",
24+
"repo": "Repo: {repo}",
25+
"period": "Period: {from} ~ {to}"
2026
},
2127
"messages": {
2228
"no_contributors_found": "No contributors found for the specified criteria",
2329
"analysis_complete": "Analysis completed successfully",
2430
"loading_pr_data": "Loading PR data...",
25-
"processing_commits": "Processing commits..."
31+
"processing_commits": "Processing commits...",
32+
"no_data": "No data"
2633
},
2734
"echo": {
2835
"response": "Echo: {text}",

packages/mcp/src/resources/locales/ko.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
"branch_analysis": "브랜치 분석 중 오류 발생:",
66
"file_analysis": "파일 '{file}' 분석 중 오류:",
77
"feature_impact_analysis": "기능 영향 분석 중 오류 발생:",
8-
"github_api_error": "GitHub API 오류:"
8+
"github_api_error": "GitHub API 오류:",
9+
"author_work_analysis": "사용자 작업 분석 중 오류 발생:",
10+
"commit_detail_fetch": "커밋 상세 정보를 가져오는 중 오류 발생:",
11+
"commit_filter": "커밋 필터링 중 오류 발생:"
912
},
1013
"notes": {
1114
"pr_recommendation": "PR #{pr} 기반 기여자 추천",
@@ -16,13 +19,17 @@
1619
"feature_dispersion": "분산도: {dispersion}",
1720
"feature_chaos": "혼돈도: {chaos}",
1821
"feature_isolation": "격리도: {isolation}",
19-
"feature_coupling": "결합도: {coupling}"
22+
"feature_coupling": "결합도: {coupling}",
23+
"author": "저자: {author}",
24+
"repo": "레포: {repo}",
25+
"period": "기간: {from} ~ {to}"
2026
},
2127
"messages": {
2228
"no_contributors_found": "지정된 조건에 맞는 기여자를 찾을 수 없습니다",
2329
"analysis_complete": "분석이 성공적으로 완료되었습니다",
2430
"loading_pr_data": "PR 데이터를 로드하는 중...",
25-
"processing_commits": "커밋을 처리하는 중..."
31+
"processing_commits": "커밋을 처리하는 중...",
32+
"no_data": "데이터가 없습니다"
2633
},
2734
"echo": {
2835
"response": "메아리: {text}",

packages/mcp/src/server.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ import { registerReactTools } from "./tools/react/register.js";
66
import { registerContributorRecommenderTool } from "./tools/analysis/contributorRecommenderTool.js";
77
import { registerFeatureImpactTool } from "./tools/analysis/featureImpactTool.js";
88
import { registerStorylineUITool } from "./tools/storyLineUI/storyLineUITool.js";
9+
import { registerAuthorWorkPatternTool } from "tools/analysis/authorWorkPatternTool.js";
910

1011
const server = new McpServer({
11-
name: "githru-mcp",
12-
version: "0.0.1"
12+
name: "githru-mcp",
13+
version: "0.0.1",
1314
});
1415

1516
registerTestTools(server);
@@ -18,14 +19,14 @@ registerReactTools(server);
1819
registerContributorRecommenderTool(server);
1920
registerFeatureImpactTool(server);
2021
registerStorylineUITool(server);
21-
22+
registerAuthorWorkPatternTool(server);
2223

2324
async function main() {
24-
const transport = new StdioServerTransport();
25-
await server.connect(transport);
25+
const transport = new StdioServerTransport();
26+
await server.connect(transport);
2627
}
2728

2829
main().catch((err) => {
29-
console.error("Server error:", err);
30-
process.exit(1);
31-
});
30+
console.error("Server error:", err);
31+
process.exit(1);
32+
});

0 commit comments

Comments
 (0)