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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오.. 이렇게 해도 되는건가요??
인자를 안넣었는데 어떻게 동작하는거죠..?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 아니에요 제 실수입니다ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
발견해주셔서 감사해요ㅎㅎㅎ


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