mcp-udacity-commit
Advanced tools
+17
-17
@@ -6,3 +6,3 @@ #!/usr/bin/env node | ||
| import { STYLE_GUIDE, BRANCH_GUIDE, validate, formatMessage, validateBranch } from "./lint.js"; | ||
| const VERSION = "1.2.0"; | ||
| const VERSION = "1.2.1"; | ||
| export function createServer() { | ||
@@ -31,12 +31,12 @@ const server = new McpServer({ name: "udacity-commit", version: VERSION }); | ||
| }, async ({ message }) => { | ||
| const r = validate(message); | ||
| const report = validate(message); | ||
| const text = [ | ||
| r.valid ? "✅ Compliant with the Udacity style guide." : "❌ Not compliant.", | ||
| ...r.problems.map((p) => ` • ${p}`), | ||
| ...r.warnings.map((w) => ` ⚠ ${w}`), | ||
| report.valid ? "✅ Compliant with the Udacity style guide." : "❌ Not compliant.", | ||
| ...report.problems.map((problem) => ` • ${problem}`), | ||
| ...report.warnings.map((warning) => ` ⚠ ${warning}`), | ||
| ].join("\n"); | ||
| const structuredContent = { | ||
| valid: r.valid, | ||
| problems: r.problems, | ||
| warnings: r.warnings, | ||
| valid: report.valid, | ||
| problems: report.problems, | ||
| warnings: report.warnings, | ||
| }; | ||
@@ -91,12 +91,12 @@ return { content: [{ type: "text", text }], structuredContent }; | ||
| }, async ({ name }) => { | ||
| const r = validateBranch(name); | ||
| const report = validateBranch(name); | ||
| const text = [ | ||
| r.valid ? "✅ Compliant branch name." : "❌ Not compliant.", | ||
| ...r.problems.map((p) => ` • ${p}`), | ||
| ...r.warnings.map((w) => ` ⚠ ${w}`), | ||
| report.valid ? "✅ Compliant branch name." : "❌ Not compliant.", | ||
| ...report.problems.map((problem) => ` • ${problem}`), | ||
| ...report.warnings.map((warning) => ` ⚠ ${warning}`), | ||
| ].join("\n"); | ||
| const structuredContent = { | ||
| valid: r.valid, | ||
| problems: r.problems, | ||
| warnings: r.warnings, | ||
| valid: report.valid, | ||
| problems: report.problems, | ||
| warnings: report.warnings, | ||
| }; | ||
@@ -111,5 +111,5 @@ return { content: [{ type: "text", text }], structuredContent }; | ||
| } | ||
| main().catch((err) => { | ||
| console.error(err); | ||
| main().catch((error) => { | ||
| console.error(error); | ||
| process.exit(1); | ||
| }); |
+24
-24
@@ -81,3 +81,3 @@ /** | ||
| ${Object.entries(TYPES) | ||
| .map(([t, d]) => `- **${t}**: ${d}`) | ||
| .map(([typeName, typeDescription]) => `- **${typeName}**: ${typeDescription}`) | ||
| .join("\n")} | ||
@@ -130,10 +130,10 @@ | ||
| /** Length in Unicode code points (not UTF-16 code units). */ | ||
| export function width(s) { | ||
| return [...s].length; | ||
| export function width(text) { | ||
| return [...text].length; | ||
| } | ||
| /** Uppercase the first code point of a string (astral-safe). */ | ||
| function capitalizeFirst(s) { | ||
| const chars = [...s]; | ||
| function capitalizeFirst(text) { | ||
| const chars = [...text]; | ||
| if (chars.length === 0) | ||
| return s; | ||
| return text; | ||
| return chars[0].toUpperCase() + chars.slice(1).join(""); | ||
@@ -192,19 +192,19 @@ } | ||
| const subject = rawSubject.replace(/\s+$/, ""); | ||
| const m = subject.match(/^(\w+): (.*)$/); | ||
| if (!m) { | ||
| const subjectMatch = subject.match(/^(\w+): (.*)$/); | ||
| if (!subjectMatch) { | ||
| problems.push(`Subject must follow "type: Subject". Got: "${subject}".`); | ||
| } | ||
| else { | ||
| const [, type, rest] = m; | ||
| const [, type, subjectText] = subjectMatch; | ||
| if (!TYPES[type]) { | ||
| problems.push(`Unknown type "${type}". Use one of: ${Object.keys(TYPES).join(", ")}.`); | ||
| } | ||
| if (!rest) { | ||
| if (!subjectText) { | ||
| problems.push("Subject text is empty after the type."); | ||
| } | ||
| else { | ||
| if (/^\p{Ll}/u.test(rest)) { | ||
| problems.push(`Subject should begin with a capital letter (got "${[...rest][0]}").`); | ||
| if (/^\p{Ll}/u.test(subjectText)) { | ||
| problems.push(`Subject should begin with a capital letter (got "${[...subjectText][0]}").`); | ||
| } | ||
| const firstWord = rest.split(/\s+/)[0]; | ||
| const firstWord = subjectText.split(/\s+/)[0]; | ||
| if (NON_IMPERATIVE.has(firstWord.toLowerCase())) { | ||
@@ -225,11 +225,11 @@ warnings.push(`"${firstWord}" looks past-tense/gerund — use the imperative mood ("Add", not "Added").`); | ||
| } | ||
| for (let i = 2; i < lines.length; i++) { | ||
| const line = lines[i]; | ||
| for (let lineIndex = 2; lineIndex < lines.length; lineIndex++) { | ||
| const line = lines[lineIndex]; | ||
| if (width(line) > BODY_WRAP) { | ||
| problems.push(`Line ${i + 1} is ${width(line)} chars; wrap body/footer at ${BODY_WRAP}.`); | ||
| problems.push(`Line ${lineIndex + 1} is ${width(line)} chars; wrap body/footer at ${BODY_WRAP}.`); | ||
| } | ||
| const fm = line.match(/^([A-Za-z][A-Za-z ]*?):\s*(.*)$/); | ||
| if (fm && FOOTER_KEYS.some((k) => k.toLowerCase() === fm[1].toLowerCase())) { | ||
| if (!/#\d+/.test(fm[2])) { | ||
| warnings.push(`Footer "${fm[1]}" should reference an issue, e.g. "${fm[1]}: #123".`); | ||
| const footerMatch = line.match(/^([A-Za-z][A-Za-z ]*?):\s*(.*)$/); | ||
| if (footerMatch && FOOTER_KEYS.some((footerKey) => footerKey.toLowerCase() === footerMatch[1].toLowerCase())) { | ||
| if (!/#\d+/.test(footerMatch[2])) { | ||
| warnings.push(`Footer "${footerMatch[1]}" should reference an issue, e.g. "${footerMatch[1]}: #123".`); | ||
| } | ||
@@ -284,9 +284,9 @@ } | ||
| } | ||
| const slash = branch.indexOf("/"); | ||
| if (slash === -1) { | ||
| const slashIndex = branch.indexOf("/"); | ||
| if (slashIndex === -1) { | ||
| problems.push(`Branch must follow "type/description". Got: "${branch}".`); | ||
| return { valid: false, problems, warnings }; | ||
| } | ||
| const type = branch.slice(0, slash); | ||
| const description = branch.slice(slash + 1); | ||
| const type = branch.slice(0, slashIndex); | ||
| const description = branch.slice(slashIndex + 1); | ||
| if (!BRANCH_TYPES.includes(type)) { | ||
@@ -293,0 +293,0 @@ problems.push(`Unknown type "${type}". Use one of: ${BRANCH_TYPES.join(", ")}.`); |
+1
-1
| { | ||
| "name": "mcp-udacity-commit", | ||
| "version": "1.2.0", | ||
| "version": "1.2.1", | ||
| "description": "MCP server that validates and formats git commit messages per the Udacity Git Commit Message Style Guide.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
24569
1.56%