viewgate-mcp 1.0.37 → 1.0.39
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.js +246 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,6 +23,8 @@ import dotenv from "dotenv";
|
|
|
23
23
|
import path from "path";
|
|
24
24
|
import os from "os";
|
|
25
25
|
import { fileURLToPath } from "url";
|
|
26
|
+
import fs from "fs";
|
|
27
|
+
import fsp from "fs/promises";
|
|
26
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
27
29
|
const __dirname = path.dirname(__filename);
|
|
28
30
|
dotenv.config({ path: path.join(__dirname, "..", ".env") });
|
|
@@ -49,6 +51,28 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
49
51
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
50
52
|
return {
|
|
51
53
|
tools: [
|
|
54
|
+
{
|
|
55
|
+
name: "get_ui_components",
|
|
56
|
+
description: "Fetch UI components created by UX/UI (progressive). Returns ONLY the pending components requested and their required props.",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
limit: { type: "number", description: "Max results.", default: 1 },
|
|
61
|
+
status: { type: "string", description: "Component status filter (pending,generated).", default: "pending" },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "generate_ui_components",
|
|
67
|
+
description: "Generate real UI components into /components (no overwrite). Also generates and uploads a preview, then marks the component as generated.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
limit: { type: "number", description: "How many pending components to generate.", default: 1 },
|
|
72
|
+
framework: { type: "string", description: "Target: react or react+vite (both output React components).", default: "react" },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
52
76
|
{
|
|
53
77
|
name: "get_annotations",
|
|
54
78
|
description: "Fetch feedback. Keys ('VG-XXXX') or IDs. Workflow: 1. Fetch, 2. Fix, 3. Mark Ready.",
|
|
@@ -66,7 +90,7 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
66
90
|
},
|
|
67
91
|
{
|
|
68
92
|
name: "mark_annotation_ready",
|
|
69
|
-
description: "Mark as ready/applied. Use internal IDs.",
|
|
93
|
+
description: "Mark as ready/applied. Use internal IDs. IMPORTANT: appliedChanges must be in the project's preferredLanguage (e.g. SPANISH).",
|
|
70
94
|
inputSchema: {
|
|
71
95
|
type: "object",
|
|
72
96
|
properties: {
|
|
@@ -76,7 +100,7 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
76
100
|
type: "object",
|
|
77
101
|
properties: {
|
|
78
102
|
id: { type: "string", description: "Internal ID." },
|
|
79
|
-
appliedChanges: { type: "string", description: "Summary." }
|
|
103
|
+
appliedChanges: { type: "string", description: "Summary of changes. (IMPORTANT: Must be in SPANISH if project is ES)" }
|
|
80
104
|
},
|
|
81
105
|
required: ["id", "appliedChanges"]
|
|
82
106
|
}
|
|
@@ -102,7 +126,7 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
102
126
|
},
|
|
103
127
|
{
|
|
104
128
|
name: "planning",
|
|
105
|
-
description: "Planning tool for backlog tickets. Fetch tickets or submit analysis.",
|
|
129
|
+
description: "Planning tool for backlog tickets. Fetch tickets or submit analysis. IMPORTANT: aiAnalysis must be in the project's preferredLanguage (e.g. SPANISH).",
|
|
106
130
|
inputSchema: {
|
|
107
131
|
type: "object",
|
|
108
132
|
properties: {
|
|
@@ -117,7 +141,7 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
117
141
|
impacto: { type: "number", minimum: 1, maximum: 3 },
|
|
118
142
|
riesgo: { type: "number", minimum: 1, maximum: 3 },
|
|
119
143
|
tipo: { type: "string", enum: ["AI-friendly", "AI-assisted", "Human-critical"] },
|
|
120
|
-
aiAnalysis: { type: "string" }
|
|
144
|
+
aiAnalysis: { type: "string", description: "Detailed analysis. (IMPORTANT: Must be in SPANISH if project is ES)" }
|
|
121
145
|
},
|
|
122
146
|
required: ["id", "complejidad", "incertidumbre", "impacto", "riesgo", "tipo", "aiAnalysis"]
|
|
123
147
|
}
|
|
@@ -165,11 +189,213 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
165
189
|
],
|
|
166
190
|
};
|
|
167
191
|
});
|
|
192
|
+
const toPascalCase = (input) => {
|
|
193
|
+
return (input || '')
|
|
194
|
+
.replace(/[^a-zA-Z0-9 ]+/g, ' ')
|
|
195
|
+
.split(' ')
|
|
196
|
+
.map((w) => w.trim())
|
|
197
|
+
.filter(Boolean)
|
|
198
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
199
|
+
.join('');
|
|
200
|
+
};
|
|
201
|
+
const normalizePropTokens = (raw) => {
|
|
202
|
+
const line = (raw || '').trim();
|
|
203
|
+
if (!line)
|
|
204
|
+
return [];
|
|
205
|
+
// Split "onClick / onChange / onFocus / onBlur" style
|
|
206
|
+
const parts = line.split('/').map((s) => s.trim()).filter(Boolean);
|
|
207
|
+
return parts.flatMap((p) => {
|
|
208
|
+
// Take part before spaces or parentheses for "variant (primary...)" patterns
|
|
209
|
+
const base = p.split('(')[0].trim();
|
|
210
|
+
const token = base.split(' ')[0].trim();
|
|
211
|
+
return token ? [token] : [];
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
const buildPropModel = (commonProps, requiredProps) => {
|
|
215
|
+
const out = {
|
|
216
|
+
hasChildren: false,
|
|
217
|
+
hasAs: false,
|
|
218
|
+
hasDataIndex: false,
|
|
219
|
+
hasAriaIndex: false,
|
|
220
|
+
common: [],
|
|
221
|
+
required: [],
|
|
222
|
+
};
|
|
223
|
+
const add = (arr, target) => {
|
|
224
|
+
for (const raw of arr) {
|
|
225
|
+
for (const token of normalizePropTokens(raw)) {
|
|
226
|
+
if (token === 'children')
|
|
227
|
+
out.hasChildren = true;
|
|
228
|
+
if (token === 'as')
|
|
229
|
+
out.hasAs = true;
|
|
230
|
+
if (token.startsWith('data-'))
|
|
231
|
+
out.hasDataIndex = true;
|
|
232
|
+
if (token.startsWith('aria-'))
|
|
233
|
+
out.hasAriaIndex = true;
|
|
234
|
+
if (token === 'ref')
|
|
235
|
+
continue; // handled via forwardRef if needed later
|
|
236
|
+
if (token === 'data-*' || token === 'aria-*')
|
|
237
|
+
continue;
|
|
238
|
+
// skip wildcard tokens, handled by index signatures
|
|
239
|
+
if (token.endsWith('*'))
|
|
240
|
+
continue;
|
|
241
|
+
target.push(token);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
add(commonProps || [], out.common);
|
|
246
|
+
add(requiredProps || [], out.required);
|
|
247
|
+
const uniq = (xs) => [...new Set(xs)].filter(Boolean);
|
|
248
|
+
out.common = uniq(out.common);
|
|
249
|
+
out.required = uniq(out.required);
|
|
250
|
+
return out;
|
|
251
|
+
};
|
|
252
|
+
const writeComponentFile = async (componentType, model) => {
|
|
253
|
+
const cwd = process.cwd();
|
|
254
|
+
const componentsDir = path.join(cwd, 'components');
|
|
255
|
+
await fsp.mkdir(componentsDir, { recursive: true });
|
|
256
|
+
const baseName = toPascalCase(componentType) || 'Component';
|
|
257
|
+
const hasTs = fs.existsSync(path.join(cwd, 'tsconfig.json'));
|
|
258
|
+
const ext = hasTs ? 'tsx' : 'jsx';
|
|
259
|
+
const fileBase = `${baseName}.${ext}`;
|
|
260
|
+
let filePath = path.join(componentsDir, fileBase);
|
|
261
|
+
if (fs.existsSync(filePath)) {
|
|
262
|
+
// never overwrite existing
|
|
263
|
+
let i = 2;
|
|
264
|
+
while (fs.existsSync(path.join(componentsDir, `${baseName}.${i}.${ext}`)))
|
|
265
|
+
i++;
|
|
266
|
+
filePath = path.join(componentsDir, `${baseName}.${i}.${ext}`);
|
|
267
|
+
}
|
|
268
|
+
const isTs = ext === 'tsx';
|
|
269
|
+
const commonOptional = model.common.map((p) => `${p}${isTs ? '?: any' : ''}`);
|
|
270
|
+
const required = model.required.map((p) => `${p}${isTs ? ': any' : ''}`);
|
|
271
|
+
const ariaIndex = model.hasAriaIndex ? (isTs ? `\n [key: \`aria-\${string}\`]: any;` : '') : '';
|
|
272
|
+
const dataIndex = model.hasDataIndex ? (isTs ? `\n [key: \`data-\${string}\`]: any;` : '') : '';
|
|
273
|
+
const propsBlock = isTs
|
|
274
|
+
? `export type ${baseName}Props = {\n${[
|
|
275
|
+
model.hasAs ? ` as?: React.ElementType;` : null,
|
|
276
|
+
model.hasChildren ? ` children?: React.ReactNode;` : null,
|
|
277
|
+
...required.map((l) => ` ${l};`),
|
|
278
|
+
...commonOptional.map((l) => ` ${l};`),
|
|
279
|
+
].filter(Boolean).join('\n')}${ariaIndex}${dataIndex}\n};`
|
|
280
|
+
: '';
|
|
281
|
+
const destructure = [
|
|
282
|
+
model.hasAs ? 'as: Comp = "div"' : null,
|
|
283
|
+
model.hasChildren ? 'children' : null,
|
|
284
|
+
...model.required,
|
|
285
|
+
...model.common,
|
|
286
|
+
'...rest'
|
|
287
|
+
].filter(Boolean).join(', ');
|
|
288
|
+
const jsxTag = model.hasAs ? '<Comp' : '<div';
|
|
289
|
+
const jsxClose = model.hasAs ? '</Comp>' : '</div>';
|
|
290
|
+
const content = isTs
|
|
291
|
+
? `import React from 'react';\n\n${propsBlock}\n\nexport function ${baseName}({ ${destructure} }: ${baseName}Props) {\n return (\n ${jsxTag} {...rest}>\n {children}\n ${jsxClose}\n );\n}\n`
|
|
292
|
+
: `import React from 'react';\n\nexport function ${baseName}(props) {\n const { ${destructure} } = props || {};\n return (\n ${jsxTag} {...rest}>\n {children}\n ${jsxClose}\n );\n}\n`;
|
|
293
|
+
await fsp.writeFile(filePath, content, 'utf8');
|
|
294
|
+
return { filePath, fileName: path.basename(filePath) };
|
|
295
|
+
};
|
|
296
|
+
const makePreviewSvg = (title, props) => {
|
|
297
|
+
const safeTitle = (title || '').replace(/[<>]/g, '');
|
|
298
|
+
const list = (props || []).slice(0, 14).map((p) => p.replace(/[<>]/g, '')).join(' • ');
|
|
299
|
+
const text = `${safeTitle}${list ? ' — ' + list : ''}`;
|
|
300
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="420">\n <defs>\n <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">\n <stop offset="0" stop-color="#0f172a"/>\n <stop offset="1" stop-color="#111827"/>\n </linearGradient>\n </defs>\n <rect width="1200" height="420" rx="32" fill="url(#g)"/>\n <rect x="40" y="40" width="1120" height="340" rx="28" fill="#0b1220" stroke="rgba(255,255,255,0.08)"/>\n <text x="80" y="150" font-family="Inter,ui-sans-serif,system-ui" font-size="46" font-weight="800" fill="#e2e8f0">${safeTitle}</text>\n <text x="80" y="220" font-family="ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace" font-size="22" font-weight="700" fill="#94a3b8">Preview (fast)</text>\n <text x="80" y="280" font-family="Inter,ui-sans-serif,system-ui" font-size="18" font-weight="600" fill="#64748b">${text}</text>\n</svg>`;
|
|
301
|
+
};
|
|
168
302
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
169
303
|
const toolName = request.params.name;
|
|
170
304
|
console.error(`[MCP] Handling tool call: ${toolName}`);
|
|
171
305
|
try {
|
|
172
306
|
switch (toolName) {
|
|
307
|
+
case "get_ui_components": {
|
|
308
|
+
const args = request.params.arguments;
|
|
309
|
+
const limit = args.limit || 1;
|
|
310
|
+
const status = args.status || 'pending';
|
|
311
|
+
const fetchUrl = new URL(`${BACKEND_URL}/api/mcp/components`);
|
|
312
|
+
fetchUrl.searchParams.append("limit", limit.toString());
|
|
313
|
+
fetchUrl.searchParams.append("status", status);
|
|
314
|
+
const response = await fetch(fetchUrl, {
|
|
315
|
+
headers: {
|
|
316
|
+
'x-api-key': apiKey,
|
|
317
|
+
...(personalKey ? { 'x-personal-key': personalKey } : {})
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
if (!response.ok) {
|
|
321
|
+
const errorBody = await response.text();
|
|
322
|
+
throw new Error(`Backend responded with ${response.status}: ${errorBody}`);
|
|
323
|
+
}
|
|
324
|
+
const data = await response.json();
|
|
325
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
326
|
+
}
|
|
327
|
+
case "generate_ui_components": {
|
|
328
|
+
const args = request.params.arguments;
|
|
329
|
+
const limit = Math.min(args.limit || 1, 10);
|
|
330
|
+
const fetchUrl = new URL(`${BACKEND_URL}/api/mcp/components`);
|
|
331
|
+
fetchUrl.searchParams.append("limit", limit.toString());
|
|
332
|
+
fetchUrl.searchParams.append("status", "pending");
|
|
333
|
+
const response = await fetch(fetchUrl, {
|
|
334
|
+
headers: {
|
|
335
|
+
'x-api-key': apiKey,
|
|
336
|
+
...(personalKey ? { 'x-personal-key': personalKey } : {})
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
const errorBody = await response.text();
|
|
341
|
+
throw new Error(`Backend responded with ${response.status}: ${errorBody}`);
|
|
342
|
+
}
|
|
343
|
+
const payload = (await response.json());
|
|
344
|
+
const items = payload?.data || [];
|
|
345
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
346
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, generated: 0, message: "No pending UI components." }, null, 2) }] };
|
|
347
|
+
}
|
|
348
|
+
const results = [];
|
|
349
|
+
for (const item of items) {
|
|
350
|
+
const componentType = item.componentType;
|
|
351
|
+
const requiredProps = (item.requiredProps || []);
|
|
352
|
+
const commonProps = (item.commonProps || []);
|
|
353
|
+
const model = buildPropModel(commonProps, requiredProps);
|
|
354
|
+
const fileInfo = await writeComponentFile(componentType, model);
|
|
355
|
+
// Fast preview generation (SVG placeholder) + upload
|
|
356
|
+
const svg = makePreviewSvg(componentType, requiredProps);
|
|
357
|
+
const svg64 = Buffer.from(svg, 'utf8').toString('base64');
|
|
358
|
+
const imageDataUrl = `data:image/svg+xml;base64,${svg64}`;
|
|
359
|
+
const uploadResp = await fetch(`${BACKEND_URL}/api/screenshots/upload`, {
|
|
360
|
+
method: 'POST',
|
|
361
|
+
headers: {
|
|
362
|
+
'Content-Type': 'application/json',
|
|
363
|
+
'x-api-key': apiKey,
|
|
364
|
+
...(personalKey ? { 'x-personal-key': personalKey } : {})
|
|
365
|
+
},
|
|
366
|
+
body: JSON.stringify({ image: imageDataUrl })
|
|
367
|
+
});
|
|
368
|
+
if (!uploadResp.ok) {
|
|
369
|
+
const errorBody = await uploadResp.text();
|
|
370
|
+
throw new Error(`Preview upload failed (${uploadResp.status}): ${errorBody}`);
|
|
371
|
+
}
|
|
372
|
+
const uploaded = (await uploadResp.json());
|
|
373
|
+
const previewUrl = uploaded?.url || null;
|
|
374
|
+
const markResp = await fetch(`${BACKEND_URL}/api/mcp/components/${item._id}/generated`, {
|
|
375
|
+
method: 'PATCH',
|
|
376
|
+
headers: {
|
|
377
|
+
'Content-Type': 'application/json',
|
|
378
|
+
'x-api-key': apiKey,
|
|
379
|
+
...(personalKey ? { 'x-personal-key': personalKey } : {})
|
|
380
|
+
},
|
|
381
|
+
body: JSON.stringify({ previewImage: previewUrl })
|
|
382
|
+
});
|
|
383
|
+
if (!markResp.ok) {
|
|
384
|
+
const errorBody = await markResp.text();
|
|
385
|
+
throw new Error(`Mark generated failed (${markResp.status}): ${errorBody}`);
|
|
386
|
+
}
|
|
387
|
+
const marked = (await markResp.json());
|
|
388
|
+
results.push({
|
|
389
|
+
componentType,
|
|
390
|
+
figmaUrl: item.figmaUrl,
|
|
391
|
+
requiredProps,
|
|
392
|
+
written: fileInfo,
|
|
393
|
+
previewImage: previewUrl,
|
|
394
|
+
db: marked?.data?._id || item._id
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return { content: [{ type: "text", text: JSON.stringify({ ok: true, generated: results.length, results }, null, 2) }] };
|
|
398
|
+
}
|
|
173
399
|
case "get_annotations": {
|
|
174
400
|
const args = request.params.arguments;
|
|
175
401
|
const limit = args.limit || 3;
|
|
@@ -260,41 +486,25 @@ function createMcpServer(apiKey, personalKey) {
|
|
|
260
486
|
const annotationsWithTips = sortedAnnotations.map((ann) => {
|
|
261
487
|
const file = ann.reference?.source?.split(':')[0];
|
|
262
488
|
const line = ann.reference?.source?.split(':')[1];
|
|
263
|
-
const pendingCorrection = Array.isArray(ann.corrections)
|
|
264
|
-
? ann.corrections.find((c) => c.status === 'pending')
|
|
265
|
-
: (typeof ann.corrections === 'string' ? { text: ann.corrections } : null);
|
|
266
489
|
const figmaLinks = Array.isArray(ann.figmaReference)
|
|
267
490
|
? ann.figmaReference.filter((fr) => fr.link).map((fr) => `[${fr.name || 'Design'}](${fr.link})`).join(', ')
|
|
268
491
|
: (ann.figmaReference && typeof ann.figmaReference === 'string' ? ann.figmaReference : null);
|
|
269
492
|
return {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
outerHtml: ann.reference?.outerHtml,
|
|
281
|
-
focusAreas: ann.focusAreas || [],
|
|
282
|
-
componentName: ann.componentName,
|
|
283
|
-
figmaReference: ann.figmaReference,
|
|
284
|
-
backendEndpoints: ann.backendEndpoints || [],
|
|
285
|
-
_ia_fix_instruction: `[AUTONOMOUS_MODE: TRUE]
|
|
286
|
-
Target: ${file} (data-vg-id="${ann.reference?.vgId}")
|
|
287
|
-
Context: ${ann.reference?.parentContext?.slice(0, 40)}...
|
|
288
|
-
${figmaLinks ? `Figma: ${figmaLinks}` : ''}
|
|
289
|
-
${ann.backendEndpoints?.length ? `Endpoints: ${ann.backendEndpoints.join(', ')}` : ''}
|
|
290
|
-
${ann.corrections && Array.isArray(ann.corrections) && ann.corrections.length > 0 ? `Feedback: ${ann.corrections[ann.corrections.length - 1].text}` : ''}
|
|
291
|
-
Task: ${ann.message}
|
|
292
|
-
Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}`
|
|
493
|
+
...ann,
|
|
494
|
+
_ia_fix_instruction: `### INSTRUCCIONES PARA LA IA (IMPORTANTE) ###
|
|
495
|
+
A continuación se presenta un ticket que requiere tu atención.
|
|
496
|
+
DEBES RESPONDER EN ESPAÑOL.
|
|
497
|
+
Proyecto: ${rawData.name || 'ViewGate'}
|
|
498
|
+
Endpoints: ${ann.backendEndpoints?.length ? ann.backendEndpoints.join(', ') : 'Ninguno'}
|
|
499
|
+
Feedback previo: ${ann.corrections && Array.isArray(ann.corrections) && ann.corrections.length > 0 ? ann.corrections[ann.corrections.length - 1].text : 'Ninguno'}
|
|
500
|
+
Tarea: ${ann.message}
|
|
501
|
+
Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}
|
|
502
|
+
(RECUERDA: Toda tu respuesta, comentarios y análisis DEBEN estar en ESPAÑOL)`
|
|
293
503
|
};
|
|
294
504
|
});
|
|
295
505
|
const langHint = rawData.preferredLanguage === 'es'
|
|
296
|
-
? "[INSTRUCTION: Project is in SPANISH. Provide all comments and aiAnalysis in SPANISH.]
|
|
297
|
-
: "[INSTRUCTION: Provide all comments and
|
|
506
|
+
? "\n*** [INSTRUCTION: Project is in SPANISH. Provide all comments, appliedChanges, and aiAnalysis in SPANISH ONLY.] ***\n\n"
|
|
507
|
+
: "\n*** [INSTRUCTION: Provide all comments and analysis in English.] ***\n\n";
|
|
298
508
|
return {
|
|
299
509
|
content: [{ type: "text", text: langHint + JSON.stringify({ preferredLanguage: rawData.preferredLanguage || 'en', annotations: annotationsWithTips }, null, 2) }],
|
|
300
510
|
};
|
|
@@ -354,8 +564,8 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}`
|
|
|
354
564
|
throw new Error(`Backend responded with ${response.status}`);
|
|
355
565
|
const data = (await response.json());
|
|
356
566
|
const langHint = data.preferredLanguage === 'es'
|
|
357
|
-
? "[INSTRUCTION: Project is in SPANISH. Provide all comments and aiAnalysis in SPANISH.]\n\n"
|
|
358
|
-
: (data.preferredLanguage === 'en' ? "[INSTRUCTION: Provide all comments and
|
|
567
|
+
? "\n*** [INSTRUCTION: Project is in SPANISH. Provide all comments, appliedChanges, and aiAnalysis in SPANISH ONLY.] ***\n\n\n\n"
|
|
568
|
+
: (data.preferredLanguage === 'en' ? "\n*** [INSTRUCTION: Provide all comments and analysis in English.] ***\n\n\n\n" : "");
|
|
359
569
|
return { content: [{ type: "text", text: langHint + JSON.stringify(data, null, 2) }] };
|
|
360
570
|
}
|
|
361
571
|
case "sync_endpoints": {
|
|
@@ -387,8 +597,8 @@ Lang: ${rawData.preferredLanguage === 'es' ? 'ES' : 'EN'}`
|
|
|
387
597
|
throw new Error(`Backend responded with ${response.status}`);
|
|
388
598
|
const data = (await response.json());
|
|
389
599
|
const langHint = data.preferredLanguage === 'es'
|
|
390
|
-
? "[INSTRUCTION: Project is in SPANISH. Provide all comments and aiAnalysis in SPANISH.]\n\n"
|
|
391
|
-
: (data.preferredLanguage === 'en' ? "[INSTRUCTION: Provide all comments and
|
|
600
|
+
? "\n*** [INSTRUCTION: Project is in SPANISH. Provide all comments, appliedChanges, and aiAnalysis in SPANISH ONLY.] ***\n\n\n\n"
|
|
601
|
+
: (data.preferredLanguage === 'en' ? "\n*** [INSTRUCTION: Provide all comments and analysis in English.] ***\n\n\n\n" : "");
|
|
392
602
|
return { content: [{ type: "text", text: langHint + JSON.stringify(data, null, 2) }] };
|
|
393
603
|
}
|
|
394
604
|
default:
|