synapse-hub 0.0.0-stage → 2.0.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.
Files changed (122) hide show
  1. package/.agents/agents/failure-analyst.md +47 -0
  2. package/.agents/agents/live-explorer.md +41 -0
  3. package/.agents/agents/orchestrator.md +43 -0
  4. package/.agents/agents/pom-crafter.md +81 -0
  5. package/.agents/agents/quality-gatekeeper.md +47 -0
  6. package/.agents/agents/test-runner.md +43 -0
  7. package/.agents/rules/context-engineering.md +88 -0
  8. package/.agents/rules/git-push-protocol.md +101 -0
  9. package/.agents/rules/graphify.md +14 -0
  10. package/.agents/rules/lessons-learned.md +23 -0
  11. package/.agents/rules/mandatory-playwright-live.md +29 -0
  12. package/.agents/rules/skill-precedence.md +42 -0
  13. package/.agents/rules/systematic-debugging.md +99 -0
  14. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +239 -0
  15. package/.agents/skills/antigravity-test-sentinel/SKILL.md +64 -0
  16. package/.agents/skills/api-network-mocking-interception/SKILL.md +142 -0
  17. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +70 -0
  18. package/.agents/skills/architecture-decision-records/SKILL.md +441 -0
  19. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +72 -0
  20. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +214 -0
  21. package/.agents/skills/bats-testing-patterns/SKILL.md +100 -0
  22. package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -0
  23. package/.agents/skills/clean-pom-crafter/SKILL.md +75 -0
  24. package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -0
  25. package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -0
  26. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +64 -0
  27. package/.agents/skills/code-refactoring-refactor-clean/resources/implementation-playbook.md +879 -0
  28. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +123 -0
  29. package/.agents/skills/e2e-testing-patterns/SKILL.md +103 -0
  30. package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -0
  31. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +54 -0
  32. package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -0
  33. package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -0
  34. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -0
  35. package/.agents/skills/file-and-report-validator/SKILL.md +90 -0
  36. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +114 -0
  37. package/.agents/skills/github-actions-templates/SKILL.md +334 -0
  38. package/.agents/skills/graphify-impact-analyzer/SKILL.md +78 -0
  39. package/.agents/skills/katalon-cli-runner/SKILL.md +76 -0
  40. package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -0
  41. package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -0
  42. package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -0
  43. package/.agents/skills/lam-script-generator/SKILL.md +83 -0
  44. package/.agents/skills/playwright-live-interaction/SKILL.md +86 -0
  45. package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -0
  46. package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -0
  47. package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -0
  48. package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -0
  49. package/.agents/skills/security-dast-sentinel/SKILL.md +176 -0
  50. package/.agents/skills/self-healing-locator/SKILL.md +73 -0
  51. package/.agents/skills/synthetic-test-data-generator/SKILL.md +135 -0
  52. package/.agents/skills/token-and-context-optimizer/SKILL.md +70 -0
  53. package/.agents/skills/visual-regression-sentinel/SKILL.md +62 -0
  54. package/.agents/skills/wcag-audit-patterns/SKILL.md +77 -0
  55. package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -0
  56. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +65 -0
  57. package/.agents/templates/completion_report.md +57 -0
  58. package/.agents/workflows/graphify.md +10 -0
  59. package/.agents/workflows/test-impact.md +15 -0
  60. package/.agents/workflows/verify.md +34 -0
  61. package/AGENTS.md +115 -0
  62. package/README.md +394 -2
  63. package/bin/agent-qa.js +90 -0
  64. package/package.json +59 -4
  65. package/skills_manifest.json +262 -0
  66. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +97 -0
  67. package/templates/PROJECT_STATE_TEMPLATE.md +20 -0
  68. package/tools/TOOLS_RUNBOOK.md +298 -0
  69. package/tools/agent_canary.py +501 -0
  70. package/tools/analyze_project.py +242 -0
  71. package/tools/api_drift_sentinel.py +208 -0
  72. package/tools/auto_heal.py +189 -0
  73. package/tools/browser_helpers.js +593 -0
  74. package/tools/bug_reporter.py +224 -0
  75. package/tools/cache_resolver.py +846 -0
  76. package/tools/check_env.py +288 -0
  77. package/tools/ci_healing_hook.py +128 -0
  78. package/tools/cli.py +402 -0
  79. package/tools/config_decoupler.py +253 -0
  80. package/tools/db_sandbox.py +341 -0
  81. package/tools/distill_log.py +152 -0
  82. package/tools/e2e_flow_prober.ps1 +65 -0
  83. package/tools/flaky_detector.py +222 -0
  84. package/tools/fuzz_payloads.py +197 -0
  85. package/tools/generate_allure_report.ps1 +30 -0
  86. package/tools/generate_api_matrix.py +193 -0
  87. package/tools/generate_tc_from_story.py +182 -0
  88. package/tools/generate_test_reports.ps1 +135 -0
  89. package/tools/git_pre_commit_guard.py +203 -0
  90. package/tools/heal_pr_automator.py +195 -0
  91. package/tools/heuristic_locator_matcher.py +202 -0
  92. package/tools/infer_linter.py +238 -0
  93. package/tools/lint_ai_docs.py +254 -0
  94. package/tools/lookup_tc.py +165 -0
  95. package/tools/lookup_xpath.py +167 -0
  96. package/tools/matrix_sharder.py +146 -0
  97. package/tools/mcp_excel_server.ps1 +251 -0
  98. package/tools/mcp_server.py +490 -0
  99. package/tools/micro_mutator.py +309 -0
  100. package/tools/network_mock_shield.py +170 -0
  101. package/tools/omni_emitter.py +500 -0
  102. package/tools/omni_parser.py +435 -0
  103. package/tools/omni_transpiler.py +168 -0
  104. package/tools/pinpoint_failure.py +88 -0
  105. package/tools/project_state.py +169 -0
  106. package/tools/repo_map.json +12 -0
  107. package/tools/repo_map.py +277 -0
  108. package/tools/repo_map.txt +7 -0
  109. package/tools/safety_firewall.py +245 -0
  110. package/tools/setup_project.py +265 -0
  111. package/tools/smart_visual_masker.py +134 -0
  112. package/tools/test_impact.py +321 -0
  113. package/tools/tms_sync_bridge.py +342 -0
  114. package/tools/trace_data_flow.py +240 -0
  115. package/tools/traffic_session_converter.py +256 -0
  116. package/tools/universal_ir.py +480 -0
  117. package/tools/validate_groovy_scripts.py +173 -0
  118. package/tools/verify_all.py +496 -0
  119. package/tools/verify_all.sh +4 -0
  120. package/tools/visual_diff_canvas.py +142 -0
  121. package/tools/visual_som_helper.py +418 -0
  122. package/tools/wcag_audit.py +214 -0
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enterprise Action Safety Firewall & Blast Radius Containment Engine
4
+ --------------------------------------------------------------------
5
+ Prevents AI agents and test runners from executing catastrophic or irreversible
6
+ actions on Live/Staging/UAT environments (e.g. Delete All, Purge, Wipe, Pay, Drop).
7
+
8
+ Supports:
9
+ - Direct action checking (--action, --text, --selector)
10
+ - Code snippet and script inspection (--code, --file)
11
+ - Pipeline stdin stream inspection (e.g. echo 'page.click(...)' | python safety_firewall.py --dry-run)
12
+ - Pre-execution validation hooks before Playwright/CDP browser dispatch
13
+ """
14
+
15
+ import os
16
+ import re
17
+ import sys
18
+ import json
19
+ import argparse
20
+ from typing import List, Dict, Any, Tuple
21
+
22
+ # Enforce UTF-8 stdout encoding for Windows console compatibility
23
+ if hasattr(sys.stdout, 'reconfigure'):
24
+ try:
25
+ sys.stdout.reconfigure(encoding='utf-8')
26
+ sys.stderr.reconfigure(encoding='utf-8')
27
+ except Exception:
28
+ pass
29
+
30
+ DANGEROUS_ACTION_KEYWORDS = [
31
+ r"\bdelete\b",
32
+ r"\bremove\b",
33
+ r"\bpurge\b",
34
+ r"\bwipe\b",
35
+ r"\bdrop\b",
36
+ r"\bpay\b",
37
+ r"\bpurchase\b",
38
+ r"\bcheckout\b",
39
+ r"\bterminate\b",
40
+ r"\bdestroy\b",
41
+ r"\breset\b",
42
+ r"\btransfer\b",
43
+ r"\bformat\b",
44
+ ]
45
+
46
+ DANGEROUS_SELECTOR_PATTERNS = [
47
+ r"\[data-action\*=['\"]?delete['\"]?\]",
48
+ r"\[data-action\*=['\"]?pay['\"]?\]",
49
+ r"\.btn-danger\b",
50
+ r"\.p-button-danger\b",
51
+ r"\[aria-label\*=['\"]?delete['\"]?\]",
52
+ r"button[#\.\-_a-zA-Z0-9]*[_\-]*(?:del|delete|purge|remove|wipe|drop)",
53
+ ]
54
+
55
+ COMPILED_TEXT_REGEX = re.compile("|".join(DANGEROUS_ACTION_KEYWORDS), re.IGNORECASE)
56
+ COMPILED_SEL_REGEX = re.compile("|".join(DANGEROUS_SELECTOR_PATTERNS), re.IGNORECASE)
57
+
58
+ # Pattern to extract interaction statements from Playwright / Selenium / Groovy
59
+ INTERACTION_CODE_PATTERNS = [
60
+ # page.click('selector'), page.fill('selector', 'val')
61
+ re.compile(r"""(?:page|frame)\.(?P<act>click|dblclick|fill|type|press|check)\s*\(\s*['"](?P<sel>[^'"]+)['"](?:\s*,\s*['"](?P<text>[^'"]*)['"])?""", re.IGNORECASE),
62
+ # page.locator('selector').click()
63
+ re.compile(r"""(?:page|frame)\.locator\s*\(\s*['"](?P<sel>[^'"]+)['"]\s*\)\.(?P<act>click|fill|press)""", re.IGNORECASE),
64
+ # page.getByText('text').click()
65
+ re.compile(r"""(?:page|frame)\.getByText\s*\(\s*['"](?P<text>[^'"]+)['"]\s*\)\.(?P<act>click)""", re.IGNORECASE),
66
+ # page.getByRole('button', { name: 'Delete' }).click()
67
+ re.compile(r"""(?:page|frame)\.getByRole\s*\(\s*['"](?P<role>[^'"]+)['"]\s*,\s*\{[^}]*name:\s*['"](?P<text>[^'"]+)['"]""", re.IGNORECASE),
68
+ # WebUI.click(findTestObject(...)) or WebUI.clickOffset
69
+ re.compile(r"""WebUI\.(?P<act>click|setText)\s*\([^)]*['"](?P<sel>[^'"]*)['"]""", re.IGNORECASE),
70
+ ]
71
+
72
+
73
+ def is_action_dangerous(action_type: str, text: str = "", selector: str = "") -> Tuple[bool, str]:
74
+ """
75
+ Evaluates whether an intended action touches a dangerous or irreversible UI element.
76
+ Returns (is_dangerous: bool, reason: str).
77
+ """
78
+ text_clean = text.strip() if text else ""
79
+ sel_clean = selector.strip() if selector else ""
80
+
81
+ # Check text content
82
+ if text_clean and COMPILED_TEXT_REGEX.search(text_clean):
83
+ match = COMPILED_TEXT_REGEX.search(text_clean).group(0)
84
+ return True, f"Element text contains dangerous keyword: '{match}'"
85
+
86
+ # Check selector
87
+ if sel_clean and COMPILED_SEL_REGEX.search(sel_clean):
88
+ return True, f"Element selector matches destructive pattern: '{sel_clean}'"
89
+
90
+ # Specific dangerous action types
91
+ if action_type.lower() in ("drop_database", "wipe_environment", "execute_payment"):
92
+ return True, f"Action type '{action_type}' is classified as irreversible"
93
+
94
+ return False, "Action passed safety firewall inspection"
95
+
96
+
97
+ def inspect_and_authorize(action_type: str, text: str = "", selector: str = "", force: bool = False) -> Dict[str, Any]:
98
+ """
99
+ Evaluates action and checks authorization policy.
100
+ Bypass is permitted only if force=True or ALLOW_DESTRUCTIVE=1 environment variable is set.
101
+ """
102
+ dangerous, reason = is_action_dangerous(action_type, text, selector)
103
+ bypass = force or os.environ.get("ALLOW_DESTRUCTIVE", "").lower() in ("true", "1", "yes")
104
+
105
+ result = {
106
+ "action": action_type,
107
+ "text": text,
108
+ "selector": selector,
109
+ "dangerous": dangerous,
110
+ "reason": reason,
111
+ "authorized": (not dangerous) or bypass,
112
+ "bypass_granted": bypass if dangerous else False
113
+ }
114
+ return result
115
+
116
+
117
+ def inspect_code_snippet(code: str, force: bool = False) -> List[Dict[str, Any]]:
118
+ """
119
+ Parses a code snippet or script and checks all discovered UI interaction statements.
120
+ """
121
+ inspections = []
122
+ lines = code.splitlines()
123
+
124
+ for line_idx, line in enumerate(lines, start=1):
125
+ stripped = line.strip()
126
+ if not stripped or stripped.startswith(("//", "#", "/*", "*")):
127
+ continue
128
+
129
+ matched = False
130
+ for pattern in INTERACTION_CODE_PATTERNS:
131
+ for match in pattern.finditer(line):
132
+ matched = True
133
+ groups = match.groupdict()
134
+ act = groups.get("act") or "click"
135
+ sel = groups.get("sel") or ""
136
+ text = groups.get("text") or ""
137
+ auth = inspect_and_authorize(act, text=text, selector=sel, force=force)
138
+ auth["line"] = line_idx
139
+ auth["code"] = stripped
140
+ inspections.append(auth)
141
+
142
+ # Fallback: line itself contains dangerous keyword and an action verb
143
+ if not matched and COMPILED_TEXT_REGEX.search(stripped) and any(w in stripped.lower() for w in ("click", "press", "submit", "post", "delete")):
144
+ auth = inspect_and_authorize("dispatch", text=stripped, selector="", force=force)
145
+ auth["line"] = line_idx
146
+ auth["code"] = stripped
147
+ inspections.append(auth)
148
+
149
+ return inspections
150
+
151
+
152
+ def main():
153
+ parser = argparse.ArgumentParser(description="Action Safety Firewall & Blast Radius Containment Engine")
154
+ parser.add_argument("--action", default="click", help="Action type (click, fill, submit)")
155
+ parser.add_argument("--text", default="", help="Text content of target element")
156
+ parser.add_argument("--selector", default="", help="CSS/XPath selector of target element")
157
+ parser.add_argument("--code", default="", help="Direct code snippet or command line to evaluate (use '-' for stdin)")
158
+ parser.add_argument("--file", default="", help="Path to script file to evaluate")
159
+ parser.add_argument("--stdin", action="store_true", help="Read code or action from standard input")
160
+ parser.add_argument("--dry-run", action="store_true", help="Execute in pre-execution dry-run mode (outputs validation report)")
161
+ parser.add_argument("--force", action="store_true", help="Force bypass safety firewall")
162
+ parser.add_argument("--json", action="store_true", help="Output results in JSON format")
163
+ args = parser.parse_args()
164
+
165
+ # Determine input source: explicit stdin flag/arg, --file, --code, or parameters
166
+ code_content = ""
167
+ if args.code and args.code != "-":
168
+ code_content = args.code
169
+ elif args.file and os.path.exists(args.file):
170
+ with open(args.file, "r", encoding="utf-8", errors="ignore") as f:
171
+ code_content = f.read()
172
+ elif args.stdin or args.code == "-":
173
+ try:
174
+ stdin_data = sys.stdin.read().strip()
175
+ if stdin_data:
176
+ # Check if stdin is JSON
177
+ if stdin_data.startswith("{") and stdin_data.endswith("}"):
178
+ try:
179
+ parsed = json.loads(stdin_data)
180
+ if "action" in parsed or "selector" in parsed:
181
+ args.action = parsed.get("action", args.action)
182
+ args.text = parsed.get("text", args.text)
183
+ args.selector = parsed.get("selector", args.selector)
184
+ else:
185
+ code_content = stdin_data
186
+ except Exception:
187
+ code_content = stdin_data
188
+ else:
189
+ code_content = stdin_data
190
+ except Exception:
191
+ pass
192
+
193
+ # Evaluation
194
+ if code_content:
195
+ inspections = inspect_code_snippet(code_content, force=args.force)
196
+ has_blocked = any(not item["authorized"] for item in inspections)
197
+ all_authorized = not has_blocked
198
+
199
+ report = {
200
+ "mode": "dry-run" if args.dry_run else "enforce",
201
+ "total_inspected": len(inspections),
202
+ "authorized": all_authorized,
203
+ "blocked_count": sum(1 for item in inspections if not item["authorized"]),
204
+ "inspections": inspections
205
+ }
206
+
207
+ if args.json:
208
+ print(json.dumps(report, indent=2))
209
+ else:
210
+ tag = "DRY-RUN INSPECTION" if args.dry_run else "FIREWALL SENTINEL"
211
+ print(f"\n🛡️ [{tag}] Inspected {len(inspections)} interaction statements:")
212
+ for item in inspections:
213
+ status = "[PASS]" if item["authorized"] else "[BLOCKED]"
214
+ line_info = f"Line {item.get('line', '?')}: " if 'line' in item else ""
215
+ print(f" {status} {line_info}{item['action']} -> {item['reason']}")
216
+ if not item["authorized"]:
217
+ print(f" Code: {item.get('code', '')}")
218
+
219
+ if all_authorized:
220
+ print(f"\n✅ All actions cleared by Safety Firewall{' (Dry-Run)' if args.dry_run else ''}.\n")
221
+ else:
222
+ print(f"\n❌ Pre-execution Blocked: Destructive actions intercepted without --force or ALLOW_DESTRUCTIVE=1.\n")
223
+
224
+ return 0 if all_authorized else 1
225
+
226
+ # Single-action check (legacy / parameter mode)
227
+ auth = inspect_and_authorize(args.action, args.text, args.selector, args.force)
228
+ auth["mode"] = "dry-run" if args.dry_run else "enforce"
229
+
230
+ if args.json:
231
+ print(json.dumps(auth, indent=2))
232
+ else:
233
+ prefix = "[DRY-RUN] " if args.dry_run else ""
234
+ if auth["authorized"]:
235
+ status_tag = f"{prefix}[PASS] Action Authorized" if not auth["dangerous"] else f"{prefix}[BYPASS] Destructive Action Force-Authorized"
236
+ print(f"🛡️ {status_tag}: {auth['reason']}")
237
+ else:
238
+ print(f"❌ {prefix}[BLOCKED] Action Safety Firewall Intercepted: {auth['reason']}")
239
+ print(" 👉 To authorize, pass --force or set ALLOW_DESTRUCTIVE=1.")
240
+
241
+ return 0 if auth["authorized"] else 1
242
+
243
+
244
+ if __name__ == "__main__":
245
+ sys.exit(main())
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enterprise QA Autonomous Project Setup & Bootstrap Engine
4
+ ----------------------------------------------------------
5
+ Transforms any target test project into an AI-Ready Enterprise Suite in 1-click.
6
+ Dispatched automatically by AI Agents (Zero-Command Protocol) or via 'agent-qa setup'.
7
+
8
+ Automated actions:
9
+ 1. Detects project tech stack (Katalon, Playwright, Cypress, Pytest).
10
+ 2. Generates tailored 'PROJECT_AI_GUIDE.md' if missing.
11
+ 3. Installs automated Git Pre-Push Hook (.git/hooks/pre-push) for zero-leakage protection.
12
+ 4. Triggers 5-in-1 Health Audit (tools/analyze_project.py):
13
+ - Maps AST method signatures (repo_map.txt).
14
+ - Scans fake passes (infer_linter).
15
+ - Audits database teardown hygiene (trace_data_flow).
16
+ - Generates in-project health report (PROJECT_HEALTH_REPORT.md).
17
+ """
18
+
19
+ import os
20
+ import sys
21
+ import shutil
22
+ import argparse
23
+ from pathlib import Path
24
+ from datetime import datetime
25
+
26
+ # Enforce UTF-8 stdout encoding for Windows console compatibility
27
+ if hasattr(sys.stdout, 'reconfigure'):
28
+ try:
29
+ sys.stdout.reconfigure(encoding='utf-8')
30
+ sys.stderr.reconfigure(encoding='utf-8')
31
+ except Exception:
32
+ pass
33
+
34
+ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
35
+ HUB_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
36
+ TEMPLATES_DIR = os.path.join(HUB_ROOT, "templates")
37
+ sys.path.insert(0, SCRIPT_DIR)
38
+
39
+ import analyze_project
40
+ import verify_all
41
+ import project_state
42
+
43
+ GREEN = "\033[92m"
44
+ RED = "\033[91m"
45
+ YELLOW = "\033[93m"
46
+ CYAN = "\033[96m"
47
+ BOLD = "\033[1m"
48
+ RESET = "\033[0m"
49
+
50
+
51
+ def resolve_target_repo(cli_target=None, cli_repo=None):
52
+ chosen = cli_repo or cli_target
53
+ if chosen:
54
+ resolved = os.path.abspath(chosen)
55
+ if not os.path.exists(resolved):
56
+ raise FileNotFoundError(f"Target repository path not found: {resolved}")
57
+ return resolved
58
+
59
+ env_dir = os.environ.get("TARGET_PROJECT_DIR")
60
+ if env_dir and os.path.exists(env_dir):
61
+ return os.path.abspath(env_dir)
62
+
63
+ cwd = os.path.abspath(os.getcwd())
64
+ if cwd.lower() != HUB_ROOT.lower():
65
+ return cwd
66
+
67
+ return HUB_ROOT
68
+
69
+
70
+ def detect_tech_stack(target_dir):
71
+ """Detects target project automation framework and script language."""
72
+ target_path = Path(target_dir)
73
+
74
+ # 1. Katalon Studio check
75
+ prj_files = list(target_path.glob("*.prj"))
76
+ if prj_files or (target_path / "Keywords").is_dir() or (target_path / "Scripts").is_dir():
77
+ prj_name = prj_files[0].stem if prj_files else target_path.name
78
+ return {
79
+ "name": prj_name,
80
+ "framework": "Katalon Studio (Enterprise Web)",
81
+ "language": "Groovy",
82
+ "page_dir": "Keywords/pages",
83
+ "test_dir": "Scripts",
84
+ "run_cmd": "./tools/run_headless_kre.sh (or katalonc CLI)"
85
+ }
86
+
87
+ # 2. Playwright check
88
+ if (target_path / "playwright.config.ts").exists() or (target_path / "playwright.config.js").exists():
89
+ is_ts = (target_path / "playwright.config.ts").exists() or (target_path / "tsconfig.json").exists()
90
+ return {
91
+ "name": target_path.name,
92
+ "framework": "Playwright Test",
93
+ "language": "TypeScript" if is_ts else "JavaScript",
94
+ "page_dir": "src/pages" if (target_path / "src" / "pages").exists() else "pages",
95
+ "test_dir": "tests",
96
+ "run_cmd": "npx playwright test --project=chromium"
97
+ }
98
+
99
+ # 3. Cypress check
100
+ if (target_path / "cypress.config.ts").exists() or (target_path / "cypress.config.js").exists():
101
+ return {
102
+ "name": target_path.name,
103
+ "framework": "Cypress E2E",
104
+ "language": "TypeScript" if (target_path / "cypress.config.ts").exists() else "JavaScript",
105
+ "page_dir": "cypress/pages",
106
+ "test_dir": "cypress/e2e",
107
+ "run_cmd": "npx cypress run --headless"
108
+ }
109
+
110
+ # 4. Pytest check
111
+ if (target_path / "pytest.ini").exists() or list(target_path.glob("test_*.py")):
112
+ return {
113
+ "name": target_path.name,
114
+ "framework": "Pytest",
115
+ "language": "Python",
116
+ "page_dir": "pages",
117
+ "test_dir": "tests",
118
+ "run_cmd": "pytest tests/ -v"
119
+ }
120
+
121
+ # Default fallback
122
+ return {
123
+ "name": target_path.name,
124
+ "framework": "Enterprise Test Automation",
125
+ "language": "TypeScript / Groovy",
126
+ "page_dir": "pages",
127
+ "test_dir": "tests",
128
+ "run_cmd": "npm test"
129
+ }
130
+
131
+
132
+ def ensure_project_ai_guide(target_dir, stack_info):
133
+ """Generates PROJECT_AI_GUIDE.md tailored to the target project if missing."""
134
+ guide_path = os.path.join(target_dir, "PROJECT_AI_GUIDE.md")
135
+ if os.path.exists(guide_path):
136
+ print(f" [PASS] PROJECT_AI_GUIDE.md already exists: {guide_path}")
137
+ return False
138
+
139
+ template_path = os.path.join(TEMPLATES_DIR, "PROJECT_AI_GUIDE_TEMPLATE.md")
140
+ content = ""
141
+ if os.path.exists(template_path):
142
+ with open(template_path, "r", encoding="utf-8") as f:
143
+ content = f.read()
144
+ else:
145
+ content = f"# PROJECT AI GUIDE: {stack_info['name']}\n\n## 1. General System Under Test\n"
146
+
147
+ # Populate template placeholders with detected project metadata
148
+ content = content.replace("[TARGET_PROJECT_NAME]", stack_info["name"])
149
+ content = content.replace("[e.g., Enterprise Billing Platform / Customer Portal]", stack_info["name"])
150
+ content = content.replace("[e.g., Playwright Test, Katalon Studio 10.x, Cypress, Selenium]", stack_info["framework"])
151
+ content = content.replace("[e.g., TypeScript, Groovy, Python, JavaScript]", stack_info["language"])
152
+ content = content.replace("src/pages/... or Keywords/pages/...", stack_info["page_dir"])
153
+ content = content.replace("tests/e2e/... or Scripts/...", stack_info["test_dir"])
154
+ content = content.replace("[e.g., npx playwright test --project=chromium --reporter=html,junit:results.xml]", stack_info["run_cmd"])
155
+
156
+ with open(guide_path, "w", encoding="utf-8") as f:
157
+ f.write(content)
158
+
159
+ print(f" {GREEN}[OK] Generated PROJECT_AI_GUIDE.md tailored to {stack_info['framework']}:{RESET} {guide_path}")
160
+ return True
161
+
162
+
163
+ def install_git_pre_push_hook(target_dir):
164
+ """Installs an automated pre-push hook into target project's .git repository."""
165
+ git_dir = os.path.join(target_dir, ".git")
166
+ if not os.path.isdir(git_dir):
167
+ print(" [SKIP] Not a Git repository root (.git folder not found). Skipped Git Hook installation.")
168
+ return False
169
+
170
+ hooks_dir = os.path.join(git_dir, "hooks")
171
+ os.makedirs(hooks_dir, exist_ok=True)
172
+ hook_file = os.path.join(hooks_dir, "pre-push")
173
+
174
+ verify_all_script = os.path.join(SCRIPT_DIR, "verify_all.py").replace("\\", "/")
175
+ analyze_script = os.path.join(SCRIPT_DIR, "analyze_project.py").replace("\\", "/")
176
+
177
+ hook_content = f"""#!/bin/sh
178
+ # Automated Zero-Leakage Pre-Push Hook (Installed by Enterprise Agent-QA)
179
+ # Automatically enforces 7 Quality Gates and syncs project health before pushing.
180
+
181
+ echo "\\n[AI-GUARD] Running Pre-Push Verification (7 Quality Gates DoD)..."
182
+ python3 "{verify_all_script}" || python "{verify_all_script}"
183
+ if [ $? -ne 0 ]; then
184
+ echo "\\n[AI-GUARD - REJECTED] Git Push aborted: 7 Quality Gates verification failed!"
185
+ echo "Please fix defects or run 'agent-qa verify' locally before pushing.\\n"
186
+ exit 1
187
+ fi
188
+
189
+ echo "[AI-GUARD - PASSED] 7 Quality Gates satisfied. Syncing project health report..."
190
+ python3 "{analyze_script}" || python "{analyze_script}"
191
+ echo "[AI-GUARD - READY] Pushing to remote...\\n"
192
+ exit 0
193
+ """
194
+ try:
195
+ with open(hook_file, "w", encoding="utf-8", newline="\n") as f:
196
+ f.write(hook_content)
197
+
198
+ # Attempt to make executable on Unix-like environments
199
+ try:
200
+ os.chmod(hook_file, 0o755)
201
+ except Exception:
202
+ pass
203
+
204
+ print(f" {GREEN}[OK] Installed automated Pre-Push Git Hook:{RESET} {hook_file}")
205
+ return True
206
+ except Exception as e:
207
+ print(f" ⚠️ Could not install Git hook: {e}")
208
+ return False
209
+
210
+
211
+ def run_setup(target_repo):
212
+ """Main execution entrypoint for automated project setup."""
213
+ print(f"\n{BOLD}{CYAN}======================================================================{RESET}")
214
+ print(f"{BOLD}{CYAN} AUTONOMOUS ZERO-COMMAND PROJECT BOOTSTRAP (AI-READY SETUP) {RESET}")
215
+ print(f"{BOLD}{CYAN}======================================================================{RESET}")
216
+ print(f"📁 Target Project : {target_repo}")
217
+
218
+ # 1. Detect Stack
219
+ stack_info = detect_tech_stack(target_repo)
220
+ print(f"🔍 Detected Stack : {BOLD}{stack_info['framework']}{RESET} ({stack_info['language']})")
221
+ print(f"📂 Structure Scope : Pages: `{stack_info['page_dir']}` | Tests: `{stack_info['test_dir']}`\n")
222
+
223
+ # 2. Ensure PROJECT_AI_GUIDE.md & PROJECT_STATE.md
224
+ print(f"{BOLD}[1/3] Checking Project AI Guide & State Anchor...{RESET}")
225
+ guide_created = ensure_project_ai_guide(target_repo, stack_info)
226
+ project_state.init_state(target_repo)
227
+
228
+ # 3. Install Pre-Push Git Hook
229
+ print(f"\n{BOLD}[2/3] Installing Zero-Leakage Git Pre-Push Hook...{RESET}")
230
+ hook_installed = install_git_pre_push_hook(target_repo)
231
+
232
+ # 4. Run 5-in-1 Comprehensive Health & AST Analysis
233
+ print(f"\n{BOLD}[3/3] Executing Initial Health Audit & AST Mapping...{RESET}")
234
+ analysis_code = analyze_project.run_full_analysis(target_repo)
235
+
236
+ print(f"\n{BOLD}{CYAN}======================================================================{RESET}")
237
+ print(f"{BOLD}{CYAN} 🎉 PROJECT IS NOW 100% AI-READY (LEVEL 5 SETUP) {RESET}")
238
+ print(f"{BOLD}{CYAN}======================================================================{RESET}")
239
+ print(f" Target Framework : {stack_info['framework']}")
240
+ print(f" AI Project Guide : {os.path.join(target_repo, 'PROJECT_AI_GUIDE.md')}")
241
+ print(f" AST Method Map : {os.path.join(target_repo, 'repo_map.txt')}")
242
+ print(f" Health Report : {os.path.join(target_repo, 'PROJECT_HEALTH_REPORT.md')}")
243
+ print(f" Pre-Push Sentinel : {'ACTIVE (.git/hooks/pre-push)' if hook_installed else 'MANUAL (No .git folder)'}")
244
+ print(f"{BOLD}{CYAN}======================================================================{RESET}\n")
245
+
246
+ return analysis_code
247
+
248
+
249
+ def main():
250
+ parser = argparse.ArgumentParser(description="Autonomous Enterprise QA Project Setup")
251
+ parser.add_argument("target", nargs="?", default=None, help="Target repository path to setup")
252
+ parser.add_argument("--repo", "--path", dest="repo_path", default=None, help="Target repository path")
253
+ args = parser.parse_args()
254
+
255
+ try:
256
+ target_repo = resolve_target_repo(cli_target=args.target, cli_repo=args.repo_path)
257
+ except FileNotFoundError as e:
258
+ print(f"❌ Error: {e}")
259
+ return 1
260
+
261
+ return run_setup(target_repo)
262
+
263
+
264
+ if __name__ == "__main__":
265
+ sys.exit(main())
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Smart Visual Masker & Anti-Flake Visual Engine
4
+ ----------------------------------------------
5
+ Eliminates 99% of false alarms in Visual Regression Testing.
6
+ Automatically disables dynamic CSS animations, freezes SVG spinners,
7
+ and masks fluctuating UI elements (timestamps, clocks, avatars, ad banners).
8
+ """
9
+
10
+ import os
11
+ import re
12
+ import json
13
+ import argparse
14
+ from typing import List, Dict, Any, Tuple
15
+
16
+
17
+ class SmartVisualMasker:
18
+ """Enterprise Visual Regression Anti-Flake & Dynamic Masking Engine."""
19
+
20
+ ANTI_FLAKE_CSS = """
21
+ *, *::before, *::after {
22
+ transition-property: none !important;
23
+ transform: none !important;
24
+ animation: none !important;
25
+ caret-color: transparent !important;
26
+ scroll-behavior: auto !important;
27
+ }
28
+ """
29
+
30
+ DEFAULT_MASK_SELECTORS = [
31
+ # Timestamps and dates
32
+ "time",
33
+ ".timestamp",
34
+ ".date-time",
35
+ ".clock",
36
+ "[data-testid*='date']",
37
+ "[data-testid*='time']",
38
+ "[class*='countdown']",
39
+ # Random avatars & dynamic user content
40
+ ".avatar",
41
+ "img.user-avatar",
42
+ "[data-testid*='avatar']",
43
+ # Ads and third-party widgets
44
+ ".ad-banner",
45
+ ".advertisement",
46
+ "iframe[id*='google']",
47
+ # Skeleton loaders & spinners
48
+ ".skeleton",
49
+ ".loading-placeholder",
50
+ ".spinner"
51
+ ]
52
+
53
+ @classmethod
54
+ def get_anti_flake_css(cls) -> str:
55
+ """Returns normalized CSS string to freeze all animations and transitions."""
56
+ return cls.ANTI_FLAKE_CSS.strip()
57
+
58
+ @classmethod
59
+ def generate_playwright_ts_options(cls, additional_selectors: List[str] = None) -> str:
60
+ """
61
+ Generates Playwright TypeScript assertion options with masking and disabled animations.
62
+ """
63
+ selectors = cls.DEFAULT_MASK_SELECTORS + (additional_selectors or [])
64
+ locators_ts = ",\n ".join([f"page.locator('{sel}')" for sel in selectors])
65
+
66
+ return f"""await expect(page).toHaveScreenshot('visual-baseline.png', {{
67
+ animations: 'disabled',
68
+ caret: 'hide',
69
+ scale: 'css',
70
+ maxDiffPixelRatio: 0.01,
71
+ mask: [
72
+ {locators_ts}
73
+ ]
74
+ }});"""
75
+
76
+ @classmethod
77
+ def generate_browser_mask_script(cls, selectors: List[str] = None) -> str:
78
+ """
79
+ Generates JavaScript snippet to run inside the live browser
80
+ to immediately freeze DOM animations and paint solid black masks
81
+ over dynamic timestamp/avatar elements.
82
+ """
83
+ target_selectors = selectors or cls.DEFAULT_MASK_SELECTORS
84
+ selectors_json = json.dumps(target_selectors)
85
+ return f"""(() => {{
86
+ // 1. Inject anti-flake styles
87
+ const style = document.createElement('style');
88
+ style.innerHTML = `{cls.get_anti_flake_css()}`;
89
+ document.head.appendChild(style);
90
+
91
+ // 2. Solid mask for dynamic elements
92
+ const selectors = {selectors_json};
93
+ let maskedCount = 0;
94
+ selectors.forEach(sel => {{
95
+ document.querySelectorAll(sel).forEach(el => {{
96
+ el.style.backgroundColor = '#000000';
97
+ el.style.color = '#000000';
98
+ el.style.visibility = 'visible';
99
+ el.setAttribute('data-visual-masked', 'true');
100
+ maskedCount++;
101
+ }});
102
+ }});
103
+ return {{ status: 'OK', maskedCount }};
104
+ }})();"""
105
+
106
+
107
+ def main():
108
+ parser = argparse.ArgumentParser(description="Smart Visual Masker & Anti-Flake Visual Engine")
109
+ parser.add_argument("--css", action="store_true", help="Print anti-flake CSS")
110
+ parser.add_argument("--js-script", action="store_true", help="Print browser in-page masking JavaScript")
111
+ parser.add_argument("--playwright-snippet", action="store_true", help="Generate Playwright TS visual assertion snippet")
112
+ parser.add_argument("--mask", action="append", default=[], help="Additional CSS selectors to mask")
113
+
114
+ args = parser.parse_args()
115
+
116
+ if args.css:
117
+ print(SmartVisualMasker.get_anti_flake_css())
118
+ elif args.js_script:
119
+ print(SmartVisualMasker.generate_browser_mask_script(args.mask or None))
120
+ elif args.playwright_snippet:
121
+ print(SmartVisualMasker.generate_playwright_ts_options(args.mask or None))
122
+ else:
123
+ print("======================================================================")
124
+ print(" SMART VISUAL MASKER & ANTI-FLAKE VISUAL ENGINE")
125
+ print("======================================================================")
126
+ print("Default Masked Selectors:")
127
+ for sel in SmartVisualMasker.DEFAULT_MASK_SELECTORS:
128
+ print(f" • {sel}")
129
+ print("\nUse --css, --js-script, or --playwright-snippet for code output.")
130
+ print("======================================================================")
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()