synapse-hub 2.2.3 → 2.4.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 (317) hide show
  1. package/.agents/agents/failure-analyst.md +11 -0
  2. package/.agents/agents/live-explorer.md +28 -0
  3. package/.agents/agents/orchestrator.md +20 -4
  4. package/.agents/agents/pom-crafter.md +12 -0
  5. package/.agents/agents/quality-gatekeeper.md +19 -4
  6. package/.agents/agents/test-runner.md +11 -0
  7. package/.agents/rules/communication-style.md +40 -0
  8. package/.agents/rules/context-engineering.md +23 -0
  9. package/.agents/rules/git-push-protocol.md +11 -17
  10. package/.agents/rules/graphify.md +14 -14
  11. package/.agents/rules/lessons-learned.md +3 -3
  12. package/.agents/rules/mandatory-playwright-live.md +29 -29
  13. package/.agents/rules/mobile_appium_rules.md +132 -0
  14. package/.agents/rules/performance_testing_rules.md +74 -0
  15. package/.agents/rules/skill-precedence.md +1 -1
  16. package/.agents/rules/systematic-debugging.md +6 -6
  17. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +64 -215
  18. package/.agents/skills/agent-orchestration-multi-agent-optimize/examples/swarm_workload_config.json +12 -0
  19. package/.agents/skills/antigravity-test-sentinel/SKILL.md +3 -3
  20. package/.agents/skills/antigravity-test-sentinel/examples/dod_gate_policy.json +11 -0
  21. package/.agents/skills/api-network-mocking-interception/SKILL.md +1 -1
  22. package/.agents/skills/api-network-mocking-interception/examples/network_route_stub.json +9 -0
  23. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +9 -9
  24. package/.agents/skills/api-spec-to-e2e-matrix/examples/sample_openapi.json +19 -0
  25. package/.agents/skills/architecture-decision-records/SKILL.md +35 -412
  26. package/.agents/skills/architecture-decision-records/examples/0001-record-architecture-decisions.md +12 -0
  27. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +4 -1
  28. package/.agents/skills/auth-session-and-cookie-manager/examples/storage_state_sample.json +11 -0
  29. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +80 -186
  30. package/.agents/skills/backend-load-and-stress-patterns/examples/k6_load_test_sample.js +13 -0
  31. package/.agents/skills/bats-testing-patterns/SKILL.md +1 -1
  32. package/.agents/skills/bats-testing-patterns/examples/cli_runner_test.bats +6 -0
  33. package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -614
  34. package/.agents/skills/clean-pom-crafter/SKILL.md +21 -3
  35. package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -28
  36. package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -138
  37. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +2 -2
  38. package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_page_sample.groovy +9 -0
  39. package/.agents/skills/code-refactoring-refactor-clean/resources/implementation-playbook.md +9 -7
  40. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +5 -5
  41. package/.agents/skills/db-state-verifier-and-seeder/examples/db_fixture_seed.json +12 -0
  42. package/.agents/skills/e2e-testing-patterns/SKILL.md +1 -1
  43. package/.agents/skills/e2e-testing-patterns/examples/playwright_sharded_config.ts +12 -0
  44. package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -522
  45. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +17 -3
  46. package/.agents/skills/failure-autopsy-and-healing/examples/sample_junit_failure.xml +8 -0
  47. package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -64
  48. package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -56
  49. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -98
  50. package/.agents/skills/file-and-report-validator/SKILL.md +1 -1
  51. package/.agents/skills/file-and-report-validator/examples/sample_export_schema.json +14 -0
  52. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +3 -3
  53. package/.agents/skills/flaky-test-sentinel-and-quarantine/examples/quarantine_registry.json +11 -0
  54. package/.agents/skills/gherkin-to-suite/SKILL.md +103 -0
  55. package/.agents/skills/gherkin-to-suite/examples/checkout.feature +39 -0
  56. package/.agents/skills/gherkin-to-suite/examples/suite_report_sample.json +334 -0
  57. package/.agents/skills/github-actions-templates/SKILL.md +47 -304
  58. package/.agents/skills/github-actions-templates/examples/matrix_test_pipeline.yml +8 -0
  59. package/.agents/skills/graphify-impact-analyzer/SKILL.md +6 -6
  60. package/.agents/skills/graphify-impact-analyzer/examples/dependency_blast_radius.json +8 -0
  61. package/.agents/skills/katalon-cli-runner/SKILL.md +2 -2
  62. package/.agents/skills/katalon-cli-runner/examples/headless_kre_exec.sh +3 -0
  63. package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -209
  64. package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -155
  65. package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -67
  66. package/.agents/skills/lam-script-generator/SKILL.md +42 -1
  67. package/.agents/skills/lam-script-generator/examples/user_story_sample.md +8 -0
  68. package/.agents/skills/lean-code-ladder/SKILL.md +83 -0
  69. package/.agents/skills/lean-code-ladder/examples/ladder_walkthrough.md +37 -0
  70. package/.agents/skills/meta-prompt-crafter/SKILL.md +107 -0
  71. package/.agents/skills/meta-prompt-crafter/examples/qa_prompt_template.md +35 -0
  72. package/.agents/skills/persistent-planning-files/SKILL.md +108 -0
  73. package/.agents/skills/persistent-planning-files/examples/task_plan_example.md +56 -0
  74. package/.agents/skills/playwright-live-interaction/SKILL.md +49 -1
  75. package/.agents/skills/playwright-live-interaction/examples/live_probe_script.js +5 -0
  76. package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -160
  77. package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -75
  78. package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -388
  79. package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -134
  80. package/.agents/skills/playwright-webapp-testing/SKILL.md +93 -0
  81. package/.agents/skills/playwright-webapp-testing/examples/server_lifecycle_playwright.py +63 -0
  82. package/.agents/skills/rbt-manual-testing/SKILL.md +150 -0
  83. package/.agents/skills/rbt-manual-testing/examples/checkout_rbt_manual_suite.md +25 -0
  84. package/.agents/skills/security-dast-sentinel/SKILL.md +57 -135
  85. package/.agents/skills/security-dast-sentinel/examples/dast_scan_config.json +10 -0
  86. package/.agents/skills/self-healing-locator/SKILL.md +24 -10
  87. package/.agents/skills/self-healing-locator/examples/aom_diff_sample.json +16 -0
  88. package/.agents/skills/skillspector-security-guard/SKILL.md +32 -90
  89. package/.agents/skills/skillspector-security-guard/examples/safe_mcp_config.json +12 -0
  90. package/.agents/skills/surgical-coding-principles/SKILL.md +89 -0
  91. package/.agents/skills/surgical-coding-principles/examples/surgical_vs_scope_creep.md +46 -0
  92. package/.agents/skills/synthetic-test-data-generator/SKILL.md +3 -3
  93. package/.agents/skills/synthetic-test-data-generator/examples/idempotent_data_recipe.json +9 -0
  94. package/.agents/skills/systematic-debugging/SKILL.md +100 -0
  95. package/.agents/skills/systematic-debugging/examples/four_phase_case.md +44 -0
  96. package/.agents/skills/test-driven-development/SKILL.md +114 -0
  97. package/.agents/skills/test-driven-development/examples/red_green_refactor_qa.md +78 -0
  98. package/.agents/skills/token-and-context-optimizer/SKILL.md +1 -1
  99. package/.agents/skills/token-and-context-optimizer/examples/distilled_log_sample.txt +3 -0
  100. package/.agents/skills/verification-before-completion/SKILL.md +100 -0
  101. package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +46 -0
  102. package/.agents/skills/visual-regression-sentinel/SKILL.md +21 -1
  103. package/.agents/skills/visual-regression-sentinel/examples/visual_mask_rules.json +9 -0
  104. package/.agents/skills/wcag-audit-patterns/SKILL.md +3 -3
  105. package/.agents/skills/wcag-audit-patterns/examples/wcag_compliance_checklist.json +10 -0
  106. package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -541
  107. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +1 -1
  108. package/.agents/skills/web-vitals-and-performance-profiler/examples/cwv_budget_policy.json +6 -0
  109. package/.agents/templates/completion_report.md +57 -57
  110. package/.agents/workflows/analyze_requirement_document.md +18 -0
  111. package/.agents/workflows/export_testcases_excel.md +32 -0
  112. package/.agents/workflows/generate_combinatorial_test_data.md +28 -0
  113. package/.agents/workflows/generate_cross_module_test_plan.md +37 -0
  114. package/.agents/workflows/generate_manual_testcases_rbt.md +56 -0
  115. package/.agents/workflows/generate_requirements_from_website.md +25 -0
  116. package/.agents/workflows/generate_testcases_from_requirements.md +21 -0
  117. package/.agents/workflows/graphify.md +10 -10
  118. package/.agents/workflows/verify.md +2 -2
  119. package/.cursor/rules/language.mdc +7 -7
  120. package/.cursor/rules/synapse-qa.mdc +23 -0
  121. package/.cursorrules +7 -0
  122. package/.pre-commit-hooks.yaml +14 -0
  123. package/.windsurfrules +7 -0
  124. package/AGENTS.md +54 -15
  125. package/CLAUDE.md +35 -12
  126. package/LICENSE +30 -0
  127. package/NOTICE +180 -0
  128. package/README.en.md +325 -0
  129. package/README.md +247 -512
  130. package/action.yml +51 -0
  131. package/benchmarks/qa_eval/README.md +71 -0
  132. package/benchmarks/qa_eval/build_dataset.py +677 -0
  133. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-06_api_contract_drift.json +22 -0
  134. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-07_api_contract_drift.json +22 -0
  135. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-08_api_contract_drift.json +22 -0
  136. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-09_api_contract_drift.json +22 -0
  137. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-10_api_contract_drift.json +22 -0
  138. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-21_fake_pass_tautology.json +21 -0
  139. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-22_fake_pass_tautology.json +21 -0
  140. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-23_fake_pass_tautology.json +21 -0
  141. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-24_fake_pass_tautology.json +21 -0
  142. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-25_fake_pass_tautology.json +21 -0
  143. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-01_locator_drift.json +23 -0
  144. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-02_locator_drift.json +23 -0
  145. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-03_locator_drift.json +23 -0
  146. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-04_locator_drift.json +23 -0
  147. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-05_locator_drift.json +23 -0
  148. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-16_stale_and_mutation.json +22 -0
  149. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-17_stale_and_mutation.json +22 -0
  150. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-18_stale_and_mutation.json +22 -0
  151. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-19_stale_and_mutation.json +22 -0
  152. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-20_stale_and_mutation.json +23 -0
  153. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-11_timing_and_race.json +22 -0
  154. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-12_timing_and_race.json +22 -0
  155. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-13_timing_and_race.json +22 -0
  156. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-14_timing_and_race.json +22 -0
  157. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-15_timing_and_race.json +22 -0
  158. package/benchmarks/qa_eval/eval_runner.py +418 -0
  159. package/benchmarks/qa_eval/reports/AB_METHODOLOGY_REPORT.md +83 -0
  160. package/benchmarks/qa_eval/reports/BENCHMARK_REPORT.md +69 -0
  161. package/benchmarks/qa_eval/reports/eval_results.json +105 -0
  162. package/bin/agent-qa.js +10 -3
  163. package/docs/ARCHITECTURE.md +107 -0
  164. package/docs/CI_CD_INTEGRATION.md +94 -0
  165. package/docs/CLI_REFERENCE.md +157 -0
  166. package/docs/CONTRIBUTING.md +91 -0
  167. package/docs/PLUGIN_DEVELOPMENT_GUIDE.md +126 -0
  168. package/docs/TOKEN_QUOTA_PLAYBOOK.md +41 -0
  169. package/docs/adopted-ecosystem-tools.md +87 -0
  170. package/docs/schemas/synapse.config.schema.json +64 -0
  171. package/examples/cypress-ecommerce-suite/README.md +45 -0
  172. package/examples/cypress-ecommerce-suite/cypress/e2e/checkout.cy.js +17 -0
  173. package/examples/cypress-ecommerce-suite/cypress/pages/BasePage.js +29 -0
  174. package/examples/cypress-ecommerce-suite/cypress/pages/CartPage.js +51 -0
  175. package/examples/cypress-ecommerce-suite/cypress.config.js +13 -0
  176. package/examples/cypress-ecommerce-suite/package.json +13 -0
  177. package/examples/katalon-suite/Keywords/pages/BasePage.groovy +19 -0
  178. package/examples/katalon-suite/Keywords/pages/DashboardPage.groovy +29 -0
  179. package/examples/katalon-suite/Keywords/pages/LoginPage.groovy +45 -0
  180. package/examples/katalon-suite/Profiles/default.glbl +12 -0
  181. package/examples/katalon-suite/README.md +18 -0
  182. package/examples/katalon-suite/Scripts/Auth/TC_LoginSuccess/Script1234567890.groovy +12 -0
  183. package/examples/katalon-suite/Scripts/Dashboard/TC_ViewSummary/Script1234567891.groovy +15 -0
  184. package/examples/katalon-suite/katalon_suite.prj +21 -0
  185. package/examples/playwright-crm-suite/README.md +40 -0
  186. package/examples/playwright-crm-suite/package.json +14 -0
  187. package/examples/playwright-crm-suite/pages/BasePage.ts +37 -0
  188. package/examples/playwright-crm-suite/pages/CustomerPage.ts +57 -0
  189. package/examples/playwright-crm-suite/pages/LoginPage.ts +32 -0
  190. package/examples/playwright-crm-suite/playwright.config.ts +27 -0
  191. package/examples/playwright-crm-suite/tests/auth.spec.ts +22 -0
  192. package/examples/playwright-crm-suite/tests/customers.spec.ts +33 -0
  193. package/examples/plugins/sample-custom-plugin/README.md +14 -0
  194. package/examples/plugins/sample-custom-plugin/plugin.json +16 -0
  195. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/SKILL.md +29 -0
  196. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/examples/audit_fixture.json +8 -0
  197. package/examples/plugins/sample-custom-plugin/tools/custom_security_headers.py +125 -0
  198. package/examples/practices/README.md +22 -0
  199. package/examples/practices/requirements/crm/requirements_contracts.md +292 -0
  200. package/examples/practices/requirements/crm/requirements_customers.md +462 -0
  201. package/examples/practices/requirements/crm/requirements_login.md +202 -0
  202. package/examples/practices/requirements/crm/requirements_projects.md +396 -0
  203. package/examples/practices/requirements/crm/requirements_tasks.md +343 -0
  204. package/examples/practices/requirements/ecommerce/requirements_category_brand.txt +402 -0
  205. package/examples/practices/requirements/jira/KAN-5/KAN-1/2026-04-03_04-28-27.png +0 -0
  206. package/examples/practices/requirements/jira/KAN-5/KAN-1/KAN-1_requirement.md +52 -0
  207. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-19.png +0 -0
  208. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-57.png +0 -0
  209. package/examples/practices/requirements/jira/KAN-5/KAN-4/KAN-4_requirement.md +119 -0
  210. package/examples/practices/requirements/jira/KAN-5/KAN-5_overview.md +9 -0
  211. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-08.png +0 -0
  212. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-23.png +0 -0
  213. package/examples/practices/requirements/jira/KAN-5/KAN-6/KAN-6_requirement.md +32 -0
  214. package/examples/practices/testcases/api/book_management_api_test_cases.md +144 -0
  215. package/examples/practices/testcases/api/user_management_api_test_cases.md +139 -0
  216. package/examples/practices/testcases/crm/test_cases_crm_login.md +148 -0
  217. package/examples/practices/testcases/crm/test_cases_customers.md +156 -0
  218. package/examples/pytest-api-suite/README.md +45 -0
  219. package/examples/pytest-api-suite/conftest.py +31 -0
  220. package/examples/pytest-api-suite/pages/user_api_client.py +36 -0
  221. package/examples/pytest-api-suite/requirements.txt +2 -0
  222. package/examples/pytest-api-suite/tests/test_user_api.py +44 -0
  223. package/examples/selenium-suite/README.md +18 -0
  224. package/examples/selenium-suite/pages/base_page.py +27 -0
  225. package/examples/selenium-suite/pages/login_page.py +29 -0
  226. package/examples/selenium-suite/pages/order_page.py +25 -0
  227. package/examples/selenium-suite/pytest.ini +6 -0
  228. package/examples/selenium-suite/requirements.txt +2 -0
  229. package/examples/selenium-suite/tests/test_login.py +10 -0
  230. package/examples/selenium-suite/tests/test_order.py +10 -0
  231. package/package.json +22 -7
  232. package/scripts/generate_skill_examples.py +244 -0
  233. package/scripts/install.ps1 +112 -0
  234. package/scripts/install_global_plugin.ps1 +94 -0
  235. package/scripts/install_global_plugin.sh +49 -0
  236. package/scripts/lint_skills.ps1 +57 -0
  237. package/scripts/package_dist.py +323 -0
  238. package/scripts/setup.sh +59 -0
  239. package/scripts/update_hub.ps1 +19 -0
  240. package/scripts/update_hub.sh +15 -0
  241. package/skills_manifest.json +209 -6
  242. package/synapse.config.json +30 -0
  243. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +7 -7
  244. package/templates/PROJECT_STATE_TEMPLATE.md +1 -1
  245. package/templates/ai_bridges/CLAUDE.md +33 -16
  246. package/templates/ai_bridges/copilot-instructions.md +14 -21
  247. package/templates/ai_bridges/cursor_synapse_qa.mdc +17 -20
  248. package/templates/ai_bridges/windsurfrules +21 -21
  249. package/templates/ci/github-actions-synapse.yml +71 -4
  250. package/templates/welcome.html.tmpl +1674 -0
  251. package/tools/.infer_baseline.json +1102 -0
  252. package/tools/TOOLS_RUNBOOK.md +92 -130
  253. package/tools/__init__.py +26 -0
  254. package/tools/agent_canary.py +501 -501
  255. package/tools/analyze_project.py +31 -17
  256. package/tools/api_fuzzer.py +278 -6
  257. package/tools/author_integrity.py +6 -6
  258. package/tools/auto_heal.py +1 -1
  259. package/tools/browser_helpers.js +105 -7
  260. package/tools/bug_reporter.py +31 -1
  261. package/tools/bump_version.py +23 -3
  262. package/tools/cache_resolver.py +248 -14
  263. package/tools/check_env.py +55 -14
  264. package/tools/check_version_sync.py +153 -0
  265. package/tools/ci_healing_hook.py +1 -1
  266. package/tools/cli.py +263 -13
  267. package/tools/count_tokens.py +1257 -0
  268. package/tools/dashboard_generator.py +2638 -0
  269. package/tools/db_sandbox.py +198 -20
  270. package/tools/e2e_flow_prober.ps1 +12 -64
  271. package/tools/e2e_flow_prober.py +99 -0
  272. package/tools/e2e_flow_prober.sh +4 -0
  273. package/tools/export_postman_collection.py +29 -7
  274. package/tools/flaky_detector.py +4 -3
  275. package/tools/generate_allure_report.ps1 +12 -29
  276. package/tools/generate_allure_report.py +77 -0
  277. package/tools/generate_allure_report.sh +4 -0
  278. package/tools/generate_jmx_plan.py +299 -68
  279. package/tools/generate_test_reports.ps1 +12 -134
  280. package/tools/generate_test_reports.py +133 -0
  281. package/tools/generate_test_reports.sh +4 -0
  282. package/tools/gherkin_suite.py +758 -0
  283. package/tools/git_pre_commit_guard.py +12 -6
  284. package/tools/goldfish_review.py +366 -0
  285. package/tools/heal_pr_automator.py +1 -1
  286. package/tools/heuristic_locator_matcher.py +170 -21
  287. package/tools/infer_linter.py +612 -70
  288. package/tools/jtl_analyzer.py +1 -1
  289. package/tools/lint_ai_docs.py +64 -0
  290. package/tools/matrix_sharder.py +1 -1
  291. package/tools/mcp_excel_server.ps1 +12 -250
  292. package/tools/mcp_excel_server.py +249 -0
  293. package/tools/mcp_excel_server.sh +4 -0
  294. package/tools/mcp_server.py +368 -144
  295. package/tools/md_to_xlsx.py +307 -0
  296. package/tools/micro_mutator.py +300 -69
  297. package/tools/omni_transpiler.py +2 -2
  298. package/tools/output_formatter.py +128 -0
  299. package/tools/pinpoint_failure.py +88 -88
  300. package/tools/project_state.py +2 -2
  301. package/tools/repo_map.py +1 -1
  302. package/tools/safety_firewall.py +197 -33
  303. package/tools/setup_project.py +294 -30
  304. package/tools/skillspector.py +8 -8
  305. package/tools/spec_drift_sentinel.py +439 -0
  306. package/tools/sync_agent_rules.py +248 -0
  307. package/tools/telemetry_correlator.py +45 -0
  308. package/tools/test_impact.py +51 -12
  309. package/tools/tms_sync_bridge.py +21 -5
  310. package/tools/universal_ir.py +3 -0
  311. package/tools/update_sentinel.py +87 -15
  312. package/tools/verify_all.ps1 +13 -0
  313. package/tools/verify_all.py +463 -79
  314. package/tools/visual_diff_canvas.py +1 -0
  315. package/tools/visual_som_helper.py +1 -1
  316. package/tools/watch_mode.py +305 -0
  317. package/tools/welcome_page.py +291 -0
