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
|
@@ -19,7 +19,7 @@ import os
|
|
|
19
19
|
import sys
|
|
20
20
|
import json
|
|
21
21
|
import argparse
|
|
22
|
-
from typing import List, Dict, Any,
|
|
22
|
+
from typing import List, Dict, Any, Optional
|
|
23
23
|
|
|
24
24
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
25
25
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -96,7 +96,7 @@ BENCHMARK_CASES = [
|
|
|
96
96
|
class SynapseQABench:
|
|
97
97
|
"""Evaluates agent tool-calling capabilities against standardized QA benchmark cases."""
|
|
98
98
|
|
|
99
|
-
def __init__(self, cases: List[Dict[str, Any]] = None):
|
|
99
|
+
def __init__(self, cases: Optional[List[Dict[str, Any]]] = None):
|
|
100
100
|
self.cases = cases or BENCHMARK_CASES
|
|
101
101
|
|
|
102
102
|
def run_benchmark(self, tool_resolver_fn=None) -> Dict[str, Any]:
|
|
@@ -112,7 +112,7 @@ class SynapseQABench:
|
|
|
112
112
|
details = []
|
|
113
113
|
|
|
114
114
|
for case in self.cases:
|
|
115
|
-
expected_tool = case["expected_tool"]
|
|
115
|
+
expected_tool = str(case["expected_tool"])
|
|
116
116
|
# Baseline keyword resolver simulation
|
|
117
117
|
selected_tool = expected_tool if tool_resolver_fn is None else tool_resolver_fn(case["intent"])
|
|
118
118
|
|
|
@@ -47,9 +47,10 @@ HEADER_HASH_TEMPLATE = """# ====================================================
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
def generate_claude_md():
|
|
50
|
+
"""Generates the unified CLAUDE.md file compiling agent rules and standards."""
|
|
50
51
|
return f"""# CLAUDE.md — Synapse-Hub Guidelines for Claude Code
|
|
51
52
|
|
|
52
|
-
> **Author & Creator:** Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
|
|
53
|
+
> **Author & Creator:** Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
|
|
53
54
|
> **License:** MIT License (Mandatory Attribution Preservation)
|
|
54
55
|
|
|
55
56
|
{HEADER_TEMPLATE}
|
|
@@ -100,6 +101,7 @@ In accordance with the MIT License, the original copyright notice crediting **Th
|
|
|
100
101
|
|
|
101
102
|
|
|
102
103
|
def generate_cursorrules():
|
|
104
|
+
"""Generates the .cursorrules file compiling agent rules for Cursor AI."""
|
|
103
105
|
return f"""# .cursorrules — Synapse-Hub Guidelines for Cursor AI
|
|
104
106
|
# Author & Creator: Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
|
|
105
107
|
# License: MIT License (Mandatory Attribution Preservation)
|
|
@@ -127,6 +129,7 @@ def generate_cursorrules():
|
|
|
127
129
|
|
|
128
130
|
|
|
129
131
|
def generate_windsurfrules():
|
|
132
|
+
"""Generates the .windsurfrules configuration for Windsurf AI."""
|
|
130
133
|
return f"""# .windsurfrules — Synapse-Hub Guidelines for Windsurf AI
|
|
131
134
|
# Author & Creator: Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
|
|
132
135
|
# License: MIT License (Mandatory Attribution Preservation)
|
|
@@ -153,6 +156,7 @@ def generate_windsurfrules():
|
|
|
153
156
|
|
|
154
157
|
|
|
155
158
|
def generate_cursor_mdc():
|
|
159
|
+
"""Generates the .cursor/rules/synapse-qa.mdc file for Cursor IDE."""
|
|
156
160
|
return f"""---
|
|
157
161
|
description: Synapse-Hub Enterprise QA Automation & Testing Rules
|
|
158
162
|
globs: **/*
|
|
@@ -177,12 +181,13 @@ Attribution notices must be preserved.
|
|
|
177
181
|
|
|
178
182
|
|
|
179
183
|
def generate_copilot_instructions():
|
|
184
|
+
"""Generates the .github/copilot-instructions.md file for GitHub Copilot."""
|
|
180
185
|
return f"""# GitHub Copilot Custom Instructions for Synapse-Hub
|
|
181
186
|
|
|
182
187
|
{HEADER_TEMPLATE}
|
|
183
188
|
|
|
184
|
-
> **Project:** Synapse-Hub Enterprise QA Automation
|
|
185
|
-
> **Author:** Thế Vị (@TeeVee998) — MIT License
|
|
189
|
+
> **Project:** Synapse-Hub Enterprise QA Automation
|
|
190
|
+
> **Author:** Thế Vị (@TeeVee998) — MIT License
|
|
186
191
|
|
|
187
192
|
## Coding Invariants
|
|
188
193
|
- Enforce Clean Page Object Model (POM) with Flat Architecture.
|
|
@@ -210,6 +215,14 @@ TARGET_GENERATORS = [
|
|
|
210
215
|
|
|
211
216
|
|
|
212
217
|
def sync_all(check_only=False):
|
|
218
|
+
"""Synchronizes AGENTS.md rules across all IDE and assistant bridge files.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
check_only: If True, only checks for rule drift without modifying files.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
0 if clean or synchronized, 1 if drift was detected in check_only mode.
|
|
225
|
+
"""
|
|
213
226
|
drift_detected = False
|
|
214
227
|
for rel_path, gen_fn in TARGET_GENERATORS:
|
|
215
228
|
full_path = os.path.join(REPO_ROOT, rel_path)
|
|
@@ -249,6 +262,7 @@ def sync_all(check_only=False):
|
|
|
249
262
|
|
|
250
263
|
|
|
251
264
|
def main():
|
|
265
|
+
"""CLI entrypoint for synchronizing AI agent rules."""
|
|
252
266
|
parser = argparse.ArgumentParser(description="Synchronize AGENTS.md to all AI IDE bridges")
|
|
253
267
|
parser.add_argument("--check", action="store_true", help="Check for drift without modifying files")
|
|
254
268
|
args = parser.parse_args()
|
|
@@ -36,7 +36,7 @@ import csv
|
|
|
36
36
|
import json
|
|
37
37
|
import math
|
|
38
38
|
import argparse
|
|
39
|
-
from typing import Dict, List, Any
|
|
39
|
+
from typing import Dict, List, Any
|
|
40
40
|
|
|
41
41
|
# Enforce UTF-8 stdout encoding for Windows/macOS/Linux console compatibility
|
|
42
42
|
if hasattr(sys.stdout, "reconfigure"):
|
|
@@ -286,7 +286,8 @@ def main():
|
|
|
286
286
|
args = parser.parse_args()
|
|
287
287
|
|
|
288
288
|
if args.sample or not args.jtl or not args.server_metrics:
|
|
289
|
-
import tempfile
|
|
289
|
+
import tempfile
|
|
290
|
+
import time
|
|
290
291
|
temp_dir = tempfile.gettempdir()
|
|
291
292
|
temp_jtl = os.path.join(temp_dir, "sample_corr_jmeter.jtl")
|
|
292
293
|
temp_srv = os.path.join(temp_dir, "sample_corr_server.csv")
|
package/tools/test_memory_os.py
CHANGED
|
@@ -27,6 +27,7 @@ import re
|
|
|
27
27
|
import json
|
|
28
28
|
import sqlite3
|
|
29
29
|
import time
|
|
30
|
+
import uuid
|
|
30
31
|
import difflib
|
|
31
32
|
import argparse
|
|
32
33
|
from datetime import datetime, timezone, timedelta
|
|
@@ -48,6 +49,15 @@ SECRET_PATTERNS = [
|
|
|
48
49
|
]
|
|
49
50
|
|
|
50
51
|
|
|
52
|
+
class ManagedConnection(sqlite3.Connection):
|
|
53
|
+
"""SQLite connection that automatically commits/rollbacks and closes on context exit."""
|
|
54
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
55
|
+
try:
|
|
56
|
+
super().__exit__(exc_type, exc_val, exc_tb)
|
|
57
|
+
finally:
|
|
58
|
+
self.close()
|
|
59
|
+
|
|
60
|
+
|
|
51
61
|
class TestMemoryOS:
|
|
52
62
|
"""3-Tier Hierarchical QA Memory OS."""
|
|
53
63
|
__test__ = False
|
|
@@ -62,8 +72,8 @@ class TestMemoryOS:
|
|
|
62
72
|
os.makedirs(os.path.dirname(os.path.abspath(self.db_path)), exist_ok=True)
|
|
63
73
|
self._init_db()
|
|
64
74
|
|
|
65
|
-
def _get_connection(self) ->
|
|
66
|
-
conn = sqlite3.connect(self.db_path)
|
|
75
|
+
def _get_connection(self) -> ManagedConnection:
|
|
76
|
+
conn = sqlite3.connect(self.db_path, factory=ManagedConnection)
|
|
67
77
|
conn.row_factory = sqlite3.Row
|
|
68
78
|
return conn
|
|
69
79
|
|
|
@@ -165,7 +175,7 @@ class TestMemoryOS:
|
|
|
165
175
|
INSERT INTO recall_memory (session_id, event_type, actor, payload_json, timestamp)
|
|
166
176
|
VALUES (?, ?, ?, ?, ?)
|
|
167
177
|
""", (session_id, event_type, actor, payload_str, now))
|
|
168
|
-
return cursor.lastrowid
|
|
178
|
+
return cursor.lastrowid or 0
|
|
169
179
|
|
|
170
180
|
def recall_log(self, session_id: str, limit: int = 50) -> List[Dict[str, Any]]:
|
|
171
181
|
"""Retrieves recent sequential steps for a session."""
|
|
@@ -199,7 +209,7 @@ class TestMemoryOS:
|
|
|
199
209
|
"""Inserts long-term searchable knowledge into archival memory."""
|
|
200
210
|
payload_str = json.dumps(payload) if not isinstance(payload, str) else payload
|
|
201
211
|
now = datetime.now(timezone.utc).isoformat()
|
|
202
|
-
mem_id = f"mem_arch_{int(time.time() * 1000) % 10000000}"
|
|
212
|
+
mem_id = f"mem_arch_{int(time.time() * 1000) % 10000000}_{uuid.uuid4().hex[:6]}"
|
|
203
213
|
|
|
204
214
|
# Flatten text content for semantic/fuzzy search
|
|
205
215
|
text_content = f"{key} {aut_version} {tags} {payload_str}".lower()
|
|
@@ -336,7 +346,7 @@ def main():
|
|
|
336
346
|
subparsers = parser.add_subparsers(dest="subcommand", help="Subcommand to execute")
|
|
337
347
|
|
|
338
348
|
# init
|
|
339
|
-
|
|
349
|
+
subparsers.add_parser("init", help="Initialize memory OS database")
|
|
340
350
|
|
|
341
351
|
# update-core
|
|
342
352
|
uc_parser = subparsers.add_parser("update-core", help="Update core memory block")
|
|
@@ -20,7 +20,7 @@ import sys
|
|
|
20
20
|
import re
|
|
21
21
|
import json
|
|
22
22
|
import argparse
|
|
23
|
-
from typing import List, Dict, Any, Tuple
|
|
23
|
+
from typing import List, Dict, Any, Tuple
|
|
24
24
|
|
|
25
25
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
26
26
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -33,10 +33,10 @@ if hasattr(sys.stdout, 'reconfigure'):
|
|
|
33
33
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
34
34
|
sys.path.insert(0, SCRIPT_DIR)
|
|
35
35
|
|
|
36
|
-
import micro_mutator
|
|
36
|
+
import micro_mutator # noqa: E402
|
|
37
37
|
|
|
38
38
|
try:
|
|
39
|
-
import textgrad
|
|
39
|
+
import textgrad # noqa: F401, E402
|
|
40
40
|
TEXTGRAD_AVAILABLE = True
|
|
41
41
|
except ImportError:
|
|
42
42
|
TEXTGRAD_AVAILABLE = False
|
package/tools/tms_sync_bridge.py
CHANGED
|
@@ -78,7 +78,7 @@ class TMSBridge:
|
|
|
78
78
|
"issue_type": "Story",
|
|
79
79
|
"status": "Ready for QA",
|
|
80
80
|
"assignee": self.user_email,
|
|
81
|
-
"description":
|
|
81
|
+
"description": "As an Enterprise Accountant, I want to reconcile invoices matching ERP records to ensure financial accuracy.",
|
|
82
82
|
"acceptance_criteria": [
|
|
83
83
|
"Given the accountant is logged in with FINANCE_ROLE",
|
|
84
84
|
"When navigating to '/billing/invoices' and filtering by date '2026-10-01'",
|
|
@@ -161,8 +161,8 @@ class TMSBridge:
|
|
|
161
161
|
bug_key = f"{proj_clean}-BUG-{int(datetime.now().timestamp()) % 10000}"
|
|
162
162
|
|
|
163
163
|
repro = reproduction_steps or [
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
"1. Launch headless browser session via Playwright Live.",
|
|
165
|
+
"2. Navigate to target application flow under test.",
|
|
166
166
|
f"3. Execute test action sequence for '{test_case_id}'.",
|
|
167
167
|
f"4. Observe failure: {error_message}"
|
|
168
168
|
]
|
|
@@ -299,9 +299,9 @@ def main():
|
|
|
299
299
|
if args.json:
|
|
300
300
|
print(json.dumps(story, indent=2))
|
|
301
301
|
else:
|
|
302
|
-
print(
|
|
302
|
+
print("\n======================================================================")
|
|
303
303
|
print(f" ENTERPRISE ALM STORY PULL: [{story.get('issue_key')}]")
|
|
304
|
-
print(
|
|
304
|
+
print("======================================================================")
|
|
305
305
|
print(f" Summary : {story.get('summary')}")
|
|
306
306
|
print(f" Status : {story.get('status')} | Assignee: {story.get('assignee')}")
|
|
307
307
|
print(f" Source : {story.get('source')}\n")
|
|
@@ -21,9 +21,8 @@ import os
|
|
|
21
21
|
import sys
|
|
22
22
|
import re
|
|
23
23
|
import json
|
|
24
|
-
import time
|
|
25
24
|
import argparse
|
|
26
|
-
from typing import Dict, Any, List, Optional,
|
|
25
|
+
from typing import Dict, Any, List, Optional, cast
|
|
27
26
|
|
|
28
27
|
# Enforce UTF-8 stdout encoding
|
|
29
28
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -43,7 +42,7 @@ class TotExploratoryTester:
|
|
|
43
42
|
def expand_story(self, story_content: str, max_depth: int = 3, beam_width: int = 3) -> Dict[str, Any]:
|
|
44
43
|
"""Decomposes story into a 3-branch Tree of Thoughts with risk scores."""
|
|
45
44
|
# Extract title and criteria
|
|
46
|
-
lines = [
|
|
45
|
+
lines = [line.strip() for line in story_content.splitlines() if line.strip()]
|
|
47
46
|
title = lines[0] if lines else "Untitled User Story"
|
|
48
47
|
if title.startswith("#"):
|
|
49
48
|
title = title.lstrip("#").strip()
|
|
@@ -93,7 +92,7 @@ class TotExploratoryTester:
|
|
|
93
92
|
]
|
|
94
93
|
|
|
95
94
|
# Beam search selection: sort by risk score descending and take beam_width
|
|
96
|
-
sorted_branches = sorted(branches, key=lambda b: b["risk_score"], reverse=True)
|
|
95
|
+
sorted_branches = sorted(branches, key=lambda b: cast(float, b["risk_score"]), reverse=True)
|
|
97
96
|
selected_branches = sorted_branches[:beam_width]
|
|
98
97
|
|
|
99
98
|
return {
|
|
@@ -169,7 +168,7 @@ class TotExploratoryTester:
|
|
|
169
168
|
"evidence": evidence
|
|
170
169
|
})
|
|
171
170
|
|
|
172
|
-
scored_hypotheses.sort(key=lambda h: h["confidence_score"], reverse=True)
|
|
171
|
+
scored_hypotheses.sort(key=lambda h: cast(float, h["confidence_score"]), reverse=True)
|
|
173
172
|
winner = scored_hypotheses[0]
|
|
174
173
|
|
|
175
174
|
recommendations = {
|
|
@@ -184,7 +183,7 @@ class TotExploratoryTester:
|
|
|
184
183
|
"status": "PASS",
|
|
185
184
|
"primary_root_cause": winner["hypothesis"],
|
|
186
185
|
"confidence": winner["confidence_score"],
|
|
187
|
-
"recommended_fix": recommendations.get(winner["hypothesis"], "Review stacktrace with team"),
|
|
186
|
+
"recommended_fix": recommendations.get(str(winner["hypothesis"]), "Review stacktrace with team"),
|
|
188
187
|
"ranked_hypotheses": scored_hypotheses
|
|
189
188
|
}
|
|
190
189
|
|
|
@@ -23,7 +23,7 @@ import re
|
|
|
23
23
|
import sys
|
|
24
24
|
import json
|
|
25
25
|
import argparse
|
|
26
|
-
from typing import Dict, Any, List
|
|
26
|
+
from typing import Dict, Any, List
|
|
27
27
|
from urllib.parse import urlparse
|
|
28
28
|
|
|
29
29
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
@@ -153,14 +153,14 @@ class TrafficConverter:
|
|
|
153
153
|
lines = [
|
|
154
154
|
"import { test, expect } from '@playwright/test';",
|
|
155
155
|
"",
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
"/**",
|
|
157
|
+
" * Automated Replay of Production Traffic Session",
|
|
158
|
+
" * Generated by traffic_session_converter.py (agent-skills Shift-Right Engine)",
|
|
159
159
|
f" * Total Actions: {len(requests)} verified network interactions",
|
|
160
|
-
|
|
160
|
+
" */",
|
|
161
161
|
f"test.describe('{test_name}', () => {{",
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
" test('replays production user journey with PII masking', async ({ page, request }) => {",
|
|
163
|
+
" // 1. Base navigation",
|
|
164
164
|
f" await page.goto('{base_url}');",
|
|
165
165
|
""
|
|
166
166
|
]
|
|
@@ -185,9 +185,9 @@ class TrafficConverter:
|
|
|
185
185
|
formatted_body = json.dumps(body_str)
|
|
186
186
|
|
|
187
187
|
lines.append(f" const resp{step_num} = await request.{method.lower()}('{url}', {{")
|
|
188
|
-
lines.append(
|
|
188
|
+
lines.append(" headers: { 'Content-Type': 'application/json' },")
|
|
189
189
|
lines.append(f" data: {formatted_body}")
|
|
190
|
-
lines.append(
|
|
190
|
+
lines.append(" });")
|
|
191
191
|
lines.append(f" expect(resp{step_num}.status()).toBe({status});")
|
|
192
192
|
else:
|
|
193
193
|
lines.append(f" const resp{step_num} = await request.get('{url}');")
|
package/tools/universal_ir.py
CHANGED
|
@@ -9,8 +9,9 @@ whether the target is Playwright, Selenium, Cypress, Katalon, or Appium.
|
|
|
9
9
|
|
|
10
10
|
from enum import Enum
|
|
11
11
|
from typing import Dict, List, Optional, Any
|
|
12
|
-
from dataclasses import dataclass, field
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
13
|
import json
|
|
14
|
+
import re
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class ActionType(str, Enum):
|
|
@@ -345,8 +346,6 @@ class ProjectIR:
|
|
|
345
346
|
return cls.from_dict(data)
|
|
346
347
|
|
|
347
348
|
|
|
348
|
-
import re
|
|
349
|
-
|
|
350
349
|
SHARED_VAR_PATTERNS = [
|
|
351
350
|
(r"\b(?:created_id|order_id|user_id|item_id|customer_id|invoice_id)\b", DataDependencyType.ID),
|
|
352
351
|
(r"\b(?:auth_token|bearer_token|access_token|jwt_token|token)\b", DataDependencyType.AUTH_TOKEN),
|
package/tools/update_sentinel.py
CHANGED
|
@@ -65,7 +65,7 @@ def get_local_version():
|
|
|
65
65
|
try:
|
|
66
66
|
with open(pkg_file, "r", encoding="utf-8") as f:
|
|
67
67
|
data = json.load(f)
|
|
68
|
-
return data.get("version", "
|
|
68
|
+
return data.get("version", "3.0.0")
|
|
69
69
|
except (OSError, json.JSONDecodeError):
|
|
70
70
|
data = {}
|
|
71
71
|
|
|
@@ -74,11 +74,11 @@ def get_local_version():
|
|
|
74
74
|
try:
|
|
75
75
|
with open(manifest_file, "r", encoding="utf-8") as f:
|
|
76
76
|
data = json.load(f)
|
|
77
|
-
return data.get("version", "
|
|
77
|
+
return data.get("version", "3.0.0")
|
|
78
78
|
except (OSError, json.JSONDecodeError):
|
|
79
79
|
data = {}
|
|
80
80
|
|
|
81
|
-
return "
|
|
81
|
+
return "3.0.0"
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def fetch_remote_version():
|
|
@@ -259,7 +259,6 @@ def perform_update():
|
|
|
259
259
|
git_dir = os.path.join(HUB_ROOT, ".git")
|
|
260
260
|
is_git_repo = os.path.exists(git_dir)
|
|
261
261
|
|
|
262
|
-
updated = False
|
|
263
262
|
|
|
264
263
|
if is_git_repo:
|
|
265
264
|
print(f"\n{BOLD}[1/3] Pulling latest updates via Git...{RESET}")
|
|
@@ -281,7 +280,6 @@ def perform_update():
|
|
|
281
280
|
print(f" {RED}⚠️ Provenance Warning: {msg}{RESET}")
|
|
282
281
|
else:
|
|
283
282
|
print(f" {GREEN}[OK] {msg}{RESET}")
|
|
284
|
-
updated = True
|
|
285
283
|
# If this is the first pull, show the welcome landing page
|
|
286
284
|
try:
|
|
287
285
|
import welcome_page
|
|
@@ -306,9 +304,8 @@ def perform_update():
|
|
|
306
304
|
)
|
|
307
305
|
if res.returncode == 0:
|
|
308
306
|
print(f" {GREEN}[OK] Global NPM package updated to latest!{RESET}")
|
|
309
|
-
updated = True
|
|
310
307
|
except (subprocess.SubprocessError, OSError):
|
|
311
|
-
|
|
308
|
+
_ = False
|
|
312
309
|
|
|
313
310
|
# 2. Check Global Plugin
|
|
314
311
|
if os.path.exists(GLOBAL_PLUGIN_DIR) and GLOBAL_PLUGIN_DIR.lower() != HUB_ROOT.lower():
|
|
@@ -319,7 +316,7 @@ def perform_update():
|
|
|
319
316
|
subprocess.run(["git", "pull", "--ff-only"], cwd=GLOBAL_PLUGIN_DIR, stdout=subprocess.DEVNULL, timeout=30.0)
|
|
320
317
|
print(f" {GREEN}[OK] Updated Global Plugin repository: {GLOBAL_PLUGIN_DIR}{RESET}")
|
|
321
318
|
except (subprocess.SubprocessError, OSError):
|
|
322
|
-
|
|
319
|
+
_ = False
|
|
323
320
|
else:
|
|
324
321
|
# Refresh metadata files in global plugin
|
|
325
322
|
try:
|
|
@@ -331,7 +328,7 @@ def perform_update():
|
|
|
331
328
|
shutil.copy2(src_f, dst_f)
|
|
332
329
|
print(f" {GREEN}[OK] Refreshed metadata & LICENSE in: {GLOBAL_PLUGIN_DIR}{RESET}")
|
|
333
330
|
except OSError:
|
|
334
|
-
|
|
331
|
+
_ = False
|
|
335
332
|
|
|
336
333
|
# 3. Re-initialize SQLite Memory Cache
|
|
337
334
|
print(f"\n{BOLD}[3/3] Synchronizing Central Agent Memory Cache...{RESET}")
|
package/tools/verify_all.py
CHANGED
|
@@ -136,6 +136,7 @@ def resolve_target_repo(cli_target=None, cli_repo=None):
|
|
|
136
136
|
|
|
137
137
|
|
|
138
138
|
def gate_0_author_integrity(repo_root, mode="AUTO"):
|
|
139
|
+
"""Gate 0: Verifies author attribution and MIT license provenance across source manifests."""
|
|
139
140
|
print("\n[GATE 0] Verifying Author Attribution & MIT License Integrity...")
|
|
140
141
|
try:
|
|
141
142
|
from author_integrity import (
|
|
@@ -194,6 +195,7 @@ MOJIBAKE_INDICATORS = (
|
|
|
194
195
|
|
|
195
196
|
|
|
196
197
|
def gate_1_bom_check(repo_root, mode="AUTO"):
|
|
198
|
+
"""Gate 1: Verifies 100% UTF-8 encoding with zero Byte Order Mark (BOM) bytes."""
|
|
197
199
|
print("\n[GATE 1] Scanning UTF-8 No BOM and Encoding Integrity across repository...")
|
|
198
200
|
ignored_dirs = {".git", ".gradle", "node_modules", "allure-results", "graphify-out", "__pycache__", "build", "dist", "claude-ecosystem-repos", "external_repos", "teamwork"}
|
|
199
201
|
ignored_files = {"verify_all.py", "test_tools.py"}
|
|
@@ -241,6 +243,7 @@ def gate_1_bom_check(repo_root, mode="AUTO"):
|
|
|
241
243
|
|
|
242
244
|
|
|
243
245
|
def gate_2_path_portability(repo_root, mode="AUTO"):
|
|
246
|
+
"""Gate 2: Scans for and rejects machine-specific absolute filepaths in code."""
|
|
244
247
|
print("\n[GATE 2] Checking Path Portability (Zero absolute machine paths in repo sources)...")
|
|
245
248
|
ignored_dirs = {
|
|
246
249
|
".git", ".gradle", "node_modules", "allure-results", "Screenshots",
|
|
@@ -286,6 +289,7 @@ def gate_2_path_portability(repo_root, mode="AUTO"):
|
|
|
286
289
|
|
|
287
290
|
|
|
288
291
|
def gate_3_clean_pom(repo_root, mode="AUTO"):
|
|
292
|
+
"""Gate 3: Enforces Clean Page Object Model by rejecting raw selectors in test scripts."""
|
|
289
293
|
print("\n[GATE 3] Checking Clean POM & Flat Architecture in Test Scripts...")
|
|
290
294
|
effective_mode = detect_repo_mode(repo_root) if mode == "AUTO" else mode
|
|
291
295
|
if effective_mode == "HUB":
|
|
@@ -346,6 +350,7 @@ def gate_3_clean_pom(repo_root, mode="AUTO"):
|
|
|
346
350
|
|
|
347
351
|
|
|
348
352
|
def gate_4_ai_docs_lint(repo_root, mode="AUTO"):
|
|
353
|
+
"""Gate 4: Reconciles PROJECT_AI_GUIDE.md against physical AST to prevent hallucinated classes."""
|
|
349
354
|
print("\n[GATE 4] Running AI Documentation & Artifact Linter...")
|
|
350
355
|
linter_script = os.path.join(SCRIPT_DIR, "lint_ai_docs.py")
|
|
351
356
|
if not os.path.exists(linter_script):
|
|
@@ -368,6 +373,7 @@ def gate_4_ai_docs_lint(repo_root, mode="AUTO"):
|
|
|
368
373
|
|
|
369
374
|
|
|
370
375
|
def gate_5_cleanup_blacklist(repo_root, mode="AUTO"):
|
|
376
|
+
"""Gate 5: Scans for prohibited temporary files, heap dumps, and local cache artifacts."""
|
|
371
377
|
print("\n[GATE 5] Checking Temporary File Blacklist...")
|
|
372
378
|
blacklist_patterns = [
|
|
373
379
|
os.path.join(repo_root, "Reports", "batch*.properties"),
|
|
@@ -392,6 +398,7 @@ def gate_5_cleanup_blacklist(repo_root, mode="AUTO"):
|
|
|
392
398
|
|
|
393
399
|
|
|
394
400
|
def gate_6_credential_security(repo_root, mode="AUTO"):
|
|
401
|
+
"""Gate 6: Scans for plaintext passwords, API tokens, and private keys across codebase."""
|
|
395
402
|
print("\n[GATE 6] Checking Credential Security across repository...")
|
|
396
403
|
violations = []
|
|
397
404
|
|
|
@@ -658,6 +665,7 @@ def check_python_ast_integrity(repo_root):
|
|
|
658
665
|
|
|
659
666
|
|
|
660
667
|
def gate_7_infer_linter(repo_root, mode="AUTO"):
|
|
668
|
+
"""Gate 7: Runs Meta Infer-style static defect sentinel catching fake passes, 0 assert, and sleeps."""
|
|
661
669
|
print("\n[GATE 7] Running Meta Infer-Style Static Defect & Fake-Pass Sentinel...")
|
|
662
670
|
effective_mode = detect_repo_mode(repo_root) if mode == "AUTO" else mode
|
|
663
671
|
|
|
@@ -737,6 +745,7 @@ def gate_7_infer_linter(repo_root, mode="AUTO"):
|
|
|
737
745
|
|
|
738
746
|
|
|
739
747
|
def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
|
|
748
|
+
"""Gate 8: Runs syntactic mutation testing and verifies assertion sensitivity score >= 80%."""
|
|
740
749
|
eval_mode_desc = f"DYNAMIC EXECUTION MODE ({test_cmd})" if test_cmd else "HEURISTIC STATIC MODE"
|
|
741
750
|
print(f"\n[GATE 8] Running Assertion Sensitivity & Syntactic Mutation Sentinel [{eval_mode_desc}] (Min Score >= 80%)...")
|
|
742
751
|
scripts_dir = os.path.join(repo_root, "Scripts")
|
|
@@ -797,6 +806,7 @@ def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
|
|
|
797
806
|
|
|
798
807
|
|
|
799
808
|
def gate_9_skill_collision(repo_root, mode="AUTO"):
|
|
809
|
+
"""Gate 9: Scans skill descriptions to ensure semantic trigger overlap remains < 0.75."""
|
|
800
810
|
print("\n[GATE 9] Running Tier-2 Skill Routing & Collision Guard (Addy Osmani Pattern)...")
|
|
801
811
|
guard_script = os.path.join(SCRIPT_DIR, "skill_collision_guard.py")
|
|
802
812
|
if not os.path.exists(guard_script):
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
from __future__ import annotations
|
|
4
3
|
"""
|
|
5
4
|
Enterprise Canvas Visual Diff & Regression Checker
|
|
6
5
|
===================================================
|
|
@@ -17,11 +16,11 @@ Usage:
|
|
|
17
16
|
python3 tools/visual_diff_canvas.py compare baseline.png current.png --diff diff.png --threshold 0.02
|
|
18
17
|
python3 tools/visual_diff_canvas.py check-blank current.png
|
|
19
18
|
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
import os
|
|
22
22
|
import sys
|
|
23
23
|
import argparse
|
|
24
|
-
from pathlib import Path
|
|
25
24
|
|
|
26
25
|
try:
|
|
27
26
|
from PIL import Image, ImageChops, ImageStat
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
from __future__ import annotations
|
|
3
2
|
"""
|
|
4
3
|
Enterprise Vision-Grounded Fallback & Set-of-Marks (SoM) Engine
|
|
5
4
|
--------------------------------------------------------------
|
|
@@ -20,27 +19,25 @@ Features:
|
|
|
20
19
|
4. Compact Vision Prompt Formatter:
|
|
21
20
|
- Formats token-efficient briefs for Multimodal Vision Models (Gemini / Claude / GPT-4o).
|
|
22
21
|
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
23
|
|
|
24
24
|
import os
|
|
25
25
|
import sys
|
|
26
26
|
import json
|
|
27
27
|
import sqlite3
|
|
28
28
|
import argparse
|
|
29
|
-
from typing import Dict, Any, List, Optional, Tuple
|
|
29
|
+
from typing import Dict, Any, List, Optional, Tuple
|
|
30
30
|
try:
|
|
31
31
|
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
|
32
32
|
HAS_PIL = True
|
|
33
33
|
except ImportError:
|
|
34
|
-
Image = None
|
|
35
|
-
ImageDraw = None
|
|
36
|
-
ImageFont = None
|
|
37
|
-
ImageFilter = None
|
|
34
|
+
Image = None # type: ignore[assignment]
|
|
35
|
+
ImageDraw = None # type: ignore[assignment]
|
|
36
|
+
ImageFont = None # type: ignore[assignment]
|
|
37
|
+
ImageFilter = None # type: ignore[assignment]
|
|
38
38
|
HAS_PIL = False
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
ImageSourceType = Union[Image.Image, str]
|
|
42
|
-
else:
|
|
43
|
-
ImageSourceType = Any
|
|
40
|
+
ImageSourceType = Any
|
|
44
41
|
|
|
45
42
|
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
46
43
|
if hasattr(sys.stdout, 'reconfigure'):
|
|
@@ -74,12 +71,12 @@ def compute_visual_hash(image_source: ImageSourceType, bbox: Optional[List[int]]
|
|
|
74
71
|
if isinstance(image_source, str):
|
|
75
72
|
if not os.path.exists(image_source):
|
|
76
73
|
raise FileNotFoundError(f"Image not found: {image_source}")
|
|
77
|
-
img = Image.open(image_source)
|
|
74
|
+
img: Any = Image.open(image_source)
|
|
78
75
|
else:
|
|
79
76
|
img = image_source
|
|
80
77
|
|
|
81
78
|
if bbox and len(bbox) == 4:
|
|
82
|
-
img = img.crop(bbox)
|
|
79
|
+
img = img.crop(tuple(bbox))
|
|
83
80
|
|
|
84
81
|
# Resize to (hash_size + 1, hash_size) in grayscale
|
|
85
82
|
resized = img.convert("L").resize((hash_size + 1, hash_size), Image.Resampling.LANCZOS)
|
|
@@ -266,6 +263,8 @@ class VisualSoMHelper:
|
|
|
266
263
|
edges = img.filter(ImageFilter.FIND_EDGES)
|
|
267
264
|
edges = edges.point(lambda p: 255 if p > edge_threshold else 0)
|
|
268
265
|
pixels = edges.load()
|
|
266
|
+
if pixels is None:
|
|
267
|
+
return []
|
|
269
268
|
|
|
270
269
|
# Identify active cells with high edge density
|
|
271
270
|
active_cells = set()
|
|
@@ -274,7 +273,10 @@ class VisualSoMHelper:
|
|
|
274
273
|
count = 0
|
|
275
274
|
for cy in range(y, min(h, y + stride)):
|
|
276
275
|
for cx in range(x, min(w, x + stride)):
|
|
277
|
-
|
|
276
|
+
px_val = pixels[cx, cy]
|
|
277
|
+
if isinstance(px_val, (int, float)) and px_val > 0:
|
|
278
|
+
count += 1
|
|
279
|
+
elif isinstance(px_val, tuple) and px_val and px_val[0] > 0:
|
|
278
280
|
count += 1
|
|
279
281
|
if count >= min_pixels_per_cell:
|
|
280
282
|
active_cells.add((x // stride, y // stride))
|
package/tools/watch_mode.py
CHANGED
|
@@ -156,7 +156,7 @@ def run_incremental_audit(file_path: str, target_dir: str) -> Dict[str, Any]:
|
|
|
156
156
|
"""Performs fast sub-100ms audit on a single changed file."""
|
|
157
157
|
start_t = time.perf_counter()
|
|
158
158
|
rel_path = os.path.relpath(file_path, target_dir)
|
|
159
|
-
results = {
|
|
159
|
+
results: Dict[str, Any] = {
|
|
160
160
|
"file": rel_path,
|
|
161
161
|
"bom_pass": True,
|
|
162
162
|
"path_pass": True,
|
package/tools/wcag_audit.py
CHANGED
|
@@ -126,6 +126,18 @@ class WCAGAuditor(HTMLParser):
|
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
def run_audit(target, is_url=False):
|
|
129
|
+
"""Executes a WCAG 2.2 accessibility audit against an HTML file or live web URL.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
target: Filepath or URL pointing to HTML content.
|
|
133
|
+
is_url: True if target should be fetched over HTTP/HTTPS.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
The populated WCAGAuditor instance containing violations and stats.
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
ValueError: If an insecure or invalid URL scheme is passed.
|
|
140
|
+
"""
|
|
129
141
|
auditor = WCAGAuditor()
|
|
130
142
|
if is_url:
|
|
131
143
|
parsed = urllib.parse.urlparse(target)
|
|
@@ -143,6 +155,15 @@ def run_audit(target, is_url=False):
|
|
|
143
155
|
|
|
144
156
|
|
|
145
157
|
def format_markdown_report(target, auditor):
|
|
158
|
+
"""Generates an executive WCAG 2.2 audit markdown report.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
target: Target URL or file audited.
|
|
162
|
+
auditor: Populated WCAGAuditor instance.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Markdown string containing compliance score, grade, and violation details.
|
|
166
|
+
"""
|
|
146
167
|
violations = auditor.violations
|
|
147
168
|
stats = {
|
|
148
169
|
"elements_scanned": auditor.elements_scanned,
|
|
@@ -158,7 +179,7 @@ def format_markdown_report(target, auditor):
|
|
|
158
179
|
grade = "PASS" if score >= 80 else "NEEDS REMEDIATION"
|
|
159
180
|
|
|
160
181
|
out = [
|
|
161
|
-
|
|
182
|
+
"# WCAG 2.2 Accessibility Audit Report",
|
|
162
183
|
f"**Target:** `{target}`",
|
|
163
184
|
f"**Elements Scanned:** {stats['elements_scanned']}",
|
|
164
185
|
f"**Violations Found:** {stats['violations_found']}",
|