@programinglive/dev-workflow-mcp-server 1.2.2 → 1.2.4
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 +14 -0
- package/README.md +7 -0
- package/package.json +4 -2
- package/scripts/build-docs.js +38 -0
- package/tests/build.test.js +9 -0
- package/web/index.html +9 -2
- package/web/server.js +43 -15
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.2.4](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.2.3...v1.2.4) (2025-11-11)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ✨ Features
|
|
9
|
+
|
|
10
|
+
* **docs:** prebuild markdown docs ([84272b6](https://github.com/programinglive/dev-workflow-mcp-server/commit/84272b690e7da88cab60f3b29449044d875261d6))
|
|
11
|
+
|
|
12
|
+
### [1.2.3](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.2.2...v1.2.3) (2025-11-11)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### ✨ Features
|
|
16
|
+
|
|
17
|
+
* **web:** highlight 20 MCP tools on dashboard ([eb76634](https://github.com/programinglive/dev-workflow-mcp-server/commit/eb766347bff18cc780e4f86b8d70a122eab3307f))
|
|
18
|
+
|
|
5
19
|
### [1.2.6](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.2.1...v1.2.6) (2025-11-11)
|
|
6
20
|
|
|
7
21
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Development Workflow MCP Server
|
|
2
2
|
|
|
3
|
+
<p align="left">
|
|
4
|
+
<a href="https://www.npmjs.com/package/@programinglive/dev-workflow-mcp-server"><img src="https://img.shields.io/npm/v/@programinglive/dev-workflow-mcp-server.svg" alt="npm version"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@programinglive/dev-workflow-mcp-server"><img src="https://img.shields.io/npm/dm/@programinglive/dev-workflow-mcp-server.svg" alt="npm downloads"></a>
|
|
6
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license: MIT"></a>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
3
9
|
An MCP (Model Context Protocol) server that helps enforce development discipline and workflow best practices. This server acts as your coding conscience, reminding you to follow proper development workflows.
|
|
4
10
|
|
|
5
11
|
## 🎯 Purpose
|
|
@@ -190,6 +196,7 @@ Open `http://localhost:3111` in a browser to view the dashboard UI (`web/index.h
|
|
|
190
196
|
Running `npm run build` generates:
|
|
191
197
|
- `dist/index.mjs` - Optimized ES module bundle
|
|
192
198
|
- Source maps and other build artifacts
|
|
199
|
+
- `dist/docs/` - Pre-rendered HTML documentation generated from Markdown via `scripts/build-docs.js`
|
|
193
200
|
|
|
194
201
|
The build bundles all source files while externalizing Node.js built-in modules and dependencies, resulting in a single file distribution.
|
|
195
202
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@programinglive/dev-workflow-mcp-server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "MCP server to enforce development workflow discipline",
|
|
5
5
|
"homepage": "https://devworkflow.programinglive.com/",
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "vite build",
|
|
13
|
+
"postbuild": "node scripts/build-docs.js",
|
|
13
14
|
"dev": "node --watch index.js",
|
|
14
15
|
"start": "node index.js",
|
|
15
16
|
"local": "node index.js",
|
|
@@ -50,7 +51,8 @@
|
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@commitlint/cli": "^20.1.0",
|
|
52
53
|
"@commitlint/config-conventional": "^20.0.0",
|
|
53
|
-
"@programinglive/commiter": "^1.1.0"
|
|
54
|
+
"@programinglive/commiter": "^1.1.0",
|
|
55
|
+
"marked": "^12.0.2"
|
|
54
56
|
},
|
|
55
57
|
"standard-version": {
|
|
56
58
|
"releaseCommitMessageFormat": "chore(release): {{currentTag}} 🚀",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { marked } from 'marked';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const projectRoot = path.join(__dirname, '..');
|
|
9
|
+
|
|
10
|
+
const docs = [
|
|
11
|
+
{
|
|
12
|
+
key: 'README.md',
|
|
13
|
+
source: path.join(projectRoot, 'README.md'),
|
|
14
|
+
output: 'README.html',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: 'web-dashboard.md',
|
|
18
|
+
source: path.join(projectRoot, 'docs', 'web-dashboard.md'),
|
|
19
|
+
output: 'web-dashboard.html',
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const distDocDir = path.join(projectRoot, 'dist', 'docs');
|
|
24
|
+
|
|
25
|
+
async function buildDocs() {
|
|
26
|
+
await mkdir(distDocDir, { recursive: true });
|
|
27
|
+
|
|
28
|
+
for (const doc of docs) {
|
|
29
|
+
const markdown = await readFile(doc.source, 'utf-8');
|
|
30
|
+
const html = marked.parse(markdown);
|
|
31
|
+
await writeFile(path.join(distDocDir, doc.output), html, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
buildDocs().catch((error) => {
|
|
36
|
+
console.error('Failed to build documentation assets:', error);
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
});
|
package/tests/build.test.js
CHANGED
|
@@ -25,6 +25,15 @@ test('build generates dist/index.mjs file', async () => {
|
|
|
25
25
|
const content = await readFile(distPath, 'utf-8');
|
|
26
26
|
assert(content.includes('main'), 'Bundle should contain main function');
|
|
27
27
|
assert(content.length > 1000, 'Bundle should be substantial');
|
|
28
|
+
|
|
29
|
+
const docDir = path.join(process.cwd(), 'dist', 'docs');
|
|
30
|
+
const docFiles = ['README.html', 'web-dashboard.html'];
|
|
31
|
+
for (const file of docFiles) {
|
|
32
|
+
const fullPath = path.join(docDir, file);
|
|
33
|
+
await access(fullPath);
|
|
34
|
+
const docContent = await readFile(fullPath, 'utf-8');
|
|
35
|
+
assert(docContent.includes('<h1'), `${file} should contain HTML output`);
|
|
36
|
+
}
|
|
28
37
|
});
|
|
29
38
|
|
|
30
39
|
test('build script exists in package.json', async () => {
|
package/web/index.html
CHANGED
|
@@ -245,7 +245,11 @@
|
|
|
245
245
|
<i data-lucide="workflow"></i>
|
|
246
246
|
Commands Reference
|
|
247
247
|
</h2>
|
|
248
|
-
<
|
|
248
|
+
<div class="mt-2 inline-flex items-center gap-2 rounded-full bg-indigo-50 px-3 py-1 text-xs font-semibold text-indigo-600 uppercase tracking-wide">
|
|
249
|
+
<i data-lucide="check-square"></i>
|
|
250
|
+
<span>All Tools · 20 commands</span>
|
|
251
|
+
</div>
|
|
252
|
+
<p class="text-slate-600 mt-3">Explore all <strong>20</strong> MCP tools that keep the workflow honest. Review prerequisites, usage tips, and next steps for each command below.</p>
|
|
249
253
|
</div>
|
|
250
254
|
<button data-doc="README.md" data-doc-heading="🛠️ Available Tools" class="doc-link inline-flex items-center gap-2 text-sm text-indigo-600 hover:text-indigo-700">
|
|
251
255
|
<i data-lucide="external-link"></i>
|
|
@@ -861,8 +865,11 @@
|
|
|
861
865
|
throw new Error(`Failed to load document (${response.status})`);
|
|
862
866
|
}
|
|
863
867
|
const payload = await response.json();
|
|
864
|
-
const
|
|
868
|
+
const rawContent = payload.content || '';
|
|
869
|
+
const format = (payload.format || 'markdown').toLowerCase();
|
|
870
|
+
const html = format === 'html' ? rawContent : marked.parse(rawContent);
|
|
865
871
|
docModalContent.innerHTML = html;
|
|
872
|
+
|
|
866
873
|
lucide.createIcons();
|
|
867
874
|
|
|
868
875
|
if (headingText) {
|
package/web/server.js
CHANGED
|
@@ -9,11 +9,39 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
const projectRoot = join(__dirname, "..");
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
"README.md":
|
|
14
|
-
|
|
12
|
+
const docSources = {
|
|
13
|
+
"README.md": {
|
|
14
|
+
markdown: join(projectRoot, "README.md"),
|
|
15
|
+
html: join(projectRoot, "dist", "docs", "README.html"),
|
|
16
|
+
},
|
|
17
|
+
"web-dashboard.md": {
|
|
18
|
+
markdown: join(projectRoot, "docs", "web-dashboard.md"),
|
|
19
|
+
html: join(projectRoot, "dist", "docs", "web-dashboard.html"),
|
|
20
|
+
},
|
|
15
21
|
};
|
|
16
22
|
|
|
23
|
+
async function resolveDocContent(docKey) {
|
|
24
|
+
const entry = docSources[docKey];
|
|
25
|
+
if (!entry) {
|
|
26
|
+
return { exists: false };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Try HTML first, fall back to Markdown in development or if HTML missing.
|
|
30
|
+
const candidates = [entry.html, entry.markdown];
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
try {
|
|
33
|
+
await access(candidate);
|
|
34
|
+
const content = await readFile(candidate, "utf-8");
|
|
35
|
+
const format = candidate.endsWith(".html") ? "html" : "markdown";
|
|
36
|
+
return { exists: true, content, format };
|
|
37
|
+
} catch {
|
|
38
|
+
// try next candidate
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { exists: false };
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
function getPreferredPort() {
|
|
18
46
|
const fromEnv = process.env.PORT || process.env.DEV_WORKFLOW_WEB_PORT;
|
|
19
47
|
|
|
@@ -59,31 +87,31 @@ async function start() {
|
|
|
59
87
|
res.end(JSON.stringify({ summary }));
|
|
60
88
|
} else if (req.method === "GET" && url.pathname === "/api/docs") {
|
|
61
89
|
const rawDocKey = url.searchParams.get("doc");
|
|
90
|
+
const availableDocs = Object.keys(docSources);
|
|
91
|
+
|
|
62
92
|
if (!rawDocKey) {
|
|
63
93
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
64
|
-
res.end(JSON.stringify({ docs:
|
|
94
|
+
res.end(JSON.stringify({ docs: availableDocs }));
|
|
65
95
|
return;
|
|
66
96
|
}
|
|
67
97
|
|
|
68
98
|
const docKey = rawDocKey.trim();
|
|
69
|
-
const resolvedKey =
|
|
99
|
+
const resolvedKey = availableDocs.find((key) => key.toLowerCase() === docKey.toLowerCase());
|
|
70
100
|
if (!resolvedKey) {
|
|
71
101
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
72
|
-
res.end(JSON.stringify({ error: "Document not found", requested: docKey, available:
|
|
102
|
+
res.end(JSON.stringify({ error: "Document not found", requested: docKey, available: availableDocs }));
|
|
73
103
|
return;
|
|
74
104
|
}
|
|
75
105
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
await access(docPath);
|
|
80
|
-
const content = await readFile(docPath, "utf-8");
|
|
81
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
82
|
-
res.end(JSON.stringify({ doc: resolvedKey, content }));
|
|
83
|
-
} catch (error) {
|
|
106
|
+
const result = await resolveDocContent(resolvedKey);
|
|
107
|
+
if (!result.exists) {
|
|
84
108
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
85
|
-
res.end(JSON.stringify({ error:
|
|
109
|
+
res.end(JSON.stringify({ error: "Document asset is missing" }));
|
|
110
|
+
return;
|
|
86
111
|
}
|
|
112
|
+
|
|
113
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
114
|
+
res.end(JSON.stringify({ doc: resolvedKey, content: result.content, format: result.format }));
|
|
87
115
|
} else {
|
|
88
116
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
89
117
|
res.end("Not found");
|