mcp-udacity-commit 1.2.0 → 1.2.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,65 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.2.2] - 2026-07-31
9
+
10
+ ### Changed
11
+
12
+ - Include `CHANGELOG.md` in the published npm package so the release
13
+ history ships alongside the code.
14
+
15
+ ## [1.2.1] - 2026-07-31
16
+
17
+ ### Changed
18
+
19
+ - Renamed terse single-letter locals across the server and lint core to
20
+ descriptive names — a pure readability pass with functionally identical
21
+ compiled output. (#4)
22
+
23
+ ### Security
24
+
25
+ - Ignore `.mcpregistry_*` so the MCP publisher's GitHub token can never be
26
+ committed; also ignore `.idea/` project settings and `*.tgz` pack
27
+ artifacts. (#5)
28
+
29
+ ## [1.2.0] - 2026-07-28
30
+
31
+ ### Added
32
+
33
+ - `validate_branch_name` tool and `udacity://branch-naming` resource
34
+ enforcing the companion `type/kebab-case` branch-naming convention. (#2)
35
+ - `release/` recognized as a typed branch that accepts version-style
36
+ descriptions (e.g. `release/1.2.0`, `release/2024-q1`). (#3)
37
+
38
+ ## [1.0.2] - 2026-07-28
39
+
40
+ ### Fixed
41
+
42
+ - Hardened the commit linter — CRLF handling, code-point length counting,
43
+ imperative-mood hints, and footer issue-reference checks — and added a
44
+ unit-test suite plus CI. (#1)
45
+ - Pointed `node --test` at explicit test files so CI runs the suite
46
+ reliably. (#1)
47
+
48
+ ## [1.0.1] - 2026-07-28
49
+
50
+ ### Changed
51
+
52
+ - Synced the published npm README and dropped maintainer-only publish
53
+ steps from it.
54
+
55
+ ## [1.0.0] - 2026-07-28
56
+
57
+ ### Added
58
+
59
+ - Initial release: the Udacity commit-message MCP server with the
60
+ `validate_commit_message` and `format_commit_message` tools and the
61
+ `udacity://commit-styleguide` resource.
62
+
63
+ [1.2.2]: https://github.com/qwertymuzaffar/mcp-udacity-commit/compare/v1.2.1...v1.2.2
64
+ [1.2.1]: https://github.com/qwertymuzaffar/mcp-udacity-commit/compare/v1.2.0...v1.2.1
65
+ [1.2.0]: https://github.com/qwertymuzaffar/mcp-udacity-commit/releases/tag/v1.2.0
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.2";
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.2",
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",
@@ -23,7 +23,8 @@
23
23
  },
24
24
  "files": [
25
25
  "build",
26
- "README.md"
26
+ "README.md",
27
+ "CHANGELOG.md"
27
28
  ],
28
29
  "engines": {
29
30
  "node": ">=18"