@@ -6,14 +6,26 @@ Resolves broken locators in under 15ms with 0 API tokens using a 3-layer
6
6
  deterministic static fallback cascade:
7
7
  Layer 1: Exact Attribute & Snapshot Cache Matching (1ms, ~98% conf)
8
8
  Layer 2: Levenshtein Distance & AOM Text Semantic Matching (5ms, ~90% conf)
9
- Layer 3: Spatial Anchor Proximity Heuristics (10ms, ~86% conf)
9
+ Layer 3: Geometric Relative Matching + Spatial Anchor Proximity (10ms, ~86% conf)
10
10
  Layer 4: Fallback to LLM agent only if confidence < 85%.
11
+
12
+ Layer 3 geometric semantics (concept borrowed from Selenium's relative
13
+ locators, Apache-2.0 - re-implemented stdlib-only): pass
14
+ `relative={"relation": "above"|"below"|"near"|"left-of"|"right-of",
15
+ "anchor": "#id-or-selector"}` and the matcher picks the interactive element
16
+ satisfying the geometric relation to the anchor's bounding box. Snapshots
17
+ without bounding boxes fall back to the order-based anchor heuristic.
11
18
  """
12
19
 
20
+ import math
13
21
  import re
14
22
  import time
15
23
  from typing import Dict, List, Any, Optional, Tuple
16
24
 
25
+ INTERACTIVE_TAGS = {"button", "a", "input", "select", "textarea"}
26
+ GEOMETRY_TOLERANCE_PX = 8.0
27
+ NEAR_DISTANCE_PX = 250.0
28
+
17
29
 
18
30
  def levenshtein_distance(s1: str, s2: str) -> int:
19
31
  """Computes Levenshtein edit distance between two strings."""
@@ -55,13 +67,84 @@ class HeuristicLocatorMatcher:
55
67
 
56
68
  CONFIDENCE_THRESHOLD = 0.85
57
69
 
70
+ @staticmethod
71
+ def _get_bbox(el: Dict[str, Any]) -> Optional[Tuple[float, float, float, float]]:
72
+ """Extracts (x, y, width, height) from a snapshot element, or None."""
73
+ for keys in (
74
+ ("x", "y", "width", "height"),
75
+ ("left", "top", "width", "height"),
76
+ ):
77
+ if all(k in el for k in keys):
78
+ return tuple(float(el[k]) for k in keys)
79
+ bbox = el.get("bbox")
80
+ if isinstance(bbox, dict) and all(k in bbox for k in ("x", "y", "width", "height")):
81
+ return (float(bbox["x"]), float(bbox["y"]),
82
+ float(bbox["width"]), float(bbox["height"]))
83
+ return None
84
+
85
+ @staticmethod
86
+ def _center_distance(bbox_a: Tuple[float, float, float, float],
87
+ bbox_b: Tuple[float, float, float, float]) -> float:
88
+ """Euclidean distance between the centers of two bounding boxes."""
89
+ ax = bbox_a[0] + bbox_a[2] / 2.0
90
+ ay = bbox_a[1] + bbox_a[3] / 2.0
91
+ bx = bbox_b[0] + bbox_b[2] / 2.0
92
+ by = bbox_b[1] + bbox_b[3] / 2.0
93
+ return math.hypot(ax - bx, ay - by)
94
+
95
+ @staticmethod
96
+ def _satisfies_relation(el_bbox: Tuple[float, float, float, float],
97
+ anchor_bbox: Tuple[float, float, float, float],
98
+ relation: str) -> bool:
99
+ """True when el_bbox stands in the requested relation to anchor_bbox."""
100
+ tol = GEOMETRY_TOLERANCE_PX
101
+ ex, ey, ew, eh = el_bbox
102
+ ax, ay, aw, ah = anchor_bbox
103
+ if relation == "above":
104
+ return ey + eh <= ay + tol
105
+ if relation == "below":
106
+ return ey >= ay + ah - tol
107
+ if relation == "left-of":
108
+ return ex + ew <= ax + tol
109
+ if relation == "right-of":
110
+ return ex >= ax + aw - tol
111
+ if relation == "near":
112
+ return HeuristicLocatorMatcher._center_distance(el_bbox, anchor_bbox) <= NEAR_DISTANCE_PX
113
+ return False
114
+
115
+ @staticmethod
116
+ def _find_anchor_element(dom_snapshot: List[Dict[str, Any]],
117
+ anchor_selector: str) -> Optional[Dict[str, Any]]:
118
+ """Finds the anchor element by id / selector / data-testid."""
119
+ bare = anchor_selector.lstrip("#")
120
+ for el in dom_snapshot:
121
+ if (el.get("id") == bare
122
+ or el.get("selector") == anchor_selector
123
+ or (el.get("data-testid") or el.get("test-id")) == bare):
124
+ return el
125
+ return None
126
+
127
+ @staticmethod
128
+ def _parse_relative_request(
129
+ relative: Optional[Dict[str, Any]],
130
+ spatial_anchor: Optional[str]
131
+ ) -> Tuple[Optional[str], Optional[str]]:
132
+ """Normalizes legacy spatial_anchor and the relative dict into
133
+ (relation, anchor_selector). Legacy anchors behave as 'below'."""
134
+ if isinstance(relative, dict) and relative.get("relation") and relative.get("anchor"):
135
+ return str(relative["relation"]).lower(), str(relative["anchor"])
136
+ if spatial_anchor:
137
+ return "below", spatial_anchor
138
+ return None, None
139
+
58
140
  @classmethod
59
141
  def match(
60
142
  cls,
61
143
  broken_locator: str,
62
144
  dom_snapshot: List[Dict[str, Any]],
63
145
  cache_entry: Optional[Dict[str, Any]] = None,
64
- spatial_anchor: Optional[str] = None
146
+ spatial_anchor: Optional[str] = None,
147
+ relative: Optional[Dict[str, Any]] = None
65
148
  ) -> Dict[str, Any]:
66
149
  """
