synapse-hub 2.7.0 → 3.0.1
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/rules/communication-style.md +4 -0
- package/.agents/rules/context-engineering.md +1 -0
- package/.agents/rules/git-push-protocol.md +17 -0
- package/.agents/skills/agentless-qa-repair/examples/three_phase_repair_workflow.md +40 -0
- package/.agents/skills/antigravity-test-sentinel/examples/sprint_dod_enforcement.md +47 -0
- package/.agents/skills/auth-session-and-cookie-manager/examples/multi_role_auth_matrix.md +70 -0
- package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_playwright_pom.ts +52 -0
- package/.agents/skills/failure-autopsy-and-healing/examples/closed_loop_healing_walkthrough.md +48 -0
- package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
- package/.agents/skills/lean-impact-scoreboard/examples/sample_scoreboard.md +1 -1
- package/.agents/skills/mipro-prompt-compiler/examples/dspy_assertion_optimizer_case.md +46 -0
- package/.agents/skills/persistent-planning-files/SKILL.md +1 -1
- package/.agents/skills/qa-aci-interface/examples/surgical_code_window_walkthrough.md +40 -0
- package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
- package/.agents/skills/review-spec/SKILL.md +1 -1
- package/.agents/skills/review-spec/examples/pocock_grilling_decision_rubric.md +40 -0
- package/.agents/skills/senior-code-review/SKILL.md +1 -1
- package/.agents/skills/skillspector-security-guard/examples/audit_threat_vectors_report.json +35 -0
- package/.agents/skills/surgical-coding-principles/SKILL.md +1 -1
- package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
- package/.agents/skills/tot-exploratory-testing/examples/payment_gateway_tot_tree.json +62 -0
- package/.agents/skills/verification-before-completion/SKILL.md +2 -2
- package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +2 -2
- package/.agents/skills/web-agent-gym-benchmarking/examples/enterprise_tasks_suite.json +38 -0
- package/.agents/skills/web-vitals-and-performance-profiler/examples/playwright_cwv_profiler_fixture.ts +61 -0
- package/AGENTS.md +6 -4
- package/CLAUDE.md +2 -1
- package/README.en.md +71 -292
- package/README.md +69 -304
- package/action.yml +1 -1
- package/bin/agent-qa.js +2 -2
- package/docs/ARCHITECTURE.en.md +192 -0
- package/docs/ARCHITECTURE.md +135 -88
- package/docs/CI_CD_INTEGRATION.en.md +96 -0
- package/docs/CI_CD_INTEGRATION.md +24 -22
- package/docs/CLI_REFERENCE.en.md +174 -0
- package/docs/CLI_REFERENCE.md +120 -115
- package/docs/CONTRIBUTING.en.md +49 -0
- package/docs/CONTRIBUTING.md +26 -68
- package/docs/QUALITY_GATES.en.md +115 -0
- package/docs/QUALITY_GATES.md +115 -0
- package/docs/research/agent_skill_training_analysis.md +1 -1
- 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 +2 -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 +10 -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 +90 -19
- package/tools/cache_resolver.py +78 -83
- package/tools/check_env.py +9 -1
- package/tools/check_version_sync.py +58 -4
- package/tools/ci_healing_hook.py +4 -6
- package/tools/cli.py +5 -7
- package/tools/config_decoupler.py +2 -3
- package/tools/contract_validator.py +141 -0
- package/tools/core/__init__.py +52 -0
- package/tools/count_tokens.py +22 -20
- package/tools/dashboard_generator.py +629 -1000
- package/tools/db_sandbox.py +87 -62
- 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 +143 -29
- 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 +9 -7
- package/tools/md_to_xlsx.py +26 -26
- package/tools/micro_mutator.py +32 -15
- 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 +3 -4
- package/tools/safety_firewall.py +2 -2
- package/tools/senior_code_review.py +4 -3
- package/tools/setup_project.py +10 -12
- package/tools/skill_collision_guard.py +3 -3
- package/tools/skill_vault.py +94 -6
- package/tools/skillspector.py +10 -8
- 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 +18 -3
- package/tools/telemetry_correlator.py +3 -2
- package/tools/test_impact.py +2 -1
- 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 +12 -2
- package/tools/visual_diff_canvas.py +4 -4
- 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
|
@@ -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 {
|
|
@@ -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}
|
|
@@ -91,6 +92,7 @@ In accordance with the MIT License, the original copyright notice crediting **Th
|
|
|
91
92
|
5. **Pragmatic Communication Protocol:**
|
|
92
93
|
- Conversational responses, explanation, and telemetry MUST be in Vietnamese.
|
|
93
94
|
- All codebase assets, test scripts, Page Classes, docstrings, and commits MUST remain strictly in English.
|
|
95
|
+
- English-First Document Ingestion: When reading bilingual docs (e.g. ARCHITECTURE.en.md vs ARCHITECTURE.md), always read the English version (*.en.md) to save 50–65% BPE tokens.
|
|
94
96
|
|
|
95
97
|
6. **Lean Senior Dev Protocol (The 7-Rung Ladder & YAGNI):**
|
|
96
98
|
- The best code is the code never written. Solve the whole problem with the least new code.
|
|
@@ -100,6 +102,7 @@ In accordance with the MIT License, the original copyright notice crediting **Th
|
|
|
100
102
|
|
|
101
103
|
|
|
102
104
|
def generate_cursorrules():
|
|
105
|
+
"""Generates the .cursorrules file compiling agent rules for Cursor AI."""
|
|
103
106
|
return f"""# .cursorrules — Synapse-Hub Guidelines for Cursor AI
|
|
104
107
|
# Author & Creator: Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
|
|
105
108
|
# License: MIT License (Mandatory Attribution Preservation)
|
|
@@ -127,6 +130,7 @@ def generate_cursorrules():
|
|
|
127
130
|
|
|
128
131
|
|
|
129
132
|
def generate_windsurfrules():
|
|
133
|
+
"""Generates the .windsurfrules configuration for Windsurf AI."""
|
|
130
134
|
return f"""# .windsurfrules — Synapse-Hub Guidelines for Windsurf AI
|
|
131
135
|
# Author & Creator: Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
|
|
132
136
|
# License: MIT License (Mandatory Attribution Preservation)
|
|
@@ -153,6 +157,7 @@ def generate_windsurfrules():
|
|
|
153
157
|
|
|
154
158
|
|
|
155
159
|
def generate_cursor_mdc():
|
|
160
|
+
"""Generates the .cursor/rules/synapse-qa.mdc file for Cursor IDE."""
|
|
156
161
|
return f"""---
|
|
157
162
|
description: Synapse-Hub Enterprise QA Automation & Testing Rules
|
|
158
163
|
globs: **/*
|
|
@@ -177,12 +182,13 @@ Attribution notices must be preserved.
|
|
|
177
182
|
|
|
178
183
|
|
|
179
184
|
def generate_copilot_instructions():
|
|
185
|
+
"""Generates the .github/copilot-instructions.md file for GitHub Copilot."""
|
|
180
186
|
return f"""# GitHub Copilot Custom Instructions for Synapse-Hub
|
|
181
187
|
|
|
182
188
|
{HEADER_TEMPLATE}
|
|
183
189
|
|
|
184
|
-
> **Project:** Synapse-Hub Enterprise QA Automation
|
|
185
|
-
> **Author:** Thế Vị (@TeeVee998) — MIT License
|
|
190
|
+
> **Project:** Synapse-Hub Enterprise QA Automation
|
|
191
|
+
> **Author:** Thế Vị (@TeeVee998) — MIT License
|
|
186
192
|
|
|
187
193
|
## Coding Invariants
|
|
188
194
|
- Enforce Clean Page Object Model (POM) with Flat Architecture.
|
|
@@ -210,6 +216,14 @@ TARGET_GENERATORS = [
|
|
|
210
216
|
|
|
211
217
|
|
|
212
218
|
def sync_all(check_only=False):
|
|
219
|
+
"""Synchronizes AGENTS.md rules across all IDE and assistant bridge files.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
check_only: If True, only checks for rule drift without modifying files.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
0 if clean or synchronized, 1 if drift was detected in check_only mode.
|
|
226
|
+
"""
|
|
213
227
|
drift_detected = False
|
|
214
228
|
for rel_path, gen_fn in TARGET_GENERATORS:
|
|
215
229
|
full_path = os.path.join(REPO_ROOT, rel_path)
|
|
@@ -249,6 +263,7 @@ def sync_all(check_only=False):
|
|
|
249
263
|
|
|
250
264
|
|
|
251
265
|
def main():
|
|
266
|
+
"""CLI entrypoint for synchronizing AI agent rules."""
|
|
252
267
|
parser = argparse.ArgumentParser(description="Synchronize AGENTS.md to all AI IDE bridges")
|
|
253
268
|
parser.add_argument("--check", action="store_true", help="Check for drift without modifying files")
|
|
254
269
|
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_impact.py
CHANGED
|
@@ -175,7 +175,8 @@ def get_git_modified_files(repo_root=None):
|
|
|
175
175
|
stdout=subprocess.PIPE,
|
|
176
176
|
stderr=subprocess.PIPE,
|
|
177
177
|
text=True,
|
|
178
|
-
check=True
|
|
178
|
+
check=True,
|
|
179
|
+
timeout=30.0
|
|
179
180
|
)
|
|
180
181
|
files = [line.strip() for line in res.stdout.splitlines() if line.strip()]
|
|
181
182
|
return files
|
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.1")
|
|
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.1")
|
|
78
78
|
except (OSError, json.JSONDecodeError):
|
|
79
79
|
data = {}
|
|
80
80
|
|
|
81
|
-
return "
|
|
81
|
+
return "3.0.1"
|
|
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,13 +289,14 @@ 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":
|
|
292
296
|
print("[N/A] Hub Mode: Not Applicable (Clean POM rules apply to target test suites in Spokes).")
|
|
293
297
|
return True
|
|
294
298
|
|
|
295
|
-
test_dirs = ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
|
|
299
|
+
test_dirs = ["tests"] if effective_mode == "HUB" else ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
|
|
296
300
|
test_files = []
|
|
297
301
|
for td in test_dirs:
|
|
298
302
|
d = os.path.join(repo_root, td)
|
|
@@ -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
|
|
|
@@ -700,7 +708,7 @@ def gate_7_infer_linter(repo_root, mode="AUTO"):
|
|
|
700
708
|
except (ImportError, OSError) as err:
|
|
701
709
|
print(f"[WARN] Failed to run check_version_sync: {err}")
|
|
702
710
|
|
|
703
|
-
test_dirs = ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
|
|
711
|
+
test_dirs = ["tests"] if effective_mode == "HUB" else ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
|
|
704
712
|
has_tests = False
|
|
705
713
|
target_scan_dir = None
|
|
706
714
|
for td in test_dirs:
|
|
@@ -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
|
|
@@ -56,11 +55,12 @@ def compare_canvas_images(
|
|
|
56
55
|
"""
|
|
57
56
|
with Image.open(baseline_path) as img_base, Image.open(current_path) as img_curr:
|
|
58
57
|
# Normalize dimensions and color mode
|
|
58
|
+
curr_image: Image.Image = img_curr
|
|
59
59
|
if img_base.size != img_curr.size:
|
|
60
|
-
|
|
60
|
+
curr_image = img_curr.resize(img_base.size, Image.Resampling.LANCZOS)
|
|
61
61
|
|
|
62
62
|
base_rgb = img_base.convert("RGB")
|
|
63
|
-
curr_rgb =
|
|
63
|
+
curr_rgb = curr_image.convert("RGB")
|
|
64
64
|
|
|
65
65
|
# C-level accelerated pixel difference via Pillow vectorization
|
|
66
66
|
diff = ImageChops.difference(base_rgb, curr_rgb)
|