@jakkrichm/create-nexus-devflow 2.13.0 → 2.15.0

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 (65) hide show
  1. package/README.md +4 -4
  2. package/dist/bin/create-nexus-devflow.d.ts +1 -1
  3. package/dist/bin/create-nexus-devflow.js +4 -3
  4. package/dist/bin/create-nexus-devflow.js.map +1 -1
  5. package/dist/lib/branch-context.d.ts +1 -0
  6. package/dist/lib/branch-context.js +8 -3
  7. package/dist/lib/branch-context.js.map +1 -1
  8. package/dist/lib/dashboard-page.d.ts +5 -2
  9. package/dist/lib/dashboard-page.js +7 -388
  10. package/dist/lib/dashboard-page.js.map +1 -1
  11. package/dist/lib/dashboard-snapshot.d.ts +22 -3
  12. package/dist/lib/dashboard-snapshot.js +154 -3
  13. package/dist/lib/dashboard-snapshot.js.map +1 -1
  14. package/dist/lib/delivery-lifecycle-engine.d.ts +1 -1
  15. package/dist/lib/delivery-lifecycle-engine.js +1 -1
  16. package/dist/lib/delivery-lifecycle-engine.js.map +1 -1
  17. package/dist/lib/mcp-server-engine.d.ts +57 -0
  18. package/dist/lib/mcp-server-engine.js +678 -0
  19. package/dist/lib/mcp-server-engine.js.map +1 -0
  20. package/dist/lib/mcp.d.ts +15 -27
  21. package/dist/lib/mcp.js +20 -646
  22. package/dist/lib/mcp.js.map +1 -1
  23. package/dist/lib/project-status-engine.d.ts +110 -0
  24. package/dist/lib/project-status-engine.js +493 -0
  25. package/dist/lib/project-status-engine.js.map +1 -0
  26. package/dist/lib/skill-manager.d.ts +2 -82
  27. package/dist/lib/skill-manager.js +15 -856
  28. package/dist/lib/skill-manager.js.map +1 -1
  29. package/dist/lib/skill-registry-engine.d.ts +121 -0
  30. package/dist/lib/skill-registry-engine.js +824 -0
  31. package/dist/lib/skill-registry-engine.js.map +1 -0
  32. package/dist/lib/status.d.ts +1 -90
  33. package/dist/lib/status.js +7 -467
  34. package/dist/lib/status.js.map +1 -1
  35. package/dist/lib/studio-view-renderer.d.ts +17 -1
  36. package/dist/lib/studio-view-renderer.js +855 -3
  37. package/dist/lib/studio-view-renderer.js.map +1 -1
  38. package/dist/lib/tooling/commands/scan-doc-contract.js +1 -0
  39. package/dist/lib/tooling/commands/scan-doc-contract.js.map +1 -1
  40. package/dist/lib/update.js +1 -1
  41. package/dist/lib/update.js.map +1 -1
  42. package/package.json +1 -1
  43. package/template/.agents/skills/complete/SKILL.md +14 -13
  44. package/template/.agents/skills/implement/SKILL.md +36 -2
  45. package/template/.claude/skills/complete/SKILL.md +14 -13
  46. package/template/.claude/skills/implement/SKILL.md +36 -2
  47. package/template/AGENTS.md +1 -1
  48. package/template/devflow/build-plan.md +5 -4
  49. package/template/devflow/context/ai-interaction.md +4 -2
  50. package/template/devflow/docs/USAGE.md +145 -0
  51. package/template/devflow/docs/diagrams/nexus-devflow-architecture.html +14821 -0
  52. package/template/devflow/docs/diagrams/nexus-devflow-architecture.json +80 -0
  53. package/template/devflow/docs/diagrams/nexus-devflow-lifecycle.html +14860 -0
  54. package/template/devflow/docs/diagrams/nexus-devflow-lifecycle.json +88 -0
  55. package/template/devflow/docs/examples/living-spec/adr.example.md +43 -0
  56. package/template/devflow/docs/examples/living-spec/current-feature.example.md +113 -0
  57. package/template/devflow/docs/examples/living-spec/discovery.example.md +45 -0
  58. package/template/devflow/docs/examples/living-spec/ideas.example.md +22 -0
  59. package/template/devflow/docs/governance-rules.md +39 -0
  60. package/template/devflow/docs/manual-review-workflow-spec.md +116 -0
  61. package/template/devflow/docs/markdown-metadata-contract.md +140 -0
  62. package/template/devflow/docs/quickstart.md +119 -0
  63. package/template/devflow/docs/release-process.md +52 -0
  64. package/template/devflow/docs/skill-selection-policy.md +64 -0
  65. package/template/devflow/docs/workflow-surface-map.md +85 -0
