Skip to content
Merged
26 changes: 26 additions & 0 deletions packages/mcp/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,29 @@ export interface DataDrivenComponentResult {
components: DataDrivenComponentDefinition[];
testQuestions: string[];
}

export interface AuthorWorkPatternArgs {
repoPath: string;
author: string;
branch?: string;
since?: string;
until?: string;
githubToken: string;
locale?: "en" | "ko";
chart?: boolean;
}

export interface AuthorWorkPatternPayload {
repo: string;
author: string;
period: { from: string | null; to: string | null };
branch: string;
metrics: {
commits: number;
insertions: number;
deletions: number;
churn: number;
};
typeMix: Array<{ label: string; value: number }>;
locale?: "en" | "ko";
}
207 changes: 207 additions & 0 deletions packages/mcp/src/html/authot-work-pattern.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>{{TITLE}}</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<style>
:root {
--card-bg: #fafafa;
--card-border: #ddd;
--muted: #666;
--thead: #f5f5f5;
}
body {
font-family: Arial, sans-serif;
margin: 20px;
}
h1 {
margin: 0 0 4px 0;
}
.sub {
color: var(--muted);
font-size: 14px;
margin-bottom: 16px;
}

.cards {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 8px 0 16px;
}
.card {
border: 1px solid var(--card-border);
border-radius: 6px;
padding: 10px 14px;
background: var(--card-bg);
min-width: 140px;
}
.card h3 {
margin: 0 0 4px 0;
font-size: 13px;
color: #444;
}
.card .val {
font-size: 18px;
font-weight: 700;
}

.grid {
display: grid;
gap: 18px;
grid-template-columns: 1fr;
}
@media (min-width: 900px) {
.grid {
grid-template-columns: 1.2fr 0.8fr;
}
}
.panel h2 {
margin: 8px 0;
}

table {
border-collapse: collapse;
width: 100%;
}
th,
td {
border: 1px solid #ddd;
padding: 6px 8px;
}
th {
background: var(--thead);
}
td.val-right {
text-align: right;
}
</style>
</head>
<body>
<h1>{{TITLE}}</h1>
<div class="sub">{{NOTES}}</div>

<div class="cards">
<div class="card">
<h3>커밋 수</h3>
<div class="val">{{COMMITS}}</div>
</div>
<div class="card">
<h3>삽입 LOC</h3>
<div class="val">{{INSERTIONS}}</div>
</div>
<div class="card">
<h3>삭제 LOC</h3>
<div class="val">{{DELETIONS}}</div>
</div>
<div class="card">
<h3>Churn(±)</h3>
<div class="val">{{CHURN}}</div>
</div>
<div class="card">
<h3>브랜치</h3>
<div class="val">{{BRANCH}}</div>
</div>
</div>

<div class="grid">
<div class="panel">
<h2>Commits vs Churn</h2>
<canvas
id="barChart"
height="160"
></canvas>
</div>
<div class="panel">
<h2>Commit Type Mix</h2>
<canvas
id="donutChart"
height="160"
></canvas>
</div>
</div>

<div style="margin-top: 22px">
<h2>타입별 건수</h2>
<table>
<thead>
<tr>
<th style="width: 50%">Type</th>
<th class="val-right">Count</th>
</tr>
</thead>
<tbody>
{{TYPE_TABLE_ROWS}}
</tbody>
</table>
</div>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const tooltipStyle = {
backgroundColor: "#1e293b",
borderColor: "#475569",
borderWidth: 2,
cornerRadius: 8,
padding: 12,
titleColor: "#f8fafc",
bodyColor: "#f1f5f9",
titleFont: { weight: "700" },
bodyFont: { weight: "600" },
};

const ctxBar = document.getElementById("barChart").getContext("2d");
new Chart(ctxBar, {
type: "bar",
data: {
labels: JSON.parse("{{BAR_LABELS_JSON}}"),
datasets: [
{
label: "값",
data: JSON.parse("{{BAR_VALUES_JSON}}"),
backgroundColor: ["rgba(54, 162, 235, 0.85)", "rgba(255, 99, 132, 0.85)"],
borderColor: ["rgba(54, 162, 235, 1)", "rgba(255, 99, 132, 1)"],
borderWidth: 1,
},
],
},
options: {
responsive: true,
plugins: {
legend: { display: false },
title: { display: true, text: "Commits & Churn" },
tooltip: tooltipStyle,
},
scales: { y: { beginAtZero: true } },
},
});

