get_conventional_commits
Analyze conventional commit usage and release patterns in a git repository by specifying a date range.
Instructions
Analyze conventional commit usage and release patterns
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| repo_path | Yes | Path to git repository | |
| since | Yes | Start date (YYYY-MM-DD) | |
| until | No | End date (YYYY-MM-DD), optional |
Implementation Reference
- src/handlers.ts:445-508 (handler)The main handler function that executes the 'get_conventional_commits' tool logic. It validates inputs, runs git log to parse conventional commits (feat, fix, docs, etc.), extracts types/scopes/breaking changes, also retrieves release tags, and returns structured results.
export function handleGetConventionalCommits(args: any) { const { repo_path, since, until } = args; validateRepoPath(repo_path); validateDate(since, "since"); if (until) validateDate(until, "until"); let cmd = `git log --since="${since}"`; if (until) cmd += ` --until="${until} 23:59:59"`; cmd += ` --pretty=format:"%H|%s|%ad" --date=short`; const output = runGitCommand(repo_path, cmd); const lines = output.trim().split("\n").filter(l => l); const types: Record<string, number> = {}; const scopes: Record<string, number> = {}; let breaking = 0; let conventional = 0; const conventionalRegex = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(([^)]+)\))?(!)?:/; for (const line of lines) { const [, message] = line.split("|"); const match = message.match(conventionalRegex); if (match) { conventional++; const [, type, , scope, isBreaking] = match; types[type] = (types[type] || 0) + 1; if (scope) scopes[scope] = (scopes[scope] || 0) + 1; if (isBreaking || message.includes("BREAKING CHANGE")) breaking++; } } const tagsCmd = `git tag --sort=-creatordate --format="%(refname:short)|%(creatordate:short)"`; const tagsOutput = runGitCommand(repo_path, tagsCmd); const tags = tagsOutput.trim().split("\n").filter(t => t); const releases = tags .map(t => { const [tag, date] = t.split("|"); return { tag, date }; }) .filter(r => { const releaseDate = new Date(r.date); const sinceDate = new Date(since); const untilDate = until ? new Date(until) : new Date(); return releaseDate >= sinceDate && releaseDate <= untilDate; }); const sortedScopes = Object.entries(scopes).sort(([,a], [,b]) => b - a); return { totalCommits: lines.length, conventionalCommits: conventional, conventionalPercentage: `${((conventional / lines.length) * 100).toFixed(1)}%`, commitTypes: Object.entries(types).sort(([,a], [,b]) => b - a).map(([type, count]) => ({ type, count })), topScopes: sortedScopes.slice(0, 10).map(([scope, count]) => ({ scope, count })), totalScopeCount: sortedScopes.length, breakingChanges: breaking, recentReleases: releases, totalReleasesCount: releases.length, releaseFrequency: releases.length > 1 ? `${releases.length} releases since ${since}` : "No releases found" }; } - src/git-metrics.ts:227-239 (schema)Tool schema definition for 'get_conventional_commits' in the ListToolsRequestHandler. Defines input parameters: repo_path (required), since (required YYYY-MM-DD), until (optional YYYY-MM-DD).
{ name: "get_conventional_commits", description: "Analyze conventional commit usage and release patterns", inputSchema: { type: "object", properties: { repo_path: { type: "string", description: "Path to git repository" }, since: { type: "string", description: "Start date (YYYY-MM-DD)" }, until: { type: "string", description: "End date (YYYY-MM-DD), optional" }, }, required: ["repo_path", "since"], }, }, - src/git-metrics.ts:275-276 (registration)Registration of the tool handler in the CallToolRequestSchema handler chain. Maps the tool name 'get_conventional_commits' to the handler function handlers.handleGetConventionalCommits(args).
} else if (request.params.name === "get_conventional_commits") { result = handlers.handleGetConventionalCommits(args); - src/git-metrics.ts:62-66 (helper)The validateDate helper function used by the handler to validate date parameters are in YYYY-MM-DD format.
export function validateDate(date: string, fieldName: string): void { if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) { throw new Error(`Invalid ${fieldName} format. Use YYYY-MM-DD (e.g., 2025-11-21)`); } } - src/git-metrics.ts:68-83 (helper)The validateRepoPath helper function used by the handler to validate that the repo_path exists and is a valid git repository.
export function validateRepoPath(repoPath: string): void { if (!repoPath || typeof repoPath !== 'string') { throw new Error('repo_path is required and must be a string'); } if (/[;&|`$()]/.test(repoPath)) { throw new Error('Invalid characters in repo_path'); } const fullPath = resolve(repoPath); if (!existsSync(fullPath)) { throw new Error(`Repository path does not exist: ${fullPath}`); } const gitPath = resolve(fullPath, '.git'); if (!existsSync(gitPath)) { throw new Error(`Not a git repository: ${fullPath}`); } }