@qa-gentic/stlc-agents 1.0.27 → 1.0.28

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 (90) hide show
  1. package/ARCHITECTURE-ADO.md +350 -0
  2. package/ARCHITECTURE-JIRA.md +203 -0
  3. package/QUICKSTART-ADO.md +400 -0
  4. package/QUICKSTART-JIRA.md +334 -0
  5. package/README.md +49 -0
  6. package/package.json +18 -6
  7. package/skills/migrate-framework/SKILL.md +207 -0
  8. package/src/stlc_agents/__pycache__/__init__.cpython-313.pyc +0 -0
  9. package/src/stlc_agents/agent_gherkin_generator/__pycache__/__init__.cpython-313.pyc +0 -0
  10. package/src/stlc_agents/agent_gherkin_generator/__pycache__/server.cpython-313.pyc +0 -0
  11. package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  12. package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/ado_gherkin.cpython-313.pyc +0 -0
  13. package/src/stlc_agents/agent_helix_writer/__pycache__/__init__.cpython-313.pyc +0 -0
  14. package/src/stlc_agents/agent_helix_writer/__pycache__/server.cpython-313.pyc +0 -0
  15. package/src/stlc_agents/agent_helix_writer/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  16. package/src/stlc_agents/agent_helix_writer/tools/__pycache__/boilerplate.cpython-313.pyc +0 -0
  17. package/src/stlc_agents/agent_helix_writer/tools/__pycache__/helix_write.cpython-313.pyc +0 -0
  18. package/src/stlc_agents/agent_jira_manager/__pycache__/__init__.cpython-313.pyc +0 -0
  19. package/src/stlc_agents/agent_jira_manager/__pycache__/server.cpython-313.pyc +0 -0
  20. package/src/stlc_agents/agent_jira_manager/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  21. package/src/stlc_agents/agent_jira_manager/tools/__pycache__/jira_workitem.cpython-313.pyc +0 -0
  22. package/src/stlc_agents/agent_migration/__init__.py +0 -0
  23. package/src/stlc_agents/agent_migration/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/src/stlc_agents/agent_migration/__pycache__/_migrate.cpython-313.pyc +0 -0
  25. package/src/stlc_agents/agent_migration/__pycache__/cli.cpython-313.pyc +0 -0
  26. package/src/stlc_agents/agent_migration/__pycache__/detector.cpython-313.pyc +0 -0
  27. package/src/stlc_agents/agent_migration/__pycache__/mapper.cpython-313.pyc +0 -0
  28. package/src/stlc_agents/agent_migration/__pycache__/reporter.cpython-313.pyc +0 -0
  29. package/src/stlc_agents/agent_migration/__pycache__/server.cpython-313.pyc +0 -0
  30. package/src/stlc_agents/agent_migration/_migrate.py +1398 -0
  31. package/src/stlc_agents/agent_migration/cli.py +217 -0
  32. package/src/stlc_agents/agent_migration/detector.py +81 -0
  33. package/src/stlc_agents/agent_migration/mapper.py +439 -0
  34. package/src/stlc_agents/agent_migration/reporter.py +86 -0
  35. package/src/stlc_agents/agent_migration/server.py +267 -0
  36. package/src/stlc_agents/agent_migration/transformer/__init__.py +0 -0
  37. package/src/stlc_agents/agent_migration/transformer/__pycache__/__init__.cpython-313.pyc +0 -0
  38. package/src/stlc_agents/agent_migration/transformer/__pycache__/config_merger.cpython-313.pyc +0 -0
  39. package/src/stlc_agents/agent_migration/transformer/__pycache__/healer_injector.cpython-313.pyc +0 -0
  40. package/src/stlc_agents/agent_migration/transformer/__pycache__/import_fixer.cpython-313.pyc +0 -0
  41. package/src/stlc_agents/agent_migration/transformer/__pycache__/js_to_ts.cpython-313.pyc +0 -0
  42. package/src/stlc_agents/agent_migration/transformer/__pycache__/local_var_hoister.cpython-313.pyc +0 -0
  43. package/src/stlc_agents/agent_migration/transformer/__pycache__/locator_moderniser.cpython-313.pyc +0 -0
  44. package/src/stlc_agents/agent_migration/transformer/__pycache__/locator_registrar.cpython-313.pyc +0 -0
  45. package/src/stlc_agents/agent_migration/transformer/__pycache__/spec_to_bdd.cpython-313.pyc +0 -0
  46. package/src/stlc_agents/agent_migration/transformer/config_merger.py +513 -0
  47. package/src/stlc_agents/agent_migration/transformer/healer_injector.py +1143 -0
  48. package/src/stlc_agents/agent_migration/transformer/import_fixer.py +419 -0
  49. package/src/stlc_agents/agent_migration/transformer/js_to_ts.py +413 -0
  50. package/src/stlc_agents/agent_migration/transformer/local_var_hoister.py +378 -0
  51. package/src/stlc_agents/agent_migration/transformer/locator_moderniser.py +132 -0
  52. package/src/stlc_agents/agent_migration/transformer/locator_registrar.py +328 -0
  53. package/src/stlc_agents/agent_migration/transformer/spec_to_bdd.py +820 -0
  54. package/src/stlc_agents/agent_playwright_generator/__pycache__/__init__.cpython-313.pyc +0 -0
  55. package/src/stlc_agents/agent_playwright_generator/__pycache__/server.cpython-313.pyc +0 -0
  56. package/src/stlc_agents/agent_playwright_generator/server.py +926 -91
  57. package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  58. package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/ado_attach.cpython-313.pyc +0 -0
  59. package/src/stlc_agents/agent_test_case_manager/__pycache__/__init__.cpython-313.pyc +0 -0
  60. package/src/stlc_agents/agent_test_case_manager/__pycache__/server.cpython-313.pyc +0 -0
  61. package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  62. package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/ado_workitem.cpython-313.pyc +0 -0
  63. package/src/stlc_agents/shared/__pycache__/__init__.cpython-313.pyc +0 -0
  64. package/src/stlc_agents/shared/__pycache__/auth.cpython-313.pyc +0 -0
  65. package/src/stlc_agents/shared/__pycache__/cost_tracker.cpython-313.pyc +0 -0
  66. package/src/stlc_agents/shared/__pycache__/pricing.cpython-313.pyc +0 -0
  67. package/src/stlc_agents/shared_jira/__pycache__/__init__.cpython-313.pyc +0 -0
  68. package/src/stlc_agents/shared_jira/__pycache__/auth.cpython-313.pyc +0 -0
  69. package/src/stlc_agents/webhook_orchestrator/__pycache__/__init__.cpython-313.pyc +0 -0
  70. package/src/stlc_agents/webhook_orchestrator/__pycache__/agent_runner.cpython-313.pyc +0 -0
  71. package/src/stlc_agents/webhook_orchestrator/__pycache__/models.cpython-313.pyc +0 -0
  72. package/src/stlc_agents/webhook_orchestrator/__pycache__/orchestrator.cpython-313.pyc +0 -0
  73. package/src/stlc_agents/webhook_orchestrator/__pycache__/webhook_bridge.cpython-313.pyc +0 -0
  74. package/src/stlc_agents/agent_gherkin_generator/__pycache__/__init__.cpython-314.pyc +0 -0
  75. package/src/stlc_agents/agent_gherkin_generator/__pycache__/server.cpython-314.pyc +0 -0
  76. package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  77. package/src/stlc_agents/agent_gherkin_generator/tools/__pycache__/ado_gherkin.cpython-314.pyc +0 -0
  78. package/src/stlc_agents/agent_helix_writer/__pycache__/server.cpython-314.pyc +0 -0
  79. package/src/stlc_agents/agent_playwright_generator/__pycache__/__init__.cpython-314.pyc +0 -0
  80. package/src/stlc_agents/agent_playwright_generator/__pycache__/server.cpython-314.pyc +0 -0
  81. package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  82. package/src/stlc_agents/agent_playwright_generator/tools/__pycache__/ado_attach.cpython-314.pyc +0 -0
  83. package/src/stlc_agents/agent_test_case_manager/__pycache__/__init__.cpython-314.pyc +0 -0
  84. package/src/stlc_agents/agent_test_case_manager/__pycache__/server.cpython-314.pyc +0 -0
  85. package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  86. package/src/stlc_agents/agent_test_case_manager/tools/__pycache__/ado_workitem.cpython-314.pyc +0 -0
  87. package/src/stlc_agents/shared/__pycache__/__init__.cpython-314.pyc +0 -0
  88. package/src/stlc_agents/shared/__pycache__/auth.cpython-314.pyc +0 -0
  89. package/src/stlc_agents/shared/__pycache__/cost_tracker.cpython-314.pyc +0 -0
  90. package/src/stlc_agents/shared/__pycache__/pricing.cpython-314.pyc +0 -0
