@programinglive/dev-workflow-mcp-server 1.2.3 → 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 CHANGED
@@ -2,6 +2,13 @@
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
+
5
12
  ### [1.2.3](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.2.2...v1.2.3) (2025-11-11)
6
13
 
7
14
 
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",
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
+ });
@@ -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
@@ -865,8 +865,11 @@
865
865
  throw new Error(`Failed to load document (${response.status})`);
866
866
  }
867
867
  const payload = await response.json();
868
- const html = marked.parse(payload.content || '');
868
+ const rawContent = payload.content || '';
869
+ const format = (payload.format || 'markdown').toLowerCase();
870
+ const html = format === 'html' ? rawContent : marked.parse(rawContent);
869
871
  docModalContent.innerHTML = html;
872
+
870
873
  lucide.createIcons();
871
874
 
872
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 allowedDocs = {
13
- "README.md": join(projectRoot, "README.md"),
14
- "web-dashboard.md": join(projectRoot, "docs", "web-dashboard.md"),
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: Object.keys(allowedDocs) }));
94
+ res.end(JSON.stringify({ docs: availableDocs }));
65
95
  return;
66
96
  }
67
97
 
68
98
  const docKey = rawDocKey.trim();
69
- const resolvedKey = Object.keys(allowedDocs).find((key) => key.toLowerCase() === docKey.toLowerCase());
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: Object.keys(allowedDocs) }));
102
+ res.end(JSON.stringify({ error: "Document not found", requested: docKey, available: availableDocs }));
73
103
  return;
74
104
  }
75
105
 
76
- const docPath = allowedDocs[resolvedKey];
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: error.message }));
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");