67
150
  Executes the 3-Layer 0-token matching cascade.
@@ -99,26 +182,32 @@ class HeuristicLocatorMatcher:
99
182
  }
100
183
 
101
184
  for el in dom_snapshot:
102
- # Check test-id
103
- if el.get("data-testid") and el["data-testid"].lower() in broken_locator.lower():
104
- elapsed_ms = (time.perf_counter() - start_time) * 1000
105
- return {
106
- "matched": True,
107
- "layer": 1,
108
- "strategy": "EXACT_TESTID_ATTRIBUTE",
109
- "healed_locator": f"[data-testid='{el['data-testid']}']",
110
- "confidence": 0.98,
111
- "needs_llm": False,
112
- "execution_time_ms": round(elapsed_ms, 2)
113
- }
114
- # Check aria-label exact match
115
- if el.get("aria-label") and el["aria-label"].lower() == primary_keyword:
185
+ # Check test-id (exact or semantic substring match)
186
+ testid = el.get("data-testid") or el.get("test-id")
187
+ if testid:
188
+ clean_broken = re.sub(r'[^a-zA-Z0-9]', '', broken_locator.lower())
189
+ clean_testid = re.sub(r'[^a-zA-Z0-9]', '', testid.lower())
190
+ if clean_broken in clean_testid or clean_testid in clean_broken or primary_keyword in testid.lower():
191
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
192
+ return {
193
+ "matched": True,
194
+ "layer": 1,
195
+ "strategy": "EXACT_TESTID_ATTRIBUTE",
196
+ "healed_locator": f"[data-testid='{testid}']",
197
+ "confidence": 0.98,
198
+ "needs_llm": False,
199
+ "execution_time_ms": round(elapsed_ms, 2)
200
+ }
201
+ # Check aria-label or data-action match
202
+ aria = el.get("aria-label") or el.get("data-action")
203
+ if aria and (aria.lower() == primary_keyword or primary_keyword in aria.lower()):
116
204
  elapsed_ms = (time.perf_counter() - start_time) * 1000