@@ -0,0 +1,678 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import readline from "node:readline";
4
+ import { readProjectStatus } from "./status.js";
5
+ import { ActiveContextEngine } from "./active-context-engine.js";
6
+ import { GatekeeperEngine, formatGateReport } from "./gatekeeper.js";
7
+ import { StudioViewRenderer } from "./studio-view-renderer.js";
8
+ import { DashboardStateEngine } from "./dashboard-engine.js";
9
+ import { addIdea, readIdeas } from "./ideas.js";
10
+ import { addFinding, resolveFinding } from "./findings.js";
11
+ import { sliceContextForStage } from "./context-slicer.js";
12
+ import { buildCodeGraph, calculateBlastRadius } from "./code-graph.js";
13
+ import { generateSwarmPlan } from "./swarm-orchestrator.js";
14
+ export const PROTOCOL_VERSION = "2024-11-05";
15
+ export const SERVER_NAME = "nexus-devflow-mcp";
16
+ export const SERVER_VERSION = "2.14.0";
17
+ export const DEVFLOW_MCP_TOOLS = [
18
+ {
19
+ name: "devflow_get_status",
20
+ description: "Get current project status, active living spec, next recommended action, git state, and quality blockers.",
21
+ inputSchema: {
22
+ type: "object",
23
+ properties: {}
24
+ }
25
+ },
26
+ {
27
+ name: "devflow_add_idea",
28
+ description: "Add a new idea to the centralized Idea Inbox (devflow/ideas.md) with automated feasibility and value assessment.",
29
+ inputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ text: {
33
+ type: "string",
34
+ description: "Full description of the idea to capture"
35
+ },
36
+ title: {
37
+ type: "string",
38
+ description: "Optional short title for the idea"
39
+ }
40
+ },
41
+ required: ["text"]
42
+ }
43
+ },
44
+ {
45
+ name: "devflow_record_finding",
46
+ description: "Record a quality defect, security vulnerability, regression risk, or architectural debt finding to findings.md.",
47
+ inputSchema: {
48
+ type: "object",
49
+ properties: {
50
+ title: {
51
+ type: "string",
52
+ description: "Finding title / short description"
53
+ },
54
+ severity: {
55
+ type: "string",
56
+ enum: ["P0", "P1", "P2", "P3"],
57
+ description: "Finding severity level: P0 (Critical Blocker), P1 (High Blocker), P2 (Medium), P3 (Low / Polish)"
58
+ },
59
+ status: {
60
+ type: "string",
61
+ enum: ["open", "unverified"],
62
+ description: "Initial status (default: open)"
63
+ },
64
+ id: {
65
+ type: "string",
66
+ description: "Optional custom ID (e.g. SEC-001)"
67
+ },
68
+ location: {
69
+ type: "string",
70
+ description: "Optional file path and line location"
71
+ }
72
+ },
73
+ required: ["title"]
74
+ }
75
+ },
76
+ {
77
+ name: "devflow_resolve_finding",
78
+ description: "Update the status of an existing finding in findings.md (e.g. mark fixed, closed, accepted, or invalid).",
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ id: {
83
+ type: "string",
84
+ description: "Finding ID to update (e.g. F-001 or SEC-001)"
85
+ },
86
+ status: {
87
+ type: "string",
88
+ enum: ["open", "fixed", "closed", "accepted", "invalid"],
89
+ description: "New status to set"
90
+ }
91
+ },
92
+ required: ["id", "status"]
93
+ }
94
+ },
95
+ {
96
+ name: "devflow_evaluate_gate",
97
+ description: "Run DevFlow Quality Gatekeeper evaluation (checks for unchecked tasks, blocking findings, and verification state).",
98
+ inputSchema: {
99
+ type: "object",
100
+ properties: {
101
+ strict: {
102
+ type: "boolean",
103
+ description: "When true, strictly blocks unverified living specs that need /check"
104
+ }
105
+ }
106
+ }
107
+ },
108
+ {
109
+ name: "devflow_get_context",
110
+ description: "Retrieve content of a DevFlow context document (overview, stage, standards, findings, ideas, or active spec).",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: {
114
+ document: {
115
+ type: "string",
116
+ enum: [
117
+ "overview",
118
+ "stage",
119
+ "standards",
120
+ "findings",
121
+ "ideas",
122
+ "feature",
123
+ "build-plan",
124
+ "project-plan"
125
+ ],
126
+ description: "Name of the DevFlow context document to read"
127
+ }
128
+ },
129
+ required: ["document"]
130
+ }
131
+ },
132
+ {
133
+ name: "devflow_get_sliced_context",
134
+ description: "Retrieve a JIT Stage-Aware sliced context for AI coding (reduces tokens by 60-70% by extracting only relevant rules, tasks, and constraints for the current stage).",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ stage: {
139
+ type: "string",
140
+ enum: ["implement", "check", "explore", "feature", "status"],
141
+ description: "Target DevFlow workflow stage"
142
+ },
143
+ maxTokens: {
144
+ type: "number",
145
+ description: "Optional maximum token budget limit"
146
+ }
147
+ },
148
+ required: ["stage"]
149
+ }
150
+ },
151
+ {
152
+ name: "devflow_detect_drift",
153
+ description: "Detect git drift between actual repository modified files and the Living Spec (identifies undocumented files and phantom files).",
154
+ inputSchema: {
155
+ type: "object",
156
+ properties: {}
157
+ }
158
+ },
159
+ {
160
+ name: "devflow_reconcile_state",
161
+ description: "Automatically reconcile and heal workspace drift (syncs undocumented files into living spec and heals stage alignment).",
162
+ inputSchema: {
163
+ type: "object",
164
+ properties: {
165
+ autoAddUndocumented: {
166
+ type: "boolean",
167
+ description: "When true, automatically adds undocumented files into active living spec (default: true)"
168
+ },
169
+ healStage: {
170
+ type: "boolean",
171
+ description: "When true, heals stage pointer to match active branch (default: true)"
172
+ }
173
+ }
174
+ }
175
+ },
176
+ {
177
+ name: "devflow_get_studio_html",
178
+ description: "Retrieve a self-contained, interactive HTML/CSS/JS Webview Studio for embedding inside IDE panels (VS Code, Cursor, Antigravity) or browsers.",
179
+ inputSchema: {
180
+ type: "object",
181
+ properties: {
182
+ theme: {
183
+ type: "string",
184
+ enum: ["auto", "dark", "light"],
185
+ description: "Theme mode (default: auto)"
186
+ }
187
+ }
188
+ }
189
+ },
190
+ {
191
+ name: "devflow_swarm_plan",
192
+ description: "Generate a multi-agent specialized swarm execution plan (Coder, QA, Security, Architect) with task allocations and context requirements.",
193
+ inputSchema: {
194
+ type: "object",
195
+ properties: {}
196
+ }
197
+ },
198
+ {
199
+ name: "devflow_query_code_graph",
200
+ description: "Query the codebase semantic dependency graph to analyze imports, exports, and calculate the Blast Radius of file changes.",
201
+ inputSchema: {
202
+ type: "object",
203
+ properties: {
204
+ file: {
205
+ type: "string",
206
+ description: "Optional target file path to calculate Blast Radius and impacted dependents"
207
+ }
208
+ }
209
+ }
210
+ }
211
+ ];
212
+ export class McpServerEngine {
213
+ projectRoot;
214
+ tools = new Map();
215
+ activeContextEngine;
216
+ gatekeeperEngine;
217
+ constructor(projectRoot = process.cwd()) {
218
+ this.projectRoot = path.resolve(projectRoot);
219
+ this.activeContextEngine = new ActiveContextEngine(this.projectRoot);
220
+ this.gatekeeperEngine = new GatekeeperEngine(this.projectRoot);
221
+ this.registerDefaultTools();
222
+ }
223
+ registerTool(definition, handler) {
224
+ this.tools.set(definition.name, { definition, handler });
225
+ }
226
+ getTools() {
227
+ return Array.from(this.tools.values()).map((t) => t.definition);
228
+ }
229
+ registerDefaultTools() {
230
+ // 1. devflow_get_status
231
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_get_status"), async () => {
232
+ const status = await readProjectStatus(this.projectRoot);
233
+ return {
234
+ content: [{ type: "text", text: JSON.stringify(status, null, 2) }]
235
+ };
236
+ });
237
+ // 2. devflow_get_active_context (dynamic tool)
238
+ this.registerTool({
239
+ name: "devflow_get_active_context",
240
+ description: "Retrieve structured active task context (workspace paths, task checklist progress, workflow stage, discoveries).",
241
+ inputSchema: { type: "object", properties: {} }
242
+ }, async () => {
243
+ const context = await this.activeContextEngine.getActiveContext();
244
+ return {
245
+ content: [{ type: "text", text: JSON.stringify(context, null, 2) }]
246
+ };
247
+ });
248
+ // 3. devflow_add_idea
249
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_add_idea"), async (args) => {
250
+ const text = typeof args.text === "string" ? args.text.trim() : "";
251
+ if (!text) {
252
+ return {
253
+ content: [{ type: "text", text: "Error: 'text' parameter is required for devflow_add_idea" }],
254
+ isError: true
255
+ };
256
+ }
257
+ const title = typeof args.title === "string" ? args.title.trim() : undefined;
258
+ const created = await addIdea(this.projectRoot, { text, title });
259
+ return {
260
+ content: [
261
+ {
262
+ type: "text",
263
+ text: `Idea added successfully:\n- ID: ${created.id}\n- Title: ${created.title}\n- Value: ${created.value}\n- Feasibility: ${created.feasibility}`
264
+ }
265
+ ]
266
+ };
267
+ });
268
+ // 4. devflow_record_finding
269
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_record_finding"), async (args) => {
270
+ const title = typeof args.title === "string" ? args.title.trim() : "";
271
+ if (!title) {
272
+ return {
273
+ content: [{ type: "text", text: "Error: 'title' parameter is required for devflow_record_finding" }],
274
+ isError: true
275
+ };
276
+ }
277
+ const severity = typeof args.severity === "string" ? args.severity.toUpperCase() : undefined;
278
+ const status = typeof args.status === "string" ? args.status.toLowerCase() : undefined;
279
+ const id = typeof args.id === "string" ? args.id.trim() : undefined;
280
+ const location = typeof args.location === "string" ? args.location.trim() : undefined;
281
+ const result = await addFinding(this.projectRoot, title, { id, severity, status, location });
282
+ if (!result.success || !result.finding) {
283
+ return {
284
+ content: [{ type: "text", text: `Failed to record finding: ${result.message}` }],
285
+ isError: true
286
+ };
287
+ }
288
+ return {
289
+ content: [
290
+ {
291
+ type: "text",
292
+ text: `Finding recorded successfully:\n- ID: ${result.finding.id}\n- Severity: [${result.finding.severity}]\n- Status: ${result.finding.status}\n- Title: ${result.finding.title}`
293
+ }
294
+ ]
295
+ };
296
+ });
297
+ // 5. devflow_resolve_finding
298
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_resolve_finding"), async (args) => {
299
+ const id = typeof args.id === "string" ? args.id.trim() : "";
300
+ const status = typeof args.status === "string" ? args.status.toLowerCase() : undefined;
301
+ if (!id || !status) {
302
+ return {
303
+ content: [{ type: "text", text: "Error: 'id' and 'status' parameters are required for devflow_resolve_finding" }],
304
+ isError: true
305
+ };
306
+ }
307
+ const result = await resolveFinding(this.projectRoot, id, status);
308
+ if (!result.success || !result.finding) {
309
+ return {
310
+ content: [{ type: "text", text: result.message }],
311
+ isError: true
312
+ };
313
+ }
314
+ return {
315
+ content: [{ type: "text", text: `Finding ${result.finding.id} updated to status: ${result.finding.status}` }]
316
+ };
317
+ });
318
+ // 6. devflow_evaluate_gate
319
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_evaluate_gate"), async (args) => {
320
+ const strict = args.strict === true;
321
+ const report = await this.gatekeeperEngine.evaluate({ strict });
322
+ const humanText = formatGateReport(report);
323
+ return {
324
+ content: [
325
+ {
326
+ type: "text",
327
+ text: `${humanText}\n\nJSON Report:\n${JSON.stringify(report, null, 2)}`
328
+ }
329
+ ],
330
+ isError: !report.passed
331
+ };
332
+ });
333
+ // 7. devflow_get_context
334
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_get_context"), async (args) => {
335
+ const doc = typeof args.document === "string" ? args.document.toLowerCase() : "";
336
+ const contextPaths = await this.activeContextEngine.getPaths();
337
+ let fullPath;
338
+ switch (doc) {
339
+ case "overview":
340
+ fullPath = path.join(this.projectRoot, "devflow", "context", "project-overview.md");
341
+ break;
342
+ case "stage":
343
+ fullPath = contextPaths.stagePath;
344
+ break;
345
+ case "standards":
346
+ fullPath = path.join(this.projectRoot, "devflow", "context", "coding-standards.md");
347
+ break;
348
+ case "findings":
349
+ fullPath = contextPaths.findingsPath || path.join(this.projectRoot, "devflow", "context", "findings.md");
350
+ break;
351
+ case "ideas":
352
+ fullPath = path.join(this.projectRoot, "devflow", "ideas.md");
353
+ break;
354
+ case "feature":
355
+ fullPath = contextPaths.featureSpecPath;
356
+ break;
357
+ case "build-plan":
358
+ fullPath = path.join(this.projectRoot, "devflow", "build-plan.md");
359
+ break;
360
+ case "project-plan":
361
+ fullPath = path.join(this.projectRoot, "devflow", "project-plan.md");
362
+ break;
363
+ default:
364
+ return {
365
+ content: [{ type: "text", text: `Error: Unknown document type '${doc}'` }],
366
+ isError: true
367
+ };
368
+ }
369
+ try {
370
+ const content = await fs.readFile(fullPath, "utf8");
371
+ return { content: [{ type: "text", text: content }] };
372
+ }
373
+ catch {
374
+ return {
375
+ content: [{ type: "text", text: `Document '${doc}' not found or empty at ${fullPath}.` }],
376
+ isError: false
377
+ };
378
+ }
379
+ });
380
+ // 8. devflow_get_sliced_context
381
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_get_sliced_context"), async (args) => {
382
+ const stage = (typeof args.stage === "string" ? args.stage.toLowerCase() : "status");
383
+ const maxTokens = typeof args.maxTokens === "number" ? args.maxTokens : undefined;
384
+ const slice = await sliceContextForStage(this.projectRoot, stage, { maxTokens });
385
+ return {
386
+ content: [
387
+ {
388
+ type: "text",
389
+ text: `[JIT Context Slice - Stage: ${slice.stage}] (Tokens: ~${slice.estimatedTokens}, Saved: ${slice.reductionPercentage}%)\n\n${slice.content}`
390
+ }
391
+ ]
392
+ };
393
+ });
394
+ // 9. devflow_detect_drift
395
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_detect_drift"), async () => {
396
+ const drift = await this.gatekeeperEngine.getDrift();
397
+ return {
398
+ content: [{ type: "text", text: JSON.stringify(drift, null, 2) }]
399
+ };
400
+ });
401
+ // 10. devflow_reconcile_state
402
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_reconcile_state"), async (args) => {
403
+ const autoAdd = args.autoAddUndocumented !== false;
404
+ const heal = args.healStage !== false;
405
+ const result = await this.gatekeeperEngine.reconcile({
406
+ autoAddUndocumented: autoAdd,
407
+ healStage: heal
408
+ });
409
+ return {
410
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
411
+ };
412
+ });
413
+ // 11. devflow_get_studio_html
414
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_get_studio_html"), async (args) => {
415
+ const theme = (typeof args.theme === "string" ? args.theme : "auto");
416
+ const dashboardEngine = new DashboardStateEngine(this.projectRoot);
417
+ const snapshot = await dashboardEngine.getSnapshot();
418
+ const ideas = await readIdeas(this.projectRoot);
419
+ const renderer = new StudioViewRenderer({ defaultTheme: theme });
420
+ const html = renderer.renderWebviewStudio(snapshot, ideas, { theme });
421
+ return {
422
+ content: [{ type: "text", text: html }]
423
+ };
424
+ });
425
+ // 12. devflow_swarm_plan
426
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_swarm_plan"), async () => {
427
+ const plan = await generateSwarmPlan(this.projectRoot);
428
+ return {
429
+ content: [{ type: "text", text: JSON.stringify(plan, null, 2) }]
430
+ };
431
+ });
432
+ // 13. devflow_query_code_graph
433
+ this.registerTool(DEVFLOW_MCP_TOOLS.find((t) => t.name === "devflow_query_code_graph"), async (args) => {
434
+ const graph = await buildCodeGraph(this.projectRoot);
435
+ if (typeof args.file === "string" && args.file.trim().length > 0) {
436
+ const blast = calculateBlastRadius(graph, args.file.trim());
437
+ return {
438
+ content: [{ type: "text", text: JSON.stringify(blast, null, 2) }]
439
+ };
440
+ }
441
+ return {
442
+ content: [
443
+ {
444
+ type: "text",
445
+ text: JSON.stringify({
446
+ totalFiles: graph.totalFiles,
447
+ totalEdges: graph.totalEdges,
448
+ files: Object.keys(graph.nodes)
449
+ }, null, 2)
450
+ }
451
+ ]
452
+ };
453
+ });
454
+ }
455
+ async executeTool(name, args = {}) {
456
+ const entry = this.tools.get(name);
457
+ if (!entry) {
458
+ return {
459
+ content: [{ type: "text", text: `Error: Unknown tool name '${name}'` }],
460
+ isError: true
461
+ };
462
+ }
463
+ try {
464
+ return await entry.handler(args);
465
+ }
466
+ catch (err) {
467
+ return {
468
+ content: [
469
+ {
470
+ type: "text",
471
+ text: `Tool execution failed: ${err instanceof Error ? err.message : String(err)}`
472
+ }
473
+ ],
474
+ isError: true
475
+ };
476
+ }
477
+ }
478
+ async handleMessage(request) {
479
+ const { id = null, method, params = {} } = request;
480
+ switch (method) {
481
+ case "initialize": {
482
+ return {
483
+ jsonrpc: "2.0",
484
+ id,
485
+ result: {
486
+ protocolVersion: PROTOCOL_VERSION,
487
+ capabilities: {
488
+ tools: {},
489
+ resources: {}
490
+ },
491
+ serverInfo: {
492
+ name: SERVER_NAME,
493
+ version: SERVER_VERSION
494
+ }
495
+ }
496
+ };
497
+ }
498
+ case "ping": {
499
+ return {
500
+ jsonrpc: "2.0",
501
+ id,
502
+ result: {}
503
+ };
504
+ }
505
+ case "tools/list": {
506
+ return {
507
+ jsonrpc: "2.0",
508
+ id,
509
+ result: {
510
+ tools: DEVFLOW_MCP_TOOLS
511
+ }
512
+ };
513
+ }
514
+ case "tools/call": {
515
+ const toolName = typeof params.name === "string" ? params.name : "";
516
+ const toolArgs = params.arguments || {};
517
+ const result = await this.executeTool(toolName, toolArgs);
518
+ return {
519
+ jsonrpc: "2.0",
520
+ id,
521
+ result
522
+ };
523
+ }
524
+ case "resources/list": {
525
+ return {
526
+ jsonrpc: "2.0",
527
+ id,
528
+ result: {
529
+ resources: [
530
+ {
531
+ uri: "devflow://overview",
532
+ name: "Project Overview",
533
+ mimeType: "text/markdown",
534
+ description: "Living project overview and architecture source of truth"
535
+ },
536
+ {
537
+ uri: "devflow://current-stage",
538
+ name: "Current Stage",
539
+ mimeType: "text/markdown",
540
+ description: "Active delivery run and stage root switch"
541
+ },
542
+ {
543
+ uri: "devflow://findings",
544
+ name: "Findings Ledger",
545
+ mimeType: "text/markdown",
546
+ description: "Recorded quality and security findings"
547
+ },
548
+ {
549
+ uri: "devflow://ideas",
550
+ name: "Ideas Inbox",
551
+ mimeType: "text/markdown",
552
+ description: "Idea backlog with feasibility and value assessment"
553
+ }
554
+ ]
555
+ }
556
+ };
557
+ }
558
+ case "resources/read": {
559
+ const uri = typeof params.uri === "string" ? params.uri : "";
560
+ let targetFile = "";
561
+ if (uri === "devflow://overview")
562
+ targetFile = path.join("devflow", "context", "project-overview.md");
563
+ else if (uri === "devflow://current-stage")
564
+ targetFile = path.join("devflow", "context", "current-stage.md");
565
+ else if (uri === "devflow://findings")
566
+ targetFile = path.join("devflow", "context", "findings.md");
567
+ else if (uri === "devflow://ideas")
568
+ targetFile = path.join("devflow", "ideas.md");
569
+ if (!targetFile) {
570
+ return {
571
+ jsonrpc: "2.0",
572
+ id,
573
+ error: {
574
+ code: -32602,
575
+ message: `Invalid resource URI: ${uri}`
576
+ }
577
+ };
578
+ }
579
+ try {
580
+ const text = await fs.readFile(path.join(this.projectRoot, targetFile), "utf8");
581
+ return {
582
+ jsonrpc: "2.0",
583
+ id,
584
+ result: {
585
+ contents: [
586
+ {
587
+ uri,
588
+ mimeType: "text/markdown",
589
+ text
590
+ }
591
+ ]
592
+ }
593
+ };
594
+ }
595
+ catch {
596
+ return {
597
+ jsonrpc: "2.0",
598
+ id,
599
+ result: {
600
+ contents: [
601
+ {
602
+ uri,
603
+ mimeType: "text/markdown",
604
+ text: ""
605
+ }
606
+ ]
607
+ }
608
+ };
609
+ }
610
+ }
611
+ default: {
612
+ return {
613
+ jsonrpc: "2.0",
614
+ id,
615
+ error: {
616
+ code: -32601,
617
+ message: `Method not found: ${method}`
618
+ }
619
+ };
620
+ }
621
+ }
622
+ }
623
+ async processRawMessage(rawMessage) {
624
+ const trimmed = rawMessage.trim();
625
+ if (!trimmed)
626
+ return null;
627
+ let request;
628
+ try {
629
+ request = JSON.parse(trimmed);
630
+ }
631
+ catch {
632
+ return {
633
+ jsonrpc: "2.0",
634
+ id: null,
635
+ error: {
636
+ code: -32700,
637
+ message: "Parse error: Invalid JSON"
638
+ }
639
+ };
640
+ }
641
+ if (request.method === "initialized" || request.method === "notifications/initialized") {
642
+ return null;
643
+ }
644
+ return this.handleMessage(request);
645
+ }
646
+ listen(inStream = process.stdin, outStream = process.stdout) {
647
+ const rl = readline.createInterface({
648
+ input: inStream,
649
+ output: undefined,
650
+ terminal: false
651
+ });
652
+ const sendResponse = (response) => {
653
+ outStream.write(`${JSON.stringify(response)}\n`);
654
+ };
655
+ rl.on("line", async (line) => {
656
+ try {
657
+ const response = await this.processRawMessage(line);
658
+ if (response) {
659
+ sendResponse(response);
660
+ }
661
+ }
662
+ catch (err) {
663
+ sendResponse({
664
+ jsonrpc: "2.0",
665
+ id: null,
666
+ error: {
667
+ code: -32603,
668
+ message: `Internal error: ${err instanceof Error ? err.message : String(err)}`
669
+ }
670
+ });
671
+ }
672
+ });
673
+ return () => {
674
+ rl.close();
675
+ };
676
+ }
677
+ }
678
+ //# sourceMappingURL=mcp-server-engine.js.map