@programinglive/dev-workflow-mcp-server 1.1.11 → 1.1.16

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/web/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createServer } from "http";
2
- import { readFile } from "fs/promises";
2
+ import { readFile, access } from "fs/promises";
3
3
  import { fileURLToPath } from "url";
4
4
  import { dirname, join } from "path";
5
5
  import getPort from "get-port";
@@ -7,6 +7,12 @@ import { getHistoryForUser, getSummaryForUser, getHistorySummary } from "../db/i
7
7
 
8
8
  const __filename = fileURLToPath(import.meta.url);
9
9
  const __dirname = dirname(__filename);
10
+ const projectRoot = join(__dirname, "..");
11
+
12
+ const allowedDocs = {
13
+ "README.md": join(projectRoot, "README.md"),
14
+ "web-dashboard.md": join(projectRoot, "docs", "web-dashboard.md"),
15
+ };
10
16
 
11
17
  async function start() {
12
18
  const PORT = process.env.DEV_WORKFLOW_WEB_PORT || await getPort({ port: 3111 });
@@ -39,6 +45,33 @@ async function start() {
39
45
  const summary = getHistorySummary(userId, { startDate, endDate, frequency });
40
46
  res.writeHead(200, { "Content-Type": "application/json" });
41
47
  res.end(JSON.stringify({ summary }));
48
+ } else if (req.method === "GET" && url.pathname === "/api/docs") {
49
+ const rawDocKey = url.searchParams.get("doc");
50
+ if (!rawDocKey) {
51
+ res.writeHead(200, { "Content-Type": "application/json" });
52
+ res.end(JSON.stringify({ docs: Object.keys(allowedDocs) }));
53
+ return;
54
+ }
55
+
56
+ const docKey = rawDocKey.trim();
57
+ const resolvedKey = Object.keys(allowedDocs).find((key) => key.toLowerCase() === docKey.toLowerCase());
58
+ if (!resolvedKey) {
59
+ res.writeHead(404, { "Content-Type": "application/json" });
60
+ res.end(JSON.stringify({ error: "Document not found", requested: docKey, available: Object.keys(allowedDocs) }));
61
+ return;
62
+ }
63
+
64
+ const docPath = allowedDocs[resolvedKey];
65
+
66
+ try {
67
+ await access(docPath);
68
+ const content = await readFile(docPath, "utf-8");
69
+ res.writeHead(200, { "Content-Type": "application/json" });
70
+ res.end(JSON.stringify({ doc: resolvedKey, content }));
71
+ } catch (error) {
72
+ res.writeHead(500, { "Content-Type": "application/json" });
73
+ res.end(JSON.stringify({ error: error.message }));
74
+ }
42
75
  } else {
43
76
  res.writeHead(404, { "Content-Type": "text/plain" });
44
77
  res.end("Not found");
package/workflow-state.js CHANGED
@@ -107,21 +107,88 @@ function hasProjectMarkers(candidate) {
107
107
  );
108
108
  }
109
109
 
110
- function selectProjectRoot(candidates) {
110
+ function normalizeAndStoreCandidate(list, candidate) {
111
+ if (!candidate) {
112
+ return;
113
+ }
114
+
115
+ const resolved = path.resolve(candidate);
116
+ if (!list.includes(resolved)) {
117
+ list.push(resolved);
118
+ }
119
+ }
120
+
121
+ function addAncestorCandidates(list, start) {
122
+ if (!start) {
123
+ return;
124
+ }
125
+
126
+ let current = path.resolve(start);
127
+ while (!isRootPath(current)) {
128
+ const parent = path.dirname(current);
129
+ if (parent === current) {
130
+ break;
131
+ }
132
+ if (!isRootPath(parent)) {
133
+ normalizeAndStoreCandidate(list, parent);
134
+ }
135
+ current = parent;
136
+ }
137
+ }
138
+
139
+ function selectProjectRoot(candidates, moduleRoot) {
140
+ const cwdResolved = path.resolve(process.cwd());
141
+ const prioritized = [];
142
+
111
143
  for (const candidate of candidates) {
112
144
  if (!candidate) {
113
145
  continue;
114
146
  }
115
147
 
116
148
  const resolved = path.resolve(candidate);
117
- if (isRootPath(resolved) && !hasProjectMarkers(resolved)) {
149
+ if (prioritized.some((entry) => entry.resolved === resolved)) {
118
150
  continue;
119
151
  }
120
152
 
121
- return resolved;
153
+ prioritized.push({
154
+ resolved,
155
+ hasMarkers: hasProjectMarkers(resolved),
156
+ isRoot: isRootPath(resolved),
157
+ isCwdAncestor: cwdResolved === resolved || cwdResolved.startsWith(`${resolved}${path.sep}`),
158
+ });
159
+ }
160
+
161
+ const ancestorWithMarkers = prioritized.find((entry) => entry.isCwdAncestor && entry.hasMarkers);
162
+ if (ancestorWithMarkers) {
163
+ return ancestorWithMarkers.resolved;
122
164
  }
123
165
 
124
- return null;
166
+ const ancestorWithoutMarkers = prioritized
167
+ .filter((entry) => entry.isCwdAncestor && !entry.hasMarkers && !entry.isRoot)
168
+ .sort((a, b) => b.resolved.length - a.resolved.length);
169
+ if (ancestorWithoutMarkers.length > 0) {
170
+ return ancestorWithoutMarkers[0].resolved;
171
+ }
172
+
173
+ if (moduleRoot) {
174
+ const moduleResolved = path.resolve(moduleRoot);
175
+ const moduleEntry = prioritized.find((entry) => entry.resolved === moduleResolved && !entry.isRoot);
176
+ if (moduleEntry) {
177
+ return moduleEntry.resolved;
178
+ }
179
+ }
180
+
181
+ const withMarkers = prioritized.find((entry) => entry.hasMarkers);
182
+ if (withMarkers) {
183
+ return withMarkers.resolved;
184
+ }
185
+
186
+ const nonRoot = prioritized.find((entry) => !entry.isRoot);
187
+ if (nonRoot) {
188
+ return nonRoot.resolved;
189
+ }
190
+
191
+ return prioritized.length > 0 ? prioritized[0].resolved : null;
125
192
  }
126
193
 
127
194
  function getUserScopedDir(baseDir) {
@@ -339,33 +406,29 @@ export function resolveStateFile() {
339
406
  const candidateRoots = [];
340
407
 
341
408
  const initProjectRoot = findProjectRoot(resolvedInitCwd);
342
- if (initProjectRoot) {
343
- candidateRoots.push(initProjectRoot);
344
- }
345
-
346
- const moduleProjectRoot = findProjectRoot(moduleRoot);
347
- if (moduleProjectRoot) {
348
- candidateRoots.push(moduleProjectRoot);
349
- } else if (!isRootPath(moduleRoot)) {
350
- candidateRoots.push(moduleRoot);
351
- }
352
-
353
409
  const cwdProjectRoot = findProjectRoot(cwdResolved);
354
- if (cwdProjectRoot) {
355
- candidateRoots.push(cwdProjectRoot);
356
- } else if (!isRootPath(cwdResolved)) {
357
- candidateRoots.push(cwdResolved);
358
- }
410
+ const moduleProjectRoot = findProjectRoot(moduleRoot);
359
411
 
412
+ normalizeAndStoreCandidate(candidateRoots, initProjectRoot);
413
+ normalizeAndStoreCandidate(candidateRoots, cwdProjectRoot);
360
414
  if (!isRootPath(resolvedInitCwd)) {
361
- candidateRoots.push(resolvedInitCwd);
415
+ normalizeAndStoreCandidate(candidateRoots, resolvedInitCwd);
416
+ addAncestorCandidates(candidateRoots, resolvedInitCwd);
417
+ }
418
+ if (!isRootPath(cwdResolved)) {
419
+ normalizeAndStoreCandidate(candidateRoots, cwdResolved);
420
+ addAncestorCandidates(candidateRoots, cwdResolved);
362
421
  }
363
422
 
423
+ normalizeAndStoreCandidate(candidateRoots, moduleProjectRoot);
424
+ if (!isRootPath(moduleRoot)) {
425
+ normalizeAndStoreCandidate(candidateRoots, moduleRoot);
426
+ }
364
427
  if (!isRootPath(__dirname)) {
365
- candidateRoots.push(__dirname);
428
+ normalizeAndStoreCandidate(candidateRoots, __dirname);
366
429
  }
367
430
 
368
- let projectRoot = selectProjectRoot(candidateRoots);
431
+ let projectRoot = selectProjectRoot(candidateRoots, moduleRoot);
369
432
 
370
433
  if (!projectRoot) {
371
434
  projectRoot = path.resolve(__dirname);