@@ -0,0 +1,267 @@
1
+ """
2
+ Agent 6: QA Migration Agent — MCP Server
3
+
4
+ Migrates existing Playwright test suites (plain JS/TS or Playwright+Cucumber JS/TS)
5
+ to the Helix-QA agent-compatible structure with full self-healing support.
6
+
7
+ Pipeline per source file:
8
+ JS → TS conversion → locator modernisation (CSS/XPath → getByRole)
9
+ → healer injection (TimingHealer + VisualIntentChecker on ALL interactions)
10
+ → import path fixing → Helix-QA disk write → MIGRATION-REPORT.md
11
+
12
+ Tools exposed:
13
+ detect_framework — auto-detect source framework type and language
14
+ preview_migration — dry-run: enumerate what would be written / conflicts
15
+ migrate_framework — full migration with per-file conflict handling
16
+ """
17
+ from __future__ import annotations
18
+ import asyncio
19
+ import json
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ from mcp.server import Server
24
+ from mcp.server.stdio import stdio_server
25
+ from mcp import types
26
+
27
+ from .detector import detect_framework as _detect
28
+ from .mapper import map_source_files
29
+ from ._migrate import run_migration
30
+
31
+ app = Server("qa-migration")
32
+
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Tool definitions
36
+ # ---------------------------------------------------------------------------
37
+
38
+ @app.list_tools()
39
+ async def list_tools() -> list[types.Tool]:
40
+ return [
41
+ types.Tool(
42
+ name="detect_framework",
43
+ description=(
44
+ "Auto-detect the Playwright framework type from a source test directory. "
45
+ "Returns: framework ('playwright-bdd-ts' | 'playwright-bdd-js' | "
46
+ "'playwright-ts' | 'playwright-js'), language, bdd flag, config files "
47
+ "found, and file counts (.ts / .js / .feature)."
48
+ ),
49
+ inputSchema={
50
+ "type": "object",
51
+ "properties": {
52
+ "source_dir": {
53
+ "type": "string",
54
+ "description": "Absolute path to the source test project directory.",
55
+ },
56
+ },
57
+ "required": ["source_dir"],
58
+ },
59
+ ),
60
+ types.Tool(
61
+ name="preview_migration",
62
+ description=(
63
+ "Dry-run: enumerate all files that would be migrated, their Helix-QA "
64
+ "target paths, roles, and any conflicts (target already exists). "
65
+ "Nothing is written to disk. Use this before migrate_framework to "
66
+ "review the migration plan and spot any surprises."
67
+ ),
68
+ inputSchema={
69
+ "type": "object",
70
+ "properties": {
71
+ "source_dir": {
72
+ "type": "string",
73
+ "description": "Absolute path to source test project directory.",
74
+ },
75
+ "helix_root": {
76
+ "type": "string",
77
+ "description": "Absolute path to the Helix-QA root directory.",
78
+ },
79
+ },
80
+ "required": ["source_dir", "helix_root"],
81
+ },
82
+ ),
83
+ types.Tool(
84
+ name="migrate_framework",
85
+ description=(
86
+ "Migrate an existing Playwright test framework to the Helix-QA "
87
+ "agent-compatible structure.\n\n"
88
+ "Transformations applied:\n"
89
+ " • JS → TS (CommonJS require → ES import, module.exports → export)\n"
90
+ " • Locator modernisation: [data-testid] → getByTestId, text= → getByText, "
91
+ "[aria-label] → getByLabel, [placeholder] → getByPlaceholder, "
92
+ "role=X[name=Y] → getByRole. Complex XPath/CSS flagged with TODO.\n"
93
+ " • Healer injection on ALL interactions: page.click → "
94
+ "_healer.clickWithHealing, page.fill → _healer.fillWithHealing, "
95
+ "page.goto wrapped with TimingHealer, expect.toBeVisible wrapped "
96
+ "with VisualIntentChecker. Applies to page objects AND step definitions.\n"
97
+ " • Import paths updated to Helix structure "
98
+ "(@utils/locators/*, @hooks/pageFixture, etc.).\n"
99
+ " • playwright.config merged into Helix playwright.config.ts.\n"
100
+ " • cucumber.config profiles merged into src/config/cucumber.config.ts.\n"
101
+ " • MIGRATION-REPORT.md written to helix_root.\n\n"
102
+ "Supports: plain Playwright JS/TS, Playwright+Cucumber JS/TS."
103
+ ),
104
+ inputSchema={
105
+ "type": "object",
106
+ "properties": {
107
+ "source_dir": {
108
+ "type": "string",
109
+ "description": "Absolute path to source test project directory.",
110
+ },
111
+ "helix_root": {
112
+ "type": "string",
113
+ "description": "Absolute path to the Helix-QA root directory.",
114
+ },
115
+ "conflict_strategy": {
116
+ "type": "string",
117
+ "enum": ["interactive", "overwrite", "skip"],
118
+ "description": (
119
+ "'overwrite' — replace existing Helix files. "
120
+ "'skip' — keep existing files untouched. "
121
+ "'interactive' (default) — write and report conflicts "
122
+ "for user review."
123
+ ),
124
+ },
125
+ "dry_run": {
126
+ "type": "boolean",
127
+ "description": (
128
+ "When true, return the migration plan without writing any files."
129
+ ),
130
+ },
131
+ },
132
+ "required": ["source_dir", "helix_root"],
133
+ },
134
+ ),
135
+ ]
136
+
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Tool dispatch
140
+ # ---------------------------------------------------------------------------
141
+
142
+ @app.call_tool()
143
+ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
144
+ try:
145
+ result = await _dispatch(name, arguments)
146
+ except Exception as exc:
147
+ result = {"error": str(exc), "tool": name}
148
+
149
+ return [types.TextContent(type="text", text=json.dumps(result, indent=2))]
150
+
151
+
152
+ async def _dispatch(name: str, args: dict) -> dict:
153
+ if name == "detect_framework":
154
+ return _detect(args["source_dir"])
155
+
156
+ if name == "preview_migration":
157
+ return _preview(args["source_dir"], args["helix_root"])
158
+
159
+ if name == "migrate_framework":
160
+ return _migrate(
161
+ args["source_dir"],
162
+ args["helix_root"],
163
+ args.get("conflict_strategy", "interactive"),
164
+ bool(args.get("dry_run", False)),
165
+ )
166
+
167
+ return {"error": f"Unknown tool: {name}"}
168
+
169
+
170
+ # ---------------------------------------------------------------------------
171
+ # Tool implementations
172
+ # ---------------------------------------------------------------------------
173
+
174
+ def _preview(source_dir: str, helix_root: str) -> dict:
175
+ detection = _detect(source_dir)
176
+ if "error" in detection:
177
+ return detection
178
+
179
+ files = map_source_files(source_dir)
180
+ mappable = [f for f in files if f["target"] is not None]
181
+ unknown = [f for f in files if f["role"] == "unknown"]
182
+ root = Path(helix_root)
183
+
184
+ conflicts = [
185
+ {"source": f["relative"], "target": f["target"]}
186
+ for f in mappable
187
+ if f["target"] and (root / f["target"]).exists()
188
+ ]
189
+
190
+ return {
191
+ "framework": detection.get("framework", "unknown"),
192
+ "language": detection.get("language", "unknown"),
193
+ "bdd": detection.get("bdd", False),
194
+ "file_counts": detection.get("file_counts", {}),
195
+ "total_files": len(files),
196
+ "mappable": len(mappable),
197
+ "unknown": len(unknown),
198
+ "conflicts": len(conflicts),
199
+ "conflict_list": conflicts,
200
+ "file_plan": [
201
+ {
202
+ "source": f["relative"],
203
+ "target": f["target"],
204
+ "role": f["role"],
205
+ "needs_js_to_ts": f.get("needs_js_to_ts", False),
206
+ }
207
+ for f in mappable
208
+ ],
209
+ "unknown_files": [f["relative"] for f in unknown],
210
+ }
211
+
212
+
213
+ def _migrate(
214
+ source_dir: str,
215
+ helix_root: str,
216
+ conflict_strategy: str,
217
+ dry_run: bool,
218
+ ) -> dict:
219
+ detection = _detect(source_dir)
220
+ if "error" in detection:
221
+ return detection
222
+
223
+ framework = detection.get("framework", "unknown")
224
+ files = map_source_files(source_dir)
225
+ mappable = [f for f in files if f["target"] is not None]
226
+
227
+ if dry_run:
228
+ return {
229
+ "dry_run": True,
230
+ "framework": framework,
231
+ "total": len(mappable),
232
+ "would_migrate": [
233
+ {"source": f["relative"], "target": f["target"], "role": f["role"]}
234
+ for f in mappable
235
+ ],
236
+ }
237
+
238
+ result = run_migration(
239
+ source_dir=source_dir,
240
+ helix_root=helix_root,
241
+ file_mappings=mappable,
242
+ conflict_strategy=conflict_strategy,
243
+ framework=framework,
244
+ )
245
+
246
+ # Summarise for the MCP response
247
+ result["summary"] = {
248
+ "framework": framework,
249
+ "written": len(result["written"]),
250
+ "skipped": len(result["skipped"]),
251
+ "conflicts": len(result["conflicts"]),
252
+ "todos": result["todo_count"],
253
+ "report_path": result.get("report_path"),
254
+ }
255
+ return result
256
+
257
+
258
+ # ---------------------------------------------------------------------------
259
+ # Entry point
260
+ # ---------------------------------------------------------------------------
261
+
262
+ def main() -> None:
263
+ asyncio.run(stdio_server(app))
264
+
265
+
266
+ if __name__ == "__main__":
267
+ main()