synapse-hub 2.5.0 → 2.7.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 (92) hide show
  1. package/.agents/agents/failure-analyst.md +17 -0
  2. package/.agents/agents/orchestrator.md +20 -2
  3. package/.agents/agents/pom-crafter.md +20 -0
  4. package/.agents/agents/quality-gatekeeper.md +20 -2
  5. package/.agents/agents/test-runner.md +16 -0
  6. package/.agents/rules/context-engineering.md +22 -102
  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 +27 -116
  11. package/.agents/rules/performance_testing_rules.md +18 -62
  12. package/.agents/rules/skill-precedence.md +22 -42
  13. package/.agents/rules/systematic-debugging.md +10 -29
  14. package/.agents/skills/actor-qa-swarm/SKILL.md +77 -0
  15. package/.agents/skills/actor-qa-swarm/examples/broker_sharding_sample.json +12 -0
  16. package/.agents/skills/agentless-qa-repair/SKILL.md +69 -0
  17. package/.agents/skills/agentless-qa-repair/examples/healing_report_sample.json +17 -0
  18. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +1 -1
  19. package/.agents/skills/clean-pom-crafter/SKILL.md +2 -2
  20. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +4 -4
  21. package/.agents/skills/event-stream-qa-orchestrator/SKILL.md +79 -0
  22. package/.agents/skills/event-stream-qa-orchestrator/examples/timeline_sample.jsonl +2 -0
  23. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +2 -2
  24. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +6 -21
  25. package/.agents/skills/katalon-cli-runner/SKILL.md +1 -1
  26. package/.agents/skills/mipro-prompt-compiler/SKILL.md +59 -0
  27. package/.agents/skills/mipro-prompt-compiler/examples/compiled_module_sample.json +22 -0
  28. package/.agents/skills/qa-aci-interface/SKILL.md +67 -0
  29. package/.agents/skills/qa-aci-interface/examples/surgical_edit_sample.json +12 -0
  30. package/.agents/skills/qa-memory-os/SKILL.md +80 -0
  31. package/.agents/skills/qa-memory-os/examples/archival_memory_sample.json +21 -0
  32. package/.agents/skills/review-spec/SKILL.md +60 -0
  33. package/.agents/skills/review-spec/examples/sample_spec_review.md +33 -0
  34. package/.agents/skills/synthetic-test-data-generator/SKILL.md +2 -2
  35. package/.agents/skills/token-and-context-optimizer/references/context_engineering_guide.md +49 -0
  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/AGENTS.md +6 -1
  42. package/CLAUDE.md +1 -1
  43. package/NOTICE +40 -4
  44. package/README.en.md +27 -11
  45. package/README.md +28 -12
  46. package/docs/ARCHITECTURE.md +39 -11
  47. package/docs/adopted-ecosystem-tools.md +9 -0
  48. package/docs/research/EXTERNAL_AGENT_SKILLS_SURVEY.md +1062 -0
  49. package/docs/research/agent_skill_training_analysis.md +1357 -0
  50. package/package.json +1 -1
  51. package/skills_manifest.json +88 -7
  52. package/synapse.config.json +3 -2
  53. package/templates/ai_bridges/CLAUDE.md +1 -1
  54. package/templates/welcome.html.tmpl +34 -25
  55. package/tools/TOOLS_RUNBOOK.md +130 -1
  56. package/tools/__init__.py +1 -1
  57. package/tools/_openapi_parser.py +158 -0
  58. package/tools/agentless_patcher.py +417 -0
  59. package/tools/analyze_project.py +9 -6
  60. package/tools/auto_heal.py +29 -0
  61. package/tools/auto_heal_reflexion.py +172 -0
  62. package/tools/autogen_qa_broker.py +318 -0
  63. package/tools/browser_helpers.js +57 -2
  64. package/tools/browsergym_eval.py +237 -0
  65. package/tools/ci_healing_hook.py +1 -1
  66. package/tools/cli.py +3 -3
  67. package/tools/dashboard_generator.py +29 -13
  68. package/tools/db_sandbox.py +4 -0
  69. package/tools/dspy_qa_signatures.py +357 -0
  70. package/tools/heal_pr_automator.py +25 -0
  71. package/tools/interview_story.py +117 -0
  72. package/tools/lint_ai_docs.py +1 -1
  73. package/tools/mcp_excel_server.py +1 -1
  74. package/tools/mcp_server.py +1 -1
  75. package/tools/mipro_assertion_tuner.py +207 -0
  76. package/tools/pinpoint_failure.py +12 -0
  77. package/tools/project_state.py +4 -0
  78. package/tools/qa_event_stream.py +336 -0
  79. package/tools/reversible_sandbox.py +161 -0
  80. package/tools/safety_firewall.py +28 -0
  81. package/tools/skill_collision_guard.py +231 -0
  82. package/tools/skill_vault.py +343 -0
  83. package/tools/swe_qa_aci.py +369 -0
  84. package/tools/synapse_qa_bench.py +190 -0
  85. package/tools/sync_agent_rules.py +1 -1
  86. package/tools/test_memory_os.py +409 -0
  87. package/tools/textgrad_mutation_optimizer.py +234 -0
  88. package/tools/tot_exploratory_tester.py +240 -0
  89. package/tools/update_sentinel.py +3 -3
  90. package/tools/verify_all.py +35 -4
  91. package/tools/watch_mode.py +1 -1
  92. package/tools/welcome_page.py +2 -2
