mcp-udacity-commit 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
5
  import { STYLE_GUIDE, BRANCH_GUIDE, validate, formatMessage, validateBranch } from "./lint.js";
6
- const VERSION = "1.2.0";
6
+ const VERSION = "1.2.1";
7
7
  export function createServer() {
8
8
  const server = new McpServer({ name: "udacity-commit", version: VERSION });
9
9
  server.registerResource("styleguide", "udacity://commit-styleguide", {
@@ -27,16 +27,16 @@ export function createServer() {
27
27
  warnings: z.array(z.string()),
28
28
  },
29
29
  }, async ({ message }) => {
30
- const r = validate(message);
30
+ const report = validate(message);
31
31
  const text = [
32
- r.valid ? "✅ Compliant with the Udacity style guide." : "❌ Not compliant.",
33
- ...r.problems.map((p) => ` • ${p}`),
34
- ...r.warnings.map((w) => ` ⚠ ${w}`),
32
+ report.valid ? "✅ Compliant with the Udacity style guide." : "❌ Not compliant.",
33
+ ...report.problems.map((problem) => ` • ${problem}`),
34
+ ...report.warnings.map((warning) => ` ⚠ ${warning}`),
35
35
  ].join("\n");
36
36
  const structuredContent = {
37
- valid: r.valid,
38
- problems: r.problems,
39
- warnings: r.warnings,
37
+ valid: report.valid,
38
+ problems: report.problems,
39
+ warnings: report.warnings,
40
40
  };
41
41
  return { content: [{ type: "text", text }], structuredContent };
42
42
  });
@@ -86,16 +86,16 @@ export function createServer() {
86
86
  warnings: z.array(z.string()),
87
87
  },
88
88
  }, async ({ name }) => {
89
- const r = validateBranch(name);
89
+ const report = validateBranch(name);
90
90
  const text = [
91
- r.valid ? "✅ Compliant branch name." : "❌ Not compliant.",
92
- ...r.problems.map((p) => ` • ${p}`),
93
- ...r.warnings.map((w) => ` ⚠ ${w}`),
91
+ report.valid ? "✅ Compliant branch name." : "❌ Not compliant.",
92
+ ...report.problems.map((problem) => ` • ${problem}`),
93
+ ...report.warnings.map((warning) => ` ⚠ ${warning}`),
94
94
  ].join("\n");
95
95
  const structuredContent = {
96
- valid: r.valid,
97
- problems: r.problems,
98
- warnings: r.warnings,
96
+ valid: report.valid,
97
+ problems: report.problems,
98
+ warnings: report.warnings,
99
99
  };
100
100
  return { content: [{ type: "text", text }], structuredContent };
101
101
  });
@@ -105,7 +105,7 @@ async function main() {
105
105
  const server = createServer();
106
106
  await server.connect(new StdioServerTransport());
107
107
  }
