siesa-agents 2.1.72-qa.2 → 2.1.72-qa.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/bmad/bmm/config.yaml +33 -0
  2. package/claude/skills/sa-agent-sre-sentinel/SKILL.md +180 -0
  3. package/claude/skills/sa-aplicar/SKILL.md +268 -0
  4. package/claude/skills/sa-auditar-servicio/SKILL.md +255 -0
  5. package/claude/skills/sa-nueva-transversal/SKILL.md +317 -0
  6. package/claude/skills/sa-nuevo-ambiente/SKILL.md +147 -0
  7. package/claude/skills/sa-nuevo-servicio/SKILL.md +530 -0
  8. package/claude/skills/sa-onboard-db/SKILL.md +122 -0
  9. package/claude/skills/sa-qa-data-generator/SKILL.md +200 -51
  10. package/claude/skills/sa-quality-process/SKILL.md +6 -0
  11. package/claude/skills/sa-registrar-permisos/SKILL.md +233 -0
  12. package/package.json +1 -1
  13. package/siesa-agents/bmm/workflows/3-solutioning/quality-process/prompts/prompt_design_test.md +1037 -60
  14. package/siesa-agents/bmm/workflows/3-solutioning/quality-process/prompts/prompt_dor_gate.md +419 -379
  15. package/siesa-agents/bmm/workflows/3-solutioning/quality-process/prompts/prompt_e2e_executor.md +273 -0
  16. package/siesa-agents/bmm/workflows/3-solutioning/quality-process/prompts/prompt_playwright_impl.md +359 -355
  17. package/siesa-agents/bmm/workflows/3-solutioning/quality-process/prompts/prompt_test_plan.md +73 -111
  18. package/siesa-agents/bmm/workflows/3-solutioning/quality-process/workflow.md +1320 -997
  19. package/siesa-agents/resources/playwright-kit/.env.example +42 -0
  20. package/siesa-agents/resources/playwright-kit/README.md +228 -0
  21. package/siesa-agents/resources/playwright-kit/allure/categories.json +26 -0
  22. package/siesa-agents/resources/playwright-kit/global-setup.ts +34 -0
  23. package/siesa-agents/resources/playwright-kit/helpers/blazor-e2e-helpers.ts +417 -0
  24. package/siesa-agents/resources/playwright-kit/helpers/react-e2e-helpers.ts +297 -0
  25. package/siesa-agents/resources/playwright-kit/package-lock.json +850 -0
  26. package/siesa-agents/resources/playwright-kit/package.json +27 -0
  27. package/siesa-agents/resources/playwright-kit/playwright.config.ts +60 -0
  28. package/siesa-agents/resources/playwright-kit/proposed/.gitkeep +0 -0
  29. package/siesa-agents/resources/playwright-kit/reporters/agiletest-reporter.ts +252 -0
  30. package/siesa-agents/resources/playwright-kit/specs/.gitkeep +0 -0
  31. package/siesa-agents/resources/playwright-kit/specs/architecture-brief-siesa-erp.md +85 -0
  32. package/siesa-agents/resources/playwright-kit/specs/architecture-brief-siesa-react.md +109 -0
  33. package/siesa-agents/resources/playwright-kit/specs/architecture-brief-template.md +69 -0
  34. package/siesa-agents/resources/playwright-kit/tests/seed-example.spec.ts +95 -0
  35. package/siesa-agents/resources/playwright-kit/tools/locator-overlay.js +1306 -0
  36. package/siesa-agents/resources/playwright-kit/tools/locator-recorder.spec.ts +986 -0
  37. package/siesa-agents/scripts/__pycache__/bmad_to_agiletest.cpython-36.pyc +0 -0
  38. package/siesa-agents/scripts/bmad_to_agiletest.py +352 -337
  39. package/siesa-agents/scripts/converters/convert_csv_to_test_design.py +327 -0
  40. package/siesa-agents/scripts/converters/convert_excel_to_test_design.py +360 -0
  41. package/siesa-agents/scripts/converters/convert_md_to_test_design.py +359 -0
  42. package/siesa-agents/scripts/converters/convert_pdf_to_test_design.py +412 -0
  43. package/siesa-agents/scripts/converters/export_yaml_to_csv.py +149 -0
  44. package/siesa-agents/scripts/converters/export_yaml_to_excel.py +233 -0
  45. package/siesa-agents/scripts/converters/export_yaml_to_markdown.py +172 -0
  46. package/siesa-agents/scripts/converters/export_yaml_to_pdf.py +297 -0
  47. package/siesa-agents/scripts/merge_test_design.py +106 -106
  48. package/siesa-agents/scripts/playwright/yaml-to-playwright-spec.ts +203 -0
  49. package/siesa-agents/scripts/requirements.txt +7 -0
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ convert_md_to_test_design.py — Convierte Markdown a test-design YAML
4
+ Lee MD con secciones ## (key-value en listas) + tabla pipe de pasos.
5
+ Uso: python convert_md_to_test_design.py <input.md> [output.yml]
6
+ """
7
+
8
+ import re
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ try:
13
+ import yaml
14
+ except ImportError:
15
+ print("ERROR: pyyaml requerido. Instalar con: pip install pyyaml")
16
+ sys.exit(1)
17
+
18
+
19
+ # --- YAML formatting helpers ---
20
+
21
+ class LiteralStr(str):
22
+ pass
23
+
24
+
25
+ def literal_representer(dumper, data):
26
+ return dumper.represent_scalar("tag:yaml.org,2002:str", data, style=">")
27
+
28
+
29
+ yaml.add_representer(LiteralStr, literal_representer)
30
+
31
+
32
+ def yaml_str_representer(dumper, data):
33
+ if any(c in data for c in (":", "{", "}", "[", "]", ",", "&", "*", "?", "|", ">", "!", "%", "@", "`")):
34
+ return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"')
35
+ return dumper.represent_scalar("tag:yaml.org,2002:str", data)
36
+
37
+
38
+ yaml.add_representer(str, yaml_str_representer)
39
+
40
+
41
+ # --- Parsing ---
42
+
43
+ def parse_pipe_list(value: str) -> list[str]:
44
+ if not value:
45
+ return []
46
+ return [v.strip() for v in value.split("|") if v.strip()]
47
+
48
+
49
+ def parse_sections(text: str) -> dict[str, list[str]]:
50
+ """Divide el markdown en secciones por headers ##."""
51
+ sections = {}
52
+ current = "_title"
53
+ sections[current] = []
54
+
55
+ for line in text.split("\n"):
56
+ stripped = line.strip()
57
+ if stripped.startswith("## "):
58
+ current = stripped[3:].strip().lower()
59
+ # Normalizar nombres de seccion
60
+ current = current.replace(" de pruebas", "_pruebas").replace(" de prueba", "_prueba")
61
+ sections[current] = []
62
+ elif stripped.startswith("# "):
63
+ sections["_title"] = [stripped[2:].strip()]
64
+ else:
65
+ sections[current].append(line)
66
+
67
+ return sections
68
+
69
+
70
+ def parse_kv_from_list(lines: list[str]) -> list[tuple[str, str]]:
71
+ """Extrae key-value de lineas con formato '- **key**: value'."""
72
+ pairs = []
73
+ for line in lines:
74
+ match = re.match(r"^-\s+\*\*(.+?)\*\*:\s*(.*)$", line.strip())
75
+ if match:
76
+ pairs.append((match.group(1).strip(), match.group(2).strip()))
77
+ return pairs
78
+
79
+
80
+ def parse_table(lines: list[str]) -> list[dict]:
81
+ """Parsea tabla Markdown pipe como lista de dicts."""
82
+ # Encontrar header y filas
83
+ table_lines = [l.strip() for l in lines if l.strip().startswith("|")]
84
+ if len(table_lines) < 3: # header + separator + al menos 1 fila
85
+ return []
86
+
87
+ # Parsear header
88
+ raw_headers = [c.strip() for c in table_lines[0].split("|")]
89
+ raw_headers = [h for h in raw_headers if h] # quitar vacios
90
+ header_map = {
91
+ "paso": "paso", "nombre": "nombre", "accion": "accion",
92
+ "resultado esperado": "resultado", "datos": "datos",
93
+ "script": "script", "automatizado": "automatizado",
94
+ "bloqueado": "bloqueado", "nota": "nota",
95
+ }
96
+ headers = [header_map.get(h.lower(), h.lower()) for h in raw_headers]
97
+
98
+ pasos = []
99
+ for row_line in table_lines[2:]: # skip header + separator
100
+ # Splitear respetando escaped pipes (\|)
101
+ cells = re.split(r"(?<!\\)\|", row_line)
102
+ cells = [c.strip().replace("\\|", "|") for c in cells if c.strip() or cells.index(c) not in (0, len(cells)-1)]
103
+ # Filtrar celdas vacias del inicio/final del split
104
+ cells = [c.strip().replace("\\|", "|") for c in row_line.split("|")][1:-1]
105
+
106
+ if not cells or not cells[0].strip():
107
+ continue
108
+
109
+ row_dict = {}
110
+ for i, val in enumerate(cells):
111
+ if i < len(headers):
112
+ row_dict[headers[i]] = val.strip().replace("\\|", "|")
113
+
114
+ paso_val = row_dict.get("paso", "").strip()
115
+ if paso_val.isdigit():
116
+ paso_key = int(paso_val)
117
+ else:
118
+ paso_key = paso_val
119
+
120
+ step = {"paso": paso_key}
121
+ step["nombre"] = row_dict.get("nombre", "")
122
+ step["accion"] = row_dict.get("accion", "")
123
+ step["resultado"] = row_dict.get("resultado", "")
124
+
125
+ datos = row_dict.get("datos", "")
126
+ if datos:
127
+ step["datos"] = datos
128
+
129
+ script = row_dict.get("script", "")
130
+ if script:
131
+ step["script"] = script
132
+
133
+ auto_str = row_dict.get("automatizado", "false").strip().lower()
134
+ step["automatizado"] = auto_str == "true"
135
+
136
+ bloqueado = row_dict.get("bloqueado", "")
137
+ if bloqueado:
138
+ step["bloqueado"] = bloqueado
139
+
140
+ nota = row_dict.get("nota", "")
141
+ if nota:
142
+ step["nota"] = nota
143
+
144
+ pasos.append(step)
145
+
146
+ return pasos
147
+
148
+
149
+ # --- YAML builders ---
150
+
151
+ def get_kv(pairs: list[tuple[str, str]], key: str, default: str = "") -> str:
152
+ d = dict(pairs)
153
+ return d.get(key, default)
154
+
155
+
156
+ def build_metadata_section(pairs: list[tuple[str, str]]) -> dict:
157
+ d = dict(pairs)
158
+ result = {}
159
+ for field in ("modulo", "version", "autor", "fecha", "stack"):
160
+ if field in d:
161
+ result[field] = d[field]
162
+ return result
163
+
164
+
165
+ def build_requisito_section(pairs: list[tuple[str, str]]) -> dict:
166
+ d = dict(pairs)
167
+ result = {}
168
+ for field in ("id", "nombre"):
169
+ if field in d:
170
+ result[field] = d[field]
171
+ desc = d.get("descripcion", "")
172
+ if desc:
173
+ result["descripcion"] = LiteralStr(desc)
174
+ criterios = parse_pipe_list(d.get("criterios_aceptacion", ""))
175
+ if criterios:
176
+ result["criterios_aceptacion"] = criterios
177
+ return result
178
+
179
+
180
+ def build_plan_section(pairs: list[tuple[str, str]]) -> dict:
181
+ d = dict(pairs)
182
+ result = {}
183
+ for field in ("id", "nombre", "framework"):
184
+ if field in d:
185
+ result[field] = d[field]
186
+ objetivo = d.get("objetivo", "")
187
+ if objetivo:
188
+ result["objetivo"] = LiteralStr(objetivo)
189
+
190
+ precondiciones = parse_pipe_list(d.get("precondiciones", ""))
191
+ if precondiciones:
192
+ result["precondiciones"] = precondiciones
193
+
194
+ ambientes = []
195
+ i = 0
196
+ while True:
197
+ nombre = d.get(f"ambiente_{i}.nombre", "")
198
+ if not nombre:
199
+ break
200
+ amb = {"nombre": nombre}
201
+ url = d.get(f"ambiente_{i}.url", "")
202
+ if url:
203
+ amb["url"] = url
204
+ usuario = d.get(f"ambiente_{i}.usuario", "")
205
+ if usuario:
206
+ amb["usuario"] = usuario
207
+ amb["validado"] = True
208
+ ambientes.append(amb)
209
+ i += 1
210
+ if ambientes:
211
+ result["ambientes"] = ambientes
212
+
213
+ tiempos = {}
214
+ for key, val in pairs:
215
+ if key.startswith("tiempos."):
216
+ t_key = key.replace("tiempos.", "")
217
+ tiempos[t_key] = val
218
+ if tiempos:
219
+ result["tiempos_referencia"] = tiempos
220
+
221
+ return result
222
+
223
+
224
+ def build_caso_prueba_section(pairs: list[tuple[str, str]], pasos: list[dict]) -> dict:
225
+ d = dict(pairs)
226
+ result = {}
227
+ for field in ("id", "nombre", "prioridad", "tipo"):
228
+ if field in d:
229
+ result[field] = d[field]
230
+ scripts = parse_pipe_list(d.get("scripts", ""))
231
+ if scripts:
232
+ result["scripts"] = scripts
233
+ result["pasos"] = pasos
234
+ return result
235
+
236
+
237
+ def build_ejecucion_section(pairs: list[tuple[str, str]]) -> dict:
238
+ d = dict(pairs)
239
+ result = {}
240
+ for field in ("id", "nombre"):
241
+ if field in d:
242
+ result[field] = d[field]
243
+
244
+ variables = []
245
+ comandos = {}
246
+ for key, val in pairs:
247
+ if key.startswith("variable."):
248
+ var_name = key.replace("variable.", "")
249
+ parts = [p.strip() for p in val.split("|")]
250
+ var = {"nombre": var_name}
251
+ if len(parts) >= 1:
252
+ var["descripcion"] = parts[0]
253
+ if len(parts) >= 2:
254
+ var["default"] = parts[1]
255
+ variables.append(var)
256
+ elif key.startswith("comando."):
257
+ cmd_name = key.replace("comando.", "")
258
+ comandos[cmd_name] = val
259
+
260
+ if variables:
261
+ result["variables_entorno"] = variables
262
+ if comandos:
263
+ result["comandos"] = comandos
264
+ return result
265
+
266
+
267
+ def build_integraciones_section(pairs: list[tuple[str, str]]) -> dict:
268
+ result = {}
269
+ for key, val in pairs:
270
+ parts = key.split(".", 1)
271
+ if len(parts) == 2:
272
+ integ_name, field = parts
273
+ if integ_name not in result:
274
+ result[integ_name] = {}
275
+ result[integ_name][field] = val
276
+ return result
277
+
278
+
279
+ def main():
280
+ if len(sys.argv) < 2:
281
+ print("Uso: python convert_md_to_test_design.py <input.md> [output.yml]")
282
+ sys.exit(1)
283
+
284
+ input_path = Path(sys.argv[1])
285
+ if not input_path.exists():
286
+ print(f"ERROR: No se encontro {input_path}")
287
+ sys.exit(1)
288
+
289
+ if len(sys.argv) >= 3:
290
+ output_path = Path(sys.argv[2])
291
+ else:
292
+ output_path = input_path.with_name(
293
+ input_path.stem.replace("test-cases-", "test-design-") + ".yml"
294
+ )
295
+
296
+ with open(input_path, "r", encoding="utf-8") as f:
297
+ text = f.read()
298
+
299
+ sections = parse_sections(text)
300
+
301
+ # Parsear secciones key-value
302
+ meta_kv = parse_kv_from_list(sections.get("metadata", []))
303
+ req_kv = parse_kv_from_list(sections.get("requisito", []))
304
+ plan_kv = parse_kv_from_list(sections.get("plan_pruebas", []))
305
+ caso_kv = parse_kv_from_list(sections.get("caso_prueba", []))
306
+ ejec_kv = parse_kv_from_list(sections.get("ejecucion", []))
307
+ integ_kv = parse_kv_from_list(sections.get("integraciones", []))
308
+
309
+ # Parsear tabla de pasos
310
+ pasos = parse_table(sections.get("pasos", []))
311
+
312
+ # Construir YAML
313
+ autor = get_kv(meta_kv, "autor", "QA Team")
314
+ doc = {}
315
+
316
+ metadata = build_metadata_section(meta_kv)
317
+ if metadata:
318
+ doc["metadata"] = metadata
319
+
320
+ requisito = build_requisito_section(req_kv)
321
+ if requisito:
322
+ doc["requisito"] = requisito
323
+
324
+ plan = build_plan_section(plan_kv)
325
+ if plan:
326
+ doc["plan_pruebas"] = plan
327
+
328
+ caso = build_caso_prueba_section(caso_kv, pasos)
329
+ doc["caso_prueba"] = caso
330
+
331
+ ejecucion = build_ejecucion_section(ejec_kv)
332
+ if ejecucion:
333
+ doc["ejecucion"] = ejecucion
334
+
335
+ integraciones = build_integraciones_section(integ_kv)
336
+ if integraciones:
337
+ doc["integraciones"] = integraciones
338
+
339
+ header = (
340
+ f"# test-design generado desde {input_path.name}\n"
341
+ f"# Input para: AgileTest + Playwright + Agente de Datos\n"
342
+ f"# Autor: {autor}\n\n"
343
+ )
344
+
345
+ with open(output_path, "w", encoding="utf-8") as f:
346
+ f.write(header)
347
+ yaml.dump(
348
+ doc, f,
349
+ default_flow_style=False,
350
+ allow_unicode=True,
351
+ sort_keys=False,
352
+ width=120,
353
+ )
354
+
355
+ print(f"OK: {input_path.name} -> {output_path.name} ({len(pasos)} pasos convertidos)")
356
+
357
+
358
+ if __name__ == "__main__":
359
+ main()