🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

mcp-udacity-commit

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mcp-udacity-commit - npm Package Compare versions

Comparing version
1.0.1
to
1.0.2
+211
build/lint.js
/**
* Pure, side-effect-free linting logic for the Udacity Git Commit Message
* Style Guide. Kept separate from the MCP server (index.ts) so it can be
* unit-tested in isolation.
*
* Length semantics: all limits are measured in Unicode code points
* (`width()`), not UTF-16 code units, so emoji / CJK / combining marks are
* counted the way a human reads them. The 50-char subject limit applies to
* the WHOLE `type: Subject` line, including the `type: ` prefix.
*/
export const SUBJECT_MAX = 50;
export const BODY_WRAP = 72;
export const TYPES = {
feat: "A new feature",
fix: "A bug fix",
docs: "Changes to documentation",
style: "Formatting, missing semicolons, etc; no code change",
refactor: "Refactoring production code",
test: "Adding tests, refactoring tests; no production code change",
chore: "Updating build tasks, package configs, etc; no production code change",
};
/** Footer keywords recognized for issue-reference validation. */
export const FOOTER_KEYS = ["Resolves", "Closes", "Fixes", "Fix", "See also", "Refs", "Ref"];
/**
* Common non-imperative first words (past tense / gerund). We match against
* this allow-known-bad list rather than a broad `/(ed|ing)$/` regex so that
* legitimate imperatives like "Bring", "Embed", "Ring" are never flagged.
* This favors precision (no false positives) over recall.
*/
const NON_IMPERATIVE = new Set([
// past tense
"added", "fixed", "updated", "changed", "removed", "deleted", "created",
"refactored", "implemented", "improved", "renamed", "moved", "merged",
"reverted", "bumped", "cleaned", "corrected", "adjusted", "enabled",
"disabled", "introduced", "resolved", "replaced", "converted", "migrated",
"dropped", "extracted", "wrapped", "tweaked", "optimized", "simplified",
"formatted", "documented", "tested", "released", "handled", "allowed",
"prevented", "ensured", "avoided", "unified", "applied", "upgraded",
"downgraded", "patched", "hardened", "restructured", "reorganized",
"deprecated", "exposed", "integrated", "validated", "normalized", "cached",
"supported", "added", "wired", "hooked",
// gerund
"adding", "fixing", "updating", "changing", "removing", "deleting",
"creating", "refactoring", "implementing", "improving", "renaming",
"moving", "merging", "reverting", "bumping", "cleaning", "correcting",
"adjusting", "enabling", "disabling", "introducing", "resolving",
"replacing", "converting", "migrating", "dropping", "extracting",
"wrapping", "tweaking", "optimizing", "simplifying", "formatting",
"documenting", "testing", "releasing", "handling", "allowing",
"preventing", "ensuring", "avoiding", "unifying", "applying", "upgrading",
"patching", "hardening", "restructuring", "reorganizing", "deprecating",
"exposing", "integrating", "validating", "normalizing", "caching",
"supporting",
]);
export const STYLE_GUIDE = `# Udacity Git Commit Message Style Guide
A commit message has three parts separated by blank lines: **subject**, optional **body**, optional **footer**.
type: Subject
Body — the what and why, not the how.
Resolves: #123
See also: #456, #789
## Types
${Object.entries(TYPES)
.map(([t, d]) => `- **${t}**: ${d}`)
.join("\n")}
## Subject
- \`type: Subject\` format
- The whole line (including the \`type: \` prefix) is no more than ${SUBJECT_MAX} characters
- Begins with a capital letter
- Imperative mood ("Add", not "Added")
- No trailing period
- Blank line separates it from the body
## Body (optional)
- Only when the commit needs explanation
- Explains the **what** and **why**, not the how
- Wrap each line at ${BODY_WRAP} characters
## Footer (optional)
- References issue-tracker IDs: \`Resolves: #123\`, \`See also: #456, #789\`
_Lengths are counted in Unicode code points._
`;
/** Length in Unicode code points (not UTF-16 code units). */
export function width(s) {
return [...s].length;
}
/** Uppercase the first code point of a string (astral-safe). */
function capitalizeFirst(s) {
const chars = [...s];
if (chars.length === 0)
return s;
return chars[0].toUpperCase() + chars.slice(1).join("");
}
/**
* Greedy word-wrap that preserves paragraph breaks AND hard-breaks any single
* token longer than `max` (URLs, long paths), so no output line ever exceeds
* the limit. This guarantees `format`'s output always passes `validate`.
*/
export function wrap(text, max = BODY_WRAP) {
return text
.split("\n")
.map((para) => {
const lines = [];
let line = "";
const flush = () => {
if (line) {
lines.push(line);
line = "";
}
};
for (let word of para.split(/\s+/).filter(Boolean)) {
// hard-break an over-long token across multiple lines
while (width(word) > max) {
flush();
const chars = [...word];
lines.push(chars.slice(0, max).join(""));
word = chars.slice(max).join("");
}
if (!line)
line = word;
else if (width(line) + 1 + width(word) <= max)
line += " " + word;
else {
flush();
line = word;
}
}
flush();
return lines.join("\n");
})
.join("\n");
}
/** Validate a full commit message against the Udacity style guide. */
export function validate(message) {
const problems = [];
const warnings = [];
// Normalize CRLF / lone CR so line-based checks are reliable.
const normalized = message.replace(/\r\n?/g, "\n");
const lines = normalized.split("\n");
// Drop trailing blank lines (a trailing newline shouldn't count as a body).
while (lines.length > 1 && lines[lines.length - 1].trim() === "")
lines.pop();
const rawSubject = lines[0] ?? "";
const subject = rawSubject.replace(/\s+$/, "");
const m = subject.match(/^(\w+): (.*)$/);
if (!m) {
problems.push(`Subject must follow "type: Subject". Got: "${subject}".`);
}
else {
const [, type, rest] = m;
if (!TYPES[type]) {
problems.push(`Unknown type "${type}". Use one of: ${Object.keys(TYPES).join(", ")}.`);
}
if (!rest) {
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]}").`);
}
const firstWord = rest.split(/\s+/)[0];
if (NON_IMPERATIVE.has(firstWord.toLowerCase())) {
warnings.push(`"${firstWord}" looks past-tense/gerund — use the imperative mood ("Add", not "Added").`);
}
}
}
if (rawSubject !== subject)
problems.push("Subject has trailing whitespace.");
if (/\.$/.test(subject))
problems.push("Subject must not end with a period.");
if (width(subject) > SUBJECT_MAX) {
problems.push(`Subject line is ${width(subject)} chars (incl. the "type: " prefix); max is ${SUBJECT_MAX}.`);
}
if (lines.length > 1 && lines[1].trim() !== "") {
problems.push("Leave a blank line between the subject and the body.");
}
for (let i = 2; i < lines.length; i++) {
const line = lines[i];
if (width(line) > BODY_WRAP) {
problems.push(`Line ${i + 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".`);
}
}
}
return { valid: problems.length === 0, problems, warnings };
}
/**
* Compose a compliant commit message from parts. Capitalizes the subject,
* strips a trailing period, and hard-wraps the body so the result always
* passes `validate`.
*/
export function formatMessage(input) {
const subject = capitalizeFirst(input.subject.trim().replace(/\.+$/, ""));
const parts = [`${input.type}: ${subject}`];
if (input.body?.trim())
parts.push("", wrap(input.body.trim(), BODY_WRAP));
if (input.footer?.trim())
parts.push("", input.footer.trim());
const message = parts.join("\n");
return { message, report: validate(message) };
}
+74
-152

