synapse-hub 2.6.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.
Files changed (143) hide show
  1. package/.agents/agents/failure-analyst.md +4 -0
  2. package/.agents/agents/live-explorer.md +2 -0
  3. package/.agents/agents/orchestrator.md +16 -0
  4. package/.agents/agents/pom-crafter.md +10 -0
  5. package/.agents/agents/quality-gatekeeper.md +5 -0
  6. package/.agents/agents/test-runner.md +8 -0
  7. package/.agents/rules/git-push-protocol.md +1 -1
  8. package/.agents/rules/graphify.md +2 -0
  9. package/.agents/rules/mandatory-playwright-live.md +9 -9
  10. package/.agents/rules/mobile_appium_rules.md +8 -1
  11. package/.agents/skills/actor-qa-swarm/SKILL.md +77 -0
  12. package/.agents/skills/actor-qa-swarm/examples/broker_sharding_sample.json +12 -0
  13. package/.agents/skills/agentless-qa-repair/SKILL.md +69 -0
  14. package/.agents/skills/agentless-qa-repair/examples/healing_report_sample.json +17 -0
  15. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +1 -1
  16. package/.agents/skills/clean-pom-crafter/SKILL.md +2 -2
  17. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +4 -4
  18. package/.agents/skills/event-stream-qa-orchestrator/SKILL.md +79 -0
  19. package/.agents/skills/event-stream-qa-orchestrator/examples/timeline_sample.jsonl +2 -0
  20. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +2 -2
  21. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +6 -21
  22. package/.agents/skills/katalon-cli-runner/SKILL.md +1 -1
  23. package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
  24. package/.agents/skills/mipro-prompt-compiler/SKILL.md +59 -0
  25. package/.agents/skills/mipro-prompt-compiler/examples/compiled_module_sample.json +22 -0
  26. package/.agents/skills/qa-aci-interface/SKILL.md +67 -0
  27. package/.agents/skills/qa-aci-interface/examples/surgical_edit_sample.json +12 -0
  28. package/.agents/skills/qa-memory-os/SKILL.md +80 -0
  29. package/.agents/skills/qa-memory-os/examples/archival_memory_sample.json +21 -0
  30. package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
  31. package/.agents/skills/review-spec/SKILL.md +60 -0
  32. package/.agents/skills/review-spec/examples/sample_spec_review.md +33 -0
  33. package/.agents/skills/senior-code-review/SKILL.md +1 -1
  34. package/.agents/skills/synthetic-test-data-generator/SKILL.md +2 -2
  35. package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
  36. package/.agents/skills/tot-exploratory-testing/SKILL.md +59 -0
  37. package/.agents/skills/tot-exploratory-testing/examples/tot_graph_sample.json +35 -0
  38. package/.agents/skills/wcag-audit-patterns/SKILL.md +2 -2
  39. package/.agents/skills/web-agent-gym-benchmarking/SKILL.md +62 -0
  40. package/.agents/skills/web-agent-gym-benchmarking/examples/benchmark_report_sample.json +22 -0
  41. package/CLAUDE.md +2 -2
  42. package/README.en.md +70 -291
  43. package/README.md +68 -303
  44. package/docs/ARCHITECTURE.md +54 -8
  45. package/docs/CONTRIBUTING.md +1 -1
  46. package/docs/QUALITY_GATES.md +113 -0
  47. package/docs/research/EXTERNAL_AGENT_SKILLS_SURVEY.md +1062 -0
  48. package/package.json +1 -1
  49. package/scripts/package_dist.py +2 -1
  50. package/skills_manifest.json +92 -11
  51. package/synapse.config.json +1 -1
  52. package/templates/ai_bridges/CLAUDE.md +2 -2
  53. package/templates/ai_bridges/copilot-instructions.md +2 -2
  54. package/tools/TOOLS_RUNBOOK.md +1 -1
  55. package/tools/__init__.py +18 -1
  56. package/tools/_openapi_parser.py +158 -0
  57. package/tools/agent_canary.py +42 -30
  58. package/tools/agentless_patcher.py +417 -0
  59. package/tools/analyze_project.py +12 -11
  60. package/tools/api_drift_sentinel.py +4 -4
  61. package/tools/api_fuzzer.py +8 -8
  62. package/tools/auto_heal.py +23 -5
  63. package/tools/auto_heal_reflexion.py +2 -2
  64. package/tools/autogen_qa_broker.py +318 -0
  65. package/tools/browser_helpers.js +2 -2
  66. package/tools/browsergym_eval.py +236 -0
  67. package/tools/bug_reporter.py +11 -13
  68. package/tools/bump_version.py +75 -5
  69. package/tools/cache_resolver.py +78 -83
  70. package/tools/check_env.py +9 -1
  71. package/tools/check_version_sync.py +40 -3
  72. package/tools/ci_healing_hook.py +5 -7
  73. package/tools/cli.py +5 -7
  74. package/tools/config_decoupler.py +2 -3
  75. package/tools/core/__init__.py +52 -0
  76. package/tools/count_tokens.py +22 -20
  77. package/tools/dashboard_generator.py +628 -999
  78. package/tools/db_sandbox.py +69 -53
  79. package/tools/debt_harvester.py +3 -3
  80. package/tools/distill_log.py +3 -3
  81. package/tools/dspy_qa_signatures.py +3 -4
  82. package/tools/export_postman_collection.py +8 -8
  83. package/tools/flaky_detector.py +2 -2
  84. package/tools/fuzz_payloads.py +5 -5
  85. package/tools/generate_allure_report.py +1 -1
  86. package/tools/generate_jmx_plan.py +4 -4
  87. package/tools/gherkin_suite.py +1 -1
  88. package/tools/git_pre_commit_guard.py +2 -2
  89. package/tools/goldfish_review.py +2 -2
  90. package/tools/heal/__init__.py +28 -0
  91. package/tools/heal_pr_automator.py +29 -4
  92. package/tools/heuristic_locator_matcher.py +2 -2
  93. package/tools/infer_linter.py +8 -8
  94. package/tools/interview_story.py +0 -1
  95. package/tools/jtl_analyzer.py +11 -11
  96. package/tools/lint/__init__.py +30 -0
  97. package/tools/lint_ai_docs.py +42 -0
  98. package/tools/lookup_tc.py +1 -1
  99. package/tools/matrix_sharder.py +15 -13
  100. package/tools/mcp/__init__.py +16 -0
  101. package/tools/mcp_excel_server.py +2 -2
  102. package/tools/mcp_server.py +3 -4
  103. package/tools/md_to_xlsx.py +26 -26
  104. package/tools/micro_mutator.py +11 -12
  105. package/tools/mipro_assertion_tuner.py +207 -0
  106. package/tools/network_mock_shield.py +27 -27
  107. package/tools/newman_analyzer.py +2 -3
  108. package/tools/omni_emitter.py +5 -8
  109. package/tools/omni_parser.py +5 -5
  110. package/tools/omni_transpiler.py +5 -5
  111. package/tools/output_formatter.py +19 -0
  112. package/tools/perf/__init__.py +24 -0
  113. package/tools/pinpoint_failure.py +13 -1
  114. package/tools/project_state.py +4 -2
  115. package/tools/qa_event_stream.py +336 -0
  116. package/tools/repo_map.py +8 -8
  117. package/tools/report/__init__.py +32 -0
  118. package/tools/reversible_sandbox.py +5 -2
  119. package/tools/safety_firewall.py +2 -2
  120. package/tools/senior_code_review.py +2 -2
  121. package/tools/setup_project.py +10 -12
  122. package/tools/skill_collision_guard.py +3 -3
  123. package/tools/skill_vault.py +12 -3
  124. package/tools/skillspector.py +5 -7
  125. package/tools/smart_visual_masker.py +4 -6
  126. package/tools/spec_drift_sentinel.py +1 -2
  127. package/tools/swe_qa_aci.py +369 -0
  128. package/tools/synapse_qa_bench.py +3 -3
  129. package/tools/sync_agent_rules.py +18 -4
  130. package/tools/telemetry_correlator.py +3 -2
  131. package/tools/test_memory_os.py +419 -0
  132. package/tools/textgrad_mutation_optimizer.py +3 -3
  133. package/tools/tms_sync_bridge.py +5 -5
  134. package/tools/tot_exploratory_tester.py +239 -0
  135. package/tools/traffic_session_converter.py +9 -9
  136. package/tools/universal_ir.py +2 -3
  137. package/tools/update_sentinel.py +6 -9
  138. package/tools/verify_all.py +10 -0
  139. package/tools/visual_diff_canvas.py +1 -2
  140. package/tools/visual_som_helper.py +15 -13
  141. package/tools/watch_mode.py +1 -1
  142. package/tools/wcag_audit.py +22 -1
  143. package/tools/welcome_page.py +5 -6
