@mr.dj2u/mcp-server 0.1.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.
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1165 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/index.d.ts +3 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +2 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/package.json +54 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1165 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { runDoctor, scanFile } from '@mr.dj2u/doctor';
|
|
8
|
+
import { buildContinueSessionBrief } from '@mr.dj2u/cli/continue';
|
|
9
|
+
import { getSkill, listKnowledgeResources, readKnowledgeResource, } from '@mr.dj2u/knowledge';
|
|
10
|
+
export function resolveSuperStackInvocation() {
|
|
11
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const localBin = path.resolve(moduleDir, '..', '..', 'create-expo-super-stack', 'dist', 'cli.js');
|
|
13
|
+
if (existsSync(localBin)) {
|
|
14
|
+
return `node "${localBin}"`;
|
|
15
|
+
}
|
|
16
|
+
return 'npx -y create-expo-super-stack';
|
|
17
|
+
}
|
|
18
|
+
export function createMrdjMcpServer() {
|
|
19
|
+
const server = new McpServer({
|
|
20
|
+
name: 'mds-dev-suite',
|
|
21
|
+
version: '0.1.0',
|
|
22
|
+
description: 'MDS Expo dev-suite Doctor, knowledge resources, and onboarding prompts.',
|
|
23
|
+
}, {
|
|
24
|
+
capabilities: {
|
|
25
|
+
resources: {},
|
|
26
|
+
prompts: {},
|
|
27
|
+
tools: {},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
registerKnowledgeResources(server);
|
|
31
|
+
registerTools(server);
|
|
32
|
+
registerPrompts(server);
|
|
33
|
+
return server;
|
|
34
|
+
}
|
|
35
|
+
export async function startStdioServer() {
|
|
36
|
+
const server = createMrdjMcpServer();
|
|
37
|
+
await server.connect(new StdioServerTransport());
|
|
38
|
+
console.error('mds-dev-suite MCP server running on stdio');
|
|
39
|
+
process.stdin.resume();
|
|
40
|
+
}
|
|
41
|
+
export function listTools() {
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
name: 'continue_project',
|
|
45
|
+
description: 'Build an MDS Continue session brief for an onboarded app folder.',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
projectPath: { type: 'string' },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'doctor_scan_project',
|
|
55
|
+
description: 'Run MDS Doctor checks against a project folder.',
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
projectPath: { type: 'string' },
|
|
60
|
+
mode: { type: 'string', enum: ['fast', 'ci', 'full'] },
|
|
61
|
+
runScripts: { type: 'boolean' },
|
|
62
|
+
},
|
|
63
|
+
required: ['projectPath'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'doctor_scan_file',
|
|
68
|
+
description: 'Run focused Doctor checks against one file.',
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
filePath: { type: 'string' },
|
|
73
|
+
projectPath: { type: 'string' },
|
|
74
|
+
},
|
|
75
|
+
required: ['filePath'],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'doctor_explain_result',
|
|
80
|
+
description: 'Explain a Doctor result in beginner-friendly language.',
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
status: { type: 'string' },
|
|
85
|
+
message: { type: 'string' },
|
|
86
|
+
details: { type: 'object' },
|
|
87
|
+
},
|
|
88
|
+
required: ['status', 'message'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'knowledge_list_resources',
|
|
93
|
+
description: 'List MDS knowledge resources by kind.',
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
kind: { type: 'string', enum: ['pattern', 'guide', 'rule', 'skill', 'reference'] },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'list_skills',
|
|
103
|
+
description: 'List bundled MDS agent skills with optional keyword filtering.',
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
query: { type: 'string' },
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'get_skill',
|
|
113
|
+
description: 'Read a bundled MDS agent skill.',
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: 'object',
|
|
116
|
+
properties: {
|
|
117
|
+
id: { type: 'string' },
|
|
118
|
+
},
|
|
119
|
+
required: ['id'],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'get_guide',
|
|
124
|
+
description: 'Read a bundled MDS knowledge guide.',
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
id: { type: 'string' },
|
|
129
|
+
},
|
|
130
|
+
required: ['id'],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'generate_refactor_plan',
|
|
135
|
+
description: 'Generate a Doctor-backed refactor plan with related MDS knowledge resources.',
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
properties: {
|
|
139
|
+
projectPath: { type: 'string' },
|
|
140
|
+
mode: { type: 'string', enum: ['fast', 'ci', 'full'] },
|
|
141
|
+
runScripts: { type: 'boolean' },
|
|
142
|
+
focus: { type: 'string' },
|
|
143
|
+
},
|
|
144
|
+
required: ['projectPath'],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: 'generate_deploy_checklist',
|
|
149
|
+
description: 'Generate a target-aware deployment checklist using Doctor findings and MDS guidance.',
|
|
150
|
+
inputSchema: {
|
|
151
|
+
type: 'object',
|
|
152
|
+
properties: {
|
|
153
|
+
projectPath: { type: 'string' },
|
|
154
|
+
target: { type: 'string', enum: ['web', 'ios', 'android', 'native', 'all'] },
|
|
155
|
+
mode: { type: 'string', enum: ['fast', 'ci', 'full'] },
|
|
156
|
+
runScripts: { type: 'boolean' },
|
|
157
|
+
},
|
|
158
|
+
required: ['projectPath'],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'generate_setup_tasks',
|
|
163
|
+
description: 'Generate post-create onboarding setup tasks.',
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: 'object',
|
|
166
|
+
properties: {
|
|
167
|
+
projectPath: { type: 'string' },
|
|
168
|
+
defaults: { type: 'array', items: { type: 'string' } },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
export async function listResources() {
|
|
175
|
+
return listKnowledgeResources().map((resource) => ({
|
|
176
|
+
uri: resource.uri,
|
|
177
|
+
name: resource.name,
|
|
178
|
+
mimeType: 'text/markdown',
|
|
179
|
+
content: resource.description,
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
export async function executeTool(name, input) {
|
|
183
|
+
switch (name) {
|
|
184
|
+
case 'continue_project': {
|
|
185
|
+
return buildContinueSessionBrief(readString(input.projectPath) ?? '.');
|
|
186
|
+
}
|
|
187
|
+
case 'doctor_scan_project': {
|
|
188
|
+
const projectPath = readString(input.projectPath) ?? '.';
|
|
189
|
+
const mode = normalizeMode(readString(input.mode));
|
|
190
|
+
const runScripts = typeof input.runScripts === 'boolean' ? input.runScripts : undefined;
|
|
191
|
+
return runDoctor(projectPath, { mode, runScripts });
|
|
192
|
+
}
|
|
193
|
+
case 'doctor_scan_file': {
|
|
194
|
+
const filePath = readString(input.filePath);
|
|
195
|
+
if (!filePath) {
|
|
196
|
+
throw new Error('doctor_scan_file requires filePath.');
|
|
197
|
+
}
|
|
198
|
+
return scanFile(filePath, { projectPath: readString(input.projectPath) });
|
|
199
|
+
}
|
|
200
|
+
case 'doctor_explain_result': {
|
|
201
|
+
return explainDoctorResult(input);
|
|
202
|
+
}
|
|
203
|
+
case 'knowledge_list_resources': {
|
|
204
|
+
return listKnowledgeResources(normalizeKind(readString(input.kind)));
|
|
205
|
+
}
|
|
206
|
+
case 'list_skills': {
|
|
207
|
+
return listSkillSummaries(readString(input.query));
|
|
208
|
+
}
|
|
209
|
+
case 'get_skill': {
|
|
210
|
+
const id = readString(input.id);
|
|
211
|
+
return id ? getSkill(id) : null;
|
|
212
|
+
}
|
|
213
|
+
case 'get_guide': {
|
|
214
|
+
const id = readString(input.id);
|
|
215
|
+
return id ? readKnowledgeResource(`mds://guides/${id}`) : null;
|
|
216
|
+
}
|
|
217
|
+
case 'generate_refactor_plan': {
|
|
218
|
+
const projectPath = readString(input.projectPath);
|
|
219
|
+
if (!projectPath) {
|
|
220
|
+
throw new Error('generate_refactor_plan requires projectPath.');
|
|
221
|
+
}
|
|
222
|
+
return generateRefactorPlan(projectPath, input);
|
|
223
|
+
}
|
|
224
|
+
case 'generate_deploy_checklist': {
|
|
225
|
+
const projectPath = readString(input.projectPath);
|
|
226
|
+
if (!projectPath) {
|
|
227
|
+
throw new Error('generate_deploy_checklist requires projectPath.');
|
|
228
|
+
}
|
|
229
|
+
return generateDeployChecklist(projectPath, input);
|
|
230
|
+
}
|
|
231
|
+
case 'generate_setup_tasks': {
|
|
232
|
+
return generateSetupTasks(readString(input.projectPath) ?? '.', readStringArray(input.defaults));
|
|
233
|
+
}
|
|
234
|
+
default:
|
|
235
|
+
throw new Error(`Unknown MCP tool: ${name}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
export async function readResource(uri) {
|
|
239
|
+
const resource = await readKnowledgeResource(uri);
|
|
240
|
+
if (!resource) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
uri: resource.uri,
|
|
245
|
+
name: resource.name,
|
|
246
|
+
mimeType: 'text/markdown',
|
|
247
|
+
content: resource.content,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function registerKnowledgeResources(server) {
|
|
251
|
+
for (const resource of listKnowledgeResources()) {
|
|
252
|
+
server.registerResource(resource.id, resource.uri, {
|
|
253
|
+
title: resource.name,
|
|
254
|
+
description: resource.description,
|
|
255
|
+
mimeType: 'text/markdown',
|
|
256
|
+
}, async () => {
|
|
257
|
+
const content = await readKnowledgeResource(resource.uri);
|
|
258
|
+
return {
|
|
259
|
+
contents: [
|
|
260
|
+
{
|
|
261
|
+
uri: resource.uri,
|
|
262
|
+
mimeType: 'text/markdown',
|
|
263
|
+
text: content?.content ?? '',
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function registerTools(server) {
|
|
271
|
+
server.registerTool('continue_project', {
|
|
272
|
+
title: 'Continue Project',
|
|
273
|
+
description: 'Build an MDS Continue session brief for an onboarded app folder.',
|
|
274
|
+
inputSchema: {
|
|
275
|
+
projectPath: z.string().optional(),
|
|
276
|
+
},
|
|
277
|
+
}, async ({ projectPath }) => {
|
|
278
|
+
const brief = await buildContinueSessionBrief(projectPath ?? '.');
|
|
279
|
+
return toolJson(brief);
|
|
280
|
+
});
|
|
281
|
+
server.registerTool('doctor_scan_project', {
|
|
282
|
+
title: 'Doctor Scan Project',
|
|
283
|
+
description: 'Run MDS Doctor checks against a project folder.',
|
|
284
|
+
inputSchema: {
|
|
285
|
+
projectPath: z.string(),
|
|
286
|
+
mode: z.enum(['fast', 'ci', 'full']).optional(),
|
|
287
|
+
runScripts: z.boolean().optional(),
|
|
288
|
+
},
|
|
289
|
+
}, async ({ projectPath, mode, runScripts }) => {
|
|
290
|
+
const report = await runDoctor(projectPath, { mode: mode ?? 'fast', runScripts });
|
|
291
|
+
return toolJson(report);
|
|
292
|
+
});
|
|
293
|
+
server.registerTool('doctor_scan_file', {
|
|
294
|
+
title: 'Doctor Scan File',
|
|
295
|
+
description: 'Run focused Doctor checks against one file.',
|
|
296
|
+
inputSchema: {
|
|
297
|
+
filePath: z.string(),
|
|
298
|
+
projectPath: z.string().optional(),
|
|
299
|
+
},
|
|
300
|
+
}, async ({ filePath, projectPath }) => {
|
|
301
|
+
const report = await scanFile(filePath, { projectPath });
|
|
302
|
+
return toolJson(report);
|
|
303
|
+
});
|
|
304
|
+
server.registerTool('knowledge_list_resources', {
|
|
305
|
+
title: 'List Knowledge Resources',
|
|
306
|
+
description: 'List MDS knowledge resources by kind.',
|
|
307
|
+
inputSchema: {
|
|
308
|
+
kind: z.enum(['pattern', 'guide', 'rule', 'skill', 'reference']).optional(),
|
|
309
|
+
},
|
|
310
|
+
}, async ({ kind }) => toolJson(listKnowledgeResources(kind)));
|
|
311
|
+
server.registerTool('list_skills', {
|
|
312
|
+
title: 'List Skills',
|
|
313
|
+
description: 'List bundled MDS agent skills with optional keyword filtering.',
|
|
314
|
+
inputSchema: {
|
|
315
|
+
query: z.string().optional(),
|
|
316
|
+
},
|
|
317
|
+
}, async ({ query }) => toolJson(listSkillSummaries(query)));
|
|
318
|
+
server.registerTool('get_skill', {
|
|
319
|
+
title: 'Get Skill',
|
|
320
|
+
description: 'Read a bundled MDS agent skill.',
|
|
321
|
+
inputSchema: {
|
|
322
|
+
id: z.string(),
|
|
323
|
+
},
|
|
324
|
+
}, async ({ id }) => toolJson(await getSkill(id)));
|
|
325
|
+
server.registerTool('get_guide', {
|
|
326
|
+
title: 'Get Guide',
|
|
327
|
+
description: 'Read a bundled MDS knowledge guide.',
|
|
328
|
+
inputSchema: {
|
|
329
|
+
id: z.string(),
|
|
330
|
+
},
|
|
331
|
+
}, async ({ id }) => toolJson(await readKnowledgeResource(`mds://guides/${id}`)));
|
|
332
|
+
server.registerTool('generate_refactor_plan', {
|
|
333
|
+
title: 'Generate Refactor Plan',
|
|
334
|
+
description: 'Generate a Doctor-backed refactor plan with related MDS knowledge resources.',
|
|
335
|
+
inputSchema: {
|
|
336
|
+
projectPath: z.string(),
|
|
337
|
+
mode: z.enum(['fast', 'ci', 'full']).optional(),
|
|
338
|
+
runScripts: z.boolean().optional(),
|
|
339
|
+
focus: z.string().optional(),
|
|
340
|
+
},
|
|
341
|
+
}, async (input) => toolJson(await generateRefactorPlan(input.projectPath, input)));
|
|
342
|
+
server.registerTool('generate_deploy_checklist', {
|
|
343
|
+
title: 'Generate Deploy Checklist',
|
|
344
|
+
description: 'Generate a target-aware deployment checklist using Doctor findings and MDS guidance.',
|
|
345
|
+
inputSchema: {
|
|
346
|
+
projectPath: z.string(),
|
|
347
|
+
target: z.enum(['web', 'ios', 'android', 'native', 'all']).optional(),
|
|
348
|
+
mode: z.enum(['fast', 'ci', 'full']).optional(),
|
|
349
|
+
runScripts: z.boolean().optional(),
|
|
350
|
+
},
|
|
351
|
+
}, async (input) => toolJson(await generateDeployChecklist(input.projectPath, input)));
|
|
352
|
+
server.registerTool('doctor_explain_result', {
|
|
353
|
+
title: 'Explain Doctor Result',
|
|
354
|
+
description: 'Explain a Doctor result in beginner-friendly language.',
|
|
355
|
+
inputSchema: {
|
|
356
|
+
status: z.string(),
|
|
357
|
+
message: z.string(),
|
|
358
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
359
|
+
},
|
|
360
|
+
}, async (input) => toolText(explainDoctorResult(input)));
|
|
361
|
+
server.registerTool('generate_setup_tasks', {
|
|
362
|
+
title: 'Generate Setup Tasks',
|
|
363
|
+
description: 'Generate post-create onboarding setup tasks.',
|
|
364
|
+
inputSchema: {
|
|
365
|
+
projectPath: z.string().optional(),
|
|
366
|
+
defaults: z.array(z.string()).optional(),
|
|
367
|
+
},
|
|
368
|
+
}, async ({ projectPath, defaults }) => toolJson(generateSetupTasks(projectPath ?? '.', defaults ?? [])));
|
|
369
|
+
}
|
|
370
|
+
function registerPrompts(server) {
|
|
371
|
+
server.registerPrompt('create_expo_super_stack', {
|
|
372
|
+
title: 'Create Expo Super Stack',
|
|
373
|
+
description: 'Run from a parent directory (e.g. F:/ReactNativeApps) to generate a brand-new Expo app via create-expo-super-stack and immediately apply MDS onboarding.',
|
|
374
|
+
argsSchema: {
|
|
375
|
+
parentDir: z.string().optional(),
|
|
376
|
+
appName: z.string().optional(),
|
|
377
|
+
},
|
|
378
|
+
}, ({ parentDir, appName }) => ({
|
|
379
|
+
messages: [
|
|
380
|
+
{
|
|
381
|
+
role: 'user',
|
|
382
|
+
content: {
|
|
383
|
+
type: 'text',
|
|
384
|
+
text: buildCreateExpoSuperStackPromptText(parentDir, appName),
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
}));
|
|
389
|
+
server.registerPrompt('onboard_new_expo_app', {
|
|
390
|
+
title: 'Onboard Existing Expo App',
|
|
391
|
+
description: 'Run from inside an existing Expo app folder to apply MDS project memory, intake, planning, and scaffolding.',
|
|
392
|
+
argsSchema: {
|
|
393
|
+
projectPath: z.string().optional(),
|
|
394
|
+
},
|
|
395
|
+
}, ({ projectPath }) => ({
|
|
396
|
+
messages: [
|
|
397
|
+
{
|
|
398
|
+
role: 'user',
|
|
399
|
+
content: {
|
|
400
|
+
type: 'text',
|
|
401
|
+
text: buildOnboardPromptText(projectPath),
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
}));
|
|
406
|
+
server.registerPrompt('continue_mds_project', {
|
|
407
|
+
title: 'Continue MDS Project',
|
|
408
|
+
description: 'Build an MDS Continue session brief for an existing Expo app folder by calling continue_project first.',
|
|
409
|
+
argsSchema: {
|
|
410
|
+
projectPath: z.string().optional(),
|
|
411
|
+
},
|
|
412
|
+
}, ({ projectPath }) => ({
|
|
413
|
+
messages: [
|
|
414
|
+
{
|
|
415
|
+
role: 'user',
|
|
416
|
+
content: {
|
|
417
|
+
type: 'text',
|
|
418
|
+
text: buildContinueProjectPromptText(projectPath),
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
const INFO_TEMPLATE_URL = 'https://davidjgrimsley.com/public-facing/ai/mds-dev-suite/templates/info.md';
|
|
425
|
+
function fileIntakePhase(label) {
|
|
426
|
+
return [
|
|
427
|
+
`====== ${label} ======`,
|
|
428
|
+
'',
|
|
429
|
+
'If the user already has a project/info.md (and optionally project/style.md), they can paste or attach it here and you will use it as intake context.',
|
|
430
|
+
'',
|
|
431
|
+
'Tell the user (paraphrase is fine, but keep all four points):',
|
|
432
|
+
' 1. They can paste/attach an existing project/info.md now and you will skip questions whose answers are unambiguous in the file.',
|
|
433
|
+
` 2. Reference template (optional): ${INFO_TEMPLATE_URL} — if the URL is not yet hosted, offer to inline a copy of the template on request.`,
|
|
434
|
+
' 3. Same offer applies to project/style.md. If they do not have one, you will ask a few short style questions later (colors, fonts, brand vibe) so the generated style.md is not empty.',
|
|
435
|
+
' 4. If they have nothing, no problem — just say "skip" / "none" and you will build it from scratch as you go.',
|
|
436
|
+
'',
|
|
437
|
+
'Wait for the user to attach/paste content or to opt out. Then:',
|
|
438
|
+
' - Parse what was provided. For each upcoming question, classify the answer as:',
|
|
439
|
+
" 'clear' → skip the question; tell the user briefly: \"(using your info.md value: <short paraphrase>)\".",
|
|
440
|
+
" 'ambiguous' → ask the question, citing the relevant line from the file as context.",
|
|
441
|
+
" 'unknown' → ask normally.",
|
|
442
|
+
' - Never invent answers. If the file is silent on a question, ask normally.',
|
|
443
|
+
'',
|
|
444
|
+
].join('\n');
|
|
445
|
+
}
|
|
446
|
+
function creditsAndWaitMessage() {
|
|
447
|
+
return [
|
|
448
|
+
'When confirmed, run the generator silently from the parent folder via your shell tool. Do NOT print the command.',
|
|
449
|
+
'',
|
|
450
|
+
'Then output the following message verbatim (it gives the user something to read while the generator runs):',
|
|
451
|
+
'',
|
|
452
|
+
' """',
|
|
453
|
+
' Generating now. This typically takes 2-5 minutes. While we wait, let\'s shout out and recognize how this is working.',
|
|
454
|
+
'',
|
|
455
|
+
' create-expo-super-stack by Mr. DJ (who also built this agentic flow) wraps create-expo-stack by Roni Oss with major contributions by Dan Stepanov. Big thanks to them and to several other teams and individuals whose work and educational materials fill the MDS-dev-suite knowledge base:',
|
|
456
|
+
'',
|
|
457
|
+
' - Expo team (Evan Bacon for Expo Router, Brent Vatne, Charlie Cheever, and the broader Expo crew)',
|
|
458
|
+
' - React and React Native core teams',
|
|
459
|
+
' - Software Mansion (Reanimated, Gesture Handler, Screens, Worklets — Krzysztof Magiera and team)',
|
|
460
|
+
' - Supabase, Drizzle, and Zustand teams',
|
|
461
|
+
' - Adam Wathan and the Tailwind CSS team (the foundation Uniwind and NativeWind build on)',
|
|
462
|
+
" - Janic Duplessis for 'The real cost of React Native animations: benchmarking every approach'",
|
|
463
|
+
' - Simon Grimm of Galaxies.dev',
|
|
464
|
+
' - Beto Adrian Maldonado of codewithbeto.dev',
|
|
465
|
+
' - Vadim of notJust.dev',
|
|
466
|
+
' - William Candillon for the React Native animation deep-dive content',
|
|
467
|
+
' - Catalin Miron for the React Native animation tutorials',
|
|
468
|
+
' - Infinite Red / Jamon Holmgren for Ignite and the broader RN community',
|
|
469
|
+
'',
|
|
470
|
+
' Their contributions to the software development community are what fill the pages of the MDS-dev-suite knowledge base, alongside contributions and organization by Mr. DJ. Please enjoy the experience of the MDS-dev-suite plugin as you continue your development.',
|
|
471
|
+
' """',
|
|
472
|
+
'',
|
|
473
|
+
'Then surface generator output as it arrives. Do not echo the assembled command. If the generator surfaces an interactive prompt despite flags, relay it to the user.',
|
|
474
|
+
].join('\n');
|
|
475
|
+
}
|
|
476
|
+
export function buildCreateExpoSuperStackPromptText(parentDir, appName) {
|
|
477
|
+
const looksLikePath = (s) => s.includes('/') || s.includes('\\') || s.includes(':');
|
|
478
|
+
const parent = parentDir && looksLikePath(parentDir) ? parentDir : 'the current working directory';
|
|
479
|
+
const appHint = appName ? ` The user already named the app: \`${appName}\`.` : '';
|
|
480
|
+
const superStack = resolveSuperStackInvocation();
|
|
481
|
+
return [
|
|
482
|
+
`You are kicking off a brand-new Expo app from ${parent}.${appHint}`,
|
|
483
|
+
'',
|
|
484
|
+
'You are NOT inside the app folder yet — the folder does not exist. The generator will create it.',
|
|
485
|
+
'',
|
|
486
|
+
'IMPORTANT: Drive this entire flow conversationally. Never echo the assembled command line, never paste a flag string. Run the generator silently when all answers are gathered. The user should feel like a chat, not a CLI session.',
|
|
487
|
+
'',
|
|
488
|
+
'IMPORTANT: Ask EXACTLY one question per turn. Number multi-choice options. Always show the default and let the user just say "default" or press enter. Accept numbers, label text, or natural language ("all", "first three", "android only").',
|
|
489
|
+
'IMPORTANT: If the very first user message is a single bare word like "go", "start", "yes", "run", or similar — treat it as "proceed" and do NOT interpret it as a folder path or app name. Use the current working directory as the parent.',
|
|
490
|
+
'',
|
|
491
|
+
fileIntakePhase('PHASE 0 — Optional: attach existing project memory'),
|
|
492
|
+
'====== PHASE 1 — Project name and parent folder ======',
|
|
493
|
+
'',
|
|
494
|
+
`Q1.1 — Confirm: ${parent} is the parent folder where the new app folder will be created. Yes / change to a different folder?`,
|
|
495
|
+
`Q1.2 — App name? (kebab-case suggested, e.g. \`my-new-app\`)${appHint ? ' — already provided, just confirm.' : ''}`,
|
|
496
|
+
'Verify the chosen app folder does not already exist before proceeding.',
|
|
497
|
+
'',
|
|
498
|
+
'====== PHASE 2 — Stack choices (passed to create-expo-stack as flags) ======',
|
|
499
|
+
'',
|
|
500
|
+
'Q2.1 — TypeScript or JavaScript?',
|
|
501
|
+
' 1. TypeScript (default — strongly recommended)',
|
|
502
|
+
' 2. JavaScript',
|
|
503
|
+
'',
|
|
504
|
+
'Q2.2 — Package manager?',
|
|
505
|
+
' 1. npm (default)',
|
|
506
|
+
' 2. pnpm',
|
|
507
|
+
' 3. yarn',
|
|
508
|
+
' 4. bun',
|
|
509
|
+
'',
|
|
510
|
+
'Q2.3 — Navigation library?',
|
|
511
|
+
' 1. Expo Router (default — file-based routing, recommended)',
|
|
512
|
+
' 2. React Navigation',
|
|
513
|
+
'',
|
|
514
|
+
'Q2.4 — Navigation type? (only ask if React Navigation; Expo Router uses file-based routing)',
|
|
515
|
+
' 1. Stack (default)',
|
|
516
|
+
' 2. Tabs',
|
|
517
|
+
' 3. Drawer',
|
|
518
|
+
'',
|
|
519
|
+
'Q2.5 — Styling system?',
|
|
520
|
+
' 1. Uniwind (default — Tailwind v4 universal styling, MDS preference)',
|
|
521
|
+
' 2. NativeWind (Tailwind v3 for React Native)',
|
|
522
|
+
' 3. Tamagui',
|
|
523
|
+
' 4. Restyle',
|
|
524
|
+
' 5. StyleSheet (no library)',
|
|
525
|
+
'',
|
|
526
|
+
'Q2.6 — State management?',
|
|
527
|
+
' 1. Zustand (default — small, simple)',
|
|
528
|
+
' 2. None / decide later',
|
|
529
|
+
'',
|
|
530
|
+
'Q2.7 — Authentication backend?',
|
|
531
|
+
' 1. None (default — wire up later)',
|
|
532
|
+
' 2. Supabase',
|
|
533
|
+
' 3. Firebase',
|
|
534
|
+
'',
|
|
535
|
+
'Q2.8 — Set up EAS now?',
|
|
536
|
+
' 1. No (default — you can add it later)',
|
|
537
|
+
' 2. Yes',
|
|
538
|
+
'',
|
|
539
|
+
'====== PHASE 3 — MDS project memory & roadmap (passed as --mds-* flags) ======',
|
|
540
|
+
'',
|
|
541
|
+
'Q3.1 — App display name? (defaults to the project name from Q1.2 — confirm or override)',
|
|
542
|
+
'',
|
|
543
|
+
'Q3.2 — Who is this app for? Include user type, demographic, role, or context if known.',
|
|
544
|
+
'',
|
|
545
|
+
'Q3.3 — What should users be able to do first? Examples: sign up, create a project, invite teammates, checkout. (Press enter / say "defer" to let an agent derive this later from project/info.md.)',
|
|
546
|
+
'',
|
|
547
|
+
'Q3.4 — Which data categories does the app need? (multi-select — reply with comma-separated numbers, or label text, or "all")',
|
|
548
|
+
' 1. Local UI/app state (default)',
|
|
549
|
+
' 2. User accounts/authentication',
|
|
550
|
+
' 3. Backend database records',
|
|
551
|
+
' 4. File/image uploads or storage',
|
|
552
|
+
' 5. External APIs/integrations',
|
|
553
|
+
' 6. Analytics/events',
|
|
554
|
+
' 7. Payments/subscriptions',
|
|
555
|
+
' 8. Realtime/collaboration',
|
|
556
|
+
' 9. Push/email notifications',
|
|
557
|
+
' 10. Offline sync/cache',
|
|
558
|
+
' 11. Admin/moderation tools',
|
|
559
|
+
' 12. Other / custom (you will be asked to describe)',
|
|
560
|
+
'',
|
|
561
|
+
'Q3.5 — Which platforms will this app target? (multi-select)',
|
|
562
|
+
' 1. Web',
|
|
563
|
+
' 2. iOS',
|
|
564
|
+
' 3. Android',
|
|
565
|
+
' 4. Apple TV',
|
|
566
|
+
' 5. Android TV',
|
|
567
|
+
' Default: 1, 2, 3',
|
|
568
|
+
' Note: Android TV builds from the same Android target with leanback config in app.json. Apple TV is a separate tvOS build target via react-native-tvos. Selecting either TV option records the intent in project memory; configuration is applied later.',
|
|
569
|
+
'',
|
|
570
|
+
'Q3.6 — Which selected platform should be the first MVP target? (only ask if Q3.5 has more than one)',
|
|
571
|
+
'',
|
|
572
|
+
'Q3.7 — When platforms diverge, how should platform-specific code be organized? (only if multi-platform)',
|
|
573
|
+
' 1. File suffixes only (default — e.g. screen.web.tsx. Good for small to medium projects targeting web and mobile)',
|
|
574
|
+
' 2. Platform folders (recommended for large projects, very different UI on each platform, or targeting TV and other platforms from one codebase)',
|
|
575
|
+
'',
|
|
576
|
+
'Q3.7a — Where should the Expo Router app folder live?',
|
|
577
|
+
' 1. src/app (default — MDS preference for new Super Stack apps)',
|
|
578
|
+
' 2. app (root-level app folder)',
|
|
579
|
+
'',
|
|
580
|
+
'Q3.7b — Do selected platforms need their own layouts? (only if multi-platform)',
|
|
581
|
+
' 1. Shared layouts (default — use platform files only where needed)',
|
|
582
|
+
' 2. Platform-specific layouts',
|
|
583
|
+
'',
|
|
584
|
+
'Q3.8 — Web output mode? (only if web is in Q3.5)',
|
|
585
|
+
' 1. Static (default — most marketing/content/app-shell exports)',
|
|
586
|
+
' 2. Server (Expo Router API routes / SSR)',
|
|
587
|
+
' 3. SPA',
|
|
588
|
+
'',
|
|
589
|
+
'Q3.9 — Server type? (only if Q3.8 = Server, OR if mobile is in Q3.5)',
|
|
590
|
+
' If Q3.8 = Server:',
|
|
591
|
+
' 1. Standard Expo (default — Expo Router API routes)',
|
|
592
|
+
' 2. Custom backend',
|
|
593
|
+
' 3. None',
|
|
594
|
+
' If mobile-only (no web):',
|
|
595
|
+
' 1. Custom backend',
|
|
596
|
+
' 2. None (default)',
|
|
597
|
+
'',
|
|
598
|
+
'Q3.10 — How will the first version reach its users? (DEPLOYMENT / DISTRIBUTION method, NOT which OSes — that was Q3.5)',
|
|
599
|
+
' Examples: TestFlight to friends, internal client demo, App Store / Play Store launch, web hosting, side-loaded APK, internal-only.',
|
|
600
|
+
'',
|
|
601
|
+
'Q3.11 — Keep the starter components that come with create-expo-app?',
|
|
602
|
+
' 1. No (default — MDS rich boilerplate replaces them)',
|
|
603
|
+
' 2. Yes',
|
|
604
|
+
'',
|
|
605
|
+
'Q3.12 — Use the latest Expo SDK even if Expo Go availability may lag?',
|
|
606
|
+
' 1. Yes (default)',
|
|
607
|
+
' 2. No',
|
|
608
|
+
'',
|
|
609
|
+
'Q3.13 — Use Expo UI for native-feeling screens? (only ask if mobile is in Q3.5)',
|
|
610
|
+
' 1. Yes (default)',
|
|
611
|
+
' 2. No',
|
|
612
|
+
'',
|
|
613
|
+
'Q3.14 — Use Expo Native Tabs? (only ask if mobile is in Q3.5)',
|
|
614
|
+
' 1. Yes (default)',
|
|
615
|
+
' 2. No',
|
|
616
|
+
'',
|
|
617
|
+
'Q3.15 — Which EAS uses should the roadmap remember? (multi-select; only ask if Q2.8 = Yes)',
|
|
618
|
+
' 1. building mobile applications',
|
|
619
|
+
' 2. hosting a deployed server',
|
|
620
|
+
' 3. hosting web apps',
|
|
621
|
+
' 4. publishing mobile applications',
|
|
622
|
+
'',
|
|
623
|
+
'Q3.16 — Use the bundled MDS project/guidelines.md template?',
|
|
624
|
+
' 1. Yes (default — recommended; MDS-specific architecture rules)',
|
|
625
|
+
' 2. No',
|
|
626
|
+
'',
|
|
627
|
+
'Q3.17 — Data starting point?',
|
|
628
|
+
' 1. Local dummy data (default — fastest for early UI work)',
|
|
629
|
+
' 2. Supabase from the start (use when auth/synced data is already central)',
|
|
630
|
+
'',
|
|
631
|
+
'Q3.18 — Use test-to-main branching safeguards? (feature branches → test branch → main, with PR checks)',
|
|
632
|
+
' 1. Yes (default)',
|
|
633
|
+
' 2. No',
|
|
634
|
+
'',
|
|
635
|
+
'====== Flag map (use these EXACTLY — DO NOT search for them in node_modules) ======',
|
|
636
|
+
'',
|
|
637
|
+
'create-expo-stack flags (Phase 2 answers):',
|
|
638
|
+
' Q2.1 TypeScript Yes → --typescript ; No → --javascript',
|
|
639
|
+
' Q2.2 npm → --npm ; pnpm → --pnpm ; yarn → --yarn ; bun → --bun',
|
|
640
|
+
' Q2.3 Expo Router → --expo-router ; React Navigation → --react-navigation',
|
|
641
|
+
' Q2.4 (only with React Navigation) Tabs → --tabs ; Drawer → --drawer+tabs ; Stack → no flag',
|
|
642
|
+
' Q2.5 Uniwind → --uniwind ; NativeWind → --nativewind ; Tamagui → --tamagui ; Restyle → --restyle ; StyleSheet → no styling flag',
|
|
643
|
+
' Q2.6 Zustand → --zustand ; None → no state flag',
|
|
644
|
+
' Q2.7 Supabase → --supabase ; Firebase → --firebase ; None → no auth flag',
|
|
645
|
+
' Q2.8 EAS Yes → --eas ; No → no flag',
|
|
646
|
+
'',
|
|
647
|
+
'mds-* flags (Phase 3 answers — wrap values containing spaces in double quotes):',
|
|
648
|
+
' Q3.1 → --mds-app-name=',
|
|
649
|
+
' Q3.2 → --mds-audience=',
|
|
650
|
+
' Q3.3 → --mds-core-flows=',
|
|
651
|
+
' Q3.4 → --mds-data-needs= (comma-joined labels)',
|
|
652
|
+
' Q3.5 → --mds-platforms= (comma-joined slugs from: web,ios,android,apple-tv,android-tv)',
|
|
653
|
+
' Q3.6 → --mds-first-platform=',
|
|
654
|
+
' Q3.7 → --mds-platform-strategy= (files-only|folders)',
|
|
655
|
+
' Q3.7a → --mds-app-directory= (src|root)',
|
|
656
|
+
' Q3.7b → --mds-platform-layouts= (shared|platform-specific)',
|
|
657
|
+
' Q3.8 → --mds-web-output= (static|server|spa)',
|
|
658
|
+
' Q3.9 → --mds-deployed-server= (standard-expo|custom|none)',
|
|
659
|
+
' Q3.10 → --mds-deployment-target=',
|
|
660
|
+
' Q3.11 → --mds-create-expo-components or --mds-no-create-expo-components',
|
|
661
|
+
' Q3.12 → --mds-latest-expo-sdk or --mds-no-latest-expo-sdk',
|
|
662
|
+
' Q3.13 → --mds-expo-ui or --mds-no-expo-ui',
|
|
663
|
+
' Q3.14 → --mds-expo-native-tabs or --mds-no-expo-native-tabs',
|
|
664
|
+
' Q3.15 → --mds-eas-uses= (comma-joined labels; only if Q2.8 = Yes)',
|
|
665
|
+
' Q3.16 → --mds-guidelines-template',
|
|
666
|
+
' Q3.17 → --mds-data-start= (local|supabase)',
|
|
667
|
+
' Q3.18 → --mds-test-to-main or --mds-no-test-to-main',
|
|
668
|
+
' Always append: --mds-yes',
|
|
669
|
+
'',
|
|
670
|
+
'====== PHASE 4 — Confirm and generate ======',
|
|
671
|
+
'',
|
|
672
|
+
'Present a SHORT plain-English summary of the choices (not flag form, not the command). Example:',
|
|
673
|
+
' "Here is what I have:',
|
|
674
|
+
' - app: my-new-app at F:/ReactNativeApps',
|
|
675
|
+
' - TypeScript, npm, Expo Router, Uniwind, Zustand, no auth provider, no EAS yet',
|
|
676
|
+
' - audience: <...>, first flow: <...>',
|
|
677
|
+
' - platforms: web + iOS + Android, first MVP: iOS, src/app routes, files-only suffixes, shared layouts',
|
|
678
|
+
' - data starts local, test-to-main on, MDS guidelines template on"',
|
|
679
|
+
'Ask the user to confirm or change anything.',
|
|
680
|
+
'',
|
|
681
|
+
creditsAndWaitMessage(),
|
|
682
|
+
'',
|
|
683
|
+
'Generator invocation (build silently from the flag map above; never echo this line):',
|
|
684
|
+
` ${superStack} <appName> <create-expo-stack flags> <--mds-* flags> --mds-yes`,
|
|
685
|
+
'',
|
|
686
|
+
'====== PHASE 5 — Verify and hand off ======',
|
|
687
|
+
'',
|
|
688
|
+
'After generation succeeds:',
|
|
689
|
+
' 1. Find the line "Onboarding next steps" in the generator output. Quote everything from that line to the end of stdout verbatim in a fenced code block. Do NOT quote the CREATED file list or anything before "Onboarding next steps".',
|
|
690
|
+
' 2. Then tell the user (in plain text, not a code block):',
|
|
691
|
+
' "Your app is ready. To keep token usage low, open a new agent session directly inside the `<appName>` folder and run `mds continue` there."',
|
|
692
|
+
' 3. If TodoForContext markers exist in the new app\'s project/ files, add one line:',
|
|
693
|
+
' "There are unresolved context markers in project/ — resolve them in your new session before starting implementation work."',
|
|
694
|
+
' 4. Do NOT walk through markers or ask questions about them in this session.',
|
|
695
|
+
'',
|
|
696
|
+
'Rules:',
|
|
697
|
+
'- Never run the generator inside an existing app folder. If you suspect you are inside one, stop and ask.',
|
|
698
|
+
'- Never echo the assembled command line. Summarize choices in plain English instead.',
|
|
699
|
+
'- One question per turn. Always show the default. Skip dependent questions when prerequisites are not met.',
|
|
700
|
+
'- The generator runs all of MDS onboarding (project memory, exposition pages, dependencies, expo-doctor) once you pass --mds-yes plus the --mds-* flags. Do not run `mds onboard` separately afterward — it is already done.',
|
|
701
|
+
].join('\n');
|
|
702
|
+
}
|
|
703
|
+
export function buildContinueProjectPromptText(projectPath) {
|
|
704
|
+
const target = projectPath ?? 'the current working directory';
|
|
705
|
+
return [
|
|
706
|
+
`Build an MDS Continue session brief for the Expo project at ${target}.`,
|
|
707
|
+
'',
|
|
708
|
+
'Call the MCP tool `continue_project` first with:',
|
|
709
|
+
` projectPath: "${target}"`,
|
|
710
|
+
'',
|
|
711
|
+
'After the tool returns:',
|
|
712
|
+
'1. Briefly summarize what you found (2-3 sentences max). Name the next step in one sentence.',
|
|
713
|
+
'2. If TodoForContext markers exist:',
|
|
714
|
+
' - Say something brief like: "Let me ask you a few questions to get to know the project and help you plan the vision."',
|
|
715
|
+
' - Then work through each marker by asking the question its hint implies. Do NOT ask the user whether to fill or delete — just ask the question.',
|
|
716
|
+
' - Ask EXACTLY ONE question per message. Never combine questions. Never ask sub-questions in the same message.',
|
|
717
|
+
' - After the user answers, write the answer into the file under the marker and delete the marker line. Then move to the next question.',
|
|
718
|
+
' - Do not offer "skip markers and implement anyway."',
|
|
719
|
+
'3. After all markers are resolved, call continue_project again to confirm blockers are cleared.',
|
|
720
|
+
].join('\n');
|
|
721
|
+
}
|
|
722
|
+
export function buildOnboardPromptText(projectPath) {
|
|
723
|
+
const target = projectPath ?? 'the current Expo project';
|
|
724
|
+
return [
|
|
725
|
+
`You are running MDS agentic onboarding inside ${target}. The app folder must already exist on disk.`,
|
|
726
|
+
'',
|
|
727
|
+
'If you are NOT yet inside an Expo app folder, stop and tell the user to either:',
|
|
728
|
+
' (a) cd into the existing app folder and re-invoke this prompt, or',
|
|
729
|
+
' (b) run the `create_expo_super_stack` MCP prompt from a parent folder to generate a new app first.',
|
|
730
|
+
'',
|
|
731
|
+
'IMPORTANT: Drive this conversationally. Never echo the assembled `mds onboard` command line. One question per turn. Number multi-choice options. Always show the default and accept "default" / number / label / natural language.',
|
|
732
|
+
'',
|
|
733
|
+
fileIntakePhase('PHASE 0a — Optional: attach existing project memory'),
|
|
734
|
+
'====== PHASE 0b — State + blocker check ======',
|
|
735
|
+
'',
|
|
736
|
+
`Check whether ${target}/project/ exists with content.`,
|
|
737
|
+
' - If project/ is missing or empty: skip the blocker check and go to PHASE 1 intake.',
|
|
738
|
+
' - If project/ files exist: read project/info.md, project/style.md, project/guidelines.md, and project/todo.md, then run the blocker check below.',
|
|
739
|
+
'',
|
|
740
|
+
'Blocker check: search each project/ file for the literal marker `# TodoForContext(optional):`.',
|
|
741
|
+
' If ONE OR MORE markers are still present:',
|
|
742
|
+
' 1. STOP. Do not begin intake. Do not propose a plan. Do not scaffold anything.',
|
|
743
|
+
" 2. List every file + line that still contains a marker, and quote the marker's hint text.",
|
|
744
|
+
' 3. Tell the user, verbatim:',
|
|
745
|
+
' "These TodoForContext lines are blocking onboarding. For each one,',
|
|
746
|
+
' either fill out the section underneath OR delete the marker line to',
|
|
747
|
+
' acknowledge you do not want to add that context. Save the file, then',
|
|
748
|
+
' tell me to re-check."',
|
|
749
|
+
' 4. Wait for the user. When they say re-check, repeat PHASE 0 from scratch.',
|
|
750
|
+
' Only when zero markers remain may you proceed to PHASE 1.',
|
|
751
|
+
'',
|
|
752
|
+
'====== PHASE 1 — Intake conversation ======',
|
|
753
|
+
'',
|
|
754
|
+
'Reuse anything project/info.md already states; only ask questions whose answers are missing or generic.',
|
|
755
|
+
'',
|
|
756
|
+
'Q1.1 — App display name? (defaults to the folder name)',
|
|
757
|
+
'',
|
|
758
|
+
'Q1.2 — Who is this app for? (audience — user types, demographics, role, or context)',
|
|
759
|
+
'',
|
|
760
|
+
'Q1.3 — What should users be able to do first? (1-3 core flows; press enter to defer)',
|
|
761
|
+
'',
|
|
762
|
+
'Q1.4 — Which data categories does the app need? (multi-select)',
|
|
763
|
+
' 1. Local UI/app state (default)',
|
|
764
|
+
' 2. User accounts/authentication',
|
|
765
|
+
' 3. Backend database records',
|
|
766
|
+
' 4. File/image uploads or storage',
|
|
767
|
+
' 5. External APIs/integrations',
|
|
768
|
+
' 6. Analytics/events',
|
|
769
|
+
' 7. Payments/subscriptions',
|
|
770
|
+
' 8. Realtime/collaboration',
|
|
771
|
+
' 9. Push/email notifications',
|
|
772
|
+
' 10. Offline sync/cache',
|
|
773
|
+
' 11. Admin/moderation tools',
|
|
774
|
+
' 12. Other / custom (you will be asked to describe)',
|
|
775
|
+
'',
|
|
776
|
+
'Q1.5 — Which platforms will this app target? (multi-select)',
|
|
777
|
+
' 1. Web',
|
|
778
|
+
' 2. iOS',
|
|
779
|
+
' 3. Android',
|
|
780
|
+
' 4. Apple TV',
|
|
781
|
+
' 5. Android TV',
|
|
782
|
+
' Default: 1, 2, 3',
|
|
783
|
+
' Note: Android TV builds from the same Android target with leanback config in app.json. Apple TV is a separate tvOS build target via react-native-tvos.',
|
|
784
|
+
'',
|
|
785
|
+
'Q1.6 — First MVP target platform? (only ask if Q1.5 has more than one)',
|
|
786
|
+
'',
|
|
787
|
+
'Q1.7 — When platforms diverge, how should platform-specific code be organized? (only if multi-platform)',
|
|
788
|
+
' 1. File suffixes only (default — e.g. screen.web.tsx. Good for small to medium projects targeting web and mobile)',
|
|
789
|
+
' 2. Platform folders (recommended for large projects, very different UI on each platform, or targeting TV and other platforms from one codebase)',
|
|
790
|
+
'',
|
|
791
|
+
'Q1.7a — Where should MDS place Expo Router route files it generates?',
|
|
792
|
+
' 1. Detected existing app folder (default; usually src/app or app)',
|
|
793
|
+
' 2. src/app',
|
|
794
|
+
' 3. app',
|
|
795
|
+
' Note: mds onboard does not move an existing route tree automatically.',
|
|
796
|
+
'',
|
|
797
|
+
'Q1.7b — Do selected platforms need their own layouts? (only if multi-platform)',
|
|
798
|
+
' 1. Shared layouts (default)',
|
|
799
|
+
' 2. Platform-specific layouts',
|
|
800
|
+
'',
|
|
801
|
+
'Q1.8 — Web output mode? (only if web is in Q1.5)',
|
|
802
|
+
' 1. Static (default)',
|
|
803
|
+
' 2. Server (Expo Router API routes / SSR)',
|
|
804
|
+
' 3. SPA',
|
|
805
|
+
'',
|
|
806
|
+
'Q1.9 — Server type? (only if Q1.8 = Server, OR if mobile is in Q1.5)',
|
|
807
|
+
' If Q1.8 = Server: Standard Expo (default) / Custom backend / None',
|
|
808
|
+
' If mobile-only: Custom backend / None (default)',
|
|
809
|
+
'',
|
|
810
|
+
'Q1.10 — How will the first version reach its users? (DEPLOYMENT/DISTRIBUTION method, NOT which OSes)',
|
|
811
|
+
' Examples: TestFlight to friends, internal client demo, App Store / Play Store launch, web hosting, side-loaded APK, internal-only.',
|
|
812
|
+
'',
|
|
813
|
+
'Q1.11 — Use latest Expo SDK even if Expo Go availability may lag? (Yes default / No)',
|
|
814
|
+
'',
|
|
815
|
+
'Q1.12 — Use Expo UI for native-feeling screens? (only if mobile target; Yes default / No)',
|
|
816
|
+
'',
|
|
817
|
+
'Q1.13 — Use Expo Native Tabs? (only if mobile target; Yes default / No)',
|
|
818
|
+
'',
|
|
819
|
+
'Q1.14 — Set up EAS now? (No default / Yes)',
|
|
820
|
+
'Q1.15 — Which EAS uses? (multi-select; only if Q1.14 = Yes)',
|
|
821
|
+
' 1. building mobile applications',
|
|
822
|
+
' 2. hosting a deployed server',
|
|
823
|
+
' 3. hosting web apps',
|
|
824
|
+
' 4. publishing mobile applications',
|
|
825
|
+
'',
|
|
826
|
+
'Q1.16 — Use the bundled MDS project/guidelines.md template? (Yes default / No)',
|
|
827
|
+
'',
|
|
828
|
+
'Q1.17 — Data starting point?',
|
|
829
|
+
' 1. Local dummy data (default)',
|
|
830
|
+
' 2. Supabase from the start',
|
|
831
|
+
'',
|
|
832
|
+
'Q1.18 — Use test-to-main branching safeguards? (Yes default / No)',
|
|
833
|
+
'',
|
|
834
|
+
'====== PHASE 2 — Confirm and scaffold ======',
|
|
835
|
+
'',
|
|
836
|
+
'Present a SHORT plain-English summary of choices (not flag form). Ask the user to confirm or change anything.',
|
|
837
|
+
'',
|
|
838
|
+
'When confirmed, run silently via your shell tool. Do NOT print the command. Just say:',
|
|
839
|
+
' "Scaffolding project memory and rich boilerplate now. This takes a few seconds."',
|
|
840
|
+
'',
|
|
841
|
+
'====== Flag map for `mds onboard` (use these EXACTLY) ======',
|
|
842
|
+
'',
|
|
843
|
+
' Q1.1 → --app-name= (wrap value in double quotes if it contains spaces)',
|
|
844
|
+
' Q1.2 → --audience=',
|
|
845
|
+
' Q1.3 → --core-flows=',
|
|
846
|
+
' Q1.4 → --data-needs= (comma-joined labels)',
|
|
847
|
+
' Q1.5 → --platforms= (comma-joined slugs from: web,ios,android,apple-tv,android-tv)',
|
|
848
|
+
' Q1.6 → --first-platform=',
|
|
849
|
+
' Q1.7 → --platform-strategy= (files-only|folders)',
|
|
850
|
+
' Q1.7a → --app-directory= (src|root)',
|
|
851
|
+
' Q1.7b → --platform-layouts= (shared|platform-specific)',
|
|
852
|
+
' Q1.8 → --web-output= (static|server|spa|none)',
|
|
853
|
+
' Q1.9 → --deployed-server= (standard-expo|custom|none)',
|
|
854
|
+
' Q1.10 → --deployment-target=',
|
|
855
|
+
' Q1.11 → --latest-expo-sdk (boolean flag — pass true/false: --latest-expo-sdk for Yes, omit or --latest-expo-sdk=false for No)',
|
|
856
|
+
' Q1.12 → --expo-ui (Yes only; omit when No)',
|
|
857
|
+
' Q1.13 → --expo-native-tabs (Yes only; omit when No)',
|
|
858
|
+
' Q1.14 → --eas-selected (Yes only)',
|
|
859
|
+
' Q1.15 → --eas-uses= (comma-joined; only if Q1.14 = Yes)',
|
|
860
|
+
' Q1.16 → --guidelines-template (Yes only)',
|
|
861
|
+
' Q1.17 → --data-start= (local|supabase)',
|
|
862
|
+
' Q1.18 → --test-to-main (Yes only)',
|
|
863
|
+
' Always append: --yes',
|
|
864
|
+
' Project path: pass --project=<absolute path to the app folder>',
|
|
865
|
+
'',
|
|
866
|
+
'After it completes, run `mds doctor` and surface any errors/warnings.',
|
|
867
|
+
'',
|
|
868
|
+
'Rules:',
|
|
869
|
+
'- Never bypass PHASE 0. The marker check is non-negotiable; the user can clear it in seconds by deleting lines.',
|
|
870
|
+
'- Never echo the assembled command line. Summarize choices in plain English instead.',
|
|
871
|
+
'- One question per turn. Show the default. Skip dependent questions when prerequisites are not met.',
|
|
872
|
+
'- Keep technical/agent rules in project/guidelines.md, not project/style.md.',
|
|
873
|
+
].join('\n');
|
|
874
|
+
}
|
|
875
|
+
async function generateRefactorPlan(projectPath, input) {
|
|
876
|
+
const mode = normalizeMode(readString(input.mode));
|
|
877
|
+
const runScripts = typeof input.runScripts === 'boolean' ? input.runScripts : false;
|
|
878
|
+
const focus = readString(input.focus)?.trim();
|
|
879
|
+
const report = await runDoctor(projectPath, { mode, runScripts });
|
|
880
|
+
const actionableChecks = report.checks
|
|
881
|
+
.filter((check) => check.status === 'error' || check.status === 'warn')
|
|
882
|
+
.filter((check) => (focus ? doctorCheckMatches(check, focus) : true))
|
|
883
|
+
.sort(sortDoctorChecks);
|
|
884
|
+
const priorities = actionableChecks.map((check, index) => ({
|
|
885
|
+
priority: index + 1,
|
|
886
|
+
status: check.status,
|
|
887
|
+
check: check.name,
|
|
888
|
+
finding: check.message,
|
|
889
|
+
relatedResources: relatedResourcesForCheck(check),
|
|
890
|
+
nextStep: nextStepForCheck(check),
|
|
891
|
+
}));
|
|
892
|
+
return {
|
|
893
|
+
kind: 'refactor-plan',
|
|
894
|
+
projectPath: report.projectPath,
|
|
895
|
+
mode: report.mode,
|
|
896
|
+
generatedAt: report.timestamp,
|
|
897
|
+
focus: focus ?? null,
|
|
898
|
+
summary: report.summary,
|
|
899
|
+
priorities,
|
|
900
|
+
recommendedOrder: priorities.length > 0
|
|
901
|
+
? priorities.map((item) => `${item.priority}. ${item.check}: ${item.nextStep}`)
|
|
902
|
+
: ['No Doctor warnings or errors matched this refactor request. Keep the current architecture intact.'],
|
|
903
|
+
verification: [
|
|
904
|
+
`Run doctor_scan_project for ${report.projectPath} in ${report.mode} mode after the refactor.`,
|
|
905
|
+
'If route, env, or SSR files changed, run doctor_scan_file on the touched files before the full scan.',
|
|
906
|
+
'Use get_skill for each related MDS skill before making broad architectural edits.',
|
|
907
|
+
],
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
async function generateDeployChecklist(projectPath, input) {
|
|
911
|
+
const mode = readString(input.mode) ? normalizeMode(readString(input.mode)) : 'ci';
|
|
912
|
+
const runScripts = typeof input.runScripts === 'boolean' ? input.runScripts : false;
|
|
913
|
+
const target = normalizeDeployTarget(readString(input.target));
|
|
914
|
+
const report = await runDoctor(projectPath, { mode, runScripts });
|
|
915
|
+
const unresolvedFindings = report.checks
|
|
916
|
+
.filter((check) => check.status === 'error' || check.status === 'warn')
|
|
917
|
+
.sort(sortDoctorChecks)
|
|
918
|
+
.map((check, index) => ({
|
|
919
|
+
priority: index + 1,
|
|
920
|
+
status: check.status,
|
|
921
|
+
check: check.name,
|
|
922
|
+
finding: check.message,
|
|
923
|
+
relatedResources: relatedResourcesForCheck(check),
|
|
924
|
+
nextStep: nextStepForCheck(check),
|
|
925
|
+
}));
|
|
926
|
+
return {
|
|
927
|
+
kind: 'deploy-checklist',
|
|
928
|
+
projectPath: report.projectPath,
|
|
929
|
+
target,
|
|
930
|
+
mode: report.mode,
|
|
931
|
+
generatedAt: report.timestamp,
|
|
932
|
+
summary: report.summary,
|
|
933
|
+
checklist: buildDeployChecklist(report, target),
|
|
934
|
+
unresolvedFindings,
|
|
935
|
+
verification: [
|
|
936
|
+
`Run doctor_scan_project with mode "${mode}" and runScripts true before release approval.`,
|
|
937
|
+
'Run the project-specific lint, typecheck, test, and production build commands that CI will run.',
|
|
938
|
+
target === 'web' || target === 'all'
|
|
939
|
+
? 'Verify metadata, canonical URLs, robots, sitemap, and SSR-safe route behavior in the production web output.'
|
|
940
|
+
: 'Verify native build/profile settings, store metadata readiness, and device smoke tests for selected native targets.',
|
|
941
|
+
],
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function listSkillSummaries(query) {
|
|
945
|
+
const normalizedQuery = query?.trim().toLowerCase();
|
|
946
|
+
return listKnowledgeResources('skill')
|
|
947
|
+
.filter((resource) => {
|
|
948
|
+
if (!normalizedQuery) {
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
return [resource.id, resource.name, resource.description, ...resource.keywords]
|
|
952
|
+
.join(' ')
|
|
953
|
+
.toLowerCase()
|
|
954
|
+
.includes(normalizedQuery);
|
|
955
|
+
})
|
|
956
|
+
.map((resource) => ({
|
|
957
|
+
id: resource.id,
|
|
958
|
+
name: resource.name,
|
|
959
|
+
description: resource.description,
|
|
960
|
+
tags: resource.keywords,
|
|
961
|
+
uri: resource.uri,
|
|
962
|
+
}));
|
|
963
|
+
}
|
|
964
|
+
function buildDeployChecklist(report, target) {
|
|
965
|
+
const hasErrors = report.summary.errors > 0;
|
|
966
|
+
const hasWarnings = report.summary.warnings > 0;
|
|
967
|
+
const checkNames = report.checks.map((check) => `${check.name} ${check.message}`.toLowerCase());
|
|
968
|
+
const hasSeoSignal = checkNames.some((value) => value.includes('seo') || value.includes('metadata'));
|
|
969
|
+
const hasEnvSignal = checkNames.some((value) => value.includes('env') || value.includes('secret'));
|
|
970
|
+
const hasSsrSignal = checkNames.some((value) => value.includes('ssr') || value.includes('window'));
|
|
971
|
+
const hasExpoSignal = checkNames.some((value) => value.includes('expo config') || value.includes('expo configuration'));
|
|
972
|
+
const items = [
|
|
973
|
+
{
|
|
974
|
+
id: 'doctor-blockers',
|
|
975
|
+
status: hasErrors ? 'blocker' : hasWarnings ? 'action' : 'pass',
|
|
976
|
+
title: 'Clear Doctor findings',
|
|
977
|
+
detail: hasErrors
|
|
978
|
+
? 'Doctor reported errors that should block release.'
|
|
979
|
+
: hasWarnings
|
|
980
|
+
? 'Doctor reported warnings; triage them before release approval.'
|
|
981
|
+
: 'Doctor found no warnings or errors for this scan.',
|
|
982
|
+
relatedResources: ['mds://skills/deployment', 'mds://skills/debugging'],
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
id: 'env-boundaries',
|
|
986
|
+
status: hasEnvSignal ? 'action' : 'manual',
|
|
987
|
+
title: 'Verify public/private environment boundaries',
|
|
988
|
+
detail: 'Confirm EXPO_PUBLIC values are safe for client bundles and private service keys stay server-only.',
|
|
989
|
+
relatedResources: ['mds://rules/env-hygiene', 'mds://skills/env-vars'],
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
id: 'expo-runtime',
|
|
993
|
+
status: hasExpoSignal ? 'action' : 'manual',
|
|
994
|
+
title: 'Verify Expo config and runtime targets',
|
|
995
|
+
detail: 'Confirm app config, platform list, web output, and build profiles match the release target.',
|
|
996
|
+
relatedResources: ['mds://patterns/project-configuration-patterns', 'mds://skills/deployment'],
|
|
997
|
+
},
|
|
998
|
+
];
|
|
999
|
+
if (target === 'web' || target === 'all') {
|
|
1000
|
+
items.push({
|
|
1001
|
+
id: 'web-metadata',
|
|
1002
|
+
status: hasSeoSignal ? 'action' : 'manual',
|
|
1003
|
+
title: 'Verify web SEO metadata',
|
|
1004
|
+
detail: 'Check title, description, canonical URL, Open Graph tags, sitemap, and robots strategy.',
|
|
1005
|
+
relatedResources: ['mds://rules/seo-metadata', 'mds://skills/seo-metadata'],
|
|
1006
|
+
}, {
|
|
1007
|
+
id: 'web-ssr-safety',
|
|
1008
|
+
status: hasSsrSignal ? 'action' : 'manual',
|
|
1009
|
+
title: 'Verify SSR-safe web/server code',
|
|
1010
|
+
detail: 'Guard browser globals and keep native-only imports out of server execution paths.',
|
|
1011
|
+
relatedResources: ['mds://rules/ssr-safety', 'mds://skills/expo-ssr-safety'],
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
if (target === 'ios' || target === 'android' || target === 'native' || target === 'all') {
|
|
1015
|
+
items.push({
|
|
1016
|
+
id: 'native-build-readiness',
|
|
1017
|
+
status: 'manual',
|
|
1018
|
+
title: 'Verify native build readiness',
|
|
1019
|
+
detail: 'Run the selected EAS/local native build profile and smoke test the target platform before store or client release.',
|
|
1020
|
+
relatedResources: ['mds://skills/deployment', 'mds://reference/package-ci-patterns'],
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
return items;
|
|
1024
|
+
}
|
|
1025
|
+
function doctorCheckMatches(check, query) {
|
|
1026
|
+
const normalizedQuery = query.toLowerCase();
|
|
1027
|
+
return [check.name, check.message, JSON.stringify(check.details ?? {})]
|
|
1028
|
+
.join(' ')
|
|
1029
|
+
.toLowerCase()
|
|
1030
|
+
.includes(normalizedQuery);
|
|
1031
|
+
}
|
|
1032
|
+
function sortDoctorChecks(a, b) {
|
|
1033
|
+
return severityRank(a.status) - severityRank(b.status) || a.name.localeCompare(b.name);
|
|
1034
|
+
}
|
|
1035
|
+
function severityRank(status) {
|
|
1036
|
+
return status === 'error' ? 0 : status === 'warn' ? 1 : status === 'skip' ? 2 : 3;
|
|
1037
|
+
}
|
|
1038
|
+
function relatedResourcesForCheck(check) {
|
|
1039
|
+
const text = `${check.name} ${check.message}`.toLowerCase();
|
|
1040
|
+
const resources = new Set(['mds://skills/debugging']);
|
|
1041
|
+
if (text.includes('env') || text.includes('secret') || text.includes('public')) {
|
|
1042
|
+
resources.add('mds://rules/env-hygiene');
|
|
1043
|
+
resources.add('mds://skills/env-vars');
|
|
1044
|
+
}
|
|
1045
|
+
if (text.includes('ssr') || text.includes('window') || text.includes('document') || text.includes('localstorage')) {
|
|
1046
|
+
resources.add('mds://rules/ssr-safety');
|
|
1047
|
+
resources.add('mds://skills/expo-ssr-safety');
|
|
1048
|
+
}
|
|
1049
|
+
if (text.includes('seo') || text.includes('metadata') || text.includes('canonical')) {
|
|
1050
|
+
resources.add('mds://rules/seo-metadata');
|
|
1051
|
+
resources.add('mds://skills/seo-metadata');
|
|
1052
|
+
}
|
|
1053
|
+
if (text.includes('app architecture') || text.includes('route') || text.includes('app/')) {
|
|
1054
|
+
resources.add('mds://rules/app-folder-architecture');
|
|
1055
|
+
resources.add('mds://skills/expo-router-architecture');
|
|
1056
|
+
}
|
|
1057
|
+
if (text.includes('styling') || text.includes('uniwind') || text.includes('tailwind')) {
|
|
1058
|
+
resources.add('mds://skills/uniwind-theming');
|
|
1059
|
+
}
|
|
1060
|
+
if (text.includes('expo') || text.includes('build') || text.includes('script') || text.includes('package')) {
|
|
1061
|
+
resources.add('mds://skills/deployment');
|
|
1062
|
+
}
|
|
1063
|
+
return [...resources];
|
|
1064
|
+
}
|
|
1065
|
+
function nextStepForCheck(check) {
|
|
1066
|
+
const text = `${check.name} ${check.message}`.toLowerCase();
|
|
1067
|
+
if (text.includes('env') || text.includes('secret')) {
|
|
1068
|
+
return 'Separate public client config from private server secrets, then rerun the affected file/project scan.';
|
|
1069
|
+
}
|
|
1070
|
+
if (text.includes('ssr') || text.includes('window') || text.includes('document')) {
|
|
1071
|
+
return 'Move client-only runtime access behind platform/lifecycle guards or isolate it from server paths.';
|
|
1072
|
+
}
|
|
1073
|
+
if (text.includes('seo') || text.includes('metadata')) {
|
|
1074
|
+
return 'Add or normalize route metadata, canonical/indexing strategy, and web output verification.';
|
|
1075
|
+
}
|
|
1076
|
+
if (text.includes('app architecture') || text.includes('route')) {
|
|
1077
|
+
return 'Move business/data logic out of route files and keep app/ focused on routing shells.';
|
|
1078
|
+
}
|
|
1079
|
+
if (text.includes('script') || text.includes('build') || text.includes('package')) {
|
|
1080
|
+
return 'Fix the failing or missing package script so local checks match CI/release expectations.';
|
|
1081
|
+
}
|
|
1082
|
+
return 'Apply the smallest targeted fix, then rerun Doctor to confirm the finding is resolved.';
|
|
1083
|
+
}
|
|
1084
|
+
function normalizeDeployTarget(value) {
|
|
1085
|
+
return value === 'web' ||
|
|
1086
|
+
value === 'ios' ||
|
|
1087
|
+
value === 'android' ||
|
|
1088
|
+
value === 'native' ||
|
|
1089
|
+
value === 'all'
|
|
1090
|
+
? value
|
|
1091
|
+
: 'all';
|
|
1092
|
+
}
|
|
1093
|
+
function normalizeMode(value) {
|
|
1094
|
+
return value === 'ci' || value === 'full' || value === 'fast' ? value : 'fast';
|
|
1095
|
+
}
|
|
1096
|
+
function normalizeKind(value) {
|
|
1097
|
+
return value === 'pattern' ||
|
|
1098
|
+
value === 'guide' ||
|
|
1099
|
+
value === 'rule' ||
|
|
1100
|
+
value === 'skill' ||
|
|
1101
|
+
value === 'reference'
|
|
1102
|
+
? value
|
|
1103
|
+
: undefined;
|
|
1104
|
+
}
|
|
1105
|
+
function readString(value) {
|
|
1106
|
+
return typeof value === 'string' ? value : undefined;
|
|
1107
|
+
}
|
|
1108
|
+
function readStringArray(value) {
|
|
1109
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === 'string') : [];
|
|
1110
|
+
}
|
|
1111
|
+
function explainDoctorResult(input) {
|
|
1112
|
+
const status = readString(input.status) ?? 'unknown';
|
|
1113
|
+
const message = readString(input.message) ?? 'No message provided.';
|
|
1114
|
+
const severity = status === 'error'
|
|
1115
|
+
? 'This should block shipping until it is fixed.'
|
|
1116
|
+
: status === 'warn'
|
|
1117
|
+
? 'This is worth fixing, but it may not block local development.'
|
|
1118
|
+
: 'This result does not require action.';
|
|
1119
|
+
return `${severity}\n\nFinding: ${message}\n\nNext step: inspect the named file or script, apply the smallest fix, then rerun mds doctor.`;
|
|
1120
|
+
}
|
|
1121
|
+
function generateSetupTasks(projectPath, defaults) {
|
|
1122
|
+
const selected = defaults.length > 0 ? defaults : ['project-docs', 'doctor', 'uniwind'];
|
|
1123
|
+
const tasks = [
|
|
1124
|
+
`Detect package manager, Expo SDK, router, app folder, aliases, and styling stack in ${projectPath}.`,
|
|
1125
|
+
'Ask for app goal, audience, core flows, data needs, and deployment target.',
|
|
1126
|
+
];
|
|
1127
|
+
if (selected.includes('project-docs')) {
|
|
1128
|
+
tasks.push('Create project/info.md, project/todo.md, project/style.md, and project/guidelines.md.');
|
|
1129
|
+
}
|
|
1130
|
+
if (selected.includes('uniwind')) {
|
|
1131
|
+
tasks.push('Add or verify Tailwind v4 plus Uniwind configuration.');
|
|
1132
|
+
}
|
|
1133
|
+
if (selected.includes('zustand')) {
|
|
1134
|
+
tasks.push('Scaffold Zustand only for shared state that route-local state cannot handle.');
|
|
1135
|
+
}
|
|
1136
|
+
if (selected.includes('supabase')) {
|
|
1137
|
+
tasks.push('Add Supabase env docs and client/server boundary guidance.');
|
|
1138
|
+
}
|
|
1139
|
+
tasks.push('Run mds doctor after scaffolding selected pieces.');
|
|
1140
|
+
return tasks;
|
|
1141
|
+
}
|
|
1142
|
+
function toolJson(value) {
|
|
1143
|
+
return toolText(JSON.stringify(value, null, 2));
|
|
1144
|
+
}
|
|
1145
|
+
function toolText(text) {
|
|
1146
|
+
return {
|
|
1147
|
+
content: [
|
|
1148
|
+
{
|
|
1149
|
+
type: 'text',
|
|
1150
|
+
text,
|
|
1151
|
+
},
|
|
1152
|
+
],
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
function isDirectRun() {
|
|
1156
|
+
return process.argv[1] === fileURLToPath(import.meta.url);
|
|
1157
|
+
}
|
|
1158
|
+
if (isDirectRun()) {
|
|
1159
|
+
startStdioServer().catch((error) => {
|
|
1160
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1161
|
+
console.error('Fatal error in mds-dev-suite MCP server:', message);
|
|
1162
|
+
process.exit(1);
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
//# sourceMappingURL=index.js.map
|