205
+ tag_prefix = el.get("tag", "button")
117
206
  return {
118
207
  "matched": True,
119
208
  "layer": 1,
120
209
  "strategy": "EXACT_ARIA_LABEL",
121
- "healed_locator": f"[aria-label='{el['aria-label']}']",
210
+ "healed_locator": f"{tag_prefix}[aria-label='{el.get('aria-label', aria)}']",
122
211
  "confidence": 0.96,
123
212
  "needs_llm": False,
124
213
  "execution_time_ms": round(elapsed_ms, 2)
@@ -164,9 +253,44 @@ class HeuristicLocatorMatcher:
164
253
  }
165
254
 
166
255
  # ---------------------------------------------------------------------
167
- # Layer 3: Spatial Anchor Proximity Heuristic (10ms)
256
+ # Layer 3: Spatial Anchor Proximity & Unique Semantic Tag Fallback
257
+ # 3a. Geometric relative matching (bbox-aware, Selenium-style)
258
+ # 3b. Order-based anchor heuristic (bbox-free fallback)
168
259
  # ---------------------------------------------------------------------
169
- if spatial_anchor:
260
+ relation, anchor_selector = cls._parse_relative_request(relative, spatial_anchor)
261
+ if relation and anchor_selector:
262
+ anchor_el = cls._find_anchor_element(dom_snapshot, anchor_selector)
263
+ anchor_bbox = cls._get_bbox(anchor_el) if anchor_el is not None else None
264
+ if anchor_bbox is not None:
265
+ candidates = [
266
+ el for el in dom_snapshot
267
+ if el is not anchor_el
268
+ and (el.get("tag") or "").lower() in INTERACTIVE_TAGS
269
+ and cls._satisfies_relation(cls._get_bbox(el) or (0, 0, 0, 0), anchor_bbox, relation)
270
+ ]
271
+ candidates = [el for el in candidates if cls._get_bbox(el) is not None]
272
+ if candidates:
273
+ target = min(candidates, key=lambda el: cls._center_distance(cls._get_bbox(el), anchor_bbox))
274
+ if target.get("id"):
275
+ healed = f"#{target['id']}"
276
+ elif target.get("data-testid") or target.get("test-id"):
277
+ healed = f"[data-testid='{target.get('data-testid') or target.get('test-id')}']"
278
+ else:
279
+ text = (target.get("text") or "").strip()
280
+ healed = f"{target.get('tag', 'button')}:has-text('{text[:40]}')" if text else target.get("tag", "button")
281
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
282
+ return {
283
+ "matched": True,
284
+ "layer": 3,
285
+ "strategy": "GEOMETRIC_RELATIVE_MATCH",
286
+ "healed_locator": healed,
287
+ "confidence": 0.88,
288
+ "needs_llm": False,
289
+ "execution_time_ms": round(elapsed_ms, 2)
290
+ }
291
+ # bbox-free snapshots fall through to the order-based heuristic
292
+
293
+ if anchor_selector:
170
294
  # Find element immediately adjacent/inside the anchor container