@@ -0,0 +1,336 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Thế Vị (@TeeVee998). All rights reserved.
3
+ # SPDX-License-Identifier: MIT
4
+ # Project: Synapse-Hub (https://github.com/TeeVee998/Synapse-Hub)
5
+
6
+ """
7
+ Immutable QA Event Stream Engine (OpenHands EventStream Pattern)
8
+ ----------------------------------------------------------------
9
+ Provides a deterministic, append-only event stream for test automation sessions:
10
+ 1. Immutable Event Logging: Sequential JSON Lines recording of all actor actions and observations.
11
+ 2. Cryptographic Hash Chain: SHA-256 hash chaining ensures audit integrity and tamper resistance.
12
+ 3. Reversible Checkpoints: Enables safe rollback to prior verified states on failures.
13
+ 4. Stream Verification: Verifies stream schema, timestamp monotonicity, and hash chain integrity.
14
+
15
+ Usage:
16
+ python tools/qa_event_stream.py init-stream --session-id "E2E_001" --out-file .agent_memory/timeline.jsonl
17
+ python tools/qa_event_stream.py record-event --stream .agent_memory/timeline.jsonl --event-type ACTION_CLICK --actor live-explorer --payload '{"target": "#btn"}'
18
+ python tools/qa_event_stream.py verify-stream --stream .agent_memory/timeline.jsonl
19
+ python tools/qa_event_stream.py rollback --stream .agent_memory/timeline.jsonl --to-checkpoint CHECKPOINT_PRE_CHECKOUT
20
+ """
21
+
22
+ import os
23
+ import sys
24
+ import json
25
+ import hashlib
26
+ import argparse
27
+ from datetime import datetime, timezone
28
+ from typing import Dict, Any, Optional
29
+
30
+ # Enforce UTF-8 stdout encoding
31
+ if hasattr(sys.stdout, 'reconfigure'):
32
+ try:
33
+ sys.stdout.reconfigure(encoding='utf-8')
34
+ sys.stderr.reconfigure(encoding='utf-8')
35
+ except Exception:
36
+ pass
37
+
38
+ GENESIS_HASH = "0" * 64
39
+ VALID_EVENT_TYPES = {
40
+ "STREAM_INIT",
41
+ "ACTION_NAVIGATE",
42
+ "ACTION_CLICK",
43
+ "ACTION_TYPE",
44
+ "OBSERVATION_DOM",
45
+ "ASSERTION_EVALUATED",
46
+ "CHECKPOINT",
47
+ "ERROR_OCCURRED",
48
+ "ROLLBACK_EXECUTED",
49
+ "TEST_COMPLETED"
50
+ }
51
+
52
+
53
+ class QaEventStream:
54
+ """Immutable Event Stream with SHA-256 Hash Chaining."""
55
+
56
+ def __init__(self, repo_root: Optional[str] = None):
57
+ self.repo_root = repo_root or os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
58
+
59
+ def _compute_hash(self, prev_hash: str, session_id: str, event_type: str,
60
+ actor: str, payload_str: str, timestamp: str) -> str:
61
+ raw = f"{prev_hash}|{session_id}|{event_type}|{actor}|{payload_str}|{timestamp}"
62
+ return hashlib.sha256(raw.encode("utf-8")).hexdigest()
63
+
64
+ def init_stream(self, session_id: str, out_file: str) -> Dict[str, Any]:
65
+ """Initializes an append-only timeline stream file."""
66
+ resolved = os.path.join(self.repo_root, out_file) if not os.path.isabs(out_file) else out_file
67
+ os.makedirs(os.path.dirname(os.path.abspath(resolved)), exist_ok=True)
68
+
69
+ now = datetime.now(timezone.utc).isoformat()
70
+ init_payload = {"session_id": session_id, "initialized_at": now}
71
+ init_payload_str = json.dumps(init_payload, sort_keys=True)
72
+ event_hash = self._compute_hash(GENESIS_HASH, session_id, "STREAM_INIT", "system", init_payload_str, now)
73
+
74
+ entry = {
75
+ "index": 0,
76
+ "session_id": session_id,
77
+ "event_type": "STREAM_INIT",
78
+ "actor": "system",
79
+ "payload": init_payload,
80
+ "timestamp": now,
81
+ "prev_hash": GENESIS_HASH,
82
+ "event_hash": event_hash
83
+ }
84
+
85
+ with open(resolved, "w", encoding="utf-8", newline="\n") as f:
86
+ f.write(json.dumps(entry) + "\n")
87
+
88
+ return {
89
+ "status": "PASS",
90
+ "session_id": session_id,
91
+ "out_file": resolved,
92
+ "genesis_hash": event_hash
93
+ }
94
+
95
+ def record_event(self, stream_file: str, event_type: str, actor: str,
96
+ payload: Any, checkpoint_id: Optional[str] = None) -> Dict[str, Any]:
97
+ """Appends an event to the stream with continuous cryptographic hash chain."""
98
+ resolved = os.path.join(self.repo_root, stream_file) if not os.path.isabs(stream_file) else stream_file
99
+ if not os.path.exists(resolved):
100
+ return {"status": "FAIL", "error": f"Stream file not found: {stream_file}"}
101
+
102
+ # Read last line to get index and prev_hash
103
+ last_entry = None
104
+ total_count = 0
105
+ with open(resolved, "r", encoding="utf-8", errors="replace") as f:
106
+ for line in f:
107
+ stripped = line.strip()
108
+ if stripped:
109
+ try:
110
+ last_entry = json.loads(stripped)
111
+ total_count += 1
112
+ except Exception as err:
113
+ sys.stderr.write(f"Notice: Skip malformed stream line: {err}\n")
114
+
115
+ if not last_entry:
116
+ return {"status": "FAIL", "error": "Stream file is empty or corrupted"}
117
+
118
+ prev_hash = last_entry.get("event_hash", GENESIS_HASH)
119
+ session_id = last_entry.get("session_id", "default_session")
120
+ now = datetime.now(timezone.utc).isoformat()
121
+
122
+ if checkpoint_id:
123
+ if isinstance(payload, dict):
124
+ payload["checkpoint_id"] = checkpoint_id
125
+ else:
126
+ payload = {"data": payload, "checkpoint_id": checkpoint_id}
127
+
128
+ payload_str = json.dumps(payload, sort_keys=True) if not isinstance(payload, str) else payload
129
+ event_hash = self._compute_hash(prev_hash, session_id, event_type, actor, payload_str, now)
130
+
131
+ new_entry = {
132
+ "index": total_count,
133
+ "session_id": session_id,
134
+ "event_type": event_type,
135
+ "actor": actor,
136
+ "payload": payload if not isinstance(payload, str) else json.loads(payload_str),
137
+ "timestamp": now,
138
+ "prev_hash": prev_hash,
139
+ "event_hash": event_hash
140
+ }
141
+
142
+ with open(resolved, "a", encoding="utf-8", newline="\n") as f:
143
+ f.write(json.dumps(new_entry) + "\n")
144
+
145
+ return {
146
+ "status": "PASS",
147
+ "index": total_count,
148
+ "event_type": event_type,
149
+ "event_hash": event_hash,
150
+ "prev_hash": prev_hash
151
+ }
152
+
153
+ def verify_stream(self, stream_file: str) -> Dict[str, Any]:
154
+ """Verifies hash chain integrity and JSON schema compliance (Gate 12)."""
155
+ resolved = os.path.join(self.repo_root, stream_file) if not os.path.isabs(stream_file) else stream_file
156
+ if not os.path.exists(resolved):
157
+ return {"status": "FAIL", "verified": False, "error": f"Stream file not found: {stream_file}"}
158
+
159
+ events = []
160
+ with open(resolved, "r", encoding="utf-8", errors="replace") as f:
161
+ for line in f:
162
+ stripped = line.strip()
163
+ if stripped:
164
+ try:
165
+ events.append(json.loads(stripped))
166
+ except Exception as err:
167
+ return {"status": "FAIL", "verified": False, "error": f"JSON decode error: {err}"}
168
+
169
+ if not events:
170
+ return {"status": "FAIL", "verified": False, "error": "Stream is empty"}
171
+
172
+ expected_prev = GENESIS_HASH
173
+ for idx, ev in enumerate(events):
174
+ if ev.get("index") != idx:
175
+ return {
176
+ "status": "FAIL",
177
+ "verified": False,
178
+ "error": f"Index mismatch at position {idx}: expected {idx}, got {ev.get('index')}"
179
+ }
180
+
181
+ if ev.get("prev_hash") != expected_prev:
182
+ return {
183
+ "status": "FAIL",
184
+ "verified": False,
185
+ "error": f"Broken hash chain at index {idx}: expected prev_hash {expected_prev}, got {ev.get('prev_hash')}"
186
+ }
187
+
188
+ payload_str = json.dumps(ev.get("payload"), sort_keys=True)
189
+ recalc = self._compute_hash(
190
+ expected_prev,
191
+ ev.get("session_id", ""),
192
+ ev.get("event_type", ""),
193
+ ev.get("actor", ""),
194
+ payload_str,
195
+ ev.get("timestamp", "")
196
+ )
197
+
198
+ if recalc != ev.get("event_hash"):
199
+ return {
200
+ "status": "FAIL",
201
+ "verified": False,
202
+ "error": f"Hash tamper detected at index {idx}: declared {ev.get('event_hash')}, computed {recalc}"
203
+ }
204
+
205
+ expected_prev = ev["event_hash"]
206
+
207
+ return {
208
+ "status": "PASS",
209
+ "verified": True,
210
+ "events_verified": len(events),
211
+ "final_event_hash": expected_prev,
212
+ "integrity": "CRYPTOGRAPHICALLY_VERIFIED"
213
+ }
214
+
215
+ def rollback(self, stream_file: str, to_checkpoint: str) -> Dict[str, Any]:
216
+ """Rolls back execution state to a designated checkpoint in the stream."""
217
+ resolved = os.path.join(self.repo_root, stream_file) if not os.path.isabs(stream_file) else stream_file
218
+ if not os.path.exists(resolved):
219
+ return {"status": "FAIL", "error": f"Stream file not found: {stream_file}"}
220
+
221
+ target_checkpoint = None
222
+ checkpoint_index = -1
223
+ events = []
224
+
225
+ with open(resolved, "r", encoding="utf-8", errors="replace") as f:
226
+ for line in f:
227
+ stripped = line.strip()
228
+ if stripped:
229
+ try:
230
+ ev = json.loads(stripped)
231
+ events.append(ev)
232
+ p = ev.get("payload", {})
233
+ if isinstance(p, dict) and p.get("checkpoint_id") == to_checkpoint:
234
+ target_checkpoint = ev
235
+ checkpoint_index = ev.get("index", -1)
236
+ except Exception as err:
237
+ sys.stderr.write(f"Notice: Skip malformed stream line during rollback: {err}\n")
238
+
239
+ if not target_checkpoint:
240
+ return {
241
+ "status": "FAIL",
242
+ "error": f"Checkpoint '{to_checkpoint}' not found in event stream."
243
+ }
244
+
245
+ rolled_back_count = len(events) - (checkpoint_index + 1)
246
+
247
+ # Record ROLLBACK_EXECUTED event in the immutable stream
248
+ rollback_payload = {
249
+ "target_checkpoint": to_checkpoint,
250
+ "target_index": checkpoint_index,
251
+ "events_rewound": rolled_back_count,
252
+ "restored_state": target_checkpoint.get("payload")
253
+ }
254
+ rec_res = self.record_event(
255
+ stream_file=stream_file,
256
+ event_type="ROLLBACK_EXECUTED",
257
+ actor="system",
258
+ payload=rollback_payload
259
+ )
260
+
261
+ return {
262
+ "status": "PASS",
263
+ "to_checkpoint": to_checkpoint,
264
+ "target_index": checkpoint_index,
265
+ "events_rewound": rolled_back_count,
266
+ "new_event_hash": rec_res.get("event_hash"),
267
+ "restored_state": target_checkpoint.get("payload")
268
+ }
269
+
270
+
271
+ def main():
272
+ parser = argparse.ArgumentParser(description="QA Event Stream Engine (OpenHands Pattern)")
273
+ subparsers = parser.add_subparsers(dest="subcommand", help="Subcommand to execute")
274
+
275
+ # init-stream
276
+ init_p = subparsers.add_parser("init-stream", help="Initialize a new event stream")
277
+ init_p.add_argument("--session-id", required=True, help="Unique test session ID")
278
+ init_p.add_argument("--out-file", required=True, help="Path to write JSONL stream")
279
+
280
+ # record-event
281
+ rec_p = subparsers.add_parser("record-event", help="Append an event to the stream")
282
+ rec_p.add_argument("--stream", required=True, help="Path to stream file")
283
+ rec_p.add_argument("--event-type", required=True, help="Event type (ACTION_CLICK, CHECKPOINT, etc.)")
284
+ rec_p.add_argument("--actor", required=True, help="Actor name (live-explorer, test-runner, etc.)")
285
+ rec_p.add_argument("--payload", required=True, help="JSON payload string")
286
+ rec_p.add_argument("--checkpoint-id", help="Optional checkpoint ID")
287
+
288
+ # verify-stream
289
+ ver_p = subparsers.add_parser("verify-stream", help="Verify cryptographic hash chain integrity")
290
+ ver_p.add_argument("--stream", required=True, help="Path to stream file")
291
+
292
+ # rollback
293
+ rb_p = subparsers.add_parser("rollback", help="Roll back state to a checkpoint")
294
+ rb_p.add_argument("--stream", required=True, help="Path to stream file")
295
+ rb_p.add_argument("--to-checkpoint", required=True, help="Target checkpoint ID")
296
+
297
+ args = parser.parse_args()
298
+ engine = QaEventStream()
299
+
300
+ if args.subcommand == "init-stream":
301
+ res = engine.init_stream(session_id=args.session_id, out_file=args.out_file)
302
+ print(json.dumps(res, indent=2))
303
+ sys.exit(0 if res["status"] == "PASS" else 1)
304
+
305
+ elif args.subcommand == "record-event":
306
+ try:
307
+ parsed_payload = json.loads(args.payload)
308
+ except Exception:
309
+ parsed_payload = args.payload
310
+ res = engine.record_event(
311
+ stream_file=args.stream,
312
+ event_type=args.event_type,
313
+ actor=args.actor,
314
+ payload=parsed_payload,
315
+ checkpoint_id=args.checkpoint_id
316
+ )
317
+ print(json.dumps(res, indent=2))
318
+ sys.exit(0 if res["status"] == "PASS" else 1)
319
+
320
+ elif args.subcommand == "verify-stream":
321
+ res = engine.verify_stream(stream_file=args.stream)
322
+ print(json.dumps(res, indent=2))
323
+ sys.exit(0 if res["status"] == "PASS" else 1)
324
+
325
+ elif args.subcommand == "rollback":
326
+ res = engine.rollback(stream_file=args.stream, to_checkpoint=args.to_checkpoint)
327
+ print(json.dumps(res, indent=2))
328
+ sys.exit(0 if res["status"] == "PASS" else 1)
329
+
330
+ else:
331
+ parser.print_help()
332
+ sys.exit(1)
333
+
334
+
335
+ if __name__ == "__main__":
336
+ main()
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
- f"# ==============================================================================",
203
- f"# ENTERPRISE AUTOMATION REPOSITORY MAP (Aider-Style AST Compression)",
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
- f"# Purpose : Token-budgeted index (< 600 tokens) for instant method lookup",
208
- f"# ==============================================================================\n"
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(f"\n======================================================================")
298
- print(f" Enterprise Repository Map Compressor (Aider-Style AST Map) ")
299
- print(f"======================================================================")
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(f"======================================================================\n")
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, Callable, Dict, Any
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'):
@@ -120,6 +119,10 @@ class ExecutionSandbox:
120
119
 
