gsd-pi 2.67.0-dev.4fb8afe → 2.67.0-dev.5399650

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.
Files changed (133) hide show
  1. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  2. package/dist/resources/extensions/gsd/auto.js +27 -0
  3. package/dist/resources/extensions/gsd/commands/handlers/core.js +38 -24
  4. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  5. package/dist/resources/extensions/gsd/init-wizard.js +34 -0
  6. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  7. package/dist/resources/extensions/gsd/workflow-mcp.js +38 -7
  8. package/dist/web/standalone/.next/BUILD_ID +1 -1
  9. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  10. package/dist/web/standalone/.next/build-manifest.json +2 -2
  11. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  12. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/index.html +1 -1
  29. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  36. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  38. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  39. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  40. package/package.json +4 -2
  41. package/packages/mcp-server/dist/cli.d.ts +9 -0
  42. package/packages/mcp-server/dist/cli.d.ts.map +1 -0
  43. package/packages/mcp-server/dist/cli.js +58 -0
  44. package/packages/mcp-server/dist/cli.js.map +1 -0
  45. package/packages/mcp-server/dist/index.d.ts +20 -0
  46. package/packages/mcp-server/dist/index.d.ts.map +1 -0
  47. package/packages/mcp-server/dist/index.js +14 -0
  48. package/packages/mcp-server/dist/index.js.map +1 -0
  49. package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
  50. package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
  51. package/packages/mcp-server/dist/readers/captures.js +67 -0
  52. package/packages/mcp-server/dist/readers/captures.js.map +1 -0
  53. package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
  54. package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
  55. package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
  56. package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
  57. package/packages/mcp-server/dist/readers/index.d.ts +14 -0
  58. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
  59. package/packages/mcp-server/dist/readers/index.js +10 -0
  60. package/packages/mcp-server/dist/readers/index.js.map +1 -0
  61. package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
  62. package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
  63. package/packages/mcp-server/dist/readers/knowledge.js +82 -0
  64. package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
  65. package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
  66. package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
  67. package/packages/mcp-server/dist/readers/metrics.js +74 -0
  68. package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
  69. package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
  70. package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
  71. package/packages/mcp-server/dist/readers/paths.js +199 -0
  72. package/packages/mcp-server/dist/readers/paths.js.map +1 -0
  73. package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
  74. package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
  75. package/packages/mcp-server/dist/readers/roadmap.js +194 -0
  76. package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
  77. package/packages/mcp-server/dist/readers/state.d.ts +43 -0
  78. package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
  79. package/packages/mcp-server/dist/readers/state.js +184 -0
  80. package/packages/mcp-server/dist/readers/state.js.map +1 -0
  81. package/packages/mcp-server/dist/server.d.ts +28 -0
  82. package/packages/mcp-server/dist/server.d.ts.map +1 -0
  83. package/packages/mcp-server/dist/server.js +319 -0
  84. package/packages/mcp-server/dist/server.js.map +1 -0
  85. package/packages/mcp-server/dist/session-manager.d.ts +54 -0
  86. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
  87. package/packages/mcp-server/dist/session-manager.js +284 -0
  88. package/packages/mcp-server/dist/session-manager.js.map +1 -0
  89. package/packages/mcp-server/dist/types.d.ts +61 -0
  90. package/packages/mcp-server/dist/types.d.ts.map +1 -0
  91. package/packages/mcp-server/dist/types.js +11 -0
  92. package/packages/mcp-server/dist/types.js.map +1 -0
  93. package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
  94. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
  95. package/packages/mcp-server/dist/workflow-tools.js +526 -0
  96. package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
  97. package/packages/mcp-server/tsconfig.json +1 -1
  98. package/packages/rpc-client/dist/index.d.ts +10 -0
  99. package/packages/rpc-client/dist/index.d.ts.map +1 -0
  100. package/packages/rpc-client/dist/index.js +9 -0
  101. package/packages/rpc-client/dist/index.js.map +1 -0
  102. package/packages/rpc-client/dist/jsonl.d.ts +17 -0
  103. package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
  104. package/packages/rpc-client/dist/jsonl.js +54 -0
  105. package/packages/rpc-client/dist/jsonl.js.map +1 -0
  106. package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
  107. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
  108. package/packages/rpc-client/dist/rpc-client.js +541 -0
  109. package/packages/rpc-client/dist/rpc-client.js.map +1 -0
  110. package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
  111. package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
  112. package/packages/rpc-client/dist/rpc-client.test.js +477 -0
  113. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
  114. package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
  115. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
  116. package/packages/rpc-client/dist/rpc-types.js +12 -0
  117. package/packages/rpc-client/dist/rpc-types.js.map +1 -0
  118. package/scripts/ensure-workspace-builds.cjs +2 -0
  119. package/scripts/link-workspace-packages.cjs +21 -14
  120. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  121. package/src/resources/extensions/gsd/auto.ts +29 -1
  122. package/src/resources/extensions/gsd/commands/handlers/core.ts +52 -25
  123. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  124. package/src/resources/extensions/gsd/init-wizard.ts +34 -0
  125. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
  126. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  127. package/src/resources/extensions/gsd/tests/init-bootstrap-completeness.test.ts +121 -0
  128. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  129. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +39 -1
  130. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  131. package/src/resources/extensions/gsd/workflow-mcp.ts +41 -7
  132. /package/dist/web/standalone/.next/static/{IBTC_HlEpTBAa4HXMoV_A → 6_QPFhgX0DQnDhhquheRc}/_buildManifest.js +0 -0
  133. /package/dist/web/standalone/.next/static/{IBTC_HlEpTBAa4HXMoV_A → 6_QPFhgX0DQnDhhquheRc}/_ssgManifest.js +0 -0
