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
@@ -10,8 +10,10 @@ Communicates via standard JSON-RPC stdio.
10
10
  import os
11
11
  import sys
12
12
  import json
13
+ import re
13
14
  import traceback
14
- from typing import Dict, Any, List
15
+ from pathlib import Path
16
+ from typing import Dict, Any, List, Optional, Set
15
17
 
16
18
  # Enforce UTF-8 stdio encoding
17
19
  if hasattr(sys.stdout, 'reconfigure'):
@@ -41,14 +43,16 @@ import tms_sync_bridge
41
43
  import db_sandbox
42
44
  import traffic_session_converter
43
45
  import visual_som_helper
46
+ import gherkin_suite
47
+ import api_fuzzer
44
48
 
45
49
  SERVER_NAME = "enterprise-agent-qa"
46
- SERVER_VERSION = "2.2.3"
50
+ SERVER_VERSION = "2.4.0"
47
51
 
48
52
  MCP_TOOLS = [
49
53
  {
50
54
  "name": "verify_all_gates",
51
- "description": "Validates the 8 Quality Gates Definition of Done (UTF-8, paths, Clean POM, AI docs, blacklist, security, infer linter, micro-mutator).",
55
+ "description": "Validates the 9 Quality Gates Definition of Done (author attribution, UTF-8, paths, Clean POM, AI docs, blacklist, security, infer linter, micro-mutator).",
52
56
  "inputSchema": {
53
57
  "type": "object",
54
58
  "properties": {
@@ -72,7 +76,9 @@ MCP_TOOLS = [
72
76
  "inputSchema": {
73
77
  "type": "object",
74
78
  "properties": {
75
- "repo_path": {"type": "string", "description": "Path to target project to bootstrap."}
79
+ "repo_path": {"type": "string", "description": "Path to target project to bootstrap."},
80
+ "dry_run": {"type": "boolean", "description": "Preview setup without modifying filesystem (default: false)."},
81
+ "no_global": {"type": "boolean", "description": "Local-only mode: do not touch ~/.gemini (default: true)."}
76
82
  }
77
83
  }
78
84
  },
@@ -95,8 +101,7 @@ MCP_TOOLS = [
95
101
  "properties": {
96
102
  "action_type": {"type": "string", "description": "Action type (e.g. click, fill, submit)."},
97
103
  "element_text": {"type": "string", "description": "Text or label of target DOM element."},
98
- "selector": {"type": "string", "description": "CSS or XPath selector of element."},
99
- "force": {"type": "boolean", "description": "Force bypass firewall."}
104
+ "selector": {"type": "string", "description": "CSS or XPath selector of element."}
100
105
  }
101
106
  }
102
107
  },
@@ -136,7 +141,7 @@ MCP_TOOLS = [
136
141
  },
137
142
  {
138
143
  "name": "run_micro_mutation",
139
- "description": "Injects AST micro-mutants (flipping operators, booleans) to verify that AI-generated test assertions are robust.",
144
+ "description": "Injects syntactic micro-mutants (flipping operators, booleans) to verify that AI-generated test assertions are robust.",
140
145
  "inputSchema": {
141
146
  "type": "object",
142
147
  "properties": {
@@ -173,7 +178,7 @@ MCP_TOOLS = [
173
178
  },
174
179
  {
175
180
  "name": "pull_alm_story",
176
- "description": "Pulls User Story and Acceptance Criteria (Given-When-Then) from Jira, Xray, or TestRail for test generation context.",
181
+ "description": "Pulls User Story and Acceptance Criteria (Given-When-Then) from Jira REST API v3 (or mock simulation) for test generation context.",
177
182
  "inputSchema": {
178
183
  "type": "object",
179
184
  "properties": {
@@ -206,7 +211,8 @@ MCP_TOOLS = [
206
211
  "properties": {
207
212
  "action": {"type": "string", "enum": ["snapshot", "restore", "audit_and_purge"], "description": "Sandbox operation to execute."},
208
213
  "db_path": {"type": "string", "description": "Path to database file (optional for SQLite)."},
209
- "snapshot_id": {"type": "string", "description": "Snapshot identifier (default: pristine_baseline)."}
214
+ "snapshot_id": {"type": "string", "description": "Snapshot identifier (default: pristine_baseline)."},
215
+ "dry_run": {"type": "boolean", "description": "For audit_and_purge: preview records to purge without deleting."}
210
216
  },
211
217
  "required": ["action"]
212
218
  }
@@ -234,145 +240,363 @@ MCP_TOOLS = [
234
240
  "mark_id": {"type": "integer", "description": "Specific Mark ID to click."}
235
241
  }
236
242
  }
243
+ },
244
+ {
245
+ "name": "compile_gherkin_suite",
246
+ "description": "Compiles Gherkin .feature files into an expanded test matrix, hub Universal IR and a proven-vs-owed coverage-gap report (0 LLM tokens).",
247
+ "inputSchema": {
248
+ "type": "object",
249
+ "properties": {
250
+ "target": {"type": "string", "description": "A .feature file or directory containing .feature files."}
251
+ },
252
+ "required": ["target"]
253
+ }
254
+ },
255
+ {
256
+ "name": "fuzz_api",
257
+ "description": "Autonomous API fuzzing (active, dry-run by default for safety) or offline passive spec audit with ZAP-style severity taxonomy.",
258
+ "inputSchema": {
259
+ "type": "object",
260
+ "properties": {
261
+ "base_url": {"type": "string", "description": "Base URL of target API (non-production allowlist enforced)."},
262
+ "spec_path": {"type": "string", "description": "Path or URL to OpenAPI/Swagger JSON spec."},
263
+ "mode": {"type": "string", "enum": ["active", "passive"], "description": "active fuzzing or passive offline spec audit."},
264
+ "dry_run": {"type": "boolean", "description": "Deterministic offline mock (default true for safety)."}
265
+ },
266
+ "required": ["base_url"]
267
+ }
237
268
  }
238
269
  ]
239
270
 
240
271
 
272
+ def _validate_safe_path(filepath: str, allowed_root: Optional[str] = None, allowed_exts: Optional[Set[str]] = None) -> str:
273
+ """
274
+ Validates that a path is safe and does not access restricted system paths or traverse outside the project root.
275
+ Resolves symlinks via Path.resolve() and enforces cross-platform boundaries (Windows, macOS, Linux).
276
+ """
277
+ if not filepath or not isinstance(filepath, str):
278
+ raise ValueError("File path must be a non-empty string")
279
+
280
+ # Reject null bytes and Windows UNC network share paths
281
+ if "\0" in filepath or filepath.startswith("\\\\"):
282
+ raise PermissionError(f"Access denied: invalid or UNC path '{filepath}'")
283
+
284
+ effective_root = allowed_root or os.environ.get("SYNAPSE_PROJECT_ROOT") or os.getcwd()
285
+ try:
286
+ root_path = Path(effective_root).resolve()
287
+ target_path = Path(filepath).resolve()
288
+ except Exception as e:
289
+ raise PermissionError(f"Access denied: cannot resolve path '{filepath}': {e}")
290
+
291
+ # Case-insensitive path comparison across OS filesystems
292
+ root_norm = os.path.normcase(str(root_path))
293
+ target_norm = os.path.normcase(str(target_path))
294
+
295
+ # Path must reside within the allowed root
296
+ if target_norm != root_norm and not target_norm.startswith(root_norm + os.sep):
297
+ raise PermissionError(f"Access denied: path '{filepath}' resolves outside allowed project root '{root_path}'")
298
+
299
+ # Reject explicit sensitive configuration / secret files
300
+ base_name = target_path.name.lower()
301
+ if base_name in {".env", ".env.local", ".env.production", ".env.staging", ".npmrc", ".netrc", ".git-credentials"}:
302
+ raise PermissionError(f"Access denied: path references sensitive configuration file '{target_path.name}'")
303
+
304
+ # Cross-platform sensitive system prefixes
305
+ try:
306
+ user_home = Path.home().resolve()
307
+ except Exception:
308
+ user_home = Path(os.path.expanduser("~")).resolve()
309
+
310
+ sensitive_prefixes = [
311
+ Path("/etc"), Path("/var/run"), Path("/proc"), Path("/sys"), Path("/dev"), Path("/root"), Path("/private/etc"),
312
+ user_home / ".ssh",
313
+ user_home / ".aws",
314
+ user_home / ".gnupg",
315
+ user_home / ".kube",
316
+ user_home / ".docker",
317
+ user_home / ".config" / "gh",
318
+ user_home / "Library" / "Keychains",
319
+ ]
320
+ if sys.platform == "win32":
321
+ for env_win in ("SystemRoot", "WINDIR", "APPDATA", "LOCALAPPDATA", "ProgramFiles", "ProgramFiles(x86)"):
322
+ win_val = os.environ.get(env_win)
323
+ if win_val:
324
+ try:
325
+ sensitive_prefixes.append(Path(win_val).resolve())
326
+ except Exception:
327
+ pass
328
+
329
+ for sp in sensitive_prefixes:
330
+ sp_norm = os.path.normcase(str(sp))
331
+ if target_norm == sp_norm or target_norm.startswith(sp_norm + os.sep):
332
+ raise PermissionError(f"Access denied: path references restricted system directory '{sp}'")
333
+
334
+ if allowed_exts:
335
+ ext = target_path.suffix.lower()
336
+ if ext not in allowed_exts and not target_path.is_dir():
337
+ raise PermissionError(f"Access denied: file extension '{ext}' is not permitted")
338
+
339
+ return str(target_path)
340
+
341
+
342
+ def _handle_verify_all_gates(arguments: Dict[str, Any]) -> str:
343
+ repo = _validate_safe_path(arguments.get("repo_path") or os.getcwd())
344
+ mode = verify_all.detect_repo_mode(repo)
345
+ passed = True
346
+ gates = [
347
+ ("Gate 0: Author Integrity", verify_all.gate_0_author_integrity),
348
+ ("Gate 1: UTF-8 No BOM", verify_all.gate_1_bom_check),
349
+ ("Gate 2: Path Portability", verify_all.gate_2_path_portability),
350
+ ("Gate 3: Clean POM", verify_all.gate_3_clean_pom),
351
+ ("Gate 4: AI Docs Linter", verify_all.gate_4_ai_docs_lint),
352
+ ("Gate 5: Temp Blacklist", verify_all.gate_5_cleanup_blacklist),
353
+ ("Gate 6: Credential Security", verify_all.gate_6_credential_security),
354
+ ("Gate 7: Infer Linter", verify_all.gate_7_infer_linter),
355
+ ("Gate 8: AST Mutation Sentinel", verify_all.gate_8_micro_mutator),
356
+ ]
357
+ results = []
358
+ for g_name, g_fn in gates:
359
+ res = g_fn(repo, mode=mode)
360
+ results.append(f"{g_name}: {'PASS' if res else 'FAIL'}")
361
+ if not res:
362
+ passed = False
363
+ summary = "100% ALL 9 GATES PASSED" if passed else "VERIFICATION FAILED"
364
+ return f"[{summary}]\n" + "\n".join(results)
365
+
366
+
367
+ def _handle_analyze_project(arguments: Dict[str, Any]) -> str:
368
+ repo = _validate_safe_path(arguments.get("repo_path") or os.getcwd())
369
+ code = analyze_project.run_full_analysis(repo)
370
+ return f"Project Analysis finished with exit code {code}. Report saved at {os.path.join(repo, 'PROJECT_HEALTH_REPORT.md')}."
371
+
372
+
373
+ def _handle_setup_project(arguments: Dict[str, Any]) -> str:
374
+ repo = _validate_safe_path(arguments.get("repo_path") or os.getcwd())
375
+ dry_run = bool(arguments.get("dry_run", False))
376
+ no_global = bool(arguments.get("no_global", True))
377
+ code = setup_project.run_setup(
378
+ repo,
379
+ dry_run=dry_run,
380
+ no_global=no_global,
381
+ no_git_hook=True,
382
+ no_browser=True,
383
+ open_browser=False
384
+ )
385
+ mode_label = " (DRY-RUN)" if dry_run else ""
386
+ return f"Project Setup finished with exit code {code}{mode_label}."
387
+
388
+
389
+ def _handle_auto_heal_failure(arguments: Dict[str, Any]) -> str:
390
+ target = _validate_safe_path(arguments.get("target_dir") or os.getcwd())
391
+ xml = _validate_safe_path(arguments["junit_xml_path"]) if arguments.get("junit_xml_path") else None
392
+ plan = auto_heal.diagnose_and_plan_heal(target, xml)
393
+ return json.dumps(plan, indent=2)
394
+
395
+
396
+ def _handle_check_action_safety(arguments: Dict[str, Any]) -> str:
397
+ auth = safety_firewall.inspect_and_authorize(
398
+ action_type=arguments.get("action_type", "click"),
399
+ text=arguments.get("element_text", ""),
400
+ selector=arguments.get("selector", ""),
401
+ force=False
402
+ )
403
+ return json.dumps(auth, indent=2)
404
+
405
+
406
+ def _handle_check_contract_drift(arguments: Dict[str, Any]) -> str:
407
+ old_path = _validate_safe_path(arguments["old_spec_path"])
408
+ new_path = _validate_safe_path(arguments["new_spec_path"])
409
+ old_data = api_drift_sentinel.load_spec(old_path)
410
+ new_data = api_drift_sentinel.load_spec(new_path)
411
+ analysis = api_drift_sentinel.compare_specs(old_data, new_data)
412
+ return api_drift_sentinel.generate_markdown_report(analysis, old_path, new_path)
413
+
414
+
415
+ def _handle_query_memory_cache(arguments: Dict[str, Any]) -> str:
416
+ res = cache_resolver.query_solution(arguments["error_signature"])
417
+ return json.dumps(res, indent=2) if res else "No matching solution found in memory cache."
418
+
419
+
420
+ def _handle_distill_log(arguments: Dict[str, Any]) -> str:
421
+ return distill_log.distill_log_content(arguments["raw_log_text"])
422
+
423
+
424
+ def _handle_run_micro_mutation(arguments: Dict[str, Any]) -> str:
425
+ target = _validate_safe_path(
426
+ arguments.get("target_path") or os.getcwd(),
427
+ allowed_exts={".py", ".ts", ".js", ".groovy", ".java"}
428
+ )
429
+ if os.path.isdir(target):
430
+ mutants = micro_mutator.scan_directory_for_mutants(target)
431
+ else:
432
+ with open(target, "r", encoding="utf-8", errors="ignore") as f:
433
+ content = f.read()
434
+ mutants = micro_mutator.generate_mutants_for_code(content, target)
435
+
436
+ sanitized_samples = []
437
+ for m in mutants[:5]:
438
+ sanitized_samples.append({
439
+ "id": m.get("id"),
440
+ "file": m.get("file"),
441
+ "line": m.get("line"),
442
+ "operator": m.get("description", "mutation"),
443
+ "original_snippet": m.get("original_snippet", ""),
444
+ "mutated_snippet": m.get("mutated_snippet", ""),
445
+ "status": m.get("status", "PENDING")
446
+ })
447
+ return json.dumps({"total_mutants": len(mutants), "sample_mutants": sanitized_samples}, indent=2)
448
+
449
+
450
+ def _handle_plan_healing_pr(arguments: Dict[str, Any]) -> str:
451
+ page_file = _validate_safe_path(arguments["page_file"])
452
+ conf = heal_pr_automator.calculate_healing_confidence(
453
+ arguments.get("aom_score", 0.95),
454
+ arguments.get("spatial_score", 0.90),
455
+ arguments.get("visual_score", 0.92)
456
+ )
457
+ pr = heal_pr_automator.generate_pr_draft(page_file, arguments["old_locator"], arguments["new_locator"], conf)
458
+ return json.dumps({"confidence": conf, "pr": pr}, indent=2)
459
+
460
+
461
+ def _handle_shard_test_matrix(arguments: Dict[str, Any]) -> str:
462
+ target = _validate_safe_path(arguments.get("target_dir") or os.getcwd())
463
+ num_shards = arguments.get("num_shards", 4)
464
+ files = matrix_sharder.find_test_files(target)
465
+ shards = matrix_sharder.shard_tests_lpt(files, target, num_shards)
466
+ return json.dumps({"total_tests": len(files), "shards": shards}, indent=2)
467
+
468
+
469
+ def _handle_pull_alm_story(arguments: Dict[str, Any]) -> str:
470
+ bridge = tms_sync_bridge.TMSBridge()
471
+ story = bridge.pull_story_acceptance_criteria(arguments["issue_key"])
472
+ return json.dumps(story, indent=2)
473
+
474
+
475
+ def _handle_report_alm_defect(arguments: Dict[str, Any]) -> str:
476
+ bridge = tms_sync_bridge.TMSBridge()
477
+ ticket = bridge.report_defect(
478
+ project_key=arguments["project_key"],
479
+ test_case_id=arguments["test_case_id"],
480
+ summary=arguments["summary"],
481
+ error_message=arguments["error_message"],
482
+ stack_trace=arguments.get("stack_trace", ""),
483
+ screenshot_path=arguments.get("screenshot_path", "")
484
+ )
485
+ return json.dumps(ticket, indent=2)
486
+
487
+
488
+ def _handle_manage_db_sandbox(arguments: Dict[str, Any]) -> str:
489
+ action = arguments["action"]
490
+ raw_db = arguments.get("db_path", "")
491
+ safe_db = _validate_safe_path(raw_db) if raw_db else ""
492
+ raw_id = arguments.get("snapshot_id", "pristine_baseline")
493
+ safe_id = re.sub(r'[^a-zA-Z0-9_\-]', '', raw_id) or "pristine_baseline"
494
+
495
+ sandbox = db_sandbox.DBSandbox(db_path=safe_db)
496
+ if action == "snapshot":
497
+ res = sandbox.create_snapshot(safe_id)
498
+ elif action == "restore":
499
+ res = sandbox.restore_snapshot(safe_id)
500
+ elif action == "audit_and_purge":
501
+ dry_run = bool(arguments.get("dry_run", False))
502
+ res = sandbox.audit_and_purge_pollution(dry_run=dry_run)
503
+ else:
504
+ raise ValueError(f"Unknown sandbox action: {action}")
505
+ return json.dumps(res, indent=2)
506
+
507
+
508
+ def _handle_convert_traffic_session(arguments: Dict[str, Any]) -> str:
509
+ input_file = _validate_safe_path(arguments["input_file"])
510
+ test_name = arguments.get("test_name", "ProductionTrafficReplay")
511
+ with open(input_file, "r", encoding="utf-8", errors="ignore") as f:
512
+ data = json.load(f)
513
+ converter = traffic_session_converter.TrafficConverter(sanitize_pii=True)
514
+ requests = converter.parse_har(data)
515
+ test_code = converter.generate_playwright_test(requests, test_name=test_name)
516
+ return json.dumps({"total_requests": len(requests), "generated_test": test_code}, indent=2)
517
+
518
+
519
+ def _handle_resolve_visual_som_coordinates(arguments: Dict[str, Any]) -> str:
520
+ helper = visual_som_helper.VisualSoMHelper()
521
+ img = arguments.get("image_path", "screenshot.png")
522
+ lbl = arguments.get("target_label")
523
+ mid = arguments.get("mark_id")
524
+ marked_img, marks_map = helper.generate_som_overlay(img, [])
525
+ resolved = helper.resolve_mark_coordinates(marks_map, target_mark_id=mid, target_label=lbl)
526
+ return json.dumps({"marked_image": marked_img, "resolved_target": resolved}, indent=2)
527
+
528
+
529
+ def _handle_compile_gherkin_suite(arguments: Dict[str, Any]) -> str:
530
+ target = arguments["target"]
531
+ res = gherkin_suite.compile_feature(target) if os.path.isfile(target) else gherkin_suite.run(target)
532
+ if isinstance(res, int):
533
+ return json.dumps({"exit_code": res, "detail": "see reports/gherkin_suite_report.json"}, indent=2)
534
+ persist = {k: v for k, v in res.items() if not k.startswith("_")}
535
+ return json.dumps({
536
+ "feature": persist.get("feature"),
537
+ "totals": persist.get("totals"),
538
+ "errors": persist.get("errors"),
539
+ "gaps": persist.get("gaps"),
540
+ "health_score_pct": persist.get("health_score_pct"),
541
+ }, indent=2)
542
+
543
+
544
+ def _handle_fuzz_api(arguments: Dict[str, Any]) -> str:
545
+ mode = arguments.get("mode", "active")
546
+ dry_run = arguments.get("dry_run", True)
547
+ spec_path = arguments.get("spec_path")
548
+ if spec_path and not (spec_path.startswith("http://") or spec_path.startswith("https://")):
549
+ spec_path = _validate_safe_path(spec_path)
550
+ fuzzer = api_fuzzer.ApiFuzzer(
551
+ base_url=arguments["base_url"],
552
+ spec_path_or_url=spec_path,
553
+ )
554
+ if mode == "passive":
555
+ scorecard = fuzzer.run_passive_scan()
556
+ return json.dumps({
557
+ "mode": scorecard["mode"],
558
+ "total_findings": scorecard["total_findings"],
559
+ "severity_summary": scorecard["severity_summary"],
560
+ "findings": scorecard["findings"],
561
+ }, indent=2)
562
+ scorecard = fuzzer.run_fuzz_suite(dry_run=dry_run)
563
+ return json.dumps({
564
+ "mode": scorecard["mode"],
565
+ "total_mutations": scorecard["total_mutations"],
566
+ "passed": scorecard["passed"],
567
+ "failed": scorecard["failed"],
568
+ "severity_summary": scorecard["severity_summary"],
569
+ }, indent=2)
570
+
571
+
572
+ TOOL_DISPATCH_TABLE: Dict[str, Any] = {
573
+ "verify_all_gates": _handle_verify_all_gates,
574
+ "analyze_project": _handle_analyze_project,
575
+ "setup_project": _handle_setup_project,
576
+ "auto_heal_failure": _handle_auto_heal_failure,
577
+ "check_action_safety": _handle_check_action_safety,
578
+ "check_contract_drift": _handle_check_contract_drift,
579
+ "query_memory_cache": _handle_query_memory_cache,
580
+ "distill_log": _handle_distill_log,
581
+ "run_micro_mutation": _handle_run_micro_mutation,
582
+ "plan_healing_pr": _handle_plan_healing_pr,
583
+ "shard_test_matrix": _handle_shard_test_matrix,
584
+ "pull_alm_story": _handle_pull_alm_story,
585
+ "report_alm_defect": _handle_report_alm_defect,
586
+ "manage_db_sandbox": _handle_manage_db_sandbox,
587
+ "convert_traffic_session": _handle_convert_traffic_session,
588
+ "resolve_visual_som_coordinates": _handle_resolve_visual_som_coordinates,
589
+ "compile_gherkin_suite": _handle_compile_gherkin_suite,
590
+ "fuzz_api": _handle_fuzz_api,
591
+ }
592
+
593
+
241
594
  def handle_tool_call(name: str, arguments: Dict[str, Any]) -> str:
242
- """Executes the corresponding internal tool function and returns text response."""
243
- if name == "verify_all_gates":
244
- repo = arguments.get("repo_path") or os.getcwd()
245
- mode = verify_all.detect_repo_mode(repo)
246
- passed = True
247
- gates = [
248
- ("Gate 1: UTF-8 No BOM", verify_all.gate_1_bom_check),
249
- ("Gate 2: Path Portability", verify_all.gate_2_path_portability),
250
- ("Gate 3: Clean POM", verify_all.gate_3_clean_pom),
251
- ("Gate 4: AI Docs Linter", verify_all.gate_4_ai_docs_lint),
252
- ("Gate 5: Temp Blacklist", verify_all.gate_5_cleanup_blacklist),
253
- ("Gate 6: Credential Security", verify_all.gate_6_credential_security),
254
- ("Gate 7: Infer Linter", verify_all.gate_7_infer_linter),
255
- ("Gate 8: AST Mutation Sentinel", verify_all.gate_8_micro_mutator),
256
- ]
257
- results = []
258
- for g_name, g_fn in gates:
259
- res = g_fn(repo, mode=mode)
260
- results.append(f"{g_name}: {'PASS' if res else 'FAIL'}")
261
- if not res:
262
- passed = False
263
- summary = "100% ALL 8 GATES PASSED" if passed else "VERIFICATION FAILED"
264
- return f"[{summary}]\n" + "\n".join(results)
265
-
266
- elif name == "analyze_project":
267
- repo = arguments.get("repo_path") or os.getcwd()
268
- code = analyze_project.run_full_analysis(repo)
269
- return f"Project Analysis finished with exit code {code}. Report saved at {os.path.join(repo, 'PROJECT_HEALTH_REPORT.md')}."
270
-
271
- elif name == "setup_project":
272
- repo = arguments.get("repo_path") or os.getcwd()
273
- code = setup_project.run_setup(repo)
274
- return f"Project Setup finished with exit code {code}."
275
-
276
- elif name == "auto_heal_failure":
277
- target = arguments.get("target_dir") or os.getcwd()
278
- xml = arguments.get("junit_xml_path")
279
- plan = auto_heal.diagnose_and_plan_heal(target, xml)
280
- return json.dumps(plan, indent=2)
281
-
282
- elif name == "check_action_safety":
283
- auth = safety_firewall.inspect_and_authorize(
284
- action_type=arguments.get("action_type", "click"),
285
- text=arguments.get("element_text", ""),
286
- selector=arguments.get("selector", ""),
287
- force=arguments.get("force", False)
288
- )
289
- return json.dumps(auth, indent=2)
290
-
291
- elif name == "check_contract_drift":
292
- old_data = api_drift_sentinel.load_spec(arguments["old_spec_path"])
293
- new_data = api_drift_sentinel.load_spec(arguments["new_spec_path"])
294
- analysis = api_drift_sentinel.compare_specs(old_data, new_data)
295
- return api_drift_sentinel.generate_markdown_report(analysis, arguments["old_spec_path"], arguments["new_spec_path"])
296
-
297
- elif name == "query_memory_cache":
298
- res = cache_resolver.query_cache(arguments["error_signature"])
299
- return json.dumps(res, indent=2) if res else "No matching solution found in memory cache."
300
-
301
- elif name == "distill_log":
302
- distilled = distill_log.distill_log_content(arguments["raw_log_text"])
303
- return distilled
304
-
305
- elif name == "run_micro_mutation":
306
- target = arguments.get("target_path") or os.getcwd()
307
- mutants = micro_mutator.scan_directory_for_mutants(target) if os.path.isdir(target) else micro_mutator.generate_mutants_for_code(open(target).read(), target)
308
- return json.dumps({"total_mutants": len(mutants), "sample_mutants": mutants[:5]}, indent=2)
309
-
310
- elif name == "plan_healing_pr":
311
- conf = heal_pr_automator.calculate_healing_confidence(
312
- arguments.get("aom_score", 0.95),
313
- arguments.get("spatial_score", 0.90),
314
- arguments.get("visual_score", 0.92)
315
- )
316
- pr = heal_pr_automator.generate_pr_draft(arguments["page_file"], arguments["old_locator"], arguments["new_locator"], conf)
317
- return json.dumps({"confidence": conf, "pr": pr}, indent=2)
318
-
319
- elif name == "shard_test_matrix":
320
- target = arguments.get("target_dir") or os.getcwd()
321
- num_shards = arguments.get("num_shards", 4)
322
- files = matrix_sharder.find_test_files(target)
323
- shards = matrix_sharder.shard_tests_lpt(files, target, num_shards)
324
- return json.dumps({"total_tests": len(files), "shards": shards}, indent=2)
325
-
326
- elif name == "pull_alm_story":
327
- bridge = tms_sync_bridge.TMSBridge()
328
- story = bridge.pull_story_acceptance_criteria(arguments["issue_key"])
329
- return json.dumps(story, indent=2)
330
-
331
- elif name == "report_alm_defect":
332
- bridge = tms_sync_bridge.TMSBridge()
333
- ticket = bridge.report_defect(
334
- project_key=arguments["project_key"],
335
- test_case_id=arguments["test_case_id"],
336
- summary=arguments["summary"],
337
- error_message=arguments["error_message"],
338
- stack_trace=arguments.get("stack_trace", ""),
339
- screenshot_path=arguments.get("screenshot_path", "")
340
- )
341
- return json.dumps(ticket, indent=2)
342
-
343
- elif name == "manage_db_sandbox":
344
- action = arguments["action"]
345
- sandbox = db_sandbox.DBSandbox(db_path=arguments.get("db_path", ""))
346
- if action == "snapshot":
347
- res = sandbox.create_snapshot(arguments.get("snapshot_id", "pristine_baseline"))
348
- elif action == "restore":
349
- res = sandbox.restore_snapshot(arguments.get("snapshot_id", "pristine_baseline"))
350
- elif action == "audit_and_purge":
351
- res = sandbox.audit_and_purge_pollution()
352
- else:
353
- raise ValueError(f"Unknown sandbox action: {action}")
354
- return json.dumps(res, indent=2)
355
-
356
- elif name == "convert_traffic_session":
357
- input_file = arguments["input_file"]
358
- test_name = arguments.get("test_name", "ProductionTrafficReplay")
359
- with open(input_file, "r", encoding="utf-8", errors="ignore") as f:
360
- data = json.load(f)
361
- converter = traffic_session_converter.TrafficConverter(sanitize_pii=True)
362
- requests = converter.parse_har(data)
363
- test_code = converter.generate_playwright_test(requests, test_name=test_name)
364
- return json.dumps({"total_requests": len(requests), "generated_test": test_code}, indent=2)
365
-
366
- elif name == "resolve_visual_som_coordinates":
367
- helper = visual_som_helper.VisualSoMHelper()
368
- img = arguments.get("image_path", "screenshot.png")
369
- lbl = arguments.get("target_label")
370
- mid = arguments.get("mark_id")
371
- marked_img, marks_map = helper.generate_som_overlay(img, [])
372
- resolved = helper.resolve_mark_coordinates(marks_map, target_mark_id=mid, target_label=lbl)
373
- return json.dumps({"marked_image": marked_img, "resolved_target": resolved}, indent=2)
374
-
375
- raise ValueError(f"Unknown MCP tool: {name}")
595
+ """Executes the corresponding internal tool function via dispatch table."""
596
+ handler = TOOL_DISPATCH_TABLE.get(name)
597
+ if not handler:
598
+ raise ValueError(f"Unknown MCP tool: {name}")
599
+ return handler(arguments)
376
600
 
377
601
 
378
602
  def process_message(msg: Dict[str, Any]) -> Dict[str, Any]:
@@ -435,7 +659,7 @@ def process_message(msg: Dict[str, Any]) -> Dict[str, Any]:
435
659
  "content": [
436
660
  {
437
661
  "type": "text",
438
- "text": f"Error executing {tool_name}: {str(e)}\n{traceback.format_exc()}"
662
+ "text": f"Error executing {tool_name}: {type(e).__name__}: {str(e)}"
439
663
  }
440
664
  ],
441
665
  "isError": True