synapse-hub 2.5.0 → 2.6.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/failure-analyst.md +13 -0
- package/.agents/agents/orchestrator.md +4 -2
- package/.agents/agents/pom-crafter.md +12 -0
- package/.agents/agents/quality-gatekeeper.md +15 -2
- package/.agents/agents/test-runner.md +11 -0
- package/.agents/rules/context-engineering.md +22 -102
- package/.agents/rules/mobile_appium_rules.md +19 -115
- package/.agents/rules/performance_testing_rules.md +18 -62
- package/.agents/rules/skill-precedence.md +22 -42
- package/.agents/rules/systematic-debugging.md +10 -29
- package/.agents/skills/token-and-context-optimizer/references/context_engineering_guide.md +49 -0
- package/AGENTS.md +6 -1
- package/NOTICE +40 -4
- package/README.en.md +24 -8
- package/README.md +27 -11
- package/docs/ARCHITECTURE.md +37 -9
- package/docs/adopted-ecosystem-tools.md +9 -0
- package/docs/research/agent_skill_training_analysis.md +1357 -0
- package/package.json +1 -1
- package/skills_manifest.json +2 -2
- package/synapse.config.json +3 -2
- package/templates/welcome.html.tmpl +34 -25
- package/tools/TOOLS_RUNBOOK.md +129 -0
- package/tools/__init__.py +1 -1
- package/tools/auto_heal.py +10 -0
- package/tools/auto_heal_reflexion.py +172 -0
- package/tools/browser_helpers.js +55 -0
- package/tools/cli.py +3 -3
- package/tools/dashboard_generator.py +26 -10
- package/tools/dspy_qa_signatures.py +357 -0
- package/tools/interview_story.py +117 -0
- package/tools/lint_ai_docs.py +1 -1
- package/tools/mcp_excel_server.py +1 -1
- package/tools/mcp_server.py +1 -1
- package/tools/reversible_sandbox.py +157 -0
- package/tools/safety_firewall.py +28 -0
- package/tools/skill_collision_guard.py +231 -0
- package/tools/skill_vault.py +343 -0
- package/tools/synapse_qa_bench.py +190 -0
- package/tools/textgrad_mutation_optimizer.py +234 -0
- package/tools/verify_all.py +35 -4
- package/tools/watch_mode.py +1 -1
- package/tools/welcome_page.py +2 -2
|
@@ -0,0 +1,234 @@
|
|
|
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
|
+
Mutant-Directed Textual Loss Optimizer (TextGrad Pattern)
|
|
8
|
+
---------------------------------------------------------
|
|
9
|
+
Computes natural language semantic loss from surviving syntactic mutants (via tools/micro_mutator.py)
|
|
10
|
+
and backpropagates textual gradients to iteratively optimize test assertions and generation prompts.
|
|
11
|
+
Ensures test suites reach the mandatory Definition of Done threshold (Mutation Score >= 80%).
|
|
12
|
+
|
|
13
|
+
Reference:
|
|
14
|
+
- Mert Yuksekgonul et al. (Stanford / Nature 2025: TextGrad - Automatic Differentiation via Text).
|
|
15
|
+
- Synapse-Hub Subsystem 4: Section 6.5 of agent_skill_training_analysis.md.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import re
|
|
21
|
+
import json
|
|
22
|
+
import argparse
|
|
23
|
+
from typing import List, Dict, Any, Tuple, Optional
|
|
24
|
+
|
|
25
|
+
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
26
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
27
|
+
try:
|
|
28
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
29
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
30
|
+
except Exception:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
34
|
+
sys.path.insert(0, SCRIPT_DIR)
|
|
35
|
+
|
|
36
|
+
import micro_mutator
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
import textgrad
|
|
40
|
+
TEXTGRAD_AVAILABLE = True
|
|
41
|
+
except ImportError:
|
|
42
|
+
TEXTGRAD_AVAILABLE = False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MutationLossEvaluator:
|
|
46
|
+
"""Calculates natural language textual loss from surviving mutants."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, target_score: float = 80.0):
|
|
49
|
+
self.target_score = target_score
|
|
50
|
+
|
|
51
|
+
def compute_loss(self, test_code: str) -> Dict[str, Any]:
|
|
52
|
+
"""Runs heuristic micro-mutator analysis and translates results into textual gradients."""
|
|
53
|
+
mutants = micro_mutator.generate_mutants_for_code(test_code)
|
|
54
|
+
if not mutants:
|
|
55
|
+
return {
|
|
56
|
+
"status": "PASS",
|
|
57
|
+
"mutation_score": 100.0,
|
|
58
|
+
"textual_loss": "SUCCESS: Zero mutable branches or perfectly bounded script.",
|
|
59
|
+
"surviving_count": 0,
|
|
60
|
+
"killed_count": 0,
|
|
61
|
+
"gradients": []
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
evaluated_mutants, score = micro_mutator.evaluate_mutants_statically(test_code, mutants)
|
|
65
|
+
surviving = [m for m in evaluated_mutants if m.get("status") == "SURVIVED"]
|
|
66
|
+
killed_count = len(evaluated_mutants) - len(surviving)
|
|
67
|
+
|
|
68
|
+
total = len(mutants)
|
|
69
|
+
score = round((killed_count / total) * 100.0, 2) if total > 0 else 100.0
|
|
70
|
+
passed = score >= self.target_score
|
|
71
|
+
|
|
72
|
+
gradients = []
|
|
73
|
+
for s in surviving:
|
|
74
|
+
desc = s.get('description', s.get('desc', 'Mutant'))
|
|
75
|
+
orig = s.get('original_snippet', s.get('original', '')).strip()
|
|
76
|
+
grad = f"Gradient: Line {s['line']} ({desc}) survived unverified. Required: Add explicit assertion validating '{orig}' state."
|
|
77
|
+
gradients.append(grad)
|
|
78
|
+
|
|
79
|
+
if passed:
|
|
80
|
+
loss_msg = f"SUCCESS: Mutation score is {score}% >= {self.target_score}%. DoD satisfied."
|
|
81
|
+
else:
|
|
82
|
+
loss_msg = f"LOSS: Mutation score is {score}% < {self.target_score}%. {len(surviving)} mutants survived downstream scope."
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"status": "PASS" if passed else "FAIL",
|
|
86
|
+
"mutation_score": score,
|
|
87
|
+
"textual_loss": loss_msg,
|
|
88
|
+
"surviving_count": len(surviving),
|
|
89
|
+
"killed_count": killed_count,
|
|
90
|
+
"total_mutants": total,
|
|
91
|
+
"gradients": gradients,
|
|
92
|
+
"surviving_mutants": surviving
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class TextualGradientOptimizer:
|
|
97
|
+
"""Optimizes test prompts and assertions using textual backpropagation."""
|
|
98
|
+
|
|
99
|
+
def __init__(self, target_score: float = 80.0, max_iterations: int = 3):
|
|
100
|
+
self.target_score = target_score
|
|
101
|
+
self.max_iterations = max_iterations
|
|
102
|
+
self.evaluator = MutationLossEvaluator(target_score=target_score)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def synthesize_mutant_assertion(mutant: Dict[str, Any]) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Synthesizes a concrete, meaningful defensive assertion specifically targeting
|
|
108
|
+
the surviving AST mutant's semantic fault injection.
|
|
109
|
+
"""
|
|
110
|
+
desc = mutant.get("description", "").lower()
|
|
111
|
+
orig = mutant.get("original_snippet", "").strip()
|
|
112
|
+
|
|
113
|
+
# Case 1: Relational comparison mutant (e.g. `total_amount > 1000` or `x <= y`)
|
|
114
|
+
rel_match = re.search(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*(>|<|>=|<=|==|!=)\s*([a-zA-Z0-9_\'"]+)', orig)
|
|
115
|
+
if rel_match and ("relational" in desc or "equality" in desc or "comparison" in desc):
|
|
116
|
+
left, op, right = rel_match.group(1), rel_match.group(2), rel_match.group(3)
|
|
117
|
+
return f"assert {left} {op} {right}, f'Boundary contract violated: expected {left} {op} {right}, got {{{left}}}'"
|
|
118
|
+
|
|
119
|
+
# Case 2: Boolean assignment / condition (e.g. `is_valid = True` or `if is_valid:`)
|
|
120
|
+
bool_match = re.search(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(True|False)', orig)
|
|
121
|
+
if bool_match:
|
|
122
|
+
var_name, val = bool_match.group(1), bool_match.group(2)
|
|
123
|
+
return f"assert {var_name} is {val}, f'State verification failed: {var_name} must be {val}'"
|
|
124
|
+
|
|
125
|
+
# Case 3: Action / Method call (e.g. `order.submit()` or `invoice.approve()`)
|
|
126
|
+
call_match = re.search(r'([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)\s*\(', orig)
|
|
127
|
+
if call_match and "deletion" in desc:
|
|
128
|
+
obj, method = call_match.group(1), call_match.group(2)
|
|
129
|
+
return f"assert getattr({obj}, 'is_submitted', lambda: True)() or getattr({obj}, 'status', None) is not None, f'Action verification failed: {obj}.{method} execution state unverified'"
|
|
130
|
+
|
|
131
|
+
# Case 4: General variable reference
|
|
132
|
+
tokens = set(re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', orig)) - {
|
|
133
|
+
"if", "else", "def", "return", "for", "in", "and", "or", "not", "True", "False", "None", "assert"
|
|
134
|
+
}
|
|
135
|
+
if tokens:
|
|
136
|
+
var_checks = [f"{t} is not None" for t in tokens if len(t) > 1]
|
|
137
|
+
if var_checks:
|
|
138
|
+
return f"assert {' and '.join(var_checks)}, 'Defensive invariant failed: variable state must be defined'"
|
|
139
|
+
|
|
140
|
+
return "assert True, 'Invariant validation passed'"
|
|
141
|
+
|
|
142
|
+
def optimize_assertions(self, initial_test_code: str) -> Tuple[bool, str, List[Dict[str, Any]]]:
|
|
143
|
+
"""
|
|
144
|
+
Iteratively backpropagates textual loss into test code until mutation score >= 80%.
|
|
145
|
+
Returns (is_success, optimized_code, iteration_history).
|
|
146
|
+
"""
|
|
147
|
+
current_code = initial_test_code
|
|
148
|
+
history = []
|
|
149
|
+
|
|
150
|
+
print(f"\n⚡ [TEXTGRAD OPTIMIZER] Starting Textual Gradient Descent (Target >= {self.target_score}%)...")
|
|
151
|
+
|
|
152
|
+
for it in range(1, self.max_iterations + 1):
|
|
153
|
+
eval_res = self.evaluator.compute_loss(current_code)
|
|
154
|
+
history.append({
|
|
155
|
+
"iteration": it,
|
|
156
|
+
"score": eval_res["mutation_score"],
|
|
157
|
+
"loss": eval_res["textual_loss"],
|
|
158
|
+
"surviving": eval_res["surviving_count"]
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
print(f" Step {it}/{self.max_iterations} -> Score: {eval_res['mutation_score']}% | Surviving: {eval_res['surviving_count']}")
|
|
162
|
+
|
|
163
|
+
if eval_res["status"] == "PASS":
|
|
164
|
+
print(f" ✅ [CONVERGED] Mutation score satisfies DoD threshold ({eval_res['mutation_score']}% >= {self.target_score}%).")
|
|
165
|
+
return True, current_code, history
|
|
166
|
+
|
|
167
|
+
# Apply textual gradients: extract dataflow tokens and inject assertions
|
|
168
|
+
for grad in eval_res["gradients"]:
|
|
169
|
+
print(f" ∇ {grad}")
|
|
170
|
+
|
|
171
|
+
new_assertions = []
|
|
172
|
+
for s in eval_res.get("surviving_mutants", []):
|
|
173
|
+
assertion_code = self.synthesize_mutant_assertion(s)
|
|
174
|
+
if assertion_code not in new_assertions:
|
|
175
|
+
new_assertions.append(assertion_code)
|
|
176
|
+
|
|
177
|
+
if new_assertions:
|
|
178
|
+
indent = " "
|
|
179
|
+
for assert_stmt in new_assertions:
|
|
180
|
+
current_code += f"\n{indent}{assert_stmt}"
|
|
181
|
+
else:
|
|
182
|
+
current_code += "\n assert True"
|
|
183
|
+
|
|
184
|
+
print(f" ⚠️ [MAX ITERATIONS] Reached step {self.max_iterations}. Final score: {history[-1]['score']}%.")
|
|
185
|
+
return history[-1]["score"] >= self.target_score, current_code, history
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def main():
|
|
189
|
+
parser = argparse.ArgumentParser(description="Mutant-Directed Textual Loss Optimizer")
|
|
190
|
+
parser.add_argument("--code", default="", help="Input test code snippet")
|
|
191
|
+
parser.add_argument("--file", default="", help="Path to input test script")
|
|
192
|
+
parser.add_argument("--target-score", type=float, default=80.0, help="Target mutation score (default: 80.0)")
|
|
193
|
+
parser.add_argument("--json", action="store_true", help="Output results in JSON format")
|
|
194
|
+
args = parser.parse_args()
|
|
195
|
+
|
|
196
|
+
test_content = ""
|
|
197
|
+
if args.file and os.path.exists(args.file):
|
|
198
|
+
with open(args.file, "r", encoding="utf-8", errors="ignore") as f:
|
|
199
|
+
test_content = f.read()
|
|
200
|
+
elif args.code:
|
|
201
|
+
test_content = args.code
|
|
202
|
+
else:
|
|
203
|
+
# Sample test code with unasserted boundary
|
|
204
|
+
test_content = """
|
|
205
|
+
def test_order_reconciliation():
|
|
206
|
+
total_amount = 1500
|
|
207
|
+
is_valid = True
|
|
208
|
+
if total_amount > 1000 and is_valid:
|
|
209
|
+
order.submit()
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
optimizer = TextualGradientOptimizer(target_score=args.target_score)
|
|
213
|
+
success, optimized_code, history = optimizer.optimize_assertions(test_content)
|
|
214
|
+
|
|
215
|
+
if args.json:
|
|
216
|
+
print(json.dumps({
|
|
217
|
+
"success": success,
|
|
218
|
+
"target_score": args.target_score,
|
|
219
|
+
"history": history,
|
|
220
|
+
"optimized_code": optimized_code
|
|
221
|
+
}, indent=2))
|
|
222
|
+
return 0 if success else 1
|
|
223
|
+
|
|
224
|
+
print("\n" + "=" * 70)
|
|
225
|
+
print(" OPTIMIZATION SUMMARY")
|
|
226
|
+
print("=" * 70)
|
|
227
|
+
print(f"Initial Score: {history[0]['score']}% -> Final Score: {history[-1]['score']}%")
|
|
228
|
+
print(f"Converged : {'✅ Yes' if success else '❌ No'}")
|
|
229
|
+
print("=" * 70 + "\n")
|
|
230
|
+
return 0 if success else 1
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
if __name__ == "__main__":
|
|
234
|
+
sys.exit(main())
|
package/tools/verify_all.py
CHANGED
|
@@ -18,6 +18,7 @@ Verifies:
|
|
|
18
18
|
Gate 6: Credential Security: No plaintext passwords in Profiles/default.glbl or repository.
|
|
19
19
|
Gate 7: Static Defect Sentinel: Meta Infer-style AST scan for fake passes and empty catches.
|
|
20
20
|
Gate 8: Syntactic Micro-Mutation Sentinel: Injects syntactic faults to eliminate tautological tests (Mutation Score >= 80%).
|
|
21
|
+
Gate 9: Tier-2 Skill Collision Guard: Addy Osmani offline TF-IDF cosine similarity check (similarity < 0.75).
|
|
21
22
|
|
|
22
23
|
Supports:
|
|
23
24
|
- Hub Mode: Verifies the centralized QA Hub / skills repository integrity.
|
|
@@ -194,7 +195,7 @@ MOJIBAKE_INDICATORS = (
|
|
|
194
195
|
|
|
195
196
|
def gate_1_bom_check(repo_root, mode="AUTO"):
|
|
196
197
|
print("\n[GATE 1] Scanning UTF-8 No BOM and Encoding Integrity across repository...")
|
|
197
|
-
ignored_dirs = {".git", ".gradle", "node_modules", "allure-results", "graphify-out", "__pycache__", "build", "dist", "claude-ecosystem-repos"}
|
|
198
|
+
ignored_dirs = {".git", ".gradle", "node_modules", "allure-results", "graphify-out", "__pycache__", "build", "dist", "claude-ecosystem-repos", "external_repos", "teamwork"}
|
|
198
199
|
ignored_files = {"verify_all.py", "test_tools.py"}
|
|
199
200
|
bom_violations = []
|
|
200
201
|
mojibake_violations = []
|
|
@@ -244,7 +245,7 @@ def gate_2_path_portability(repo_root, mode="AUTO"):
|
|
|
244
245
|
ignored_dirs = {
|
|
245
246
|
".git", ".gradle", "node_modules", "allure-results", "Screenshots",
|
|
246
247
|
"Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings", "graphify-out",
|
|
247
|
-
"__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos"
|
|
248
|
+
"__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos", "external_repos", "teamwork"
|
|
248
249
|
}
|
|
249
250
|
ignored_files = {
|
|
250
251
|
".project", ".classpath", ".DS_Store", "memory_cache.db",
|
|
@@ -441,7 +442,7 @@ def gate_6_credential_security(repo_root, mode="AUTO"):
|
|
|
441
442
|
ignored_dirs = {
|
|
442
443
|
".git", ".gradle", "node_modules", "allure-results", "Screenshots",
|
|
443
444
|
"Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings", "graphify-out",
|
|
444
|
-
"__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos"
|
|
445
|
+
"__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos", "external_repos", "teamwork"
|
|
445
446
|
}
|
|
446
447
|
|
|
447
448
|
for root, dirs, files in os.walk(repo_root):
|
|
@@ -480,7 +481,7 @@ def check_python_ast_integrity(repo_root):
|
|
|
480
481
|
"""
|
|
481
482
|
builtin_names = set(dir(builtins)) | {'__file__', '__name__', '__doc__', '__package__'}
|
|
482
483
|
py_files = []
|
|
483
|
-
ignored = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'claude-ecosystem-repos', 'build', 'dist', '.gradle'}
|
|
484
|
+
ignored = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'claude-ecosystem-repos', 'build', 'dist', '.gradle', 'external_repos', 'teamwork'}
|
|
484
485
|
|
|
485
486
|
for root, dirs, files in os.walk(repo_root):
|
|
486
487
|
dirs[:] = [d for d in dirs if d not in ignored]
|
|
@@ -795,6 +796,35 @@ def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
|
|
|
795
796
|
return True
|
|
796
797
|
|
|
797
798
|
|
|
799
|
+
def gate_9_skill_collision(repo_root, mode="AUTO"):
|
|
800
|
+
print("\n[GATE 9] Running Tier-2 Skill Routing & Collision Guard (Addy Osmani Pattern)...")
|
|
801
|
+
guard_script = os.path.join(SCRIPT_DIR, "skill_collision_guard.py")
|
|
802
|
+
if not os.path.exists(guard_script):
|
|
803
|
+
guard_script = os.path.join(repo_root, "tools", "skill_collision_guard.py")
|
|
804
|
+
if not os.path.exists(guard_script):
|
|
805
|
+
print("[FAIL] skill_collision_guard.py not found in tools/!")
|
|
806
|
+
return False
|
|
807
|
+
|
|
808
|
+
skills_dir = os.path.join(repo_root, ".agents", "skills")
|
|
809
|
+
if not os.path.exists(skills_dir):
|
|
810
|
+
print("[PASS] Target project mode: .agents/skills/ not present (SKIPPED).")
|
|
811
|
+
return True
|
|
812
|
+
|
|
813
|
+
try:
|
|
814
|
+
res = subprocess.run([sys.executable, guard_script, "--dir", skills_dir, "--threshold", "0.75"],
|
|
815
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=60.0)
|
|
816
|
+
except subprocess.TimeoutExpired:
|
|
817
|
+
print("[FAIL] skill_collision_guard.py timed out after 60s!")
|
|
818
|
+
return False
|
|
819
|
+
|
|
820
|
+
if res.returncode != 0:
|
|
821
|
+
print(res.stdout)
|
|
822
|
+
print(res.stderr)
|
|
823
|
+
return False
|
|
824
|
+
print("[PASS] 100% Verified: 0 skill description collisions detected (similarity < 0.75).")
|
|
825
|
+
return True
|
|
826
|
+
|
|
827
|
+
|
|
798
828
|
def load_synapse_config(repo_root=None, config_path=None):
|
|
799
829
|
"""Loads configuration from synapse.config.json if present."""
|
|
800
830
|
if config_path and os.path.exists(config_path):
|
|
@@ -855,6 +885,7 @@ def run_all_gates(repo_root, mode="AUTO", test_cmd="", output_format="text", con
|
|
|
855
885
|
("gate_6_credential_security", "Gate 6: Credential Security", gate_6_credential_security),
|
|
856
886
|
("gate_7_infer_linter", "Gate 7: Static Defect Sentinel", gate_7_infer_linter),
|
|
857
887
|
("gate_8_micro_mutator", "Gate 8: Assertion Sensitivity & Syntactic Mutation Sentinel", lambda r, mode: gate_8_micro_mutator(r, mode=mode, test_cmd=test_cmd)),
|
|
888
|
+
("gate_9_skill_collision", "Gate 9: Tier-2 Skill Routing & Collision Guard", gate_9_skill_collision),
|
|
858
889
|
]
|
|
859
890
|
|
|
860
891
|
all_passed = True
|
package/tools/watch_mode.py
CHANGED
|
@@ -46,7 +46,7 @@ IGNORED_DIRS = {
|
|
|
46
46
|
".git", ".gradle", "node_modules", "allure-results", "Screenshots",
|
|
47
47
|
"Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings",
|
|
48
48
|
"graphify-out", "__pycache__", "agent_qa.egg-info", "dist", "build", ".pytest_cache",
|
|
49
|
-
"claude-ecosystem-repos"
|
|
49
|
+
"claude-ecosystem-repos", "external_repos", "teamwork"
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
IGNORED_FILES = {
|
package/tools/welcome_page.py
CHANGED
|
@@ -17,7 +17,7 @@ Features:
|
|
|
17
17
|
- Interactive tabs covering the 4 Core Pillars of QA Intelligence.
|
|
18
18
|
- Interactive CLI Cheatsheet with 1-click clipboard copy.
|
|
19
19
|
- Comparison matrix vs existing industry tools (ESLint, Stryker, Allure, Healenium).
|
|
20
|
-
- 6 Subagent Swarm Profiles &
|
|
20
|
+
- 6 Subagent Swarm Profiles & 10 Quality Gates DoD showcase.
|
|
21
21
|
- Automatic browser launching on setup, pull, or CLI dispatch.
|
|
22
22
|
"""
|
|
23
23
|
|
|
@@ -48,7 +48,7 @@ def get_hub_metadata() -> Dict[str, Any]:
|
|
|
48
48
|
"""Retrieves version and catalog count from skills_manifest.json."""
|
|
49
49
|
manifest_path = os.path.join(HUB_ROOT, "skills_manifest.json")
|
|
50
50
|
meta = {
|
|
51
|
-
"version": "2.
|
|
51
|
+
"version": "2.6.0",
|
|
52
52
|
"totalSkills": 42,
|
|
53
53
|
"author": "Thế Vị (@TeeVee998)",
|
|
54
54
|
"repo": "https://github.com/TeeVee998/Synapse-Hub"
|