121
120
  return results
122
121
 
122
+ def stack_size(self) -> int:
123
+ """Returns the number of pending undo actions in the stack."""
124
+ return len(self.undo_stack)
125
+
123
126
  def __enter__(self):
124
127
  return self
125
128
 
@@ -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(f"\n❌ Pre-execution Blocked: Destructive actions intercepted without --force or ALLOW_DESTRUCTIVE=1.\n")
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, Optional, Tuple
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 as e:
86
+ except Exception:
87
87
  return ""
88
88
 
89
89
 
@@ -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 verify_all
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
- unlinked = False
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
- junction_created = False
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(f"Operational Mode : APPLY CHANGES\n")
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(f" • Would deploy AI bridges: .cursor/rules/synapse-qa.mdc, .github/copilot-instructions.md, .windsurfrules")
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(f" • Would initialize: Central Agent Memory (~/.agent_memory/global_cache.db)")
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
- guide_created = ensure_project_ai_guide(target_repo, stack_info, profile=profile)
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
- bridges_deployed = install_universal_ai_bridges(target_repo)
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(f" Universal Bridges : Cursor (.cursor/rules), Claude Code, Copilot, Windsurf")
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, any]]]:
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(f"\n✅ [PASS] 0 routing collisions detected. All skill descriptions uniquely segmented.\n")
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
 
@@ -22,7 +22,7 @@ import json
22
22
  import re
23
23
  import difflib
24
24
  import argparse
25
- from dataclasses import dataclass, asdict
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) -> sqlite3.Connection:
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
 
@@ -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, Optional, Set, Tuple
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 l in lines:
530
+ for line_str in lines:
533
531
  line_offsets.append(curr)
534
- curr += len(l)
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 as e:
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(f"🛡️ Engine Source : NVIDIA/SkillSpector Spec (71 Patterns, 17 Categories)")
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}")