synapse-hub 2.7.0 → 3.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.
- package/.agents/agents/live-explorer.md +2 -0
- package/.agents/agents/pom-crafter.md +2 -0
- package/.agents/agents/test-runner.md +3 -0
- package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
- package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
- package/.agents/skills/review-spec/SKILL.md +1 -1
- package/.agents/skills/senior-code-review/SKILL.md +1 -1
- package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.en.md +70 -291
- package/README.md +68 -303
- package/docs/ARCHITECTURE.md +48 -2
- package/docs/CONTRIBUTING.md +1 -1
- package/docs/QUALITY_GATES.md +113 -0
- package/package.json +1 -1
- package/scripts/package_dist.py +2 -1
- package/skills_manifest.json +6 -6
- package/synapse.config.json +1 -1
- package/templates/ai_bridges/CLAUDE.md +1 -1
- package/templates/ai_bridges/copilot-instructions.md +2 -2
- package/tools/__init__.py +18 -1
- package/tools/_openapi_parser.py +1 -1
- package/tools/agent_canary.py +42 -30
- package/tools/agentless_patcher.py +2 -2
- package/tools/analyze_project.py +3 -5
- package/tools/api_drift_sentinel.py +4 -4
- package/tools/api_fuzzer.py +8 -8
- package/tools/auto_heal.py +4 -5
- package/tools/auto_heal_reflexion.py +2 -2
- package/tools/autogen_qa_broker.py +2 -2
- package/tools/browser_helpers.js +2 -2
- package/tools/browsergym_eval.py +2 -3
- package/tools/bug_reporter.py +11 -13
- package/tools/bump_version.py +75 -5
- package/tools/cache_resolver.py +78 -83
- package/tools/check_env.py +9 -1
- package/tools/check_version_sync.py +40 -3
- package/tools/ci_healing_hook.py +4 -6
- package/tools/cli.py +5 -7
- package/tools/config_decoupler.py +2 -3
- package/tools/core/__init__.py +52 -0
- package/tools/count_tokens.py +22 -20
- package/tools/dashboard_generator.py +627 -998
- package/tools/db_sandbox.py +65 -53
- package/tools/debt_harvester.py +3 -3
- package/tools/distill_log.py +3 -3
- package/tools/dspy_qa_signatures.py +3 -4
- package/tools/export_postman_collection.py +8 -8
- package/tools/flaky_detector.py +2 -2
- package/tools/fuzz_payloads.py +5 -5
- package/tools/generate_allure_report.py +1 -1
- package/tools/generate_jmx_plan.py +4 -4
- package/tools/gherkin_suite.py +1 -1
- package/tools/git_pre_commit_guard.py +2 -2
- package/tools/goldfish_review.py +2 -2
- package/tools/heal/__init__.py +28 -0
- package/tools/heal_pr_automator.py +6 -6
- package/tools/heuristic_locator_matcher.py +2 -2
- package/tools/infer_linter.py +8 -8
- package/tools/interview_story.py +0 -1
- package/tools/jtl_analyzer.py +11 -11
- package/tools/lint/__init__.py +30 -0
- package/tools/lint_ai_docs.py +42 -0
- package/tools/lookup_tc.py +1 -1
- package/tools/matrix_sharder.py +15 -13
- package/tools/mcp/__init__.py +16 -0
- package/tools/mcp_excel_server.py +2 -2
- package/tools/mcp_server.py +3 -4
- package/tools/md_to_xlsx.py +26 -26
- package/tools/micro_mutator.py +11 -12
- package/tools/network_mock_shield.py +27 -27
- package/tools/newman_analyzer.py +2 -3
- package/tools/omni_emitter.py +5 -8
- package/tools/omni_parser.py +5 -5
- package/tools/omni_transpiler.py +5 -5
- package/tools/output_formatter.py +19 -0
- package/tools/perf/__init__.py +24 -0
- package/tools/pinpoint_failure.py +1 -1
- package/tools/project_state.py +0 -2
- package/tools/qa_event_stream.py +1 -1
- package/tools/repo_map.py +8 -8
- package/tools/report/__init__.py +32 -0
- package/tools/reversible_sandbox.py +1 -2
- package/tools/safety_firewall.py +2 -2
- package/tools/senior_code_review.py +2 -2
- package/tools/setup_project.py +10 -12
- package/tools/skill_collision_guard.py +3 -3
- package/tools/skill_vault.py +12 -3
- package/tools/skillspector.py +5 -7
- package/tools/smart_visual_masker.py +4 -6
- package/tools/spec_drift_sentinel.py +1 -2
- package/tools/swe_qa_aci.py +2 -2
- package/tools/synapse_qa_bench.py +3 -3
- package/tools/sync_agent_rules.py +17 -3
- package/tools/telemetry_correlator.py +3 -2
- package/tools/test_memory_os.py +15 -5
- package/tools/textgrad_mutation_optimizer.py +3 -3
- package/tools/tms_sync_bridge.py +5 -5
- package/tools/tot_exploratory_tester.py +5 -6
- package/tools/traffic_session_converter.py +9 -9
- package/tools/universal_ir.py +2 -3
- package/tools/update_sentinel.py +6 -9
- package/tools/verify_all.py +10 -0
- package/tools/visual_diff_canvas.py +1 -2
- package/tools/visual_som_helper.py +15 -13
- package/tools/watch_mode.py +1 -1
- package/tools/wcag_audit.py +22 -1
- package/tools/welcome_page.py +5 -6
package/tools/omni_emitter.py
CHANGED
|
@@ -12,18 +12,15 @@ Supports:
|
|
|
12
12
|
5. Cypress JavaScript (cypress-js)
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
import
|
|
16
|
-
from typing import Dict, List, Optional
|
|
15
|
+
from typing import Dict
|
|
17
16
|
|
|
18
17
|
try:
|
|
19
18
|
from tools.universal_ir import (
|
|
20
|
-
ProjectIR,
|
|
21
|
-
ActionType, LocatorStrategy, AssertionType
|
|
19
|
+
ProjectIR, ActionType, LocatorStrategy
|
|
22
20
|
)
|
|
23
21
|
except ImportError:
|
|
24
22
|
from universal_ir import (
|
|
25
|
-
ProjectIR,
|
|
26
|
-
ActionType, LocatorStrategy, AssertionType
|
|
23
|
+
ProjectIR, ActionType, LocatorStrategy
|
|
27
24
|
)
|
|
28
25
|
|
|
29
26
|
|
|
@@ -243,7 +240,7 @@ export async function safeInteract(
|
|
|
243
240
|
for tc in project.test_cases:
|
|
244
241
|
t_code.extend([
|
|
245
242
|
f"test('{escape_js(tc.id)}: {escape_js(tc.title)}', async ({{ page }}) => {{",
|
|
246
|
-
|
|
243
|
+
" const uniqueCode = 'AUTO_' + Date.now();",
|
|
247
244
|
])
|
|
248
245
|
|
|
249
246
|
# Emit Hermetic Pre-flight Fixtures if present
|
|
@@ -445,7 +442,7 @@ export async function safeInteract(
|
|
|
445
442
|
"import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI",
|
|
446
443
|
"import pages.*",
|
|
447
444
|
"",
|
|
448
|
-
|
|
445
|
+
"String uniqueCode = BasePage.uniqueCode('AUTO_')",
|
|
449
446
|
"",
|
|
450
447
|
]
|
|
451
448
|
for s in tc.steps:
|
package/tools/omni_parser.py
CHANGED
|
@@ -10,7 +10,6 @@ the Universal Intermediate Representation (universal_ir.ProjectIR).
|
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
12
|
import re
|
|
13
|
-
import sys
|
|
14
13
|
from typing import Dict, List, Optional, Tuple
|
|
15
14
|
|
|
16
15
|
try:
|
|
@@ -38,7 +37,7 @@ class OmniParser:
|
|
|
38
37
|
@staticmethod
|
|
39
38
|
def detect_language_and_framework(file_path: str, content: str) -> Tuple[str, str]:
|
|
40
39
|
ext = os.path.splitext(file_path)[1].lower()
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
# Groovy / Katalon
|
|
43
42
|
if ext == ".groovy":
|
|
44
43
|
return "groovy", "katalon"
|
|
@@ -369,8 +368,8 @@ class OmniParser:
|
|
|
369
368
|
# 0. Detect Hardcoded Sleep Antipatterns (Thread.sleep, WebUI.delay, cy.wait, time.sleep)
|
|
370
369
|
sleep_m = re.search(r"(?:Thread\.sleep|WebUI\.delay|time\.sleep|cy\.wait)\s*\(\s*(\d+)\s*\)", line_str, re.IGNORECASE)
|
|
371
370
|
if sleep_m:
|
|
372
|
-
|
|
373
|
-
ms =
|
|
371
|
+
sleep_val = int(sleep_m.group(1))
|
|
372
|
+
ms = sleep_val * 1000 if sleep_val <= 60 else sleep_val # WebUI.delay(3) vs Thread.sleep(3000)
|
|
374
373
|
steps.append(StepIR(
|
|
375
374
|
action=ActionType.WAIT,
|
|
376
375
|
wait_strategy=WaitStrategy.STATIC_SLEEP,
|
|
@@ -389,7 +388,8 @@ class OmniParser:
|
|
|
389
388
|
if cy_act == "click":
|
|
390
389
|
steps.append(StepIR(action=ActionType.CLICK, target=sel, description=f"Cypress click on {sel}"))
|
|
391
390
|
elif cy_act in ["type", "clear"]:
|
|
392
|
-
|
|
391
|
+
m_type = re.search(r"(?:\"([^\"]*)\"|'([^']*)')", cy_args)
|
|
392
|
+
type_val = extract_quoted_string((m_type.group(1), m_type.group(2))) if m_type else ""
|
|
393
393
|
steps.append(StepIR(action=ActionType.FILL, target=sel, value=type_val, description=f"Cypress type on {sel}"))
|
|
394
394
|
elif cy_act == "should":
|
|
395
395
|
steps.append(StepIR(action=ActionType.ASSERT, target=sel, assertion_type=AssertionType.VISIBLE, description=line_str))
|
package/tools/omni_transpiler.py
CHANGED
|
@@ -12,15 +12,15 @@ import os
|
|
|
12
12
|
import sys
|
|
13
13
|
import json
|
|
14
14
|
import argparse
|
|
15
|
-
from typing import Dict,
|
|
15
|
+
from typing import Dict, Any, Optional
|
|
16
16
|
|
|
17
17
|
# Dynamic imports
|
|
18
18
|
try:
|
|
19
|
-
from tools.universal_ir import
|
|
19
|
+
from tools.universal_ir import resolve_hermetic_fixtures
|
|
20
20
|
from tools.omni_parser import OmniParser
|
|
21
21
|
from tools.omni_emitter import OmniEmitter
|
|
22
22
|
except ImportError:
|
|
23
|
-
from universal_ir import
|
|
23
|
+
from universal_ir import resolve_hermetic_fixtures
|
|
24
24
|
from omni_parser import OmniParser
|
|
25
25
|
from omni_emitter import OmniEmitter
|
|
26
26
|
|
|
@@ -65,7 +65,7 @@ def scan_repository(repo_path: str) -> Dict[str, Any]:
|
|
|
65
65
|
if not os.path.exists(repo_path):
|
|
66
66
|
raise FileNotFoundError(f"Path does not exist: {repo_path}")
|
|
67
67
|
|
|
68
|
-
inventory = {
|
|
68
|
+
inventory: Dict[str, Any] = {
|
|
69
69
|
"repository": os.path.abspath(repo_path),
|
|
70
70
|
"total_files_scanned": 0,
|
|
71
71
|
"languages": {},
|
|
@@ -88,7 +88,7 @@ def scan_repository(repo_path: str) -> Dict[str, Any]:
|
|
|
88
88
|
try:
|
|
89
89
|
p_ir = OmniParser.parse_file(file_full)
|
|
90
90
|
inventory["total_files_scanned"] += 1
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
# Language counts
|
|
93
93
|
inventory["languages"][p_ir.source_language] = inventory["languages"].get(p_ir.source_language, 0) + 1
|
|
94
94
|
inventory["frameworks"][p_ir.source_framework] = inventory["frameworks"].get(p_ir.source_framework, 0) + 1
|
|
@@ -58,26 +58,32 @@ class Colors:
|
|
|
58
58
|
|
|
59
59
|
@classmethod
|
|
60
60
|
def cyan(cls, text: str) -> str:
|
|
61
|
+
"""Wraps text with ANSI cyan color if color output is enabled."""
|
|
61
62
|
return f"{cls._c(cls.CYAN)}{text}{cls._c(cls.RESET)}"
|
|
62
63
|
|
|
63
64
|
@classmethod
|
|
64
65
|
def green(cls, text: str) -> str:
|
|
66
|
+
"""Wraps text with ANSI green color if color output is enabled."""
|
|
65
67
|
return f"{cls._c(cls.GREEN)}{text}{cls._c(cls.RESET)}"
|
|
66
68
|
|
|
67
69
|
@classmethod
|
|
68
70
|
def yellow(cls, text: str) -> str:
|
|
71
|
+
"""Wraps text with ANSI yellow color if color output is enabled."""
|
|
69
72
|
return f"{cls._c(cls.YELLOW)}{text}{cls._c(cls.RESET)}"
|
|
70
73
|
|
|
71
74
|
@classmethod
|
|
72
75
|
def red(cls, text: str) -> str:
|
|
76
|
+
"""Wraps text with ANSI red color if color output is enabled."""
|
|
73
77
|
return f"{cls._c(cls.RED)}{text}{cls._c(cls.RESET)}"
|
|
74
78
|
|
|
75
79
|
@classmethod
|
|
76
80
|
def bold(cls, text: str) -> str:
|
|
81
|
+
"""Wraps text with ANSI bold weight if color output is enabled."""
|
|
77
82
|
return f"{cls._c(cls.BOLD)}{text}{cls._c(cls.RESET)}"
|
|
78
83
|
|
|
79
84
|
@classmethod
|
|
80
85
|
def dim(cls, text: str) -> str:
|
|
86
|
+
"""Wraps text with ANSI dim intensity if color output is enabled."""
|
|
81
87
|
return f"{cls._c(cls.DIM)}{text}{cls._c(cls.RESET)}"
|
|
82
88
|
|
|
83
89
|
|
|
@@ -85,27 +91,34 @@ class OutputFormatter:
|
|
|
85
91
|
"""Standardized console & machine-readable output renderer."""
|
|
86
92
|
|
|
87
93
|
def __init__(self, quiet: bool = False, json_mode: bool = False, verbose: bool = False):
|
|
94
|
+
"""Initializes the output formatter with verbosity and mode toggles."""
|
|
88
95
|
self.quiet = quiet
|
|
89
96
|
self.json_mode = json_mode
|
|
90
97
|
self.verbose = verbose
|
|
91
98
|
|
|
92
99
|
def debug(self, message: str):
|
|
100
|
+
"""Prints a debug message when verbose mode is active."""
|
|
93
101
|
if self.verbose and not self.quiet and not self.json_mode:
|
|
94
102
|
print(f"{Colors.dim('🔍 [DEBUG]')} {message}")
|
|
95
103
|
|
|
96
104
|
def info(self, message: str):
|
|
105
|
+
"""Alias for info_msg printing informational text."""
|
|
97
106
|
self.info_msg(message)
|
|
98
107
|
|
|
99
108
|
def warn(self, message: str):
|
|
109
|
+
"""Alias for warn_msg printing warning text."""
|
|
100
110
|
self.warn_msg(message)
|
|
101
111
|
|
|
102
112
|
def error(self, message: str):
|
|
113
|
+
"""Alias for fail_msg printing failure/error text."""
|
|
103
114
|
self.fail_msg(message)
|
|
104
115
|
|
|
105
116
|
def success(self, message: str):
|
|
117
|
+
"""Alias for pass_msg printing passing/success text."""
|
|
106
118
|
self.pass_msg(message)
|
|
107
119
|
|
|
108
120
|
def header(self, title: str):
|
|
121
|
+
"""Renders an eye-catching framed header box for CLI stages."""
|
|
109
122
|
if not self.quiet and not self.json_mode:
|
|
110
123
|
border = "=" * 70
|
|
111
124
|
print(f"\n{Colors.cyan(border)}")
|
|
@@ -113,26 +126,32 @@ class OutputFormatter:
|
|
|
113
126
|
print(f"{Colors.cyan(border)}\n")
|
|
114
127
|
|
|
115
128
|
def step(self, current: int, total: int, title: str):
|
|
129
|
+
"""Prints a numbered step indicator for multi-phase operations."""
|
|
116
130
|
if not self.quiet and not self.json_mode:
|
|
117
131
|
print(f"{Colors.bold(f'[{current}/{total}]')} {title}")
|
|
118
132
|
|
|
119
133
|
def pass_msg(self, message: str):
|
|
134
|
+
"""Prints a green checkmark pass badge with the given message."""
|
|
120
135
|
if not self.json_mode:
|
|
121
136
|
print(f"{Colors.green('✅ [PASS]')} {message}")
|
|
122
137
|
|
|
123
138
|
def fail_msg(self, message: str):
|
|
139
|
+
"""Prints a red cross fail badge with the given message."""
|
|
124
140
|
if not self.json_mode:
|
|
125
141
|
print(f"{Colors.red('❌ [FAIL]')} {message}")
|
|
126
142
|
|
|
127
143
|
def warn_msg(self, message: str):
|
|
144
|
+
"""Prints a yellow warning badge with the given message."""
|
|
128
145
|
if not self.quiet and not self.json_mode:
|
|
129
146
|
print(f"{Colors.yellow('⚠️ [WARN]')} {message}")
|
|
130
147
|
|
|
131
148
|
def info_msg(self, message: str):
|
|
149
|
+
"""Prints a cyan informational badge with the given message."""
|
|
132
150
|
if not self.quiet and not self.json_mode:
|
|
133
151
|
print(f"{Colors.cyan('ℹ️ [INFO]')} {message}")
|
|
134
152
|
|
|
135
153
|
def print_data(self, data: Any, format_type: str = "text"):
|
|
154
|
+
"""Formats and outputs data payload as either indented JSON or raw text."""
|
|
136
155
|
if format_type == "json" or self.json_mode:
|
|
137
156
|
if isinstance(data, (dict, list)):
|
|
138
157
|
print(json.dumps(data, indent=2, ensure_ascii=False))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Synapse Hub — Performance, Stress & Flakiness Profilers (tools.perf)
|
|
3
|
+
---------------------------------------------------------------------
|
|
4
|
+
Domain package grouping all JMeter test plan generators, flakiness
|
|
5
|
+
sentinels, BFCL agent gym benchmarks, and test matrix sharders.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from tools import (
|
|
9
|
+
browsergym_eval,
|
|
10
|
+
db_sandbox,
|
|
11
|
+
flaky_detector,
|
|
12
|
+
generate_jmx_plan,
|
|
13
|
+
matrix_sharder,
|
|
14
|
+
synapse_qa_bench,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"browsergym_eval",
|
|
19
|
+
"db_sandbox",
|
|
20
|
+
"flaky_detector",
|
|
21
|
+
"generate_jmx_plan",
|
|
22
|
+
"matrix_sharder",
|
|
23
|
+
"synapse_qa_bench",
|
|
24
|
+
]
|
|
@@ -58,7 +58,7 @@ def parse_failures(xml_path):
|
|
|
58
58
|
if reason_match:
|
|
59
59
|
reason = reason_match.group(1).strip()
|
|
60
60
|
else:
|
|
61
|
-
lines = [
|
|
61
|
+
lines = [line.strip() for line in combined.split("\n") if line.strip()]
|
|
62
62
|
reason = lines[0] if lines else "Unknown Error"
|
|
63
63
|
|
|
64
64
|
# Extract Failed Line in Groovy Page / Script
|
package/tools/project_state.py
CHANGED
|
@@ -8,9 +8,7 @@ Enforces a strict Rolling Window (< 50 lines, ~200 tokens) to guarantee zero tok
|
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
10
|
import sys
|
|
11
|
-
import json
|
|
12
11
|
from datetime import datetime
|
|
13
|
-
from pathlib import Path
|
|
14
12
|
|
|
15
13
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
16
14
|
if hasattr(sys.stdout, 'reconfigure'):
|
package/tools/qa_event_stream.py
CHANGED
|
@@ -25,7 +25,7 @@ import json
|
|
|
25
25
|
import hashlib
|
|
26
26
|
import argparse
|
|
27
27
|
from datetime import datetime, timezone
|
|
28
|
-
from typing import Dict, Any,
|
|
28
|
+
from typing import Dict, Any, Optional
|
|
29
29
|
|
|
30
30
|
# Enforce UTF-8 stdout encoding
|
|
31
31
|
if hasattr(sys.stdout, 'reconfigure'):
|
package/tools/repo_map.py
CHANGED
|
@@ -199,13 +199,13 @@ def build_repo_map(target_repo=None, output_txt=None, output_json=None):
|
|
|
199
199
|
# Render text map
|
|
200
200
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
201
201
|
out_lines = [
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
"# ==============================================================================",
|
|
203
|
+
"# ENTERPRISE AUTOMATION REPOSITORY MAP (Aider-Style AST Compression)",
|
|
204
204
|
f"# Generated : {timestamp}",
|
|
205
205
|
f"# Target : {repo}",
|
|
206
206
|
f"# Scope : {len(classes)} Classes ({total_raw_lines} source lines, {total_raw_bytes / 1024:.1f} KB)",
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
"# Purpose : Token-budgeted index (< 600 tokens) for instant method lookup",
|
|
208
|
+
"# ==============================================================================\n"
|
|
209
209
|
]
|
|
210
210
|
|
|
211
211
|
total_methods = 0
|
|
@@ -294,9 +294,9 @@ def main():
|
|
|
294
294
|
return 1
|
|
295
295
|
|
|
296
296
|
stats = build_repo_map(target_repo=target_repo)
|
|
297
|
-
print(
|
|
298
|
-
print(
|
|
299
|
-
print(
|
|
297
|
+
print("\n======================================================================")
|
|
298
|
+
print(" Enterprise Repository Map Compressor (Aider-Style AST Map) ")
|
|
299
|
+
print("======================================================================")
|
|
300
300
|
print(f" Target Repository : {stats['target_repo']}")
|
|
301
301
|
print(f" Indexed Classes : {stats['classes_count']} Page Classes & Helpers")
|
|
302
302
|
print(f" Extracted Methods : {stats['methods_count']} API signatures")
|
|
@@ -304,7 +304,7 @@ def main():
|
|
|
304
304
|
print(f" Compressed Map Size : {stats['compressed_bytes'] / 1024:.1f} KB (~{stats['estimated_tokens']} tokens)")
|
|
305
305
|
print(f" Compression Ratio : {stats['compression_ratio_pct']:.1f}% reduction")
|
|
306
306
|
print(f" Artifact Generated : {stats['output_file']}")
|
|
307
|
-
print(
|
|
307
|
+
print("======================================================================\n")
|
|
308
308
|
|
|
309
309
|
if args.print:
|
|
310
310
|
with open(stats['output_file'], "r", encoding="utf-8") as f:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Synapse Hub — Reporting & Telemetry Engines (tools.report)
|
|
3
|
+
----------------------------------------------------------
|
|
4
|
+
Domain package grouping all HTML dashboard, spreadsheet, JTL/Newman
|
|
5
|
+
metric parser, and visual reporting tools.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from tools import (
|
|
9
|
+
bug_reporter,
|
|
10
|
+
dashboard_generator,
|
|
11
|
+
generate_allure_report,
|
|
12
|
+
generate_test_reports,
|
|
13
|
+
jtl_analyzer,
|
|
14
|
+
md_to_xlsx,
|
|
15
|
+
newman_analyzer,
|
|
16
|
+
output_formatter,
|
|
17
|
+
telemetry_correlator,
|
|
18
|
+
welcome_page,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"bug_reporter",
|
|
23
|
+
"dashboard_generator",
|
|
24
|
+
"generate_allure_report",
|
|
25
|
+
"generate_test_reports",
|
|
26
|
+
"jtl_analyzer",
|
|
27
|
+
"md_to_xlsx",
|
|
28
|
+
"newman_analyzer",
|
|
29
|
+
"output_formatter",
|
|
30
|
+
"telemetry_correlator",
|
|
31
|
+
"welcome_page",
|
|
32
|
+
]
|
|
@@ -17,11 +17,10 @@ Reference:
|
|
|
17
17
|
|
|
18
18
|
import os
|
|
19
19
|
import sys
|
|
20
|
-
import json
|
|
21
20
|
import shlex
|
|
22
21
|
import subprocess
|
|
23
22
|
from dataclasses import dataclass, field
|
|
24
|
-
from typing import List, Optional,
|
|
23
|
+
from typing import List, Optional, Dict, Any
|
|
25
24
|
|
|
26
25
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
27
26
|
if hasattr(sys.stdout, 'reconfigure'):
|
package/tools/safety_firewall.py
CHANGED
|
@@ -245,7 +245,7 @@ def detect_action_loop(action_history: List[str], repeat_threshold: int = 4) ->
|
|
|
245
245
|
"""
|
|
246
246
|
Detects infinite action loops and repeating cycle patterns among agent tool calls.
|
|
247
247
|
Inspired by agentblazo/blazo deterministic detection rules.
|
|
248
|
-
|
|
248
|
+
|
|
249
249
|
Checks:
|
|
250
250
|
1. Consecutive identical actions (>= repeat_threshold)
|
|
251
251
|
2. 2-action repeating cycle: [A, B, A, B]
|
|
@@ -411,7 +411,7 @@ def main():
|
|
|
411
411
|
if all_authorized:
|
|
412
412
|
print(f"\n✅ All actions cleared by Safety Firewall{' (Dry-Run)' if args.dry_run else ''}.\n")
|
|
413
413
|
else:
|
|
414
|
-
print(
|
|
414
|
+
print("\n❌ Pre-execution Blocked: Destructive actions intercepted without --force or ALLOW_DESTRUCTIVE=1.\n")
|
|
415
415
|
|
|
416
416
|
return 0 if all_authorized else 1
|
|
417
417
|
|
|
@@ -28,7 +28,7 @@ import re
|
|
|
28
28
|
import json
|
|
29
29
|
import subprocess
|
|
30
30
|
import argparse
|
|
31
|
-
from typing import Dict, List, Any,
|
|
31
|
+
from typing import Dict, List, Any, Tuple
|
|
32
32
|
|
|
33
33
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
34
34
|
try:
|
|
@@ -83,7 +83,7 @@ def get_git_diff(repo_dir: str, target: str = "uncommitted") -> str:
|
|
|
83
83
|
check=True
|
|
84
84
|
)
|
|
85
85
|
return res.stdout
|
|
86
|
-
except Exception
|
|
86
|
+
except Exception:
|
|
87
87
|
return ""
|
|
88
88
|
|
|
89
89
|
|
package/tools/setup_project.py
CHANGED
|
@@ -24,7 +24,6 @@ import json
|
|
|
24
24
|
import shutil
|
|
25
25
|
import argparse
|
|
26
26
|
from pathlib import Path
|
|
27
|
-
from datetime import datetime
|
|
28
27
|
|
|
29
28
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
30
29
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -39,9 +38,8 @@ HUB_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, ".."))
|
|
|
39
38
|
TEMPLATES_DIR = os.path.join(HUB_ROOT, "templates")
|
|
40
39
|
sys.path.insert(0, SCRIPT_DIR)
|
|
41
40
|
|
|
42
|
-
import analyze_project
|
|
43
|
-
import
|
|
44
|
-
import project_state
|
|
41
|
+
import analyze_project # noqa: E402
|
|
42
|
+
import project_state # noqa: E402
|
|
45
43
|
|
|
46
44
|
GREEN = "\033[92m"
|
|
47
45
|
RED = "\033[91m"
|
|
@@ -412,7 +410,7 @@ def inject_antigravity_global_plugin():
|
|
|
412
410
|
try:
|
|
413
411
|
os.unlink(dst)
|
|
414
412
|
except OSError:
|
|
415
|
-
|
|
413
|
+
_ = False
|
|
416
414
|
if sys.platform == "win32":
|
|
417
415
|
try:
|
|
418
416
|
import _winapi
|
|
@@ -420,7 +418,7 @@ def inject_antigravity_global_plugin():
|
|
|
420
418
|
_winapi.CreateJunction(src, dst)
|
|
421
419
|
return
|
|
422
420
|
except (OSError, ImportError, AttributeError):
|
|
423
|
-
|
|
421
|
+
_ = False
|
|
424
422
|
shutil.copytree(src, dst, dirs_exist_ok=True, ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.tmp"))
|
|
425
423
|
|
|
426
424
|
sync_dir(os.path.join(HUB_ROOT, ".agents", "skills"), os.path.join(target_plugin_dir, "skills"))
|
|
@@ -612,26 +610,26 @@ def run_setup(target_repo, dry_run=False, profile="auto", no_global=True, no_git
|
|
|
612
610
|
if dry_run:
|
|
613
611
|
print(f"Operational Mode : {YELLOW}DRY-RUN (Simulating without modifying files on disk){RESET}\n")
|
|
614
612
|
else:
|
|
615
|
-
print(
|
|
613
|
+
print("Operational Mode : APPLY CHANGES\n")
|
|
616
614
|
|
|
617
615
|
if dry_run:
|
|
618
616
|
print(f"{YELLOW}[DRY-RUN PREVIEW]{RESET}")
|
|
619
617
|
print(f" • Profile: {profile.upper()}")
|
|
620
618
|
print(f" • Would generate: {os.path.join(target_repo, 'PROJECT_AI_GUIDE.md')}")
|
|
621
|
-
print(
|
|
619
|
+
print(" • Would deploy AI bridges: .cursor/rules/synapse-qa.mdc, .github/copilot-instructions.md, .windsurfrules")
|
|
622
620
|
print(f" • Would install pre-push hook: {os.path.join(target_repo, '.git', 'hooks', 'pre-push')}")
|
|
623
|
-
print(
|
|
621
|
+
print(" • Would initialize: Central Agent Memory (~/.agent_memory/global_cache.db)")
|
|
624
622
|
print(f"\n{GREEN}✅ Dry-run completed successfully. Zero changes written to disk.{RESET}\n")
|
|
625
623
|
return 0
|
|
626
624
|
|
|
627
625
|
# 2. Ensure PROJECT_AI_GUIDE.md & PROJECT_STATE.md
|
|
628
626
|
print(f"{BOLD}[1/5] Checking Project AI Guide & State Anchor...{RESET}")
|
|
629
|
-
|
|
627
|
+
ensure_project_ai_guide(target_repo, stack_info, profile=profile)
|
|
630
628
|
project_state.init_state(target_repo)
|
|
631
629
|
|
|
632
630
|
# 3. Deploy Universal AI Bridges (Cursor, Claude Code, GitHub Copilot, Windsurf)
|
|
633
631
|
print(f"\n{BOLD}[2/5] Deploying Universal AI Bridges (Cursor, Claude, Copilot, Windsurf)...{RESET}")
|
|
634
|
-
|
|
632
|
+
install_universal_ai_bridges(target_repo)
|
|
635
633
|
|
|
636
634
|
# 4. Inject Antigravity Global Plugin
|
|
637
635
|
plugin_injected = False
|
|
@@ -687,7 +685,7 @@ def run_setup(target_repo, dry_run=False, profile="auto", no_global=True, no_git
|
|
|
687
685
|
print(f" Target Framework : {stack_info['framework']}")
|
|
688
686
|
print(f" Active Profile : {profile.upper()}")
|
|
689
687
|
print(f" AI Project Guide : {os.path.join(target_repo, 'PROJECT_AI_GUIDE.md')}")
|
|
690
|
-
print(
|
|
688
|
+
print(" Universal Bridges : Cursor (.cursor/rules), Claude Code, Copilot, Windsurf")
|
|
691
689
|
print(f" Antigravity Plugin : {'ACTIVE (~/.gemini/config/plugins/synapse-hub)' if plugin_injected else 'STANDALONE'}")
|
|
692
690
|
print(f" AST Method Map : {os.path.join(target_repo, 'repo_map.txt')}")
|
|
693
691
|
print(f" Health Report : {os.path.join(target_repo, 'PROJECT_HEALTH_REPORT.md')}")
|
|
@@ -23,8 +23,8 @@ import sys
|
|
|
23
23
|
import re
|
|
24
24
|
import math
|
|
25
25
|
import json
|
|
26
|
+
from typing import Dict, List, Tuple, Any
|
|
26
27
|
import argparse
|
|
27
|
-
from typing import Dict, List, Tuple
|
|
28
28
|
from collections import Counter
|
|
29
29
|
|
|
30
30
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
@@ -136,7 +136,7 @@ class SkillCollisionGuard:
|
|
|
136
136
|
fallback_name = os.path.basename(os.path.dirname(path))
|
|
137
137
|
return fallback_name, ""
|
|
138
138
|
|
|
139
|
-
def audit(self) -> Tuple[bool, List[Dict[str,
|
|
139
|
+
def audit(self) -> Tuple[bool, List[Dict[str, Any]]]:
|
|
140
140
|
"""
|
|
141
141
|
Computes pairwise similarity.
|
|
142
142
|
Returns (is_clean, report_items).
|
|
@@ -211,7 +211,7 @@ def run_guard(skills_dir: str = ".agents/skills", threshold: float = 0.75, warn_
|
|
|
211
211
|
print("=" * 70 + "\n")
|
|
212
212
|
return 1
|
|
213
213
|
|
|
214
|
-
print(
|
|
214
|
+
print("\n✅ [PASS] 0 routing collisions detected. All skill descriptions uniquely segmented.\n")
|
|
215
215
|
print("=" * 70 + "\n")
|
|
216
216
|
return 0
|
|
217
217
|
|
package/tools/skill_vault.py
CHANGED
|
@@ -22,7 +22,7 @@ import json
|
|
|
22
22
|
import re
|
|
23
23
|
import difflib
|
|
24
24
|
import argparse
|
|
25
|
-
from dataclasses import dataclass
|
|
25
|
+
from dataclasses import dataclass
|
|
26
26
|
from typing import List, Optional, Dict, Any, Set
|
|
27
27
|
|
|
28
28
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
@@ -101,6 +101,15 @@ class PageClassPrimitive:
|
|
|
101
101
|
tags: str = "" # Comma-separated domain tags, e.g. "auth,sso,login"
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
class ManagedConnection(sqlite3.Connection):
|
|
105
|
+
"""SQLite connection that automatically commits/rollbacks and closes on context exit."""
|
|
106
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
107
|
+
try:
|
|
108
|
+
super().__exit__(exc_type, exc_val, exc_tb)
|
|
109
|
+
finally:
|
|
110
|
+
self.close()
|
|
111
|
+
|
|
112
|
+
|
|
104
113
|
class SkillVault:
|
|
105
114
|
"""Persistent library of verified, self-contained Page Class methods."""
|
|
106
115
|
|
|
@@ -113,8 +122,8 @@ class SkillVault:
|
|
|
113
122
|
self.db_path = db_path
|
|
114
123
|
self._init_schema()
|
|
115
124
|
|
|
116
|
-
def _get_connection(self) ->
|
|
117
|
-
conn = sqlite3.connect(self.db_path)
|
|
125
|
+
def _get_connection(self) -> ManagedConnection:
|
|
126
|
+
conn = sqlite3.connect(self.db_path, factory=ManagedConnection)
|
|
118
127
|
conn.row_factory = sqlite3.Row
|
|
119
128
|
return conn
|
|
120
129
|
|
package/tools/skillspector.py
CHANGED
|
@@ -42,17 +42,15 @@ Usage:
|
|
|
42
42
|
from __future__ import annotations
|
|
43
43
|
|
|
44
44
|
import ast
|
|
45
|
-
import fnmatch
|
|
46
45
|
import hashlib
|
|
47
46
|
import json
|
|
48
47
|
import os
|
|
49
48
|
import re
|
|
50
49
|
import sys
|
|
51
|
-
import unicodedata
|
|
52
50
|
from dataclasses import dataclass, field
|
|
53
51
|
from enum import Enum
|
|
54
52
|
from pathlib import Path
|
|
55
|
-
from typing import Any, Dict, List,
|
|
53
|
+
from typing import Any, Dict, List, Tuple
|
|
56
54
|
|
|
57
55
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
58
56
|
if hasattr(sys.stdout, "reconfigure"):
|
|
@@ -529,9 +527,9 @@ def scan_text_patterns(file_path: str, content: str) -> List[Finding]:
|
|
|
529
527
|
# Line number offset indexing
|
|
530
528
|
line_offsets = []
|
|
531
529
|
curr = 0
|
|
532
|
-
for
|
|
530
|
+
for line_str in lines:
|
|
533
531
|
line_offsets.append(curr)
|
|
534
|
-
curr += len(
|
|
532
|
+
curr += len(line_str)
|
|
535
533
|
|
|
536
534
|
def get_line_num(char_idx: int) -> int:
|
|
537
535
|
for idx, offset in enumerate(line_offsets):
|
|
@@ -925,7 +923,7 @@ def scan_file(file_path: str) -> List[Finding]:
|
|
|
925
923
|
try:
|
|
926
924
|
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
927
925
|
content = f.read()
|
|
928
|
-
except Exception
|
|
926
|
+
except Exception:
|
|
929
927
|
return []
|
|
930
928
|
|
|
931
929
|
findings: List[Finding] = []
|
|
@@ -1013,7 +1011,7 @@ def format_terminal(report: ScanReport) -> str:
|
|
|
1013
1011
|
out.append(f"{BOLD}{CYAN}======================================================================{RESET}")
|
|
1014
1012
|
out.append(f"📁 Target Scope : {report.target_path}")
|
|
1015
1013
|
out.append(f"📄 Scanned Files : {report.scanned_files}")
|
|
1016
|
-
out.append(
|
|
1014
|
+
out.append("🛡️ Engine Source : NVIDIA/SkillSpector Spec (71 Patterns, 17 Categories)")
|
|
1017
1015
|
|
|
1018
1016
|
rec_color = GREEN if report.recommendation == Recommendation.SAFE else (YELLOW if report.recommendation == Recommendation.CAUTION else RED)
|
|
1019
1017
|
out.append(f"\n{BOLD}🎯 AUDIT VERDICT :{RESET} {rec_color}{BOLD}{report.recommendation.value}{RESET}")
|
|
@@ -7,11 +7,9 @@ Automatically disables dynamic CSS animations, freezes SVG spinners,
|
|
|
7
7
|
and masks fluctuating UI elements (timestamps, clocks, avatars, ad banners).
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
import os
|
|
11
|
-
import re
|
|
12
10
|
import json
|
|
13
11
|
import argparse
|
|
14
|
-
from typing import List,
|
|
12
|
+
from typing import List, Optional
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
class SmartVisualMasker:
|
|
@@ -56,13 +54,13 @@ class SmartVisualMasker:
|
|
|
56
54
|
return cls.ANTI_FLAKE_CSS.strip()
|
|
57
55
|
|
|
58
56
|
@classmethod
|
|
59
|
-
def generate_playwright_ts_options(cls, additional_selectors: List[str] = None) -> str:
|
|
57
|
+
def generate_playwright_ts_options(cls, additional_selectors: Optional[List[str]] = None) -> str:
|
|
60
58
|
"""
|
|
61
59
|
Generates Playwright TypeScript assertion options with masking and disabled animations.
|
|
62
60
|
"""
|
|
63
61
|
selectors = cls.DEFAULT_MASK_SELECTORS + (additional_selectors or [])
|
|
64
62
|
locators_ts = ",\n ".join([f"page.locator('{sel}')" for sel in selectors])
|
|
65
|
-
|
|
63
|
+
|
|
66
64
|
return f"""await expect(page).toHaveScreenshot('visual-baseline.png', {{
|
|
67
65
|
animations: 'disabled',
|
|
68
66
|
caret: 'hide',
|
|
@@ -74,7 +72,7 @@ class SmartVisualMasker:
|
|
|
74
72
|
}});"""
|
|
75
73
|
|
|
76
74
|
@classmethod
|
|
77
|
-
def generate_browser_mask_script(cls, selectors: List[str] = None) -> str:
|
|
75
|
+
def generate_browser_mask_script(cls, selectors: Optional[List[str]] = None) -> str:
|
|
78
76
|
"""
|
|
79
77
|
Generates JavaScript snippet to run inside the live browser
|
|
80
78
|
to immediately freeze DOM animations and paint solid black masks
|
|
@@ -206,7 +206,6 @@ def compute_drift(spec_criteria: List[Dict[str, Any]], code_files: List[Tuple[st
|
|
|
206
206
|
all_code_actions.extend([(file_path, a) for a in actions])
|
|
207
207
|
combined_code += "\n" + content
|
|
208
208
|
|
|
209
|
-
combined_code_lower = combined_code.lower()
|
|
210
209
|
code_tokens = tokenize_text(combined_code)
|
|
211
210
|
|
|
212
211
|
covered_criteria = []
|
|
@@ -289,7 +288,7 @@ def generate_markdown_report(result: Dict[str, Any], spec_file: str, code_path:
|
|
|
289
288
|
md.append("> Generated autonomously by Synapse-Hub `tools/spec_drift_sentinel.py` ")
|
|
290
289
|
md.append(f"> **Specification:** `{spec_file}` | **Code Target:** `{code_path}` \n")
|
|
291
290
|
md.append("---")
|
|
292
|
-
|
|
291
|
+
|
|
293
292
|
status_icon = "✅" if result["passed"] else "❌"
|
|
294
293
|
md.append(f"## 📊 Executive Summary: {status_icon} [{result['status']}]")
|
|
295
294
|
md.append(f"- **Alignment Score:** `{result['alignment_score']}%` (Pass threshold: >= 80% & zero Deficit Drift)")
|
package/tools/swe_qa_aci.py
CHANGED
|
@@ -216,8 +216,8 @@ class SweQaAci:
|
|
|
216
216
|
f.write(new_content)
|
|
217
217
|
|
|
218
218
|
# Diff summary formatting
|
|
219
|
-
target_lines = [f"- {
|
|
220
|
-
replacement_lines = [f"+ {
|
|
219
|
+
target_lines = [f"- {line}" for line in norm_target.splitlines()]
|
|
220
|
+
replacement_lines = [f"+ {line}" for line in norm_replacement.splitlines()]
|
|
221
221
|
diff_summary = "\n".join(target_lines + replacement_lines)
|
|
222
222
|
|
|
223
223
|
return {
|