171
295
  anchor_found = False
172
296
  for idx, el in enumerate(dom_snapshot):
@@ -174,7 +298,7 @@ class HeuristicLocatorMatcher:
174
298
  # Next interactive element after anchor
175
299
  if el.get("tag") in ["button", "a", "input"]:
176
300
  elapsed_ms = (time.perf_counter() - start_time) * 1000
177
- target_loc = el.get("id") and f"#{el['id']}" or f"{el.get('tag')}:below('{spatial_anchor}')"
301
+ target_loc = el.get("id") and f"#{el['id']}" or f"{el.get('tag')}:below('{anchor_selector}')"
178
302
  return {
179
303
  "matched": True,
180
304
  "layer": 3,
@@ -184,9 +308,34 @@ class HeuristicLocatorMatcher:
184
308
  "needs_llm": False,
185
309
  "execution_time_ms": round(elapsed_ms, 2)
186
310
  }
187
- if el.get("id") == spatial_anchor.replace("#", "") or el.get("selector") == spatial_anchor:
311
+ if el.get("id") == anchor_selector.replace("#", "") or el.get("selector") == anchor_selector:
188
312
  anchor_found = True
189
313
 
314
+ # Unique interactive element fallback (e.g. single CTA button or input in snapshot)
315
+ if len(dom_snapshot) == 1:
316
+ el = dom_snapshot[0]
317
+ tag = el.get("tag", "").lower()
318
+ text = (el.get("text") or "").lower()
319
+ is_interactive = tag in ["button", "input", "select", "a", "textarea"] or bool(el.get("type"))
320
+ has_keyword_match = bool(primary_keyword and (primary_keyword in text or primary_keyword in el.get("id", "").lower()))
321
+ if is_interactive or has_keyword_match:
322
+ if el.get("id"):
323
+ target_loc = f"#{el['id']}"
324
+ elif el.get("type"):
325
+ target_loc = f"{el.get('tag', 'button')}[type='{el['type']}']"
326
+ else:
327
+ target_loc = el.get("tag", "button")
328
+ elapsed_ms = (time.perf_counter() - start_time) * 1000
329
+ return {
330
+ "matched": True,
331
+ "layer": 3,
332
+ "strategy": "UNIQUE_DOM_ELEMENT",
333
+ "healed_locator": target_loc,
334
+ "confidence": 0.90,
335
+ "needs_llm": False,
336
+ "execution_time_ms": round(elapsed_ms, 2)
337
+ }
338
+
190
339
  # ---------------------------------------------------------------------
191
340
  # Layer 4: Fallback to LLM (< 85% confidence)
192
341
  # ---------------------------------------------------------------------