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 +17 -17
- package/build/lint.js +24 -24
- package/package.json +1 -1
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.
|
|
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
|
|
30
|
+
const report = validate(message);
|
|
31
31
|
const text = [
|
|
32
|
-
|
|
33
|
-
...
|
|
34
|
-
...
|
|
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:
|
|
38
|
-
problems:
|
|
39
|
-
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
|
|
89
|
+
const report = validateBranch(name);
|
|
90
90
|
const text = [
|
|
91
|
-
|
|
92
|
-
...
|
|
93
|
-
...
|
|
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:
|
|
97
|
-
problems:
|
|
98
|
-
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((
|
|
109
|
-
console.error(
|
|
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(([
|
|
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(
|
|
130
|
-
return [...
|
|
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(
|
|
134
|
-
const chars = [...
|
|
133
|
+
function capitalizeFirst(text) {
|
|
134
|
+
const chars = [...text];
|
|
135
135
|
if (chars.length === 0)
|
|
136
|
-
return
|
|
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
|
|
191
|
-
if (!
|
|
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,
|
|
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 (!
|
|
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(
|
|
204
|
-
problems.push(`Subject should begin with a capital letter (got "${[...
|
|
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 =
|
|
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
|
|
223
|
-
const line = lines[
|
|
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 ${
|
|
225
|
+
problems.push(`Line ${lineIndex + 1} is ${width(line)} chars; wrap body/footer at ${BODY_WRAP}.`);
|
|
226
226
|
}
|
|
227
|
-
const
|
|
228
|
-
if (
|
|
229
|
-
if (!/#\d+/.test(
|
|
230
|
-
warnings.push(`Footer "${
|
|
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
|
|
281
|
-
if (
|
|
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,
|
|
286
|
-
const description = branch.slice(
|
|
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