@@ -0,0 +1,194 @@
1
+ // GSD MCP Server — roadmap structure reader
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { resolveGsdRoot, findMilestoneIds, resolveMilestoneFile, findSliceIds, resolveSliceFile, findTaskFiles, } from './paths.js';
5
+ // ---------------------------------------------------------------------------
6
+ // ROADMAP.md table parser
7
+ // ---------------------------------------------------------------------------
8
+ function parseRoadmapTable(content) {
9
+ const results = [];
10
+ // Try table format first: | S01 | Title | risk | depends | done-icon | demo |
11
+ const tableSection = content.match(/## (?:Slice[s]?|Slice Overview|Slice Table)\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
12
+ if (tableSection) {
13
+ const lines = tableSection[1].split('\n');
14
+ for (const line of lines) {
15
+ if (!line.includes('|'))
16
+ continue;
17
+ const cells = line.split('|').map((c) => c.trim()).filter(Boolean);
18
+ if (cells.length < 4)
19
+ continue;
20
+ if (cells[0] === 'ID' || cells[0].startsWith('--'))
21
+ continue;
22
+ const id = cells[0].match(/S\d+/)?.[0];
23
+ if (!id)
24
+ continue;
25
+ const done = cells.some((c) => c === '\u2611' || c === '\u2705' || c.toLowerCase() === 'done');
26
+ const depends = (cells[3] ?? '').replace(/\u2014/g, '').split(',').map((d) => d.trim()).filter(Boolean);
27
+ results.push({
28
+ id,
29
+ title: cells[1] ?? '',
30
+ risk: cells[2] ?? 'medium',
31
+ depends,
32
+ done,
33
+ demo: cells[5] ?? '',
34
+ });
35
+ }
36
+ if (results.length > 0)
37
+ return results;
38
+ }
39
+ // Try checkbox format: - [x] **S01: Title** `risk:high` `depends:[S01]`
40
+ const checkboxRe = /^-\s+\[([ xX])\]\s+\*\*(S\d+):\s*(.+?)\*\*(?:.*?`risk:(\w+)`)?(?:.*?`depends:\[([^\]]*)\]`)?/gm;
41
+ let match;
42
+ while ((match = checkboxRe.exec(content)) !== null) {
43
+ const [, checked, id, title, risk, deps] = match;
44
+ results.push({
45
+ id,
46
+ title: title.trim(),
47
+ risk: risk ?? 'medium',
48
+ depends: deps ? deps.split(',').map((d) => d.trim()).filter(Boolean) : [],
49
+ done: checked !== ' ',
50
+ demo: '',
51
+ });
52
+ }
53
+ if (results.length > 0)
54
+ return results;
55
+ // Try prose headers: ## S01: Title
56
+ const headerRe = /^##\s+(S\d+):\s*(.+)/gm;
57
+ while ((match = headerRe.exec(content)) !== null) {
58
+ results.push({
59
+ id: match[1],
60
+ title: match[2].trim(),
61
+ risk: 'medium',
62
+ depends: [],
63
+ done: false,
64
+ demo: '',
65
+ });
66
+ }
67
+ return results;
68
+ }
69
+ // ---------------------------------------------------------------------------
70
+ // PLAN.md task parser
71
+ // ---------------------------------------------------------------------------
72
+ function parseSlicePlanTasks(content) {
73
+ const results = [];
74
+ // Checkbox format: - [x] **T01: Title** — description
75
+ const taskRe = /^-\s+\[([ xX])\]\s+\*\*(T\d+):\s*(.+?)\*\*/gm;
76
+ let match;
77
+ while ((match = taskRe.exec(content)) !== null) {
78
+ results.push({
79
+ id: match[2],
80
+ title: match[3].trim(),
81
+ done: match[1] !== ' ',
82
+ });
83
+ }
84
+ if (results.length > 0)
85
+ return results;
86
+ // H3 format: ### T01: Title
87
+ const h3Re = /^###\s+(T\d+):\s*(.+)/gm;
88
+ while ((match = h3Re.exec(content)) !== null) {
89
+ results.push({
90
+ id: match[1],
91
+ title: match[2].trim(),
92
+ done: false,
93
+ });
94
+ }
95
+ return results;
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Milestone title from CONTEXT.md or ROADMAP.md H1
99
+ // ---------------------------------------------------------------------------
100
+ function readMilestoneTitle(gsdRoot, mid) {
101
+ const ctxPath = resolveMilestoneFile(gsdRoot, mid, 'CONTEXT');
102
+ if (ctxPath && existsSync(ctxPath)) {
103
+ const content = readFileSync(ctxPath, 'utf-8');
104
+ const h1 = content.match(/^#\s+(?:M\d+:?\s*)?(.+)/m);
105
+ if (h1)
106
+ return h1[1].trim();
107
+ }
108
+ const roadmapPath = resolveMilestoneFile(gsdRoot, mid, 'ROADMAP');
109
+ if (roadmapPath && existsSync(roadmapPath)) {
110
+ const content = readFileSync(roadmapPath, 'utf-8');
111
+ const h1 = content.match(/^#\s+(?:M\d+:?\s*)?(.+)/m);
112
+ if (h1)
113
+ return h1[1].trim();
114
+ }
115
+ return mid;
116
+ }
117
+ function readVision(gsdRoot, mid) {
118
+ const roadmapPath = resolveMilestoneFile(gsdRoot, mid, 'ROADMAP');
119
+ if (!roadmapPath || !existsSync(roadmapPath))
120
+ return '';
121
+ const content = readFileSync(roadmapPath, 'utf-8');
122
+ const section = content.match(/## Vision\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
123
+ return section ? section[1].trim() : '';
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Public API
127
+ // ---------------------------------------------------------------------------
128
+ export function readRoadmap(projectDir, filterMilestoneId) {
129
+ const gsd = resolveGsdRoot(projectDir);
130
+ let milestoneIds = findMilestoneIds(gsd);
131
+ if (filterMilestoneId) {
132
+ milestoneIds = milestoneIds.filter((id) => id === filterMilestoneId);
133
+ }
134
+ const milestones = [];
135
+ for (const mid of milestoneIds) {
136
+ const title = readMilestoneTitle(gsd, mid);
137
+ const vision = readVision(gsd, mid);
138
+ const summaryPath = resolveMilestoneFile(gsd, mid, 'SUMMARY');
139
+ const hasSummary = summaryPath !== null && existsSync(summaryPath);
140
+ const roadmapPath = resolveMilestoneFile(gsd, mid, 'ROADMAP');
141
+ let roadmapSlices = [];
142
+ if (roadmapPath && existsSync(roadmapPath)) {
143
+ roadmapSlices = parseRoadmapTable(readFileSync(roadmapPath, 'utf-8'));
144
+ }
145
+ const fsSliceIds = findSliceIds(gsd, mid);
146
+ const sliceIdSet = new Set([
147
+ ...roadmapSlices.map((s) => s.id),
148
+ ...fsSliceIds,
149
+ ]);
150
+ const slices = [];
151
+ for (const sid of Array.from(sliceIdSet).sort()) {
152
+ const roadmapEntry = roadmapSlices.find((s) => s.id === sid);
153
+ const taskFiles = findTaskFiles(gsd, mid, sid);
154
+ const planPath = resolveSliceFile(gsd, mid, sid, 'PLAN');
155
+ let planTasks = [];
156
+ if (planPath && existsSync(planPath)) {
157
+ planTasks = parseSlicePlanTasks(readFileSync(planPath, 'utf-8'));
158
+ }
159
+ const tasks = [];
160
+ const seenIds = new Set();
161
+ for (const pt of planTasks) {
162
+ const fsTask = taskFiles.find((t) => t.id === pt.id);
163
+ const done = fsTask?.hasSummary ?? pt.done;
164
+ tasks.push({ id: pt.id, title: pt.title, status: done ? 'done' : 'pending' });
165
+ seenIds.add(pt.id);
166
+ }
167
+ for (const ft of taskFiles) {
168
+ if (seenIds.has(ft.id))
169
+ continue;
170
+ tasks.push({ id: ft.id, title: ft.id, status: ft.hasSummary ? 'done' : 'pending' });
171
+ }
172
+ const allDone = tasks.length > 0 && tasks.every((t) => t.status === 'done');
173
+ const anyDone = tasks.some((t) => t.status === 'done');
174
+ const sliceStatus = allDone ? 'done' : anyDone ? 'active' : 'pending';
175
+ slices.push({
176
+ id: sid,
177
+ title: roadmapEntry?.title ?? sid,
178
+ status: sliceStatus,
179
+ risk: roadmapEntry?.risk ?? 'medium',
180
+ depends: roadmapEntry?.depends ?? [],
181
+ demo: roadmapEntry?.demo ?? '',
182
+ tasks,
183
+ });
184
+ }
185
+ const allSlicesDone = slices.length > 0 && slices.every((s) => s.status === 'done');
186
+ const anySliceActive = slices.some((s) => s.status === 'active' || s.status === 'done');
187
+ const milestoneStatus = hasSummary
188
+ ? 'done'
189
+ : allSlicesDone ? 'done' : anySliceActive ? 'active' : 'pending';
190
+ milestones.push({ id: mid, title, status: milestoneStatus, vision, slices });
191
+ }
192
+ return { milestones };
193
+ }
194
+ //# sourceMappingURL=roadmap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap.js","sourceRoot":"","sources":["../../src/readers/roadmap.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD MCP Server — roadmap structure reader\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport {\n resolveGsdRoot,\n findMilestoneIds,\n resolveMilestoneFile,\n findSliceIds,\n resolveSliceFile,\n findTaskFiles,\n} from './paths.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TaskInfo {\n id: string;\n title: string;\n status: 'done' | 'pending';\n}\n\nexport interface SliceInfo {\n id: string;\n title: string;\n status: 'done' | 'active' | 'pending';\n risk: string;\n depends: string[];\n demo: string;\n tasks: TaskInfo[];\n}\n\nexport interface MilestoneInfo {\n id: string;\n title: string;\n status: 'done' | 'active' | 'pending' | 'parked';\n vision: string;\n slices: SliceInfo[];\n}\n\nexport interface RoadmapResult {\n milestones: MilestoneInfo[];\n}\n\n// ---------------------------------------------------------------------------\n// ROADMAP.md table parser\n// ---------------------------------------------------------------------------\n\nfunction parseRoadmapTable(content: string): Array<{\n id: string; title: string; risk: string; depends: string[]; done: boolean; demo: string;\n}> {\n const results: Array<{\n id: string; title: string; risk: string; depends: string[]; done: boolean; demo: string;\n }> = [];\n\n // Try table format first: | S01 | Title | risk | depends | done-icon | demo |\n const tableSection = content.match(/## (?:Slice[s]?|Slice Overview|Slice Table)\\s*\\n([\\s\\S]*?)(?=\\n##|\\n$|$)/i);\n if (tableSection) {\n const lines = tableSection[1].split('\\n');\n for (const line of lines) {\n if (!line.includes('|')) continue;\n const cells = line.split('|').map((c) => c.trim()).filter(Boolean);\n if (cells.length < 4) continue;\n if (cells[0] === 'ID' || cells[0].startsWith('--')) continue;\n\n const id = cells[0].match(/S\\d+/)?.[0];\n if (!id) continue;\n\n const done = cells.some((c) => c === '\\u2611' || c === '\\u2705' || c.toLowerCase() === 'done');\n const depends = (cells[3] ?? '').replace(/\\u2014/g, '').split(',').map((d) => d.trim()).filter(Boolean);\n\n results.push({\n id,\n title: cells[1] ?? '',\n risk: cells[2] ?? 'medium',\n depends,\n done,\n demo: cells[5] ?? '',\n });\n }\n if (results.length > 0) return results;\n }\n\n // Try checkbox format: - [x] **S01: Title** `risk:high` `depends:[S01]`\n const checkboxRe = /^-\\s+\\[([ xX])\\]\\s+\\*\\*(S\\d+):\\s*(.+?)\\*\\*(?:.*?`risk:(\\w+)`)?(?:.*?`depends:\\[([^\\]]*)\\]`)?/gm;\n let match: RegExpExecArray | null;\n while ((match = checkboxRe.exec(content)) !== null) {\n const [, checked, id, title, risk, deps] = match;\n results.push({\n id,\n title: title.trim(),\n risk: risk ?? 'medium',\n depends: deps ? deps.split(',').map((d) => d.trim()).filter(Boolean) : [],\n done: checked !== ' ',\n demo: '',\n });\n }\n if (results.length > 0) return results;\n\n // Try prose headers: ## S01: Title\n const headerRe = /^##\\s+(S\\d+):\\s*(.+)/gm;\n while ((match = headerRe.exec(content)) !== null) {\n results.push({\n id: match[1],\n title: match[2].trim(),\n risk: 'medium',\n depends: [],\n done: false,\n demo: '',\n });\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// PLAN.md task parser\n// ---------------------------------------------------------------------------\n\nfunction parseSlicePlanTasks(content: string): Array<{ id: string; title: string; done: boolean }> {\n const results: Array<{ id: string; title: string; done: boolean }> = [];\n\n // Checkbox format: - [x] **T01: Title** — description\n const taskRe = /^-\\s+\\[([ xX])\\]\\s+\\*\\*(T\\d+):\\s*(.+?)\\*\\*/gm;\n let match: RegExpExecArray | null;\n while ((match = taskRe.exec(content)) !== null) {\n results.push({\n id: match[2],\n title: match[3].trim(),\n done: match[1] !== ' ',\n });\n }\n if (results.length > 0) return results;\n\n // H3 format: ### T01: Title\n const h3Re = /^###\\s+(T\\d+):\\s*(.+)/gm;\n while ((match = h3Re.exec(content)) !== null) {\n results.push({\n id: match[1],\n title: match[2].trim(),\n done: false,\n });\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Milestone title from CONTEXT.md or ROADMAP.md H1\n// ---------------------------------------------------------------------------\n\nfunction readMilestoneTitle(gsdRoot: string, mid: string): string {\n const ctxPath = resolveMilestoneFile(gsdRoot, mid, 'CONTEXT');\n if (ctxPath && existsSync(ctxPath)) {\n const content = readFileSync(ctxPath, 'utf-8');\n const h1 = content.match(/^#\\s+(?:M\\d+:?\\s*)?(.+)/m);\n if (h1) return h1[1].trim();\n }\n\n const roadmapPath = resolveMilestoneFile(gsdRoot, mid, 'ROADMAP');\n if (roadmapPath && existsSync(roadmapPath)) {\n const content = readFileSync(roadmapPath, 'utf-8');\n const h1 = content.match(/^#\\s+(?:M\\d+:?\\s*)?(.+)/m);\n if (h1) return h1[1].trim();\n }\n\n return mid;\n}\n\nfunction readVision(gsdRoot: string, mid: string): string {\n const roadmapPath = resolveMilestoneFile(gsdRoot, mid, 'ROADMAP');\n if (!roadmapPath || !existsSync(roadmapPath)) return '';\n\n const content = readFileSync(roadmapPath, 'utf-8');\n const section = content.match(/## Vision\\s*\\n([\\s\\S]*?)(?=\\n##|\\n$|$)/i);\n return section ? section[1].trim() : '';\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function readRoadmap(projectDir: string, filterMilestoneId?: string): RoadmapResult {\n const gsd = resolveGsdRoot(projectDir);\n let milestoneIds = findMilestoneIds(gsd);\n\n if (filterMilestoneId) {\n milestoneIds = milestoneIds.filter((id) => id === filterMilestoneId);\n }\n\n const milestones: MilestoneInfo[] = [];\n\n for (const mid of milestoneIds) {\n const title = readMilestoneTitle(gsd, mid);\n const vision = readVision(gsd, mid);\n\n const summaryPath = resolveMilestoneFile(gsd, mid, 'SUMMARY');\n const hasSummary = summaryPath !== null && existsSync(summaryPath);\n\n const roadmapPath = resolveMilestoneFile(gsd, mid, 'ROADMAP');\n let roadmapSlices: ReturnType<typeof parseRoadmapTable> = [];\n if (roadmapPath && existsSync(roadmapPath)) {\n roadmapSlices = parseRoadmapTable(readFileSync(roadmapPath, 'utf-8'));\n }\n\n const fsSliceIds = findSliceIds(gsd, mid);\n const sliceIdSet = new Set([\n ...roadmapSlices.map((s) => s.id),\n ...fsSliceIds,\n ]);\n\n const slices: SliceInfo[] = [];\n for (const sid of Array.from(sliceIdSet).sort()) {\n const roadmapEntry = roadmapSlices.find((s) => s.id === sid);\n const taskFiles = findTaskFiles(gsd, mid, sid);\n\n const planPath = resolveSliceFile(gsd, mid, sid, 'PLAN');\n let planTasks: ReturnType<typeof parseSlicePlanTasks> = [];\n if (planPath && existsSync(planPath)) {\n planTasks = parseSlicePlanTasks(readFileSync(planPath, 'utf-8'));\n }\n\n const tasks: TaskInfo[] = [];\n const seenIds = new Set<string>();\n\n for (const pt of planTasks) {\n const fsTask = taskFiles.find((t) => t.id === pt.id);\n const done = fsTask?.hasSummary ?? pt.done;\n tasks.push({ id: pt.id, title: pt.title, status: done ? 'done' : 'pending' });\n seenIds.add(pt.id);\n }\n for (const ft of taskFiles) {\n if (seenIds.has(ft.id)) continue;\n tasks.push({ id: ft.id, title: ft.id, status: ft.hasSummary ? 'done' : 'pending' });\n }\n\n const allDone = tasks.length > 0 && tasks.every((t) => t.status === 'done');\n const anyDone = tasks.some((t) => t.status === 'done');\n const sliceStatus: SliceInfo['status'] = allDone ? 'done' : anyDone ? 'active' : 'pending';\n\n slices.push({\n id: sid,\n title: roadmapEntry?.title ?? sid,\n status: sliceStatus,\n risk: roadmapEntry?.risk ?? 'medium',\n depends: roadmapEntry?.depends ?? [],\n demo: roadmapEntry?.demo ?? '',\n tasks,\n });\n }\n\n const allSlicesDone = slices.length > 0 && slices.every((s) => s.status === 'done');\n const anySliceActive = slices.some((s) => s.status === 'active' || s.status === 'done');\n const milestoneStatus: MilestoneInfo['status'] = hasSummary\n ? 'done'\n : allSlicesDone ? 'done' : anySliceActive ? 'active' : 'pending';\n\n milestones.push({ id: mid, title, status: milestoneStatus, vision, slices });\n }\n\n return { milestones };\n}\n"]}
@@ -0,0 +1,43 @@
1
+ export interface ProgressResult {
2
+ activeMilestone: {
3
+ id: string;
4
+ title: string;
5
+ } | null;
6
+ activeSlice: {
7
+ id: string;
8
+ title: string;
9
+ } | null;
10
+ activeTask: {
11
+ id: string;
12
+ title: string;
13
+ } | null;
14
+ phase: string;
15
+ milestones: {
16
+ total: number;
17
+ done: number;
18
+ active: number;
19
+ pending: number;
20
+ parked: number;
21
+ };
22
+ slices: {
23
+ total: number;
24
+ done: number;
25
+ active: number;
26
+ pending: number;
27
+ };
28
+ tasks: {
29
+ total: number;
30
+ done: number;
31
+ pending: number;
32
+ };
33
+ requirements: {
34
+ active: number;
35
+ validated: number;
36
+ deferred: number;
37
+ outOfScope: number;
38
+ } | null;
39
+ blockers: string[];
40
+ nextAction: string;
41
+ }
42
+ export declare function readProgress(projectDir: string): ProgressResult;
43
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/readers/state.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACtD,WAAW,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAClD,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjG,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAkID,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CA+D/D"}
@@ -0,0 +1,184 @@
1
+ // GSD MCP Server — project state reader
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { resolveGsdRoot, resolveRootFile, findMilestoneIds, findSliceIds, findTaskFiles, } from './paths.js';
5
+ // ---------------------------------------------------------------------------
6
+ // STATE.md parser
7
+ // ---------------------------------------------------------------------------
8
+ function parseBoldField(content, label) {
9
+ const re = new RegExp(`\\*\\*${label}:\\*\\*\\s*(.+)`, 'i');
10
+ const m = content.match(re);
11
+ return m ? m[1].trim() : null;
12
+ }
13
+ function parseActiveRef(value) {
14
+ if (!value || value.toLowerCase() === 'none' || value === '—')
15
+ return null;
16
+ // "M001: Flight Simulator" or "M001"
17
+ const m = value.match(/^(M\d+|S\d+|T\d+):?\s*(.*)/);
18
+ if (m)
19
+ return { id: m[1], title: m[2] || m[1] };
20
+ return { id: value, title: value };
21
+ }
22
+ function parsePhase(value) {
23
+ if (!value)
24
+ return 'unknown';
25
+ const lower = value.toLowerCase().trim();
26
+ if (lower.includes('research') || lower.includes('discuss'))
27
+ return 'research';
28
+ if (lower.includes('plan'))
29
+ return 'plan';
30
+ if (lower.includes('execut'))
31
+ return 'execute';
32
+ if (lower.includes('complete') || lower.includes('done'))
33
+ return 'complete';
34
+ return lower;
35
+ }
36
+ function parseRequirementsLine(value) {
37
+ if (!value)
38
+ return null;
39
+ const active = value.match(/(\d+)\s*active/i);
40
+ const validated = value.match(/(\d+)\s*validated/i);
41
+ const deferred = value.match(/(\d+)\s*deferred/i);
42
+ const outOfScope = value.match(/(\d+)\s*out.of.scope/i);
43
+ if (!active && !validated && !deferred && !outOfScope)
44
+ return null;
45
+ return {
46
+ active: active ? parseInt(active[1], 10) : 0,
47
+ validated: validated ? parseInt(validated[1], 10) : 0,
48
+ deferred: deferred ? parseInt(deferred[1], 10) : 0,
49
+ outOfScope: outOfScope ? parseInt(outOfScope[1], 10) : 0,
50
+ };
51
+ }
52
+ function parseBlockers(content) {
53
+ const section = content.match(/## Blockers\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
54
+ if (!section)
55
+ return [];
56
+ return section[1]
57
+ .split('\n')
58
+ .map((l) => l.replace(/^[-*]\s*/, '').trim())
59
+ .filter(Boolean);
60
+ }
61
+ function parseNextAction(content) {
62
+ const section = content.match(/## Next Action\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
63
+ if (!section)
64
+ return '';
65
+ return section[1].trim().split('\n')[0] || '';
66
+ }
67
+ function parseMilestoneRegistry(content) {
68
+ const section = content.match(/## Milestone Registry\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
69
+ if (!section)
70
+ return [];
71
+ const entries = [];
72
+ for (const line of section[1].split('\n')) {
73
+ const m = line.match(/[-*]\s*(☑|✅|🔄|⬜|⏸)\s*\*\*(M\d+):\*\*/);
74
+ if (!m)
75
+ continue;
76
+ const [, icon, id] = m;
77
+ let status = 'pending';
78
+ if (icon === '☑' || icon === '✅')
79
+ status = 'done';
80
+ else if (icon === '🔄')
81
+ status = 'active';
82
+ else if (icon === '⏸')
83
+ status = 'parked';
84
+ entries.push({ id, status });
85
+ }
86
+ return entries;
87
+ }
88
+ // ---------------------------------------------------------------------------
89
+ // Count slices/tasks by walking filesystem
90
+ // ---------------------------------------------------------------------------
91
+ function countSlicesAndTasks(gsdRoot, milestoneIds) {
92
+ let sliceTotal = 0, sliceDone = 0, sliceActive = 0;
93
+ let taskTotal = 0, taskDone = 0;
94
+ for (const mid of milestoneIds) {
95
+ const sliceIds = findSliceIds(gsdRoot, mid);
96
+ sliceTotal += sliceIds.length;
97
+ for (const sid of sliceIds) {
98
+ const tasks = findTaskFiles(gsdRoot, mid, sid);
99
+ taskTotal += tasks.length;
100
+ const allDone = tasks.length > 0 && tasks.every((t) => t.hasSummary);
101
+ const anyDone = tasks.some((t) => t.hasSummary);
102
+ if (allDone) {
103
+ sliceDone++;
104
+ taskDone += tasks.length;
105
+ }
106
+ else {
107
+ if (anyDone)
108
+ sliceActive++;
109
+ taskDone += tasks.filter((t) => t.hasSummary).length;
110
+ }
111
+ }
112
+ }
113
+ return {
114
+ slices: {
115
+ total: sliceTotal,
116
+ done: sliceDone,
117
+ active: sliceActive,
118
+ pending: sliceTotal - sliceDone - sliceActive,
119
+ },
120
+ tasks: { total: taskTotal, done: taskDone, pending: taskTotal - taskDone },
121
+ };
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // Public API
125
+ // ---------------------------------------------------------------------------
126
+ export function readProgress(projectDir) {
127
+ const gsd = resolveGsdRoot(projectDir);
128
+ const statePath = resolveRootFile(gsd, 'STATE.md');
129
+ // Defaults
130
+ const result = {
131
+ activeMilestone: null,
132
+ activeSlice: null,
133
+ activeTask: null,
134
+ phase: 'unknown',
135
+ milestones: { total: 0, done: 0, active: 0, pending: 0, parked: 0 },
136
+ slices: { total: 0, done: 0, active: 0, pending: 0 },
137
+ tasks: { total: 0, done: 0, pending: 0 },
138
+ requirements: null,
139
+ blockers: [],
140
+ nextAction: '',
141
+ };
142
+ if (!existsSync(statePath)) {
143
+ // No STATE.md — derive from filesystem only
144
+ const milestoneIds = findMilestoneIds(gsd);
145
+ result.milestones.total = milestoneIds.length;
146
+ result.milestones.pending = milestoneIds.length;
147
+ const counts = countSlicesAndTasks(gsd, milestoneIds);
148
+ result.slices = counts.slices;
149
+ result.tasks = counts.tasks;
150
+ return result;
151
+ }
152
+ const content = readFileSync(statePath, 'utf-8');
153
+ // Parse STATE.md fields
154
+ result.activeMilestone = parseActiveRef(parseBoldField(content, 'Active Milestone'));
155
+ result.activeSlice = parseActiveRef(parseBoldField(content, 'Active Slice'));
156
+ result.activeTask = parseActiveRef(parseBoldField(content, 'Active Task'));
157
+ result.phase = parsePhase(parseBoldField(content, 'Phase'));
158
+ result.requirements = parseRequirementsLine(parseBoldField(content, 'Requirements Status'));
159
+ result.blockers = parseBlockers(content);
160
+ result.nextAction = parseNextAction(content);
161
+ // Milestone counts from registry
162
+ const registry = parseMilestoneRegistry(content);
163
+ if (registry.length > 0) {
164
+ result.milestones.total = registry.length;
165
+ result.milestones.done = registry.filter((e) => e.status === 'done').length;
166
+ result.milestones.active = registry.filter((e) => e.status === 'active').length;
167
+ result.milestones.parked = registry.filter((e) => e.status === 'parked').length;
168
+ result.milestones.pending = registry.length -
169
+ result.milestones.done - result.milestones.active - result.milestones.parked;
170
+ }
171
+ else {
172
+ // Fallback: count directories
173
+ const milestoneIds = findMilestoneIds(gsd);
174
+ result.milestones.total = milestoneIds.length;
175
+ result.milestones.pending = milestoneIds.length;
176
+ }
177
+ // Slice/task counts from filesystem
178
+ const milestoneIds = findMilestoneIds(gsd);
179
+ const counts = countSlicesAndTasks(gsd, milestoneIds);
180
+ result.slices = counts.slices;
181
+ result.tasks = counts.tasks;
182
+ return result;
183
+ }
184
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/readers/state.ts"],"names":[],"mappings":"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","sourcesContent":["// GSD MCP Server — project state reader\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport {\n resolveGsdRoot,\n resolveRootFile,\n findMilestoneIds,\n resolveMilestoneDir,\n resolveMilestoneFile,\n findSliceIds,\n findTaskFiles,\n} from './paths.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ProgressResult {\n activeMilestone: { id: string; title: string } | null;\n activeSlice: { id: string; title: string } | null;\n activeTask: { id: string; title: string } | null;\n phase: string;\n milestones: { total: number; done: number; active: number; pending: number; parked: number };\n slices: { total: number; done: number; active: number; pending: number };\n tasks: { total: number; done: number; pending: number };\n requirements: { active: number; validated: number; deferred: number; outOfScope: number } | null;\n blockers: string[];\n nextAction: string;\n}\n\n// ---------------------------------------------------------------------------\n// STATE.md parser\n// ---------------------------------------------------------------------------\n\nfunction parseBoldField(content: string, label: string): string | null {\n const re = new RegExp(`\\\\*\\\\*${label}:\\\\*\\\\*\\\\s*(.+)`, 'i');\n const m = content.match(re);\n return m ? m[1].trim() : null;\n}\n\nfunction parseActiveRef(value: string | null): { id: string; title: string } | null {\n if (!value || value.toLowerCase() === 'none' || value === '—') return null;\n // \"M001: Flight Simulator\" or \"M001\"\n const m = value.match(/^(M\\d+|S\\d+|T\\d+):?\\s*(.*)/);\n if (m) return { id: m[1], title: m[2] || m[1] };\n return { id: value, title: value };\n}\n\nfunction parsePhase(value: string | null): string {\n if (!value) return 'unknown';\n const lower = value.toLowerCase().trim();\n if (lower.includes('research') || lower.includes('discuss')) return 'research';\n if (lower.includes('plan')) return 'plan';\n if (lower.includes('execut')) return 'execute';\n if (lower.includes('complete') || lower.includes('done')) return 'complete';\n return lower;\n}\n\nfunction parseRequirementsLine(value: string | null): ProgressResult['requirements'] | null {\n if (!value) return null;\n const active = value.match(/(\\d+)\\s*active/i);\n const validated = value.match(/(\\d+)\\s*validated/i);\n const deferred = value.match(/(\\d+)\\s*deferred/i);\n const outOfScope = value.match(/(\\d+)\\s*out.of.scope/i);\n if (!active && !validated && !deferred && !outOfScope) return null;\n return {\n active: active ? parseInt(active[1], 10) : 0,\n validated: validated ? parseInt(validated[1], 10) : 0,\n deferred: deferred ? parseInt(deferred[1], 10) : 0,\n outOfScope: outOfScope ? parseInt(outOfScope[1], 10) : 0,\n };\n}\n\nfunction parseBlockers(content: string): string[] {\n const section = content.match(/## Blockers\\s*\\n([\\s\\S]*?)(?=\\n##|\\n$|$)/i);\n if (!section) return [];\n return section[1]\n .split('\\n')\n .map((l) => l.replace(/^[-*]\\s*/, '').trim())\n .filter(Boolean);\n}\n\nfunction parseNextAction(content: string): string {\n const section = content.match(/## Next Action\\s*\\n([\\s\\S]*?)(?=\\n##|\\n$|$)/i);\n if (!section) return '';\n return section[1].trim().split('\\n')[0] || '';\n}\n\n// ---------------------------------------------------------------------------\n// Milestone registry from STATE.md\n// ---------------------------------------------------------------------------\n\ninterface RegistryEntry { id: string; status: 'done' | 'active' | 'pending' | 'parked' }\n\nfunction parseMilestoneRegistry(content: string): RegistryEntry[] {\n const section = content.match(/## Milestone Registry\\s*\\n([\\s\\S]*?)(?=\\n##|\\n$|$)/i);\n if (!section) return [];\n const entries: RegistryEntry[] = [];\n for (const line of section[1].split('\\n')) {\n const m = line.match(/[-*]\\s*(☑|✅|🔄|⬜|⏸)\\s*\\*\\*(M\\d+):\\*\\*/);\n if (!m) continue;\n const [, icon, id] = m;\n let status: RegistryEntry['status'] = 'pending';\n if (icon === '☑' || icon === '✅') status = 'done';\n else if (icon === '🔄') status = 'active';\n else if (icon === '⏸') status = 'parked';\n entries.push({ id, status });\n }\n return entries;\n}\n\n// ---------------------------------------------------------------------------\n// Count slices/tasks by walking filesystem\n// ---------------------------------------------------------------------------\n\nfunction countSlicesAndTasks(gsdRoot: string, milestoneIds: string[]): {\n slices: ProgressResult['slices'];\n tasks: ProgressResult['tasks'];\n} {\n let sliceTotal = 0, sliceDone = 0, sliceActive = 0;\n let taskTotal = 0, taskDone = 0;\n\n for (const mid of milestoneIds) {\n const sliceIds = findSliceIds(gsdRoot, mid);\n sliceTotal += sliceIds.length;\n\n for (const sid of sliceIds) {\n const tasks = findTaskFiles(gsdRoot, mid, sid);\n taskTotal += tasks.length;\n\n const allDone = tasks.length > 0 && tasks.every((t) => t.hasSummary);\n const anyDone = tasks.some((t) => t.hasSummary);\n\n if (allDone) {\n sliceDone++;\n taskDone += tasks.length;\n } else {\n if (anyDone) sliceActive++;\n taskDone += tasks.filter((t) => t.hasSummary).length;\n }\n }\n }\n\n return {\n slices: {\n total: sliceTotal,\n done: sliceDone,\n active: sliceActive,\n pending: sliceTotal - sliceDone - sliceActive,\n },\n tasks: { total: taskTotal, done: taskDone, pending: taskTotal - taskDone },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nexport function readProgress(projectDir: string): ProgressResult {\n const gsd = resolveGsdRoot(projectDir);\n const statePath = resolveRootFile(gsd, 'STATE.md');\n\n // Defaults\n const result: ProgressResult = {\n activeMilestone: null,\n activeSlice: null,\n activeTask: null,\n phase: 'unknown',\n milestones: { total: 0, done: 0, active: 0, pending: 0, parked: 0 },\n slices: { total: 0, done: 0, active: 0, pending: 0 },\n tasks: { total: 0, done: 0, pending: 0 },\n requirements: null,\n blockers: [],\n nextAction: '',\n };\n\n if (!existsSync(statePath)) {\n // No STATE.md — derive from filesystem only\n const milestoneIds = findMilestoneIds(gsd);\n result.milestones.total = milestoneIds.length;\n result.milestones.pending = milestoneIds.length;\n const counts = countSlicesAndTasks(gsd, milestoneIds);\n result.slices = counts.slices;\n result.tasks = counts.tasks;\n return result;\n }\n\n const content = readFileSync(statePath, 'utf-8');\n\n // Parse STATE.md fields\n result.activeMilestone = parseActiveRef(parseBoldField(content, 'Active Milestone'));\n result.activeSlice = parseActiveRef(parseBoldField(content, 'Active Slice'));\n result.activeTask = parseActiveRef(parseBoldField(content, 'Active Task'));\n result.phase = parsePhase(parseBoldField(content, 'Phase'));\n result.requirements = parseRequirementsLine(parseBoldField(content, 'Requirements Status'));\n result.blockers = parseBlockers(content);\n result.nextAction = parseNextAction(content);\n\n // Milestone counts from registry\n const registry = parseMilestoneRegistry(content);\n if (registry.length > 0) {\n result.milestones.total = registry.length;\n result.milestones.done = registry.filter((e) => e.status === 'done').length;\n result.milestones.active = registry.filter((e) => e.status === 'active').length;\n result.milestones.parked = registry.filter((e) => e.status === 'parked').length;\n result.milestones.pending = registry.length -\n result.milestones.done - result.milestones.active - result.milestones.parked;\n } else {\n // Fallback: count directories\n const milestoneIds = findMilestoneIds(gsd);\n result.milestones.total = milestoneIds.length;\n result.milestones.pending = milestoneIds.length;\n }\n\n // Slice/task counts from filesystem\n const milestoneIds = findMilestoneIds(gsd);\n const counts = countSlicesAndTasks(gsd, milestoneIds);\n result.slices = counts.slices;\n result.tasks = counts.tasks;\n\n return result;\n}\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MCP Server — registers GSD orchestration, project-state, and workflow tools.
3
+ *
4
+ * Session tools (6): gsd_execute, gsd_status, gsd_result, gsd_cancel, gsd_query, gsd_resolve_blocker
5
+ * Read-only tools (6): gsd_progress, gsd_roadmap, gsd_history, gsd_doctor, gsd_captures, gsd_knowledge
6
+ * Workflow tools (17): planning, replanning, completion, validation, reassessment, gate result, and milestone status tools
7
+ *
8
+ * Uses dynamic imports for @modelcontextprotocol/sdk because TS Node16
9
+ * cannot resolve the SDK's subpath exports statically (same pattern as
10
+ * src/mcp-server.ts in the main package).
11
+ */
12
+ import type { SessionManager } from './session-manager.js';
13
+ interface McpServerInstance {
14
+ tool(name: string, description: string, params: Record<string, unknown>, handler: (args: Record<string, unknown>) => Promise<unknown>): unknown;
15
+ connect(transport: unknown): Promise<void>;
16
+ close(): Promise<void>;
17
+ }
18
+ /**
19
+ * Create and configure an MCP server with session, read-only, and workflow tools.
20
+ *
21
+ * Returns the McpServer instance — call `connect(transport)` to start serving.
22
+ * Uses dynamic imports for the MCP SDK to avoid TS subpath resolution issues.
23
+ */
24
+ export declare function createMcpServer(sessionManager: SessionManager): Promise<{
25
+ server: McpServerInstance;
26
+ }>;
27
+ export {};
28
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AA6F3D,UAAU,iBAAiB;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAChJ,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAMD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC;IAC7E,MAAM,EAAE,iBAAiB,CAAC;CAC3B,CAAC,CA8RD"}