@@ -5,156 +5,78 @@ #!/usr/bin/env node

import { z } from "zod";
const SUBJECT_MAX = 50;
const BODY_WRAP = 72;
const TYPES = {
feat: "A new feature",
fix: "A bug fix",
docs: "Changes to documentation",
style: "Formatting, missing semicolons, etc; no code change",
refactor: "Refactoring production code",
test: "Adding tests, refactoring tests; no production code change",
chore: "Updating build tasks, package configs, etc; no production code change",
};
const STYLE_GUIDE = `# Udacity Git Commit Message Style Guide
A commit message has three parts separated by blank lines: **subject**, optional **body**, optional **footer**.
type: Subject
Body — the what and why, not the how.
Resolves: #123
See also: #456, #789
## Types
${Object.entries(TYPES)
.map(([t, d]) => `- **${t}**: ${d}`)
.join("\n")}
## Subject
- \`type: Subject\` format
- No more than ${SUBJECT_MAX} characters
- Begins with a capital letter
- Imperative mood ("Add", not "Added")
- No trailing period
- Blank line separates it from the body
## Body (optional)
- Only when the commit needs explanation
- Explains the **what** and **why**, not the how
- Wrap each line at ${BODY_WRAP} characters
## Footer (optional)
- References issue-tracker IDs: \`Resolves: #123\`, \`See also: #456, #789\`
`;
/** Greedy word-wrap that preserves existing paragraph breaks. */
function wrap(text, width) {
return text
.split("\n")
.map((para) => {
const words = para.split(/\s+/).filter(Boolean);
const lines = [];
let line = "";
for (const w of words) {
if (!line)
line = w;
else if ((line + " " + w).length <= width)
line += " " + w;
else {
lines.push(line);
line = w;
}
}
if (line)
lines.push(line);
return lines.join("\n");
})
.join("\n");
import { STYLE_GUIDE, validate, formatMessage } from "./lint.js";
const VERSION = "1.0.2";
export function createServer() {
const server = new McpServer({ name: "udacity-commit", version: VERSION });
server.registerResource("styleguide", "udacity://commit-styleguide", {
title: "Udacity Git Commit Style Guide",
description: "The commit-message rules (types, subject, body, footer).",
mimeType: "text/markdown",
}, async (uri) => ({ contents: [{ uri: uri.href, text: STYLE_GUIDE }] }));
server.registerTool("validate_commit_message", {
title: "Validate a commit message",
description: "Check a commit message against the Udacity Git Commit Message Style Guide. " +
"Returns whether it is compliant plus any problems (violations) and warnings (hints).",
inputSchema: { message: z.string().describe("The full commit message to check") },
outputSchema: {
valid: z.boolean(),
problems: z.array(z.string()),
warnings: z.array(z.string()),
},
}, async ({ message }) => {
const r = validate(message);
const text = [
r.valid ? "✅ Compliant with the Udacity style guide." : "❌ Not compliant.",
...r.problems.map((p) => ` • ${p}`),
...r.warnings.map((w) => ` ⚠ ${w}`),
].join("\n");
const structuredContent = {
valid: r.valid,
problems: r.problems,
warnings: r.warnings,
};
return { content: [{ type: "text", text }], structuredContent };
});
server.registerTool("format_commit_message", {
title: "Format a Udacity-style commit message",
description: "Compose a compliant commit message from its parts. The subject is capitalized, " +
"a trailing period is removed, and the body is wrapped at 72 characters.",
inputSchema: {
type: z.enum(["feat", "fix", "docs", "style", "refactor", "test", "chore"]),
subject: z.string().describe("Imperative subject; auto-capitalized, trailing period removed"),
body: z.string().optional().describe("What & why; auto-wrapped at 72 chars"),
footer: z.string().optional().describe('Issue refs, e.g. "Resolves: #123"'),
},
outputSchema: {
message: z.string(),
valid: z.boolean(),
problems: z.array(z.string()),
warnings: z.array(z.string()),
},
}, async (input) => {
const { message, report } = formatMessage(input);
const note = !report.valid
? "❌ " + [...report.problems, ...report.warnings].join("; ")
: report.warnings.length
? "✅ compliant (hint: " + report.warnings.join("; ") + ")"
: "✅ compliant";
const structuredContent = {
message,
valid: report.valid,
problems: report.problems,
warnings: report.warnings,
};
return {
content: [{ type: "text", text: `${message}\n\n--- ${note}` }],
structuredContent,
};
});
return server;
}
function validate(message) {
const problems = [];
const warnings = [];
const lines = message.replace(/\s+$/, "").split("\n");
const subject = lines[0] ?? "";
const m = subject.match(/^(\w+): (.*)$/);
if (!m) {
problems.push(`Subject must follow "type: Subject". Got: "${subject}".`);
}
else {
const [, type, rest] = m;
if (!TYPES[type]) {
problems.push(`Unknown type "${type}". Use one of: ${Object.keys(TYPES).join(", ")}.`);
}
if (!rest) {
problems.push("Subject text is empty after the type.");
}
else {
if (!/^[A-Z]/.test(rest)) {
problems.push(`Subject should begin with a capital letter (got "${rest[0]}").`);
}
const first = rest.split(/\s+/)[0];
if (/(ed|ing)$/i.test(first)) {
warnings.push(`"${first}" looks past-tense/gerund — use imperative mood ("Add", not "Added").`);
}
}
}
if (/\.$/.test(subject))
problems.push("Subject must not end with a period.");
if (subject.length > SUBJECT_MAX) {
problems.push(`Subject is ${subject.length} chars; max is ${SUBJECT_MAX}.`);
}
if (lines.length > 1 && lines[1].trim() !== "") {
problems.push("Leave a blank line between the subject and the body.");
}
for (let i = 2; i < lines.length; i++) {
if (lines[i].length > BODY_WRAP) {
problems.push(`Line ${i + 1} is ${lines[i].length} chars; wrap body/footer at ${BODY_WRAP}.`);
}
}
return { valid: problems.length === 0, problems, warnings };
async function main() {
const server = createServer();
await server.connect(new StdioServerTransport());
}
const server = new McpServer({ name: "udacity-commit", version: "1.0.1" });
server.registerResource("styleguide", "udacity://commit-styleguide", {
title: "Udacity Git Commit Style Guide",
description: "The commit-message rules (types, subject, body, footer).",
mimeType: "text/markdown",
}, async (uri) => ({ contents: [{ uri: uri.href, text: STYLE_GUIDE }] }));
server.registerTool("validate_commit_message", {
title: "Validate a commit message",
description: "Check a commit message against the Udacity Git Commit Message Style Guide.",
inputSchema: { message: z.string().describe("The full commit message to check") },
}, async ({ message }) => {
const r = validate(message);
const out = [
r.valid ? "✅ Compliant with the Udacity style guide." : "❌ Not compliant.",
...r.problems.map((p) => ` • ${p}`),
...r.warnings.map((w) => ` ⚠ ${w}`),
].join("\n");
return { content: [{ type: "text", text: out }] };
main().catch((err) => {
console.error(err);
process.exit(1);
});
server.registerTool("format_commit_message", {
title: "Format a Udacity-style commit message",
description: "Compose a compliant commit message from its parts.",
inputSchema: {
type: z.enum(["feat", "fix", "docs", "style", "refactor", "test", "chore"]),
subject: z
.string()
.describe("Imperative subject; auto-capitalized, trailing period removed"),
body: z.string().optional().describe("What & why; auto-wrapped at 72 chars"),
footer: z.string().optional().describe('Issue refs, e.g. "Resolves: #123"'),
},
}, async ({ type, subject, body, footer }) => {
let s = subject.trim().replace(/\.+$/, "");
s = s.charAt(0).toUpperCase() + s.slice(1);
const parts = [`${type}: ${s}`];
if (body?.trim())
parts.push("", wrap(body.trim(), BODY_WRAP));
if (footer?.trim())
parts.push("", footer.trim());
const msg = parts.join("\n");
const v = validate(msg);
const note = v.valid ? "✅ compliant" : "❌ " + v.problems.join("; ");
return { content: [{ type: "text", text: `${msg}\n\n--- ${note}` }] };
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map
{
"name": "mcp-udacity-commit",
"version": "1.0.1",
"version": "1.0.2",
"description": "MCP server that validates and formats git commit messages per the Udacity Git Commit Message Style Guide.",

@@ -32,5 +32,7 @@ "type": "module",

"scripts": {
"build": "tsc && chmod 755 build/index.js",
"build": "rm -rf build && tsc && chmod 755 build/index.js",
"start": "node build/index.js",
"prepublishOnly": "npm run build",
"pretest": "npm run build",
"test": "node --test test/*.test.mjs",
"prepublishOnly": "npm test",
"test:client": "node test-client.mjs"

@@ -37,0 +39,0 @@ },

#!/usr/bin/env node
export {};
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}