zugzbot-sdd 1.5.25 → 1.5.26
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.
|
@@ -3,7 +3,7 @@ import fs from "fs";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { execSync } from "child_process";
|
|
5
5
|
export default tool({
|
|
6
|
-
description: "Audita estáticamente cualquier archivo (o conjunto de archivos modificados) para diagnosticar fallas de sintaxis, paréntesis/llaves rotas, errores sintácticos o problemas de formato en múltiples tecnologías (JS, TS, Python, JSON, HTML).",
|
|
6
|
+
description: "Audita estáticamente cualquier archivo (o conjunto de archivos modificados) para diagnosticar fallas de sintaxis, paréntesis/llaves rotas, errores sintácticos o problemas de formato en múltiples tecnologías (JS, TS, Python, JSON, HTML, GS, CSS, CPP, SH, YAML, PHP, MD).",
|
|
7
7
|
args: {
|
|
8
8
|
filePath: tool.schema.string().optional().describe("Ruta absoluta o relativa del archivo a auditar de forma quirúrgica. Si se omite, detectará automáticamente los archivos modificados vía git.")
|
|
9
9
|
},
|
|
@@ -24,7 +24,6 @@ export default tool({
|
|
|
24
24
|
gitStatus.split("\n").forEach(line => {
|
|
25
25
|
const trimmed = line.trim();
|
|
26
26
|
if (trimmed) {
|
|
27
|
-
// git status --porcelain outputs e.g., "M src/index.js" or "A src/test.js"
|
|
28
27
|
const parts = trimmed.split(/\s+/);
|
|
29
28
|
if (parts.length >= 2) {
|
|
30
29
|
const relPath = parts.slice(1).join(" ");
|
|
@@ -45,12 +44,45 @@ export default tool({
|
|
|
45
44
|
diagnostics: []
|
|
46
45
|
}, null, 2);
|
|
47
46
|
}
|
|
47
|
+
// Heurística de balanceo de paréntesis/llaves/corchetes general
|
|
48
|
+
const hasBalancedTokens = (str) => {
|
|
49
|
+
const stack = [];
|
|
50
|
+
const pairs = { ')': '(', '}': '{', ']': '[' };
|
|
51
|
+
let lineNum = 1;
|
|
52
|
+
for (let i = 0; i < str.length; i++) {
|
|
53
|
+
const char = str[i];
|
|
54
|
+
if (char === '\n') {
|
|
55
|
+
lineNum++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (['(', '{', '['].includes(char)) {
|
|
59
|
+
stack.push({ char, index: i, line: lineNum });
|
|
60
|
+
}
|
|
61
|
+
else if ([')', '}', ']'].includes(char)) {
|
|
62
|
+
const top = stack.pop();
|
|
63
|
+
if (!top || top.char !== pairs[char]) {
|
|
64
|
+
return {
|
|
65
|
+
balanced: false,
|
|
66
|
+
message: `Error de balanceo: Se encontró '${char}' en línea ${lineNum} pero se esperaba cierre para '${top ? top.char : 'ninguno'}' (línea ${top ? top.line : 'N/A'}).`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (stack.length > 0) {
|
|
72
|
+
const unclosed = stack.pop();
|
|
73
|
+
return {
|
|
74
|
+
balanced: false,
|
|
75
|
+
message: `Error de balanceo: El token '${unclosed.char}' abierto en la línea ${unclosed.line} nunca fue cerrado.`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return { balanced: true };
|
|
79
|
+
};
|
|
48
80
|
for (const file of filesToAudit) {
|
|
49
|
-
const ext = path.extname(file);
|
|
81
|
+
const ext = path.extname(file).toLowerCase();
|
|
50
82
|
const relFile = path.relative(projectRoot, file);
|
|
51
83
|
try {
|
|
52
84
|
const content = fs.readFileSync(file, "utf-8");
|
|
53
|
-
// 1.
|
|
85
|
+
// 1. JSON
|
|
54
86
|
if (ext === ".json") {
|
|
55
87
|
try {
|
|
56
88
|
JSON.parse(content);
|
|
@@ -65,53 +97,34 @@ export default tool({
|
|
|
65
97
|
});
|
|
66
98
|
}
|
|
67
99
|
}
|
|
68
|
-
// 2.
|
|
100
|
+
// 2. JavaScript / Google Apps Script (.js, .jsx, .gs)
|
|
69
101
|
else if (ext === ".js" || ext === ".jsx" || ext === ".gs") {
|
|
70
102
|
try {
|
|
71
103
|
execSync(`node --check "${file}"`, { stdio: "pipe" });
|
|
72
104
|
}
|
|
73
105
|
catch (err) {
|
|
74
106
|
const stderr = err.stderr?.toString() || err.message || "";
|
|
75
|
-
// Parse line number from node check, e.g. "index.js:5" or "line 5"
|
|
76
107
|
const lineMatch = stderr.match(/:(\d+)\r?\n/m) || stderr.match(/line (\d+)/i);
|
|
77
108
|
diagnostics.push({
|
|
78
109
|
file: relFile,
|
|
79
|
-
type: "
|
|
110
|
+
type: "JS/GS Syntax Error",
|
|
80
111
|
line: lineMatch ? parseInt(lineMatch[1]) : undefined,
|
|
81
112
|
message: stderr.split("\n")[0] || "Syntax error detected"
|
|
82
113
|
});
|
|
83
114
|
}
|
|
84
115
|
}
|
|
85
|
-
// 3.
|
|
116
|
+
// 3. TypeScript / TSX
|
|
86
117
|
else if (ext === ".ts" || ext === ".tsx") {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (['(', '{', '['].includes(char)) {
|
|
95
|
-
stack.push(char);
|
|
96
|
-
}
|
|
97
|
-
else if ([')', '}', ']'].includes(char)) {
|
|
98
|
-
if (stack.pop() !== pairs[char])
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return stack.length === 0;
|
|
103
|
-
};
|
|
104
|
-
if (!hasBalancedParentheses(content)) {
|
|
105
|
-
diagnostics.push({
|
|
106
|
-
file: relFile,
|
|
107
|
-
type: "TypeScript Token Mismatch",
|
|
108
|
-
message: "Falla de balanceo en paréntesis, llaves o corchetes detectada mediante auditoría estática."
|
|
109
|
-
});
|
|
110
|
-
}
|
|
118
|
+
const balanceCheck = hasBalancedTokens(content);
|
|
119
|
+
if (!balanceCheck.balanced) {
|
|
120
|
+
diagnostics.push({
|
|
121
|
+
file: relFile,
|
|
122
|
+
type: "TypeScript Syntax/Token Mismatch",
|
|
123
|
+
message: balanceCheck.message || "Falla de balanceo en tokens estructurados."
|
|
124
|
+
});
|
|
111
125
|
}
|
|
112
|
-
catch (err) { }
|
|
113
126
|
}
|
|
114
|
-
// 4.
|
|
127
|
+
// 4. Python (.py)
|
|
115
128
|
else if (ext === ".py") {
|
|
116
129
|
try {
|
|
117
130
|
execSync(`python -m py_compile "${file}"`, { stdio: "pipe" });
|
|
@@ -127,7 +140,7 @@ export default tool({
|
|
|
127
140
|
});
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
|
-
// 5.
|
|
143
|
+
// 5. HTML
|
|
131
144
|
else if (ext === ".html") {
|
|
132
145
|
const unclosedTags = [];
|
|
133
146
|
const tags = content.match(/<[^>]+>/g) || [];
|
|
@@ -157,11 +170,92 @@ export default tool({
|
|
|
157
170
|
});
|
|
158
171
|
}
|
|
159
172
|
}
|
|
173
|
+
// 6. Shell Scripts (.sh, .bash)
|
|
174
|
+
else if (ext === ".sh" || ext === ".bash") {
|
|
175
|
+
try {
|
|
176
|
+
execSync(`bash -n "${file}"`, { stdio: "pipe" });
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
const stderr = err.stderr?.toString() || err.message || "";
|
|
180
|
+
const lineMatch = stderr.match(/: line (\d+):/i) || stderr.match(/:(\d+):/i);
|
|
181
|
+
diagnostics.push({
|
|
182
|
+
file: relFile,
|
|
183
|
+
type: "Shell Script Syntax Error",
|
|
184
|
+
line: lineMatch ? parseInt(lineMatch[1]) : undefined,
|
|
185
|
+
message: stderr.split("\n")[0] || "Shell script syntax check failed"
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// 7. CSS
|
|
190
|
+
else if (ext === ".css") {
|
|
191
|
+
const balanceCheck = hasBalancedTokens(content);
|
|
192
|
+
if (!balanceCheck.balanced) {
|
|
193
|
+
diagnostics.push({
|
|
194
|
+
file: relFile,
|
|
195
|
+
type: "CSS Rule Mismatch",
|
|
196
|
+
message: `Falla de balanceo en CSS: ${balanceCheck.message}`
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// 8. C / C++ (.c, .cpp, .h, .hpp)
|
|
201
|
+
else if (ext === ".c" || ext === ".cpp" || ext === ".h" || ext === ".hpp") {
|
|
202
|
+
const balanceCheck = hasBalancedTokens(content);
|
|
203
|
+
if (!balanceCheck.balanced) {
|
|
204
|
+
diagnostics.push({
|
|
205
|
+
file: relFile,
|
|
206
|
+
type: "C/C++ Syntax Token Mismatch",
|
|
207
|
+
message: `Falla estructural C/C++: ${balanceCheck.message}`
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// 9. YAML / YML
|
|
212
|
+
else if (ext === ".yaml" || ext === ".yml") {
|
|
213
|
+
// Chequeo básico de indentación y formato YAML sin requerir librerías externas pesadas
|
|
214
|
+
const lines = content.split("\n");
|
|
215
|
+
lines.forEach((line, idx) => {
|
|
216
|
+
const spaces = line.match(/^(\s*)/)?.[1].length || 0;
|
|
217
|
+
if (spaces % 2 !== 0 && line.trim().length > 0 && !line.trim().startsWith("#")) {
|
|
218
|
+
diagnostics.push({
|
|
219
|
+
file: relFile,
|
|
220
|
+
type: "YAML Indentation Warning",
|
|
221
|
+
line: idx + 1,
|
|
222
|
+
message: `Indentación sospechosa (${spaces} espacios). YAML prefiere múltiplos de 2.`
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
// 10. PHP
|
|
228
|
+
else if (ext === ".php") {
|
|
229
|
+
try {
|
|
230
|
+
execSync(`php -l "${file}"`, { stdio: "pipe" });
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
const stderr = err.stderr?.toString() || err.message || "";
|
|
234
|
+
const lineMatch = stderr.match(/on line (\d+)/i);
|
|
235
|
+
diagnostics.push({
|
|
236
|
+
file: relFile,
|
|
237
|
+
type: "PHP Syntax Error",
|
|
238
|
+
line: lineMatch ? parseInt(lineMatch[1]) : undefined,
|
|
239
|
+
message: stderr.split("\n")[0] || "PHP syntax check failed"
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// 11. Markdown (.md)
|
|
244
|
+
else if (ext === ".md") {
|
|
245
|
+
const openBlocks = (content.match(/^```/gm) || []).length;
|
|
246
|
+
if (openBlocks % 2 !== 0) {
|
|
247
|
+
diagnostics.push({
|
|
248
|
+
file: relFile,
|
|
249
|
+
type: "Markdown Fenced Block Mismatch",
|
|
250
|
+
message: "Se detectó un bloque de código fenced (```) sin cerrar en el archivo markdown."
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
160
254
|
}
|
|
161
255
|
catch (e) {
|
|
162
256
|
diagnostics.push({
|
|
163
257
|
file: relFile,
|
|
164
|
-
type: "Read Error",
|
|
258
|
+
type: "Read/Compile Error",
|
|
165
259
|
message: `Imposible leer o analizar el archivo: ${e.message}`
|
|
166
260
|
});
|
|
167
261
|
}
|
|
@@ -169,7 +263,7 @@ export default tool({
|
|
|
169
263
|
if (diagnostics.length > 0) {
|
|
170
264
|
return JSON.stringify({
|
|
171
265
|
status: "FAILED",
|
|
172
|
-
message: `❌ VALIDACIÓN DE SINTAXIS FALLIDA: Se detectaron ${diagnostics.length} errores
|
|
266
|
+
message: `❌ VALIDACIÓN DE SINTAXIS MULTI-TECNOLOGÍA FALLIDA: Se detectaron ${diagnostics.length} errores estáticos o de formato en los archivos modificados.`,
|
|
173
267
|
diagnostics
|
|
174
268
|
}, null, 2);
|
|
175
269
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import path from "path"
|
|
|
4
4
|
import { execSync } from "child_process"
|
|
5
5
|
|
|
6
6
|
export default tool({
|
|
7
|
-
description: "Audita estáticamente cualquier archivo (o conjunto de archivos modificados) para diagnosticar fallas de sintaxis, paréntesis/llaves rotas, errores sintácticos o problemas de formato en múltiples tecnologías (JS, TS, Python, JSON, HTML).",
|
|
7
|
+
description: "Audita estáticamente cualquier archivo (o conjunto de archivos modificados) para diagnosticar fallas de sintaxis, paréntesis/llaves rotas, errores sintácticos o problemas de formato en múltiples tecnologías (JS, TS, Python, JSON, HTML, GS, CSS, CPP, SH, YAML, PHP, MD).",
|
|
8
8
|
args: {
|
|
9
9
|
filePath: tool.schema.string().optional().describe("Ruta absoluta o relativa del archivo a auditar de forma quirúrgica. Si se omite, detectará automáticamente los archivos modificados vía git.")
|
|
10
10
|
},
|
|
@@ -26,7 +26,6 @@ export default tool({
|
|
|
26
26
|
gitStatus.split("\n").forEach(line => {
|
|
27
27
|
const trimmed = line.trim()
|
|
28
28
|
if (trimmed) {
|
|
29
|
-
// git status --porcelain outputs e.g., "M src/index.js" or "A src/test.js"
|
|
30
29
|
const parts = trimmed.split(/\s+/)
|
|
31
30
|
if (parts.length >= 2) {
|
|
32
31
|
const relPath = parts.slice(1).join(" ")
|
|
@@ -48,14 +47,51 @@ export default tool({
|
|
|
48
47
|
}, null, 2)
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
// Heurística de balanceo de paréntesis/llaves/corchetes general
|
|
51
|
+
const hasBalancedTokens = (str: string): { balanced: boolean; message?: string } => {
|
|
52
|
+
const stack: { char: string; index: number; line: number }[] = []
|
|
53
|
+
const pairs: Record<string, string> = { ')': '(', '}': '{', ']': '[' }
|
|
54
|
+
let lineNum = 1
|
|
55
|
+
|
|
56
|
+
for (let i = 0; i < str.length; i++) {
|
|
57
|
+
const char = str[i]
|
|
58
|
+
if (char === '\n') {
|
|
59
|
+
lineNum++
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (['(', '{', '['].includes(char)) {
|
|
64
|
+
stack.push({ char, index: i, line: lineNum })
|
|
65
|
+
} else if ([')', '}', ']'].includes(char)) {
|
|
66
|
+
const top = stack.pop()
|
|
67
|
+
if (!top || top.char !== pairs[char]) {
|
|
68
|
+
return {
|
|
69
|
+
balanced: false,
|
|
70
|
+
message: `Error de balanceo: Se encontró '${char}' en línea ${lineNum} pero se esperaba cierre para '${top ? top.char : 'ninguno'}' (línea ${top ? top.line : 'N/A'}).`
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (stack.length > 0) {
|
|
77
|
+
const unclosed = stack.pop()!
|
|
78
|
+
return {
|
|
79
|
+
balanced: false,
|
|
80
|
+
message: `Error de balanceo: El token '${unclosed.char}' abierto en la línea ${unclosed.line} nunca fue cerrado.`
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { balanced: true }
|
|
85
|
+
}
|
|
86
|
+
|
|
51
87
|
for (const file of filesToAudit) {
|
|
52
|
-
const ext = path.extname(file)
|
|
88
|
+
const ext = path.extname(file).toLowerCase()
|
|
53
89
|
const relFile = path.relative(projectRoot, file)
|
|
54
90
|
|
|
55
91
|
try {
|
|
56
92
|
const content = fs.readFileSync(file, "utf-8")
|
|
57
93
|
|
|
58
|
-
// 1.
|
|
94
|
+
// 1. JSON
|
|
59
95
|
if (ext === ".json") {
|
|
60
96
|
try {
|
|
61
97
|
JSON.parse(content)
|
|
@@ -70,51 +106,35 @@ export default tool({
|
|
|
70
106
|
}
|
|
71
107
|
}
|
|
72
108
|
|
|
73
|
-
// 2.
|
|
109
|
+
// 2. JavaScript / Google Apps Script (.js, .jsx, .gs)
|
|
74
110
|
else if (ext === ".js" || ext === ".jsx" || ext === ".gs") {
|
|
75
111
|
try {
|
|
76
112
|
execSync(`node --check "${file}"`, { stdio: "pipe" })
|
|
77
113
|
} catch (err: any) {
|
|
78
114
|
const stderr = err.stderr?.toString() || err.message || ""
|
|
79
|
-
// Parse line number from node check, e.g. "index.js:5" or "line 5"
|
|
80
115
|
const lineMatch = stderr.match(/:(\d+)\r?\n/m) || stderr.match(/line (\d+)/i)
|
|
81
116
|
diagnostics.push({
|
|
82
117
|
file: relFile,
|
|
83
|
-
type: "
|
|
118
|
+
type: "JS/GS Syntax Error",
|
|
84
119
|
line: lineMatch ? parseInt(lineMatch[1]) : undefined,
|
|
85
120
|
message: stderr.split("\n")[0] || "Syntax error detected"
|
|
86
121
|
})
|
|
87
122
|
}
|
|
88
123
|
}
|
|
89
124
|
|
|
90
|
-
// 3.
|
|
125
|
+
// 3. TypeScript / TSX
|
|
91
126
|
else if (ext === ".ts" || ext === ".tsx") {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
stack.push(char)
|
|
101
|
-
} else if ([')', '}', ']'].includes(char)) {
|
|
102
|
-
if (stack.pop() !== pairs[char]) return false
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return stack.length === 0
|
|
106
|
-
}
|
|
107
|
-
if (!hasBalancedParentheses(content)) {
|
|
108
|
-
diagnostics.push({
|
|
109
|
-
file: relFile,
|
|
110
|
-
type: "TypeScript Token Mismatch",
|
|
111
|
-
message: "Falla de balanceo en paréntesis, llaves o corchetes detectada mediante auditoría estática."
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
} catch (err: any) {}
|
|
127
|
+
const balanceCheck = hasBalancedTokens(content)
|
|
128
|
+
if (!balanceCheck.balanced) {
|
|
129
|
+
diagnostics.push({
|
|
130
|
+
file: relFile,
|
|
131
|
+
type: "TypeScript Syntax/Token Mismatch",
|
|
132
|
+
message: balanceCheck.message || "Falla de balanceo en tokens estructurados."
|
|
133
|
+
})
|
|
134
|
+
}
|
|
115
135
|
}
|
|
116
136
|
|
|
117
|
-
// 4.
|
|
137
|
+
// 4. Python (.py)
|
|
118
138
|
else if (ext === ".py") {
|
|
119
139
|
try {
|
|
120
140
|
execSync(`python -m py_compile "${file}"`, { stdio: "pipe" })
|
|
@@ -130,7 +150,7 @@ export default tool({
|
|
|
130
150
|
}
|
|
131
151
|
}
|
|
132
152
|
|
|
133
|
-
// 5.
|
|
153
|
+
// 5. HTML
|
|
134
154
|
else if (ext === ".html") {
|
|
135
155
|
const unclosedTags: string[] = []
|
|
136
156
|
const tags = content.match(/<[^>]+>/g) || []
|
|
@@ -161,10 +181,95 @@ export default tool({
|
|
|
161
181
|
}
|
|
162
182
|
}
|
|
163
183
|
|
|
184
|
+
// 6. Shell Scripts (.sh, .bash)
|
|
185
|
+
else if (ext === ".sh" || ext === ".bash") {
|
|
186
|
+
try {
|
|
187
|
+
execSync(`bash -n "${file}"`, { stdio: "pipe" })
|
|
188
|
+
} catch (err: any) {
|
|
189
|
+
const stderr = err.stderr?.toString() || err.message || ""
|
|
190
|
+
const lineMatch = stderr.match(/: line (\d+):/i) || stderr.match(/:(\d+):/i)
|
|
191
|
+
diagnostics.push({
|
|
192
|
+
file: relFile,
|
|
193
|
+
type: "Shell Script Syntax Error",
|
|
194
|
+
line: lineMatch ? parseInt(lineMatch[1]) : undefined,
|
|
195
|
+
message: stderr.split("\n")[0] || "Shell script syntax check failed"
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 7. CSS
|
|
201
|
+
else if (ext === ".css") {
|
|
202
|
+
const balanceCheck = hasBalancedTokens(content)
|
|
203
|
+
if (!balanceCheck.balanced) {
|
|
204
|
+
diagnostics.push({
|
|
205
|
+
file: relFile,
|
|
206
|
+
type: "CSS Rule Mismatch",
|
|
207
|
+
message: `Falla de balanceo en CSS: ${balanceCheck.message}`
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 8. C / C++ (.c, .cpp, .h, .hpp)
|
|
213
|
+
else if (ext === ".c" || ext === ".cpp" || ext === ".h" || ext === ".hpp") {
|
|
214
|
+
const balanceCheck = hasBalancedTokens(content)
|
|
215
|
+
if (!balanceCheck.balanced) {
|
|
216
|
+
diagnostics.push({
|
|
217
|
+
file: relFile,
|
|
218
|
+
type: "C/C++ Syntax Token Mismatch",
|
|
219
|
+
message: `Falla estructural C/C++: ${balanceCheck.message}`
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 9. YAML / YML
|
|
225
|
+
else if (ext === ".yaml" || ext === ".yml") {
|
|
226
|
+
// Chequeo básico de indentación y formato YAML sin requerir librerías externas pesadas
|
|
227
|
+
const lines = content.split("\n")
|
|
228
|
+
lines.forEach((line, idx) => {
|
|
229
|
+
const spaces = line.match(/^(\s*)/)?.[1].length || 0
|
|
230
|
+
if (spaces % 2 !== 0 && line.trim().length > 0 && !line.trim().startsWith("#")) {
|
|
231
|
+
diagnostics.push({
|
|
232
|
+
file: relFile,
|
|
233
|
+
type: "YAML Indentation Warning",
|
|
234
|
+
line: idx + 1,
|
|
235
|
+
message: `Indentación sospechosa (${spaces} espacios). YAML prefiere múltiplos de 2.`
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// 10. PHP
|
|
242
|
+
else if (ext === ".php") {
|
|
243
|
+
try {
|
|
244
|
+
execSync(`php -l "${file}"`, { stdio: "pipe" })
|
|
245
|
+
} catch (err: any) {
|
|
246
|
+
const stderr = err.stderr?.toString() || err.message || ""
|
|
247
|
+
const lineMatch = stderr.match(/on line (\d+)/i)
|
|
248
|
+
diagnostics.push({
|
|
249
|
+
file: relFile,
|
|
250
|
+
type: "PHP Syntax Error",
|
|
251
|
+
line: lineMatch ? parseInt(lineMatch[1]) : undefined,
|
|
252
|
+
message: stderr.split("\n")[0] || "PHP syntax check failed"
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// 11. Markdown (.md)
|
|
258
|
+
else if (ext === ".md") {
|
|
259
|
+
const openBlocks = (content.match(/^```/gm) || []).length
|
|
260
|
+
if (openBlocks % 2 !== 0) {
|
|
261
|
+
diagnostics.push({
|
|
262
|
+
file: relFile,
|
|
263
|
+
type: "Markdown Fenced Block Mismatch",
|
|
264
|
+
message: "Se detectó un bloque de código fenced (```) sin cerrar en el archivo markdown."
|
|
265
|
+
})
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
164
269
|
} catch (e: any) {
|
|
165
270
|
diagnostics.push({
|
|
166
271
|
file: relFile,
|
|
167
|
-
type: "Read Error",
|
|
272
|
+
type: "Read/Compile Error",
|
|
168
273
|
message: `Imposible leer o analizar el archivo: ${e.message}`
|
|
169
274
|
})
|
|
170
275
|
}
|
|
@@ -173,7 +278,7 @@ export default tool({
|
|
|
173
278
|
if (diagnostics.length > 0) {
|
|
174
279
|
return JSON.stringify({
|
|
175
280
|
status: "FAILED",
|
|
176
|
-
message: `❌ VALIDACIÓN DE SINTAXIS FALLIDA: Se detectaron ${diagnostics.length} errores
|
|
281
|
+
message: `❌ VALIDACIÓN DE SINTAXIS MULTI-TECNOLOGÍA FALLIDA: Se detectaron ${diagnostics.length} errores estáticos o de formato en los archivos modificados.`,
|
|
177
282
|
diagnostics
|
|
178
283
|
}, null, 2)
|
|
179
284
|
}
|