const ctxDonut = document.getElementById("donutChart").getContext("2d");
new Chart(ctxDonut, {
type: "doughnut",
data: {
labels: JSON.parse("{{DONUT_LABELS_JSON}}"),
datasets: [
{
data: JSON.parse("{{DONUT_VALUES_JSON}}"),
backgroundColor: JSON.parse("{{DONUT_COLORS_JSON}}"),
borderWidth: 0,
},
],
},
options: {
responsive: true,
plugins: {
legend: { position: "right" },
tooltip: tooltipStyle,
},
cutout: "55%",
},
});
</script>
</body>
</html>
13 changes: 10 additions & 3 deletions packages/mcp/src/resources/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"branch_analysis": "Branch analysis error:",
"file_analysis": "Error analyzing file '{file}':",
"feature_impact_analysis": "Feature impact analysis error:",
"github_api_error": "GitHub API error:"
"github_api_error": "GitHub API error:",
"author_work_analysis": "Error occurred during Author Work Pattern analysis:",
"commit_detail_fetch": "Error fetching commit details:",
"commit_filter": "Error filtering commits:"
},
"notes": {
"pr_recommendation": "PR #{pr} based recommendation",
Expand All @@ -16,13 +19,17 @@
"feature_dispersion": "Dispersion: {dispersion}",
"feature_chaos": "Chaos: {chaos}",
"feature_isolation": "Isolation: {isolation}",
"feature_coupling": "Coupling: {coupling}"
"feature_coupling": "Coupling: {coupling}",
"author": "Author: {author}",
"repo": "Repo: {repo}",
"period": "Period: {from} ~ {to}"
},
"messages": {
"no_contributors_found": "No contributors found for the specified criteria",
"analysis_complete": "Analysis completed successfully",
"loading_pr_data": "Loading PR data...",
"processing_commits": "Processing commits..."
"processing_commits": "Processing commits...",
"no_data": "No data"
},
"echo": {
"response": "Echo: {text}",
Expand Down
13 changes: 10 additions & 3 deletions packages/mcp/src/resources/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"branch_analysis": "브랜치 분석 중 오류 발생:",
"file_analysis": "파일 '{file}' 분석 중 오류:",
"feature_impact_analysis": "기능 영향 분석 중 오류 발생:",
"github_api_error": "GitHub API 오류:"
"github_api_error": "GitHub API 오류:",
"author_work_analysis": "사용자 작업 분석 중 오류 발생:",
"commit_detail_fetch": "커밋 상세 정보를 가져오는 중 오류 발생:",
"commit_filter": "커밋 필터링 중 오류 발생:"
},
"notes": {
"pr_recommendation": "PR #{pr} 기반 기여자 추천",
Expand All @@ -16,13 +19,17 @@
"feature_dispersion": "분산도: {dispersion}",
"feature_chaos": "혼돈도: {chaos}",
"feature_isolation": "격리도: {isolation}",
"feature_coupling": "결합도: {coupling}"
"feature_coupling": "결합도: {coupling}",
"author": "저자: {author}",
"repo": "레포: {repo}",
"period": "기간: {from} ~ {to}"
},
"messages": {
"no_contributors_found": "지정된 조건에 맞는 기여자를 찾을 수 없습니다",
"analysis_complete": "분석이 성공적으로 완료되었습니다",
"loading_pr_data": "PR 데이터를 로드하는 중...",
"processing_commits": "커밋을 처리하는 중..."
"processing_commits": "커밋을 처리하는 중...",
"no_data": "데이터가 없습니다"
},
"echo": {
"response": "메아리: {text}",
Expand Down
17 changes: 9 additions & 8 deletions packages/mcp/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { registerReactTools } from "./tools/react/register.js";
import { registerContributorRecommenderTool } from "./tools/analysis/contributorRecommenderTool.js";
import { registerFeatureImpactTool } from "./tools/analysis/featureImpactTool.js";
import { registerStorylineUITool } from "./tools/storyLineUI/storyLineUITool.js";
import { registerAuthorWorkPatternTool } from "tools/analysis/authorWorkPatternTool.js";

const server = new McpServer({
name: "githru-mcp",
version: "0.0.1"
name: "githru-mcp",
version: "0.0.1",
});

registerTestTools(server);
Expand All @@ -18,14 +19,14 @@ registerReactTools(server);
registerContributorRecommenderTool(server);
registerFeatureImpactTool(server);
registerStorylineUITool(server);

registerAuthorWorkPatternTool(server);

async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
const transport = new StdioServerTransport();
await server.connect(transport);
}

main().catch((err) => {
console.error("Server error:", err);
process.exit(1);
});
console.error("Server error:", err);
process.exit(1);
});
Loading