@@ -0,0 +1,161 @@
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
+ Reversible Execution Sandbox & Automated Teardown Engine (GoEx Pattern)
8
+ ------------------------------------------------------------------------
9
+ Maintains a LIFO undo-stack of stateful mutations (DB inserts, file modifications,
10
+ auth session cookies). Guarantees 100% idempotent teardown and zero database leakage
11
+ even when test execution crashes or is cancelled.
12
+
13
+ Reference:
14
+ - Shishir Patil et al. (Gorilla / GoEx): Runtime Reversible Sandboxed Execution.
15
+ - Synapse-Hub Subsystem 5: Section 6.6 of agent_skill_training_analysis.md.
16
+ """
17
+
18
+ import os
19
+ import sys
20
+ import json
21
+ import shlex
22
+ import subprocess
23
+ from dataclasses import dataclass, field
24
+ from typing import List, Optional, Callable, Dict, Any
25
+
26
+ # Enforce UTF-8 stdout encoding for Windows console compatibility
27
+ if hasattr(sys.stdout, 'reconfigure'):
28
+ try:
29
+ sys.stdout.reconfigure(encoding='utf-8')
30
+ sys.stderr.reconfigure(encoding='utf-8')
31
+ except Exception:
32
+ pass
33
+
34
+
35
+ @dataclass
36
+ class ReversibleAction:
37
+ action_type: str # e.g., "DB_INSERT", "FS_WRITE", "AUTH_SESSION", "STATE_MUTATION"
38
+ target_resource: str # e.g., "users table", "tmp/test_export.csv", "Bearer token"
39
+ undo_command: str # Exact SQL DELETE, shell command, or callback instruction
40
+ metadata: Dict[str, Any] = field(default_factory=dict)
41
+
42
+
43
+ class ExecutionSandbox:
44
+ """Manages an undo stack of stateful mutations for guaranteed autonomous cleanup."""
45
+
46
+ def __init__(self, auto_rollback_on_exit: bool = True):
47
+ self.undo_stack: List[ReversibleAction] = []
48
+ self.auto_rollback_on_exit = auto_rollback_on_exit
49
+
50
+ def record_mutation(self, action_type: str, target_resource: str, undo_command: str, metadata: Optional[Dict[str, Any]] = None):
51
+ """Pushes a rollback action onto the LIFO undo stack."""
52
+ action = ReversibleAction(
53
+ action_type=action_type,
54
+ target_resource=target_resource,
55
+ undo_command=undo_command,
56
+ metadata=metadata or {}
57
+ )
58
+ self.undo_stack.append(action)
59
+
60
+ def execute_stateful_action(self, action_type: str, target_resource: str, forward_cmd: str, undo_cmd: str) -> bool:
61
+ """Executes a forward shell command and immediately records its reversible undo counterpart."""
62
+ try:
63
+ args = shlex.split(forward_cmd) if isinstance(forward_cmd, str) else forward_cmd
64
+ res = subprocess.run(args, text=True, capture_output=True)
65
+ if res.returncode == 0:
66
+ self.record_mutation(action_type, target_resource, undo_cmd, {"forward_cmd": forward_cmd})
67
+ return True
68
+ return False
69
+ except Exception:
70
+ return False
71
+
72
+ def rollback_all(self, silent: bool = False) -> List[Dict[str, Any]]:
73
+ """Unwinds the undo stack in strictly reverse (LIFO) order."""
74
+ results = []
75
+ while self.undo_stack:
76
+ action = self.undo_stack.pop()
77
+ success = True
78
+ err_msg = ""
79
+ cmd = action.undo_command.strip()
80
+
81
+ if cmd.startswith("FILE_DELETE:"):
82
+ fpath = cmd.split(":", 1)[1].strip()
83
+ if os.path.exists(fpath):
84
+ try:
85
+ os.remove(fpath)
86
+ except OSError as e:
87
+ success = False
88
+ err_msg = str(e)
89
+ elif cmd.startswith("DIR_DELETE:"):
90
+ dpath = cmd.split(":", 1)[1].strip()
91
+ if os.path.isdir(dpath):
92
+ try:
93
+ import shutil
94
+ shutil.rmtree(dpath, ignore_errors=True)
95
+ except Exception as e:
96
+ success = False
97
+ err_msg = str(e)
98
+ elif cmd:
99
+ try:
100
+ args = shlex.split(cmd) if isinstance(cmd, str) else cmd
101
+ res = subprocess.run(args, text=True, capture_output=True)
102
+ success = (res.returncode == 0)
103
+ if not success:
104
+ err_msg = res.stderr.strip()
105
+ except Exception as e:
106
+ success = False
107
+ err_msg = str(e)
108
+
109
+ result_entry = {
110
+ "action_type": action.action_type,
111
+ "target_resource": action.target_resource,
112
+ "undo_command": action.undo_command,
113
+ "success": success,
114
+ "error": err_msg
115
+ }
116
+ results.append(result_entry)
117
+ if not silent:
118
+ status_icon = "✅" if success else "⚠️"
119
+ print(f" {status_icon} [ROLLBACK] {action.action_type} on '{action.target_resource}': {action.undo_command}")
120
+
121
+ return results
122
+
123
+ def stack_size(self) -> int:
124
+ """Returns the number of pending undo actions in the stack."""
125
+ return len(self.undo_stack)
126
+
127
+ def __enter__(self):
128
+ return self
129
+
130
+ def __exit__(self, exc_type, exc_val, exc_tb):
131
+ if exc_type is not None or self.auto_rollback_on_exit:
132
+ self.rollback_all(silent=True)
133
+
134
+
135
+ GLOBAL_SANDBOX = ExecutionSandbox(auto_rollback_on_exit=False)
136
+
137
+
138
+ def record_mutation(action_type: str, target: str, undo_cmd: str):
139
+ GLOBAL_SANDBOX.record_mutation(action_type, target, undo_cmd)
140
+
141
+
142
+ def rollback_all():
143
+ return GLOBAL_SANDBOX.rollback_all()
144
+
145
+
146
+ if __name__ == "__main__":
147
+ import argparse
148
+ parser = argparse.ArgumentParser(description="GoEx Reversible Teardown Sandbox Stack")
149
+ parser.add_argument("--demo", action="store_true", help="Run reversible stack demonstration")
150
+ parser.add_argument("--status", action="store_true", help="Display active undo stack count")
151
+ args = parser.parse_args()
152
+
153
+ if args.demo:
154
+ print("Running GoEx Reversible Sandbox demo...")
155
+ with ExecutionSandbox(auto_rollback_on_exit=True) as sb:
156
+ sb.record_mutation("fs_create", "test_demo.tmp", "echo rolled back")
157
+ print(f"Stack size inside sandbox: {sb.stack_size()}")
158
+ print("Exited sandbox context, stack rolled back cleanly.")
159
+ else:
160
+ print(f"Active global undo stack size: {GLOBAL_SANDBOX.stack_size()}")
161
+
@@ -19,6 +19,11 @@ import json
19
19
  import argparse
20
20
  from typing import List, Dict, Any, Tuple
21
21
 
22
+ try:
23
+ import reversible_sandbox
24
+ except ImportError:
25
+ reversible_sandbox = None
26
+
22
27
  # Enforce UTF-8 stdout encoding for Windows console compatibility
23
28
  if hasattr(sys.stdout, 'reconfigure'):
24
29
  try:
@@ -302,8 +307,31 @@ def main():
302
307
  parser.add_argument("--json", action="store_true", help="Output results in JSON format")
303
308
  parser.add_argument("--history", default="", help="Comma-separated or JSON list of historical action names to check for infinite loops")
304
309
  parser.add_argument("--check-loop", action="store_true", help="Explicitly audit action history for cycle/infinite loops")
310
+ parser.add_argument("--rollback-all", action="store_true", help="Unwind all recorded reversible actions via GoEx teardown stack")
311
+ parser.add_argument("--record-undo", default="", help="Undo command to push onto reversible sandbox stack")
312
+ parser.add_argument("--undo-target", default="STATE_MUTATION", help="Target resource or type for recorded undo action")
305
313
  args = parser.parse_args()
306
314
 
315
+ # Reversible Execution Sandbox teardown (GoEx pattern)
316
+ if args.rollback_all:
317
+ if reversible_sandbox:
318
+ res = reversible_sandbox.rollback_all()
319
+ if args.json:
320
+ print(json.dumps(res, indent=2))
321
+ return 0
322
+ else:
323
+ print("❌ reversible_sandbox module not available.")
324
+ return 1
325
+
326
+ if args.record_undo:
327
+ if reversible_sandbox:
328
+ reversible_sandbox.record_mutation("STATE_MUTATION", args.undo_target, args.record_undo)
329
+ print(f"✅ [GOEX SANDBOX] Reversible action registered for '{args.undo_target}': {args.record_undo}")
330
+ return 0
331
+ else:
332
+ print("❌ reversible_sandbox module not available.")
333
+ return 1
334
+
307
335
  # Loop check mode (Blazo-inspired)
308
336
  if args.history or args.check_loop:
309
337
  hist_items = []
@@ -0,0 +1,231 @@
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
+ Tier-2 Skill Routing & Collision Guard (Addy Osmani Pattern)
8
+ ------------------------------------------------------------
9
+ Computes pairwise cosine similarity across all SKILL.md descriptions.
10
+ Operates 100% offline with zero LLM tokens using a lightweight suffix-stripping
11
+ stemmer heuristic and TF-IDF word frequency representation.
12
+
13
+ Enforces:
14
+ - Warning when description similarity >= 0.50 (semantic proximity)
15
+ - Critical Error (Quality Gate Failure) when similarity >= 0.75 (ambiguous routing)
16
+
17
+ Usage:
18
+ python tools/skill_collision_guard.py [--dir .agents/skills] [--threshold 0.75] [--json]
19
+ """
20
+
21
+ import os
22
+ import sys
23
+ import re
24
+ import math
25
+ import json
26
+ import argparse
27
+ from typing import Dict, List, Tuple
28
+ from collections import Counter
29
+
30
+ # Enforce UTF-8 stdout encoding for Windows console compatibility
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
+ STOPWORDS = {
39
+ "a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are",
40
+ "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both",
41
+ "but", "by", "can", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't",
42
+ "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't",
43
+ "has", "hasn't", "have", "haven't", "having", "he", "her", "here", "hers", "herself", "him",
44
+ "himself", "his", "how", "i", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself",
45
+ "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on",
46
+ "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same",
47
+ "shan't", "she", "should", "shouldn't", "so", "some", "such", "than", "that", "the", "their",
48
+ "theirs", "them", "themselves", "then", "there", "these", "they", "this", "those", "through",
49
+ "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "were", "weren't", "what",
50
+ "when", "where", "which", "while", "who", "whom", "why", "with", "won't", "would", "wouldn't",
51
+ "you", "your", "yours", "yourself", "yourselves", "use", "when", "allows", "provides", "designed"
52
+ }
53
+
54
+
55
+ def strip_suffix(word: str) -> str:
56
+ """Lightweight suffix-stripping heuristic (Addy Osmani Tier-2 standard)."""
57
+ if len(word) <= 4:
58
+ return word
59
+ for suffix in ("ing", "tion", "ment", "ness", "able", "ible", "ize", "ise", "ies", "es", "ed", "ly", "al", "s"):
60
+ if word.endswith(suffix) and len(word) - len(suffix) >= 3:
61
+ return word[:-len(suffix)]
62
+ return word
63
+
64
+
65
+ def tokenize(text: str) -> List[str]:
66
+ """Tokenizes text, strips punctuation, removes stopwords and applies suffix stripping."""
67
+ words = re.findall(r"\b[a-zA-Z0-9_\-]+\b", text.lower())
68
+ tokens = []
69
+ for w in words:
70
+ cleaned = re.sub(r"^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$", "", w)
71
+ if len(cleaned) >= 2 and cleaned not in STOPWORDS:
72
+ tokens.append(strip_suffix(cleaned))
73
+ return tokens
74
+
75
+
76
+ def build_tf_vector(tokens: List[str]) -> Dict[str, float]:
77
+ """Builds normalized term-frequency vector."""
78
+ counts = Counter(tokens)
79
+ total = len(tokens)
80
+ if total == 0:
81
+ return {}
82
+ return {word: count / total for word, count in counts.items()}
83
+
84
+
85
+ def cosine_similarity(vec1: Dict[str, float], vec2: Dict[str, float]) -> float:
86
+ """Computes cosine similarity between two term-frequency vectors."""
87
+ intersection = set(vec1.keys()) & set(vec2.keys())
88
+ numerator = sum(vec1[x] * vec2[x] for x in intersection)
89
+ sum1 = sum(v ** 2 for v in vec1.values())
90
+ sum2 = sum(v ** 2 for v in vec2.values())
91
+ denominator = math.sqrt(sum1) * math.sqrt(sum2)
92
+ if not denominator:
93
+ return 0.0
94
+ return float(numerator) / denominator
95
+
96
+
97
+ class SkillCollisionGuard:
98
+ """Audits skill descriptions across repository for routing collisions and ambiguous triggers."""
99
+
100
+ def __init__(self, skills_dir: str = ".agents/skills", threshold: float = 0.75, warn_threshold: float = 0.50):
101
+ self.skills_dir = skills_dir
102
+ self.threshold = threshold
103
+ self.warn_threshold = warn_threshold
104
+
105
+ def load_descriptions(self) -> Dict[str, str]:
106
+ """Loads all SKILL.md files and extracts their description strings."""
107
+ descriptions = {}
108
+ if not os.path.exists(self.skills_dir):
109
+ return descriptions
110
+
111
+ for root, _, files in os.walk(self.skills_dir):
112
+ for file in files:
113
+ if file.lower() == "skill.md":
114
+ full_path = os.path.join(root, file)
115
+ skill_name, desc = self._extract_frontmatter(full_path)
116
+ if skill_name and desc:
117
+ descriptions[skill_name] = desc
118
+ return descriptions
119
+
120
+ def _extract_frontmatter(self, path: str) -> Tuple[str, str]:
121
+ try:
122
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
123
+ content = f.read()
124
+ except OSError:
125
+ return "", ""
126
+
127
+ match = re.search(r"^---\s*\n(.*?)\n---", content, re.DOTALL)
128
+ if match:
129
+ fm = match.group(1)
130
+ name_m = re.search(r"^name:\s*(.+)$", fm, re.MULTILINE)
131
+ desc_m = re.search(r"^description:\s*[\"']?(.*?)[\"']?$", fm, re.MULTILINE)
132
+ name = name_m.group(1).strip() if name_m else os.path.basename(os.path.dirname(path))
133
+ desc = desc_m.group(1).strip() if desc_m else ""
134
+ return name, desc
135
+
136
+ fallback_name = os.path.basename(os.path.dirname(path))
137
+ return fallback_name, ""
138
+
139
+ def audit(self) -> Tuple[bool, List[Dict[str, any]]]:
140
+ """
141
+ Computes pairwise similarity.
142
+ Returns (is_clean, report_items).
143
+ """
144
+ descriptions = self.load_descriptions()
145
+ names = sorted(list(descriptions.keys()))
146
+ vectors = {name: build_tf_vector(tokenize(descriptions[name])) for name in names}
147
+
148
+ findings = []
149
+ has_critical = False
150
+
151
+ for i in range(len(names)):
152
+ for j in range(i + 1, len(names)):
153
+ name_a = names[i]
154
+ name_b = names[j]
155
+ sim = cosine_similarity(vectors[name_a], vectors[name_b])
156
+
157
+ if sim >= self.threshold:
158
+ has_critical = True
159
+ findings.append({
160
+ "level": "CRITICAL",
161
+ "skill_a": name_a,
162
+ "skill_b": name_b,
163
+ "similarity": round(sim, 3),
164
+ "message": f"Critical Collision: '{name_a}' and '{name_b}' have cosine similarity {sim:.3f} >= {self.threshold}"
165
+ })
166
+ elif sim >= self.warn_threshold:
167
+ findings.append({
168
+ "level": "WARNING",
169
+ "skill_a": name_a,
170
+ "skill_b": name_b,
171
+ "similarity": round(sim, 3),
172
+ "message": f"Proximity Warning: '{name_a}' and '{name_b}' have similarity {sim:.3f} >= {self.warn_threshold}"
173
+ })
174
+
175
+ return (not has_critical), findings
176
+
177
+
178
+ def run_guard(skills_dir: str = ".agents/skills", threshold: float = 0.75, warn_threshold: float = 0.50, output_json: bool = False) -> int:
179
+ guard = SkillCollisionGuard(skills_dir, threshold=threshold, warn_threshold=warn_threshold)
180
+ passed, findings = guard.audit()
181
+
182
+ if output_json:
183
+ print(json.dumps({
184
+ "skills_dir": skills_dir,
185
+ "passed": passed,
186
+ "threshold": threshold,
187
+ "warn_threshold": warn_threshold,
188
+ "findings": findings
189
+ }, indent=2))
190
+ return 0 if passed else 1
191
+
192
+ print("\n" + "=" * 70)
193
+ print(" TIER-2 SKILL ROUTING & COLLISION GUARD (ADDY OSMANI PATTERN)")
194
+ print("=" * 70)
195
+ print(f"Skills Directory : {skills_dir}")
196
+ print(f"Collision Threshold: {threshold} (FAIL) | Warning Threshold: {warn_threshold} (WARN)")
197
+
198
+ criticals = [f for f in findings if f["level"] == "CRITICAL"]
199
+ warnings = [f for f in findings if f["level"] == "WARNING"]
200
+
201
+ if warnings:
202
+ print(f"\n⚠️ {len(warnings)} Near-Neighbor Warnings detected (similarity >= {warn_threshold}):")
203
+ for w in warnings:
204
+ print(f" - {w['message']}")
205
+
206
+ if criticals:
207
+ print(f"\n❌ {len(criticals)} CRITICAL COLLISION(S) detected (similarity >= {threshold}):")
208
+ for c in criticals:
209
+ print(f" [FAIL] {c['message']}")
210
+ print("\n[DoD BLOCKED] High similarity causes routing ambiguity and context contamination.")
211
+ print("=" * 70 + "\n")
212
+ return 1
213
+
214
+ print(f"\n✅ [PASS] 0 routing collisions detected. All skill descriptions uniquely segmented.\n")
215
+ print("=" * 70 + "\n")
216
+ return 0
217
+
218
+
219
+ def main():
220
+ parser = argparse.ArgumentParser(description="Tier-2 Skill Routing & Collision Guard")
221
+ parser.add_argument("--dir", default=".agents/skills", help="Directory containing skill packages")
222
+ parser.add_argument("--threshold", type=float, default=0.75, help="Critical similarity threshold (default: 0.75)")
223
+ parser.add_argument("--warn-threshold", type=float, default=0.50, help="Warning similarity threshold (default: 0.50)")
224
+ parser.add_argument("--json", action="store_true", help="Output results in JSON format")
225
+ args = parser.parse_args()
226
+
227
+ return run_guard(skills_dir=args.dir, threshold=args.threshold, warn_threshold=args.warn_threshold, output_json=args.json)
228
+
229
+
230
+ if __name__ == "__main__":
231
+ sys.exit(main())