108
- main().catch((err) => {
109
- console.error(err);
108
+ main().catch((error) => {
109
+ console.error(error);
110
110
  process.exit(1);
111
111
  });
package/build/lint.js CHANGED
@@ -78,7 +78,7 @@ A commit message has three parts separated by blank lines: **subject**, optional
78
78
 
79
79
  ## Types
80
80
  ${Object.entries(TYPES)
81
- .map(([t, d]) => `- **${t}**: ${d}`)
81
+ .map(([typeName, typeDescription]) => `- **${typeName}**: ${typeDescription}`)
82
82
  .join("\n")}
83
83
 
84
84
  ## Subject
@@ -126,14 +126,14 @@ same commit \`type\` set.
126
126
  Base branches (${[...BASE_BRANCHES].join(", ")}) are exempt.
127
127
  `;
128
128
  /** Length in Unicode code points (not UTF-16 code units). */
129
- export function width(s) {
130
- return [...s].length;
129
+ export function width(text) {
130
+ return [...text].length;
131
131
  }
132
132
  /** Uppercase the first code point of a string (astral-safe). */
133
- function capitalizeFirst(s) {
134
- const chars = [...s];
133
+ function capitalizeFirst(text) {
134
+ const chars = [...text];
135
135
  if (chars.length === 0)
136
- return s;
136
+ return text;
137
137
  return chars[0].toUpperCase() + chars.slice(1).join("");
138
138
  }
139
139
  /**
@@ -187,23 +187,23 @@ export function validate(message) {
187
187
  lines.pop();
188
188
  const rawSubject = lines[0] ?? "";
189
189
  const subject = rawSubject.replace(/\s+$/, "");
190
- const m = subject.match(/^(\w+): (.*)$/);
191
- if (!m) {
190
+ const subjectMatch = subject.match(/^(\w+): (.*)$/);
191
+ if (!subjectMatch) {
192
192
  problems.push(`Subject must follow "type: Subject". Got: "${subject}".`);
193
193
  }
194
194
  else {
195
- const [, type, rest] = m;
195
+ const [, type, subjectText] = subjectMatch;
196
196
  if (!TYPES[type]) {
197
197
  problems.push(`Unknown type "${type}". Use one of: ${Object.keys(TYPES).join(", ")}.`);
198
198
  }
199
- if (!rest) {
199
+ if (!subjectText) {
200
200
  problems.push("Subject text is empty after the type.");
201
201
  }
202
202
  else {
203
- if (/^\p{Ll}/u.test(rest)) {
204
- problems.push(`Subject should begin with a capital letter (got "${[...rest][0]}").`);
203
+ if (/^\p{Ll}/u.test(subjectText)) {
204
+ problems.push(`Subject should begin with a capital letter (got "${[...subjectText][0]}").`);
205
205
  }
206
- const firstWord = rest.split(/\s+/)[0];
206
+ const firstWord = subjectText.split(/\s+/)[0];
207
207
  if (NON_IMPERATIVE.has(firstWord.toLowerCase())) {
208
208
  warnings.push(`"${firstWord}" looks past-tense/gerund — use the imperative mood ("Add", not "Added").`);
209
209
  }
@@ -219,15 +219,15 @@ export function validate(message) {
219
219
  if (lines.length > 1 && lines[1].trim() !== "") {
220
220
  problems.push("Leave a blank line between the subject and the body.");
221
221
  }
222
- for (let i = 2; i < lines.length; i++) {
223
- const line = lines[i];
222
+ for (let lineIndex = 2; lineIndex < lines.length; lineIndex++) {
223
+ const line = lines[lineIndex];
224
224
  if (width(line) > BODY_WRAP) {
225
- problems.push(`Line ${i + 1} is ${width(line)} chars; wrap body/footer at ${BODY_WRAP}.`);
225
+ problems.push(`Line ${lineIndex + 1} is ${width(line)} chars; wrap body/footer at ${BODY_WRAP}.`);
226
226
  }
227
- const fm = line.match(/^([A-Za-z][A-Za-z ]*?):\s*(.*)$/);
228
- if (fm && FOOTER_KEYS.some((k) => k.toLowerCase() === fm[1].toLowerCase())) {
229
- if (!/#\d+/.test(fm[2])) {
230
- warnings.push(`Footer "${fm[1]}" should reference an issue, e.g. "${fm[1]}: #123".`);
227
+ const footerMatch = line.match(/^([A-Za-z][A-Za-z ]*?):\s*(.*)$/);
228
+ if (footerMatch && FOOTER_KEYS.some((footerKey) => footerKey.toLowerCase() === footerMatch[1].toLowerCase())) {
229
+ if (!/#\d+/.test(footerMatch[2])) {
230
+ warnings.push(`Footer "${footerMatch[1]}" should reference an issue, e.g. "${footerMatch[1]}: #123".`);
231
231
  }
232
232
  }
233
233
  }
@@ -277,13 +277,13 @@ export function validateBranch(name) {
277
277
  warnings.push(`"${branch}" is a base branch — feature-branch naming rules don't apply.`);
278
278
  return { valid: problems.length === 0, problems, warnings };
279
279
  }
280
- const slash = branch.indexOf("/");
281
- if (slash === -1) {
280
+ const slashIndex = branch.indexOf("/");
281
+ if (slashIndex === -1) {
282
282
  problems.push(`Branch must follow "type/description". Got: "${branch}".`);
283
283
  return { valid: false, problems, warnings };
284
284
  }
285
- const type = branch.slice(0, slash);
286
- const description = branch.slice(slash + 1);
285
+ const type = branch.slice(0, slashIndex);
286
+ const description = branch.slice(slashIndex + 1);
287
287
  if (!BRANCH_TYPES.includes(type)) {
288
288
  problems.push(`Unknown type "${type}". Use one of: ${BRANCH_TYPES.join(", ")}.`);
289
289
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-udacity-commit",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "MCP server that validates and formats git commit messages per the Udacity Git Commit Message Style Guide.",
5
5
  "type": "module",
6
6
  "license": "MIT",