0xray 2.0.1 → 2.1.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 (240) hide show
  1. package/.opencode/codex.codex +1 -1
  2. package/AGENTS.md +1 -1
  3. package/dist/AGENTS.md +1 -1
  4. package/dist/benchmark/performance-benchmark.d.ts +2 -2
  5. package/dist/benchmark/performance-benchmark.js +3 -3
  6. package/dist/cli/commands/hermes-install.js +4 -4
  7. package/dist/cli/commands/mcp-install.js +1 -1
  8. package/dist/cli/commands/openclaw-install.js +1 -1
  9. package/dist/cli/commands/plugin-commands.js +2 -2
  10. package/dist/cli/commands/publish-agent.js +5 -5
  11. package/dist/cli/commands/skill-install.js +1 -1
  12. package/dist/cli/commands/status.js +3 -3
  13. package/dist/cli/commands/storyteller.js +2 -2
  14. package/dist/cli/index.js +8 -8
  15. package/dist/cli/server.js +1 -1
  16. package/dist/core/agent-spawn-gate.d.ts +1 -1
  17. package/dist/core/agent-spawn-gate.js +1 -1
  18. package/dist/core/boot-orchestrator.d.ts +4 -4
  19. package/dist/core/boot-orchestrator.js +26 -26
  20. package/dist/core/bridge.mjs +22 -22
  21. package/dist/core/codex-formatter.js +2 -2
  22. package/dist/core/codex-injector.d.ts +4 -3
  23. package/dist/core/codex-injector.js +11 -10
  24. package/dist/core/config-loader.d.ts +5 -4
  25. package/dist/core/config-loader.js +4 -2
  26. package/dist/core/config-paths.d.ts +9 -8
  27. package/dist/core/config-paths.js +24 -23
  28. package/dist/core/context-loader.d.ts +4 -3
  29. package/dist/core/context-loader.js +7 -5
  30. package/dist/core/index.d.ts +1 -1
  31. package/dist/core/index.js +1 -1
  32. package/dist/core/orchestrator.d.ts +1 -0
  33. package/dist/core/orchestrator.js +3 -2
  34. package/dist/core/system-prompt-generator.js +2 -2
  35. package/dist/core/xray-activation.d.ts +4 -4
  36. package/dist/core/xray-activation.js +29 -24
  37. package/dist/delegation/agent-delegator.d.ts +3 -3
  38. package/dist/delegation/agent-delegator.js +3 -3
  39. package/dist/delegation/metrics-aggregator.d.ts +11 -11
  40. package/dist/delegation/session-coordinator.d.ts +3 -3
  41. package/dist/delegation/voting-coordinator.d.ts +3 -3
  42. package/dist/enforcement/enforcer-tools.js +2 -2
  43. package/dist/enforcement/loaders/codex-loader.d.ts +1 -1
  44. package/dist/enforcement/loaders/codex-loader.js +2 -2
  45. package/dist/index.d.ts +5 -5
  46. package/dist/index.js +6 -6
  47. package/dist/inference/deploy-verifier.js +1 -1
  48. package/dist/inference/inference-cycle.js +7 -7
  49. package/dist/integrations/base/README.md +2 -2
  50. package/dist/integrations/governance/index.js +1 -1
  51. package/dist/integrations/grok/grok-cli.d.ts +1 -1
  52. package/dist/integrations/grok/grok-cli.js +6 -6
  53. package/dist/integrations/grok/hooks/pre-tool-use.js +1 -1
  54. package/dist/integrations/grok/plugin/0xray/.mcp.json +3 -3
  55. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +2 -2
  56. package/dist/integrations/hermes-agent/__init__.py +77 -71
  57. package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
  58. package/dist/integrations/hermes-agent/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  59. package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
  60. package/dist/integrations/hermes-agent/__pycache__/test_plugin.cpython-313-pytest-9.0.2.pyc +0 -0
  61. package/dist/integrations/hermes-agent/__pycache__/test_plugin.cpython-313.pyc +0 -0
  62. package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
  63. package/dist/integrations/hermes-agent/after-install.md +14 -14
  64. package/dist/integrations/hermes-agent/bridge.mjs +14 -14
  65. package/dist/integrations/hermes-agent/logs/framework/routing-outcomes.json +1 -0
  66. package/dist/integrations/hermes-agent/plugin.yaml +5 -5
  67. package/dist/integrations/hermes-agent/schemas.py +12 -12
  68. package/dist/integrations/hermes-agent/test_plugin.py +128 -125
  69. package/dist/integrations/hermes-agent/tools.py +85 -23
  70. package/dist/integrations/openclaw/README.md +16 -16
  71. package/dist/integrations/openclaw/api-server.d.ts +4 -4
  72. package/dist/integrations/openclaw/api-server.js +9 -9
  73. package/dist/integrations/openclaw/config.js +1 -1
  74. package/dist/integrations/openclaw/hooks/{strray-hooks.d.ts → xray-hooks.d.ts} +4 -4
  75. package/dist/integrations/openclaw/index.d.ts +4 -4
  76. package/dist/integrations/openclaw/index.js +4 -4
  77. package/dist/integrations/openclaw/types.d.ts +1 -1
  78. package/dist/integrations/plugins/plugin-registry.js +2 -2
  79. package/dist/mcps/architect-tools.server.d.ts +2 -2
  80. package/dist/mcps/architect-tools.server.js +3 -3
  81. package/dist/mcps/auto-format.server.d.ts +2 -2
  82. package/dist/mcps/auto-format.server.js +3 -3
  83. package/dist/mcps/boot-orchestrator.server.d.ts +2 -2
  84. package/dist/mcps/boot-orchestrator.server.js +3 -3
  85. package/dist/mcps/config/server-config-registry.d.ts +1 -1
  86. package/dist/mcps/config/server-config-registry.js +7 -7
  87. package/dist/mcps/connection/mcp-connection.js +1 -1
  88. package/dist/mcps/enforcer-tools.server.d.ts +2 -2
  89. package/dist/mcps/enforcer-tools.server.js +3 -3
  90. package/dist/mcps/framework-compliance-audit.server.d.ts +2 -2
  91. package/dist/mcps/framework-compliance-audit.server.js +3 -3
  92. package/dist/mcps/framework-help.server.js +6 -6
  93. package/dist/mcps/in-process-skill-registry.js +6 -6
  94. package/dist/mcps/knowledge-skills/api-design.server.d.ts +2 -2
  95. package/dist/mcps/knowledge-skills/api-design.server.js +3 -3
  96. package/dist/mcps/knowledge-skills/architecture-patterns.server.d.ts +2 -2
  97. package/dist/mcps/knowledge-skills/architecture-patterns.server.js +3 -3
  98. package/dist/mcps/knowledge-skills/code-review.server.d.ts +2 -2
  99. package/dist/mcps/knowledge-skills/code-review.server.js +3 -3
  100. package/dist/mcps/knowledge-skills/database-design.server.d.ts +2 -2
  101. package/dist/mcps/knowledge-skills/database-design.server.js +3 -3
  102. package/dist/mcps/knowledge-skills/devops-deployment.server.d.ts +2 -2
  103. package/dist/mcps/knowledge-skills/devops-deployment.server.js +3 -3
  104. package/dist/mcps/knowledge-skills/git-workflow.server.d.ts +2 -2
  105. package/dist/mcps/knowledge-skills/git-workflow.server.js +3 -3
  106. package/dist/mcps/knowledge-skills/mobile-development.server.d.ts +2 -2
  107. package/dist/mcps/knowledge-skills/mobile-development.server.js +3 -3
  108. package/dist/mcps/knowledge-skills/performance-optimization.server.d.ts +2 -2
  109. package/dist/mcps/knowledge-skills/performance-optimization.server.js +3 -3
  110. package/dist/mcps/knowledge-skills/refactoring-strategies.server.d.ts +2 -2
  111. package/dist/mcps/knowledge-skills/refactoring-strategies.server.js +3 -3
  112. package/dist/mcps/knowledge-skills/security-audit.server.d.ts +2 -2
  113. package/dist/mcps/knowledge-skills/security-audit.server.js +3 -3
  114. package/dist/mcps/knowledge-skills/tech-writer.server.d.ts +2 -2
  115. package/dist/mcps/knowledge-skills/tech-writer.server.js +3 -3
  116. package/dist/mcps/knowledge-skills/testing-best-practices.server.d.ts +2 -2
  117. package/dist/mcps/knowledge-skills/testing-best-practices.server.js +3 -3
  118. package/dist/mcps/knowledge-skills/testing-strategy.server.d.ts +2 -2
  119. package/dist/mcps/knowledge-skills/testing-strategy.server.js +3 -3
  120. package/dist/mcps/knowledge-skills/ui-ux-design.server.d.ts +2 -2
  121. package/dist/mcps/knowledge-skills/ui-ux-design.server.js +5 -5
  122. package/dist/mcps/lint.server.d.ts +2 -2
  123. package/dist/mcps/lint.server.js +5 -5
  124. package/dist/mcps/mcp-client.js +3 -3
  125. package/dist/mcps/model-health-check.server.d.ts +2 -2
  126. package/dist/mcps/model-health-check.server.js +3 -3
  127. package/dist/mcps/orchestrator/server.js +2 -2
  128. package/dist/mcps/performance-analysis.server.d.ts +2 -2
  129. package/dist/mcps/performance-analysis.server.js +3 -3
  130. package/dist/mcps/processor-pipeline.server.d.ts +2 -2
  131. package/dist/mcps/processor-pipeline.server.js +3 -3
  132. package/dist/mcps/researcher.server.d.ts +3 -3
  133. package/dist/mcps/researcher.server.js +4 -4
  134. package/dist/mcps/security-scan.server.d.ts +2 -2
  135. package/dist/mcps/security-scan.server.js +3 -3
  136. package/dist/mcps/simulation/server-simulations.js +3 -3
  137. package/dist/metrics/agent-metrics.d.ts +4 -4
  138. package/dist/metrics/agent-metrics.js +1 -1
  139. package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +2 -2
  140. package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +2 -2
  141. package/dist/orchestrator/intelligent-commit-batcher.d.ts +1 -1
  142. package/dist/orchestrator/intelligent-commit-batcher.js +3 -3
  143. package/dist/orchestrator/multi-agent-orchestration-coordinator.d.ts +3 -3
  144. package/dist/orchestrator/multi-agent-orchestration-coordinator.js +9 -9
  145. package/dist/orchestrator/orchestrator.d.ts +3 -2
  146. package/dist/orchestrator/orchestrator.js +8 -6
  147. package/dist/plugin/xray-codex-injection.d.ts +1 -1
  148. package/dist/plugin/xray-codex-injection.js +8 -7
  149. package/dist/postprocessor/PostProcessor.d.ts +2 -2
  150. package/dist/postprocessor/PostProcessor.js +1 -1
  151. package/dist/postprocessor/monitoring/MonitoringEngine.d.ts +2 -2
  152. package/dist/postprocessor/services/RegressionAnalysisService.js +2 -2
  153. package/dist/postprocessor/triggers/GitHookTrigger.js +14 -14
  154. package/dist/processors/implementations/agents-md-validation-processor.js +2 -2
  155. package/dist/processors/implementations/inference-improvement-processor.js +2 -2
  156. package/dist/processors/implementations/log-protection-processor.d.ts +1 -1
  157. package/dist/processors/implementations/log-protection-processor.js +2 -2
  158. package/dist/processors/implementations/publish-preflight-processor.d.ts +1 -1
  159. package/dist/processors/implementations/publish-preflight-processor.js +3 -3
  160. package/dist/processors/implementations/regression-testing-processor.js +2 -2
  161. package/dist/processors/implementations/session-summary-processor.js +2 -2
  162. package/dist/processors/implementations/storytelling-trigger-processor.js +2 -2
  163. package/dist/processors/processor-manager.d.ts +2 -2
  164. package/dist/public/about.html +6 -6
  165. package/dist/public/enterprise.html +1 -1
  166. package/dist/public/features.html +2 -2
  167. package/dist/public/index.html +4 -4
  168. package/dist/reporting/report-formatter.js +1 -1
  169. package/dist/scripts/activate-kernel-pipeline.js +2 -2
  170. package/dist/scripts/pre-command +1 -1
  171. package/dist/services/inference-tuner.js +2 -2
  172. package/dist/session/session-cleanup-manager.d.ts +3 -3
  173. package/dist/session/session-monitor.d.ts +3 -3
  174. package/dist/session/session-state-manager.d.ts +3 -3
  175. package/dist/skills/hermes-agent/SKILL.md +11 -11
  176. package/dist/skills/storyteller/SKILL.md +1 -1
  177. package/dist/skills/xray-orchestrator/SKILL.md +141 -0
  178. package/dist/skills/xray-orchestrator/index.d.ts +13 -0
  179. package/dist/skills/xray-orchestrator/index.js +224 -0
  180. package/dist/state/index.d.ts +2 -1
  181. package/dist/state/index.js +2 -1
  182. package/dist/state/state-manager.d.ts +2 -2
  183. package/dist/state/state-manager.js +5 -4
  184. package/dist/utils/import-resolver.js +1 -1
  185. package/dist/utils/path-resolver.js +2 -2
  186. package/dist/utils/token-manager.js +1 -1
  187. package/dist/validation/estimation-validator.js +1 -1
  188. package/package.json +2 -2
  189. package/scripts/hooks/pre-command +1 -1
  190. package/scripts/hooks/run-hook.js +2 -2
  191. package/scripts/node/auto-reflection-generator.mjs +2 -2
  192. package/scripts/node/postinstall.cjs +1 -1
  193. package/scripts/node/release.js +6 -6
  194. package/scripts/node/release.mjs +10 -10
  195. package/scripts/node/version-manager.mjs +1 -1
  196. package/src/integrations/grok/plugin/0xray/.mcp.json +3 -3
  197. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +2 -2
  198. package/src/mcps/architect-tools.server.ts +3 -3
  199. package/src/mcps/auto-format.server.ts +3 -3
  200. package/src/mcps/boot-orchestrator.server.ts +3 -3
  201. package/src/mcps/config/server-config-registry.ts +7 -7
  202. package/src/mcps/connection/mcp-connection.ts +1 -1
  203. package/src/mcps/enforcer-tools.server.ts +3 -3
  204. package/src/mcps/framework-compliance-audit.server.ts +3 -3
  205. package/src/mcps/framework-help.server.ts +6 -6
  206. package/src/mcps/in-process-skill-registry.ts +6 -6
  207. package/src/mcps/knowledge-skills/api-design.server.test.ts +8 -8
  208. package/src/mcps/knowledge-skills/api-design.server.ts +3 -3
  209. package/src/mcps/knowledge-skills/architecture-patterns.server.ts +3 -3
  210. package/src/mcps/knowledge-skills/code-review.server.ts +3 -3
  211. package/src/mcps/knowledge-skills/database-design.server.ts +3 -3
  212. package/src/mcps/knowledge-skills/devops-deployment.server.ts +3 -3
  213. package/src/mcps/knowledge-skills/git-workflow.server.ts +3 -3
  214. package/src/mcps/knowledge-skills/mobile-development.server.ts +3 -3
  215. package/src/mcps/knowledge-skills/performance-optimization.server.ts +3 -3
  216. package/src/mcps/knowledge-skills/refactoring-strategies.server.ts +3 -3
  217. package/src/mcps/knowledge-skills/security-audit.server.test.ts +23 -23
  218. package/src/mcps/knowledge-skills/security-audit.server.ts +3 -3
  219. package/src/mcps/knowledge-skills/tech-writer.server.ts +3 -3
  220. package/src/mcps/knowledge-skills/testing-best-practices.server.test.ts +28 -28
  221. package/src/mcps/knowledge-skills/testing-best-practices.server.ts +3 -3
  222. package/src/mcps/knowledge-skills/testing-strategy.server.test.ts +19 -19
  223. package/src/mcps/knowledge-skills/testing-strategy.server.ts +3 -3
  224. package/src/mcps/knowledge-skills/ui-ux-design.server.ts +5 -5
  225. package/src/mcps/lint.server.ts +5 -5
  226. package/src/mcps/mcp-client.ts +3 -3
  227. package/src/mcps/model-health-check.server.ts +3 -3
  228. package/src/mcps/orchestrator/server.ts +2 -2
  229. package/src/mcps/performance-analysis.server.ts +3 -3
  230. package/src/mcps/processor-pipeline.server.ts +3 -3
  231. package/src/mcps/researcher.server.ts +4 -4
  232. package/src/mcps/security-scan.server.ts +3 -3
  233. package/src/mcps/simulation/server-simulations.ts +3 -3
  234. package/src/opencode/codex.codex +1 -1
  235. package/src/skills/hermes-agent/SKILL.md +11 -11
  236. package/src/skills/storyteller/SKILL.md +1 -1
  237. package/src/skills/xray-orchestrator/SKILL.md +141 -0
  238. package/src/skills/xray-orchestrator/index.ts +268 -0
  239. package/scripts/validate-stringray-comprehensive.js +0 -636
  240. /package/dist/integrations/openclaw/hooks/{strray-hooks.js → xray-hooks.js} +0 -0
@@ -1,4 +1,4 @@
1
- """Comprehensive tests for the StringRay Hermes Plugin v2.
1
+ """Comprehensive tests for the 0xRay Hermes Plugin v2.
2
2
 
3
3
  Tests all 3 tools, both hooks, the slash command, bridge integration,
4
4
  logging to disk, and the full register() wiring.
@@ -30,18 +30,18 @@ sys.path.insert(0, PLUGIN_DIR)
30
30
 
31
31
  # Force reimport
32
32
  for mod in list(sys.modules):
33
- if "strray" in mod and ("schemas" in mod or "tools" in mod or "plugin" in mod):
33
+ if "xray" in mod and ("schemas" in mod or "tools" in mod or "plugin" in mod):
34
34
  del sys.modules[mod]
35
35
 
36
36
  schemas = importlib.import_module("schemas")
37
37
  tools_mod = importlib.import_module("tools")
38
38
 
39
39
  # Create a fake package for __init__.py execution
40
- pkg = types.ModuleType("strray_hermes_pkg")
40
+ pkg = types.ModuleType("xray_hermes_pkg")
41
41
  pkg.__path__ = [PLUGIN_DIR]
42
42
  pkg.__dict__["schemas"] = schemas
43
43
  pkg.__dict__["tools"] = tools_mod
44
- sys.modules["strray_hermes_pkg"] = pkg
44
+ sys.modules["xray_hermes_pkg"] = pkg
45
45
 
46
46
  init_path = os.path.join(PLUGIN_DIR, "__init__.py")
47
47
  with open(init_path) as f:
@@ -56,8 +56,8 @@ pi = pkg # plugin init module
56
56
 
57
57
  class TestSchemas(unittest.TestCase):
58
58
  def test_validate_schema_has_required_fields(self):
59
- s = schemas.STRRAY_VALIDATE
60
- self.assertEqual(s["name"], "strray_validate")
59
+ s = schemas.XRAY_VALIDATE
60
+ self.assertEqual(s["name"], "xray_validate")
61
61
  params = s["parameters"]
62
62
  self.assertEqual(params["type"], "object")
63
63
  self.assertIn("files", params["properties"])
@@ -66,51 +66,51 @@ class TestSchemas(unittest.TestCase):
66
66
  self.assertEqual(params["properties"]["files"]["type"], "array")
67
67
 
68
68
  def test_codex_check_schema(self):
69
- s = schemas.STRRAY_CODEX_CHECK
70
- self.assertEqual(s["name"], "strray_codex_check")
69
+ s = schemas.XRAY_CODEX_CHECK
70
+ self.assertEqual(s["name"], "xray_codex_check")
71
71
  fa = s["parameters"]["properties"]["focus_areas"]
72
72
  self.assertIn("enum", fa["items"])
73
73
  self.assertIn("error-handling", fa["items"]["enum"])
74
74
 
75
75
  def test_health_schema_no_required(self):
76
- s = schemas.STRRAY_HEALTH
76
+ s = schemas.XRAY_HEALTH
77
77
  self.assertEqual(len(s["parameters"].get("required", [])), 0)
78
78
 
79
79
  def test_descriptions_non_empty(self):
80
- for name, schema in [("v", schemas.STRRAY_VALIDATE), ("c", schemas.STRRAY_CODEX_CHECK), ("h", schemas.STRRAY_HEALTH)]:
80
+ for name, schema in [("v", schemas.XRAY_VALIDATE), ("c", schemas.XRAY_CODEX_CHECK), ("h", schemas.XRAY_HEALTH)]:
81
81
  self.assertTrue(len(schema["description"]) > 20, f"{name}")
82
82
 
83
83
 
84
- class TestRunStrrayHelper(unittest.TestCase):
84
+ class TestRunXrayHelper(unittest.TestCase):
85
85
  """Test the CLI fallback helper (still exists for bridge-less environments)."""
86
86
 
87
87
  def test_successful_command(self):
88
88
  with patch("subprocess.run") as m:
89
89
  m.return_value = MagicMock(returncode=0, stdout="all good", stderr="")
90
- r = json.loads(tools_mod._run_strray(["health"]))
90
+ r = json.loads(tools_mod._run_xray(["health"]))
91
91
  self.assertEqual(r["status"], "ok")
92
92
  self.assertEqual(m.call_args[0][0], ["npx", "0xray", "health"])
93
93
 
94
94
  def test_command_failure(self):
95
95
  with patch("subprocess.run") as m:
96
96
  m.return_value = MagicMock(returncode=1, stdout="", stderr="broke")
97
- r = json.loads(tools_mod._run_strray(["validate"]))
97
+ r = json.loads(tools_mod._run_xray(["validate"]))
98
98
  self.assertEqual(r["status"], "error")
99
99
 
100
100
  def test_file_not_found(self):
101
101
  with patch("subprocess.run", side_effect=FileNotFoundError):
102
- r = json.loads(tools_mod._run_strray(["health"]))
102
+ r = json.loads(tools_mod._run_xray(["health"]))
103
103
  self.assertIn("not found", r["error"])
104
104
 
105
105
  def test_timeout(self):
106
106
  with patch("subprocess.run", side_effect=subprocess.TimeoutExpired("c", 30)):
107
- r = json.loads(tools_mod._run_strray(["health"], timeout=15))
107
+ r = json.loads(tools_mod._run_xray(["health"], timeout=15))
108
108
  self.assertIn("15s", r["error"])
109
109
 
110
110
  def test_custom_timeout(self):
111
111
  with patch("subprocess.run") as m:
112
112
  m.return_value = MagicMock(returncode=0, stdout="", stderr="")
113
- tools_mod._run_strray(["health"], timeout=60)
113
+ tools_mod._run_xray(["health"], timeout=60)
114
114
  self.assertEqual(m.call_args[1]["timeout"], 60)
115
115
 
116
116
 
@@ -143,11 +143,11 @@ class TestBridgeHelper(unittest.TestCase):
143
143
  self.assertIn("timed out", r["error"])
144
144
 
145
145
 
146
- class TestStrrayHealth(unittest.TestCase):
146
+ class TestXrayHealth(unittest.TestCase):
147
147
  def test_health_via_bridge(self):
148
148
  """v2: health uses bridge first."""
149
149
  with patch.object(tools_mod, "_bridge_call", return_value={"status": "ok", "framework": "loaded", "version": "1.15.0", "components": {}}) as m:
150
- r = json.loads(tools_mod.strray_health({}))
150
+ r = json.loads(tools_mod.xray_health({}))
151
151
  self.assertEqual(r["status"], "ok")
152
152
  self.assertEqual(r["via"], "bridge")
153
153
  m.assert_called_once_with({"command": "health"}, timeout=10)
@@ -155,21 +155,21 @@ class TestStrrayHealth(unittest.TestCase):
155
155
  def test_health_fallback_to_cli(self):
156
156
  """v2: falls back to CLI when bridge fails."""
157
157
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "node not found"}):
158
- with patch.object(tools_mod, "_run_strray", return_value='{"status":"ok","output":"healthy"}') as cli:
159
- r = json.loads(tools_mod.strray_health({}))
158
+ with patch.object(tools_mod, "_run_xray", return_value='{"status":"ok","output":"healthy"}') as cli:
159
+ r = json.loads(tools_mod.xray_health({}))
160
160
  cli.assert_called_once()
161
161
 
162
162
  def test_health_ignores_extra_args(self):
163
163
  with patch.object(tools_mod, "_bridge_call", return_value={"status": "ok", "framework": "loaded", "version": "1.0", "components": {}}):
164
- r = json.loads(tools_mod.strray_health({"x": 1}))
164
+ r = json.loads(tools_mod.xray_health({"x": 1}))
165
165
  self.assertEqual(r["status"], "ok")
166
166
 
167
167
 
168
- class TestStrrayValidate(unittest.TestCase):
168
+ class TestXrayValidate(unittest.TestCase):
169
169
  def test_with_files_via_bridge(self):
170
170
  """v2: validate uses bridge first."""
171
171
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "fileResults": []}) as m:
172
- r = json.loads(tools_mod.strray_validate({"files": ["a.ts", "b.ts"], "operation": "commit"}))
172
+ r = json.loads(tools_mod.xray_validate({"files": ["a.ts", "b.ts"], "operation": "commit"}))
173
173
  self.assertEqual(r["status"], "passed")
174
174
  self.assertEqual(r["files_checked"], 2)
175
175
  self.assertEqual(r["via"], "bridge")
@@ -177,86 +177,86 @@ class TestStrrayValidate(unittest.TestCase):
177
177
 
178
178
  def test_bridge_violations(self):
179
179
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": False, "fileResults": [{"file": "a.ts", "passed": False, "violations": ["tests-required"]}]}) as m:
180
- r = json.loads(tools_mod.strray_validate({"files": ["a.ts"]}))
180
+ r = json.loads(tools_mod.xray_validate({"files": ["a.ts"]}))
181
181
  self.assertEqual(r["status"], "violations")
182
182
  self.assertEqual(r["file_results"][0]["violations"], ["tests-required"])
183
183
 
184
184
  def test_bridge_error_fallback_to_cli(self):
185
185
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "node not found"}):
186
- with patch.object(tools_mod, "_run_strray", return_value='{"status":"ok","output":"valid"}') as cli:
187
- r = json.loads(tools_mod.strray_validate({"files": ["a.ts"]}))
186
+ with patch.object(tools_mod, "_run_xray", return_value='{"status":"ok","output":"valid"}') as cli:
187
+ r = json.loads(tools_mod.xray_validate({"files": ["a.ts"]}))
188
188
  self.assertEqual(r["via"], "cli")
189
189
  cli.assert_called_once()
190
190
 
191
191
  def test_no_files_error(self):
192
- r = json.loads(tools_mod.strray_validate({"files": []}))
192
+ r = json.loads(tools_mod.xray_validate({"files": []}))
193
193
  self.assertIn("No files", r["error"])
194
194
 
195
195
  def test_no_files_key_error(self):
196
- r = json.loads(tools_mod.strray_validate({}))
196
+ r = json.loads(tools_mod.xray_validate({}))
197
197
  self.assertIn("error", r)
198
198
 
199
199
  def test_default_operation(self):
200
200
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "fileResults": []}):
201
- r = json.loads(tools_mod.strray_validate({"files": ["a.ts"]}))
201
+ r = json.loads(tools_mod.xray_validate({"files": ["a.ts"]}))
202
202
  self.assertEqual(r["operation"], "commit")
203
203
 
204
204
  def test_100_files(self):
205
205
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "fileResults": []}) as m:
206
206
  fs = [f"f{i}.ts" for i in range(100)]
207
- r = json.loads(tools_mod.strray_validate({"files": fs}))
207
+ r = json.loads(tools_mod.xray_validate({"files": fs}))
208
208
  self.assertEqual(r["files_checked"], 100)
209
209
 
210
210
 
211
- class TestStrrayCodexCheck(unittest.TestCase):
211
+ class TestXrayCodexCheck(unittest.TestCase):
212
212
  def test_with_code_via_bridge(self):
213
213
  """v2: codex check uses bridge for real quality gate analysis."""
214
214
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "violations": [], "checks": []}) as m:
215
- r = json.loads(tools_mod.strray_codex_check({"code": "const x = null;", "operation": "create"}))
215
+ r = json.loads(tools_mod.xray_codex_check({"code": "const x = null;", "operation": "create"}))
216
216
  self.assertEqual(r["status"], "passed")
217
217
  self.assertEqual(r["via"], "bridge")
218
218
  m.assert_called_once()
219
219
 
220
220
  def test_with_code_bridge_violations(self):
221
221
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": False, "violations": ["console.log found"], "checks": []}):
222
- r = json.loads(tools_mod.strray_codex_check({"code": "console.log(x)", "operation": "create"}))
222
+ r = json.loads(tools_mod.xray_codex_check({"code": "console.log(x)", "operation": "create"}))
223
223
  self.assertEqual(r["status"], "violations")
224
224
  self.assertEqual(r["violations"], ["console.log found"])
225
225
 
226
226
  def test_with_focus_areas(self):
227
227
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "violations": [], "checks": []}) as m:
228
- tools_mod.strray_codex_check({"code": "eval()", "operation": "modify", "focus_areas": ["security"]})
228
+ tools_mod.xray_codex_check({"code": "eval()", "operation": "modify", "focus_areas": ["security"]})
229
229
  self.assertEqual(m.call_args[0][0]["focusAreas"], ["security"])
230
230
 
231
231
  def test_empty_string_code_treated_as_code(self):
232
232
  """BUG FIX: empty string '' should still be treated as code (is not None)."""
233
233
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "violations": [], "checks": []}) as m:
234
- r = json.loads(tools_mod.strray_codex_check({"code": "", "operation": "create"}))
234
+ r = json.loads(tools_mod.xray_codex_check({"code": "", "operation": "create"}))
235
235
  self.assertEqual(r["status"], "passed")
236
236
  self.assertEqual(r["code_length"], 0)
237
237
 
238
238
  def test_no_code_bridge_health(self):
239
239
  """No code provided — bridge health check."""
240
240
  with patch.object(tools_mod, "_bridge_call", return_value={"framework": "loaded", "version": "1.15.0", "components": {}}) as m:
241
- r = json.loads(tools_mod.strray_codex_check({"operation": "refactor"}))
241
+ r = json.loads(tools_mod.xray_codex_check({"operation": "refactor"}))
242
242
  self.assertEqual(r["status"], "ok")
243
243
  self.assertIn("Pass", r["note"])
244
244
 
245
245
  def test_no_code_bridge_error_fallback(self):
246
246
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "node not found"}):
247
- with patch.object(tools_mod, "_run_strray", return_value='{"status":"ok","output":"healthy"}') as cli:
248
- r = json.loads(tools_mod.strray_codex_check({"operation": "create"}))
247
+ with patch.object(tools_mod, "_run_xray", return_value='{"status":"ok","output":"healthy"}') as cli:
248
+ r = json.loads(tools_mod.xray_codex_check({"operation": "create"}))
249
249
  cli.assert_called_once()
250
250
 
251
251
  def test_default_operation(self):
252
252
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "violations": [], "checks": []}):
253
- r = json.loads(tools_mod.strray_codex_check({"code": "x"}))
253
+ r = json.loads(tools_mod.xray_codex_check({"code": "x"}))
254
254
  self.assertEqual(r["operation"], "create")
255
255
 
256
256
  def test_multiline_code(self):
257
257
  code = "function foo() {\n return null;\n}\n"
258
258
  with patch.object(tools_mod, "_bridge_call", return_value={"passed": True, "violations": [], "checks": []}):
259
- r = json.loads(tools_mod.strray_codex_check({"code": code, "operation": "create"}))
259
+ r = json.loads(tools_mod.xray_codex_check({"code": code, "operation": "create"}))
260
260
  self.assertEqual(r["code_length"], len(code))
261
261
 
262
262
 
@@ -270,10 +270,10 @@ class TestPreToolCallHook(unittest.TestCase):
270
270
  pi._session_stats["session_id"] = None
271
271
 
272
272
  @patch.object(pi, "_call_bridge")
273
- def test_strray_mcp_no_bridge(self, mock_bridge):
274
- """StringRay MCP tools skip bridge entirely."""
275
- pi._on_pre_tool_call("mcp_strray_lint_lint", {}, "t1")
276
- self.assertEqual(pi._session_stats["strray_mcp_calls"], 1)
273
+ def test_xray_mcp_no_bridge(self, mock_bridge):
274
+ """0xRay MCP tools skip bridge entirely."""
275
+ pi._on_pre_tool_call("mcp_xray_lint_lint", {}, "t1")
276
+ self.assertEqual(pi._session_stats["xray_mcp_calls"], 1)
277
277
  self.assertEqual(pi._session_stats["native_tool_calls"], 0)
278
278
  mock_bridge.assert_not_called()
279
279
 
@@ -316,26 +316,26 @@ class TestPreToolCallHook(unittest.TestCase):
316
316
  """Terminal nudge only fires when command matches a known pattern."""
317
317
  # No command arg — no nudge
318
318
  with self.assertRaises(AssertionError):
319
- with self.assertLogs("strray-hermes", level="INFO"):
319
+ with self.assertLogs("xray-hermes", level="INFO"):
320
320
  pi._on_pre_tool_call("terminal", {}, "t1")
321
321
 
322
322
  # Generic command (git, ls) — no nudge
323
323
  with self.assertRaises(AssertionError):
324
- with self.assertLogs("strray-hermes", level="INFO"):
324
+ with self.assertLogs("xray-hermes", level="INFO"):
325
325
  pi._on_pre_tool_call("terminal", {"command": "git status"}, "t1")
326
326
 
327
327
  # Grep command — should nudge
328
- with self.assertLogs("strray-hermes", level="INFO") as cm:
328
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
329
329
  pi._on_pre_tool_call("terminal", {"command": "grep -r 'pattern' src/"}, "t1")
330
330
  self.assertTrue(any("grep" in m for m in cm.output))
331
331
 
332
332
  # npm audit — should nudge
333
- with self.assertLogs("strray-hermes", level="INFO") as cm:
333
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
334
334
  pi._on_pre_tool_call("terminal", {"command": "npm audit"}, "t1")
335
335
  self.assertTrue(any("audit" in m for m in cm.output))
336
336
 
337
337
  def test_nudge_search_files(self):
338
- with self.assertLogs("strray-hermes", level="INFO") as cm:
338
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
339
339
  pi._on_pre_tool_call("search_files", {}, "t1")
340
340
  self.assertTrue(any("Tip" in m for m in cm.output))
341
341
 
@@ -343,7 +343,7 @@ class TestPreToolCallHook(unittest.TestCase):
343
343
  """write_file is a code tool — no nudge, gets bridge instead."""
344
344
  with patch.object(pi, "_call_bridge", return_value={"passed": True, "qualityGate": {"passed": True}, "processors": {"ran": False}}):
345
345
  with self.assertRaises(AssertionError):
346
- with self.assertLogs("strray-hermes", level="INFO"):
346
+ with self.assertLogs("xray-hermes", level="INFO"):
347
347
  pi._on_pre_tool_call("write_file", {}, "t1")
348
348
 
349
349
  def test_accumulates(self):
@@ -352,12 +352,12 @@ class TestPreToolCallHook(unittest.TestCase):
352
352
  pi._on_pre_tool_call("terminal", {}, "t1")
353
353
  self.assertEqual(pi._session_stats["total_tool_calls"], 5)
354
354
 
355
- def test_is_strray_mcp(self):
356
- self.assertTrue(pi._is_strray_mcp("mcp_strray_lint_lint"))
357
- self.assertTrue(pi._is_strray_mcp("mcp_strray_enforcer_codex_enforcement"))
358
- self.assertFalse(pi._is_strray_mcp("terminal"))
359
- self.assertFalse(pi._is_strray_mcp("strray_validate"))
360
- self.assertFalse(pi._is_strray_mcp("mcp_other_tool"))
355
+ def test_is_xray_mcp(self):
356
+ self.assertTrue(pi._is_xray_mcp("mcp_xray_lint_lint"))
357
+ self.assertTrue(pi._is_xray_mcp("mcp_xray_enforcer_codex_enforcement"))
358
+ self.assertFalse(pi._is_xray_mcp("terminal"))
359
+ self.assertFalse(pi._is_xray_mcp("xray_validate"))
360
+ self.assertFalse(pi._is_xray_mcp("mcp_other_tool"))
361
361
 
362
362
 
363
363
  class TestPostToolCallHook(unittest.TestCase):
@@ -415,7 +415,7 @@ class TestSlashCommand(unittest.TestCase):
415
415
  "session_id": "test-session",
416
416
  "code_operations": 5,
417
417
  "total_tool_calls": 20,
418
- "strray_mcp_calls": 12,
418
+ "xray_mcp_calls": 12,
419
419
  "native_tool_calls": 8,
420
420
  "quality_gate_runs": 10,
421
421
  "quality_gate_blocks": 2,
@@ -429,7 +429,7 @@ class TestSlashCommand(unittest.TestCase):
429
429
  }
430
430
 
431
431
  def test_stats(self):
432
- o = pi._strray_command("stats")
432
+ o = pi._xray_command("stats")
433
433
  self.assertIn("20", o)
434
434
  self.assertIn("12", o)
435
435
  self.assertIn("8", o)
@@ -440,32 +440,32 @@ class TestSlashCommand(unittest.TestCase):
440
440
  self.assertIn("15", o) # bridge_calls
441
441
 
442
442
  def test_help(self):
443
- o = pi._strray_command("help")
443
+ o = pi._xray_command("help")
444
444
  self.assertIn("status", o)
445
445
  self.assertIn("help", o)
446
446
 
447
447
  def test_status_via_bridge(self):
448
- """v2: status uses bridge, not tools.strray_health."""
448
+ """v2: status uses bridge, not tools.xray_health."""
449
449
  with patch.object(pi, "_call_bridge", return_value={"framework": "loaded", "version": "1.15.0", "components": {"qualityGate": True, "processorManager": True}}) as m:
450
- o = pi._strray_command("status")
450
+ o = pi._xray_command("status")
451
451
  self.assertIn("loaded", o)
452
452
  self.assertIn("1.15.0", o)
453
453
  m.assert_called_once_with({"command": "health"}, timeout=10)
454
454
 
455
455
  def test_status_bridge_error(self):
456
456
  with patch.object(pi, "_call_bridge", return_value={"error": "node not found"}):
457
- o = pi._strray_command("status")
457
+ o = pi._xray_command("status")
458
458
  self.assertIn("node not found", o)
459
459
 
460
460
  def test_default_status(self):
461
461
  with patch.object(pi, "_call_bridge", return_value={"framework": "loaded", "version": "1.0", "components": {}}):
462
- o = pi._strray_command("")
462
+ o = pi._xray_command("")
463
463
  self.assertIn("loaded", o)
464
464
 
465
465
  def test_stats_null_started_at(self):
466
466
  pi._session_stats["started_at"] = None
467
467
  pi._session_stats["session_id"] = None
468
- o = pi._strray_command("stats")
468
+ o = pi._xray_command("stats")
469
469
  self.assertIn("N/A", o)
470
470
 
471
471
 
@@ -484,7 +484,7 @@ class TestSessionStartHook(unittest.TestCase):
484
484
  self.assertEqual(pi._session_stats["session_id"], "s1")
485
485
 
486
486
  def test_logs(self):
487
- with self.assertLogs("strray-hermes", level="INFO") as cm:
487
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
488
488
  pi._on_session_start("s1", "telegram")
489
489
  self.assertTrue(any("s1" in m for m in cm.output))
490
490
  self.assertTrue(any("telegram" in m for m in cm.output))
@@ -495,29 +495,29 @@ class TestRegisterIntegration(unittest.TestCase):
495
495
  ctx = MagicMock()
496
496
  pi.register(ctx)
497
497
  names = [c[1]["name"] for c in ctx.register_tool.call_args_list]
498
- self.assertEqual(set(names), {"strray_validate", "strray_codex_check", "strray_health", "strray_hooks"})
498
+ self.assertEqual(set(names), {"xray_validate", "xray_codex_check", "xray_health", "xray_hooks"})
499
499
 
500
500
  def test_toolset_name(self):
501
501
  ctx = MagicMock()
502
502
  pi.register(ctx)
503
503
  for c in ctx.register_tool.call_args_list:
504
- self.assertEqual(c[1]["toolset"], "strray-hermes")
504
+ self.assertEqual(c[1]["toolset"], "0xray-hermes")
505
505
 
506
506
  def test_schemas_wired(self):
507
507
  ctx = MagicMock()
508
508
  pi.register(ctx)
509
509
  sm = {c[1]["name"]: c[1]["schema"] for c in ctx.register_tool.call_args_list}
510
- self.assertIs(sm["strray_validate"], schemas.STRRAY_VALIDATE)
511
- self.assertIs(sm["strray_codex_check"], schemas.STRRAY_CODEX_CHECK)
512
- self.assertIs(sm["strray_health"], schemas.STRRAY_HEALTH)
510
+ self.assertIs(sm["xray_validate"], schemas.XRAY_VALIDATE)
511
+ self.assertIs(sm["xray_codex_check"], schemas.XRAY_CODEX_CHECK)
512
+ self.assertIs(sm["xray_health"], schemas.XRAY_HEALTH)
513
513
 
514
514
  def test_handlers_wired(self):
515
515
  ctx = MagicMock()
516
516
  pi.register(ctx)
517
517
  hm = {c[1]["name"]: c[1]["handler"] for c in ctx.register_tool.call_args_list}
518
- self.assertIs(hm["strray_validate"], tools_mod.strray_validate)
519
- self.assertIs(hm["strray_codex_check"], tools_mod.strray_codex_check)
520
- self.assertIs(hm["strray_health"], tools_mod.strray_health)
518
+ self.assertIs(hm["xray_validate"], tools_mod.xray_validate)
519
+ self.assertIs(hm["xray_codex_check"], tools_mod.xray_codex_check)
520
+ self.assertIs(hm["xray_health"], tools_mod.xray_health)
521
521
 
522
522
  def test_hooks_registered(self):
523
523
  ctx = MagicMock()
@@ -551,7 +551,7 @@ class TestRegisterIntegration(unittest.TestCase):
551
551
 
552
552
  def test_logs_on_load(self):
553
553
  ctx = MagicMock()
554
- with self.assertLogs("strray-hermes", level="INFO") as cm:
554
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
555
555
  pi.register(ctx)
556
556
  self.assertTrue(any("Plugin" in m and "loaded" in m for m in cm.output))
557
557
 
@@ -648,7 +648,10 @@ class TestLiveBridge(unittest.TestCase):
648
648
  self.assertNotIn("error", r)
649
649
  # console.log is a violation
650
650
  if r.get("passed") is False:
651
- self.assertTrue(any("console.log" in v for v in r.get("violations", [])))
651
+ self.assertTrue(
652
+ any("console.log" in v.get("message", "") for v in r.get("violations", [])),
653
+ f"Expected 'console.log' in violations, got: {r.get('violations', [])}",
654
+ )
652
655
 
653
656
  def test_bridge_positional_health(self):
654
657
  """Positional arg mode: node bridge.mjs health --cwd /path (no stdin pipe)."""
@@ -657,8 +660,8 @@ class TestLiveBridge(unittest.TestCase):
657
660
  self.skipTest("bridge.mjs not built yet")
658
661
 
659
662
  # Use stringray dev repo as project root (has package.json)
660
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
661
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
663
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
664
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
662
665
 
663
666
  r = subprocess.run(
664
667
  ["node", str(bridge_path), "health", "--cwd", cwd],
@@ -675,8 +678,8 @@ class TestLiveBridge(unittest.TestCase):
675
678
  if not bridge_path.exists():
676
679
  self.skipTest("bridge.mjs not built yet")
677
680
 
678
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
679
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
681
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
682
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
680
683
 
681
684
  r = subprocess.run(
682
685
  ["node", str(bridge_path), "stats", "--cwd", cwd],
@@ -692,8 +695,8 @@ class TestLiveBridge(unittest.TestCase):
692
695
  if not bridge_path.exists():
693
696
  self.skipTest("bridge.mjs not built yet")
694
697
 
695
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
696
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
698
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
699
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
697
700
 
698
701
  r = subprocess.run(
699
702
  ["node", str(bridge_path), "validate", "--cwd", cwd,
@@ -710,8 +713,8 @@ class TestLiveBridge(unittest.TestCase):
710
713
  if not bridge_path.exists():
711
714
  self.skipTest("bridge.mjs not built yet")
712
715
 
713
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
714
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
716
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
717
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
715
718
 
716
719
  r = subprocess.run(
717
720
  ["node", str(bridge_path), "validate", "--cwd", cwd,
@@ -739,32 +742,32 @@ class TestBridgeErrorPaths(unittest.TestCase):
739
742
  r = tools_mod._bridge_call({"command": "health"})
740
743
  self.assertIn("error", r)
741
744
 
742
- def test_bridge_generic_exception_in_run_strray(self):
743
- """_run_strray catches non-standard exceptions."""
745
+ def test_bridge_generic_exception_in_run_xray(self):
746
+ """_run_xray catches non-standard exceptions."""
744
747
  with patch("subprocess.run", side_effect=RuntimeError("unexpected")):
745
- r = json.loads(tools_mod._run_strray(["health"]))
748
+ r = json.loads(tools_mod._run_xray(["health"]))
746
749
  self.assertIn("unexpected", r["error"])
747
750
 
748
751
  def test_validate_cli_fallback_error(self):
749
- """strray_validate CLI fallback when CLI returns an error JSON."""
752
+ """xray_validate CLI fallback when CLI returns an error JSON."""
750
753
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "bridge down"}):
751
- with patch.object(tools_mod, "_run_strray", return_value='{"error": "validation failed"}'):
752
- r = json.loads(tools_mod.strray_validate({"files": ["a.ts"]}))
754
+ with patch.object(tools_mod, "_run_xray", return_value='{"error": "validation failed"}'):
755
+ r = json.loads(tools_mod.xray_validate({"files": ["a.ts"]}))
753
756
  # CLI error path returns raw result without "via" key
754
757
  self.assertIn("error", r)
755
758
 
756
759
  def test_codex_check_static_fallback(self):
757
- """strray_codex_check with code but bridge down falls back to static analysis."""
760
+ """xray_codex_check with code but bridge down falls back to static analysis."""
758
761
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "bridge down"}):
759
- r = json.loads(tools_mod.strray_codex_check({"code": "console.log(1)", "operation": "create"}))
762
+ r = json.loads(tools_mod.xray_codex_check({"code": "console.log(1)", "operation": "create"}))
760
763
  self.assertEqual(r["via"], "static")
761
764
  self.assertIn("basic analysis", r["note"])
762
765
 
763
766
  def test_codex_check_cli_health_error(self):
764
- """strray_codex_check no-code path: bridge error + CLI also errors."""
767
+ """xray_codex_check no-code path: bridge error + CLI also errors."""
765
768
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "no node"}):
766
- with patch.object(tools_mod, "_run_strray", return_value='{"error": "0xray not found"}'):
767
- r = json.loads(tools_mod.strray_codex_check({"operation": "create"}))
769
+ with patch.object(tools_mod, "_run_xray", return_value='{"error": "0xray not found"}'):
770
+ r = json.loads(tools_mod.xray_codex_check({"operation": "create"}))
768
771
  self.assertIn("error", r)
769
772
 
770
773
 
@@ -850,17 +853,17 @@ class TestPreToolCallEdgeCases(unittest.TestCase):
850
853
  self.assertEqual(pi._session_stats["code_operations"], 1, f"{tool} should be a code tool")
851
854
 
852
855
  @patch.object(pi, "_call_bridge")
853
- def test_unknown_tool_not_strray_mcp(self, mock_bridge):
856
+ def test_unknown_tool_not_xray_mcp(self, mock_bridge):
854
857
  """Unknown tools should be treated as native tools."""
855
858
  pi._on_pre_tool_call("some_random_tool", {}, "t1")
856
859
  self.assertEqual(pi._session_stats["native_tool_calls"], 1)
857
860
  mock_bridge.assert_not_called()
858
861
 
859
- def test_strray_validate_tool_not_treated_as_mcp(self):
860
- """strray_validate is a native tool, not an MCP tool."""
861
- pi._on_pre_tool_call("strray_validate", {}, "t1")
862
+ def test_xray_validate_tool_not_treated_as_mcp(self):
863
+ """xray_validate is a native tool, not an MCP tool."""
864
+ pi._on_pre_tool_call("xray_validate", {}, "t1")
862
865
  self.assertEqual(pi._session_stats["native_tool_calls"], 1)
863
- self.assertEqual(pi._session_stats["strray_mcp_calls"], 0)
866
+ self.assertEqual(pi._session_stats["xray_mcp_calls"], 0)
864
867
 
865
868
  @patch.object(pi, "_call_bridge", return_value={"passed": True, "qualityGate": {"passed": True, "violations": []}, "processors": {"ran": True, "success": True, "processorCount": 3, "details": [{"name": "p1", "success": True}, {"name": "p2", "success": True}, {"name": "p3", "success": False, "error": "failed"}]}})
866
869
  def test_pre_processor_partial_failure(self, mock_bridge):
@@ -875,12 +878,12 @@ class TestSlashCommandEdgeCases(unittest.TestCase):
875
878
  def test_unknown_command_defaults_to_status(self):
876
879
  """Unknown args default to status."""
877
880
  with patch.object(pi, "_call_bridge", return_value={"framework": "loaded", "version": "1.0", "components": {}}) as m:
878
- pi._strray_command("something-random")
881
+ pi._xray_command("something-random")
879
882
  m.assert_called_once_with({"command": "health"}, timeout=10)
880
883
 
881
884
  def test_case_insensitive(self):
882
885
  """Command arg is lowercased."""
883
- o = pi._strray_command(" STATS ")
886
+ o = pi._xray_command(" STATS ")
884
887
  self.assertIn("Session", o)
885
888
 
886
889
 
@@ -940,13 +943,13 @@ class TestBridgeHelperTimeoutDefault(unittest.TestCase):
940
943
  def test_custom_timeout(self):
941
944
  """_bridge_call respects custom timeout."""
942
945
  with patch("subprocess.run") as m:
943
- m.return_value = MagicMock(returncode=0, stdout='{\"ok\":true}', stderr="")
946
+ m.return_value = MagicMock(returncode=0, stdout='{"ok":true}', stderr="")
944
947
  tools_mod._bridge_call({"command": "health"}, timeout=5)
945
948
  self.assertEqual(m.call_args[1]["timeout"], 5)
946
949
 
947
950
 
948
- class TestStrrayHooksTool(unittest.TestCase):
949
- """Tests for the strray_hooks tool."""
951
+ class TestXrayHooksTool(unittest.TestCase):
952
+ """Tests for the xray_hooks tool."""
950
953
 
951
954
  def test_list_via_bridge(self):
952
955
  """list action uses bridge when available."""
@@ -954,7 +957,7 @@ class TestStrrayHooksTool(unittest.TestCase):
954
957
  "status": "ok", "action": "list",
955
958
  "hooks": {"managed": ["pre-commit"], "missing": [], "external": [], "stale": []},
956
959
  }) as m:
957
- r = json.loads(tools_mod.strray_hooks({"action": "list"}))
960
+ r = json.loads(tools_mod.xray_hooks({"action": "list"}))
958
961
  self.assertEqual(r["status"], "ok")
959
962
  self.assertEqual(r["via"], "bridge")
960
963
  m.assert_called_once()
@@ -968,7 +971,7 @@ class TestStrrayHooksTool(unittest.TestCase):
968
971
  "status": "ok", "action": "install", "installed": ["pre-commit", "post-commit"],
969
972
  "skipped": [], "errors": [],
970
973
  }) as m:
971
- r = json.loads(tools_mod.strray_hooks({"action": "install"}))
974
+ r = json.loads(tools_mod.xray_hooks({"action": "install"}))
972
975
  self.assertEqual(r["via"], "bridge")
973
976
  self.assertEqual(len(r["result"]["installed"]), 2)
974
977
 
@@ -977,14 +980,14 @@ class TestStrrayHooksTool(unittest.TestCase):
977
980
  with patch.object(tools_mod, "_bridge_call", return_value={
978
981
  "status": "ok", "action": "uninstall", "removed": ["pre-commit"], "restored": [],
979
982
  }) as m:
980
- r = json.loads(tools_mod.strray_hooks({"action": "uninstall"}))
983
+ r = json.loads(tools_mod.xray_hooks({"action": "uninstall"}))
981
984
  self.assertEqual(r["via"], "bridge")
982
985
 
983
986
  def test_bridge_error_fallback(self):
984
987
  """Falls back to file-based when bridge errors."""
985
988
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "node not found"}):
986
989
  # Without a real git repo, should return error
987
- r = json.loads(tools_mod.strray_hooks({"action": "list"}))
990
+ r = json.loads(tools_mod.xray_hooks({"action": "list"}))
988
991
  self.assertIn("via", r)
989
992
 
990
993
  def test_specific_hooks(self):
@@ -993,7 +996,7 @@ class TestStrrayHooksTool(unittest.TestCase):
993
996
  "status": "ok", "action": "list",
994
997
  "hooks": {"managed": [], "missing": ["pre-commit"], "external": [], "stale": []},
995
998
  }) as m:
996
- tools_mod.strray_hooks({"action": "list", "hooks": ["pre-commit"]})
999
+ tools_mod.xray_hooks({"action": "list", "hooks": ["pre-commit"]})
997
1000
  call_cmd = m.call_args[0][0]
998
1001
  self.assertEqual(call_cmd["hooks"], ["pre-commit"])
999
1002
 
@@ -1003,7 +1006,7 @@ class TestStrrayHooksTool(unittest.TestCase):
1003
1006
  "status": "ok", "action": "status",
1004
1007
  "hooks": {"managed": [], "missing": [], "external": [], "stale": []},
1005
1008
  }) as m:
1006
- r = json.loads(tools_mod.strray_hooks({"action": "status"}))
1009
+ r = json.loads(tools_mod.xray_hooks({"action": "status"}))
1007
1010
  self.assertEqual(r["status"], "ok")
1008
1011
  m.assert_called_once()
1009
1012
 
@@ -1013,23 +1016,23 @@ class TestStrrayHooksTool(unittest.TestCase):
1013
1016
  "status": "ok", "action": "list",
1014
1017
  "hooks": {"managed": [], "missing": [], "external": [], "stale": []},
1015
1018
  }) as m:
1016
- tools_mod.strray_hooks({})
1019
+ tools_mod.xray_hooks({})
1017
1020
  call_cmd = m.call_args[0][0]
1018
1021
  self.assertEqual(call_cmd["action"], "list")
1019
1022
 
1020
1023
 
1021
- class TestStrrayHooksSchema(unittest.TestCase):
1022
- """Tests for the STRRAY_HOOKS schema."""
1024
+ class TestXrayHooksSchema(unittest.TestCase):
1025
+ """Tests for the XRAY_HOOKS schema."""
1023
1026
 
1024
1027
  def test_schema_has_required_fields(self):
1025
- s = schemas.STRRAY_HOOKS
1026
- self.assertEqual(s["name"], "strray_hooks")
1028
+ s = schemas.XRAY_HOOKS
1029
+ self.assertEqual(s["name"], "xray_hooks")
1027
1030
  self.assertIn("action", s["parameters"]["properties"])
1028
1031
  self.assertIn("hooks", s["parameters"]["properties"])
1029
1032
  self.assertIn("action", s["parameters"]["required"])
1030
1033
 
1031
1034
  def test_action_enum(self):
1032
- s = schemas.STRRAY_HOOKS
1035
+ s = schemas.XRAY_HOOKS
1033
1036
  action = s["parameters"]["properties"]["action"]
1034
1037
  self.assertIn("install", action["enum"])
1035
1038
  self.assertIn("uninstall", action["enum"])
@@ -1037,7 +1040,7 @@ class TestStrrayHooksSchema(unittest.TestCase):
1037
1040
  self.assertIn("status", action["enum"])
1038
1041
 
1039
1042
  def test_hooks_enum(self):
1040
- s = schemas.STRRAY_HOOKS
1043
+ s = schemas.XRAY_HOOKS
1041
1044
  hooks = s["parameters"]["properties"]["hooks"]
1042
1045
  self.assertIn("pre-commit", hooks["items"]["enum"])
1043
1046
  self.assertIn("post-commit", hooks["items"]["enum"])
@@ -1045,7 +1048,7 @@ class TestStrrayHooksSchema(unittest.TestCase):
1045
1048
  self.assertIn("post-push", hooks["items"]["enum"])
1046
1049
 
1047
1050
  def test_description_mentions_hooks(self):
1048
- s = schemas.STRRAY_HOOKS
1051
+ s = schemas.XRAY_HOOKS
1049
1052
  self.assertIn("git hooks", s["description"])
1050
1053
 
1051
1054
 
@@ -1057,21 +1060,21 @@ class TestRegisterIntegrationV2_1(unittest.TestCase):
1057
1060
  pi.register(ctx)
1058
1061
  names = [c[1]["name"] for c in ctx.register_tool.call_args_list]
1059
1062
  self.assertEqual(set(names), {
1060
- "strray_validate", "strray_codex_check",
1061
- "strray_health", "strray_hooks",
1063
+ "xray_validate", "xray_codex_check",
1064
+ "xray_health", "xray_hooks",
1062
1065
  })
1063
1066
 
1064
- def test_strray_hooks_schema_wired(self):
1067
+ def test_xray_hooks_schema_wired(self):
1065
1068
  ctx = MagicMock()
1066
1069
  pi.register(ctx)
1067
1070
  sm = {c[1]["name"]: c[1]["schema"] for c in ctx.register_tool.call_args_list}
1068
- self.assertIs(sm["strray_hooks"], schemas.STRRAY_HOOKS)
1071
+ self.assertIs(sm["xray_hooks"], schemas.XRAY_HOOKS)
1069
1072
 
1070
- def test_strray_hooks_handler_wired(self):
1073
+ def test_xray_hooks_handler_wired(self):
1071
1074
  ctx = MagicMock()
1072
1075
  pi.register(ctx)
1073
1076
  hm = {c[1]["name"]: c[1]["handler"] for c in ctx.register_tool.call_args_list}
1074
- self.assertIs(hm["strray_hooks"], tools_mod.strray_hooks)
1077
+ self.assertIs(hm["xray_hooks"], tools_mod.xray_hooks)
1075
1078
 
1076
1079
  def test_registers_two_hooks(self):
1077
1080
  ctx = MagicMock()
@@ -1090,7 +1093,7 @@ class TestRegisterIntegrationV2_1(unittest.TestCase):
1090
1093
 
1091
1094
  def test_v2_2_log_message(self):
1092
1095
  ctx = MagicMock()
1093
- with self.assertLogs("strray-hermes", level="INFO") as cm:
1096
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
1094
1097
  pi.register(ctx)
1095
1098
  self.assertTrue(any("v2.2" in m for m in cm.output))
1096
1099
  self.assertTrue(any("4 tools" in m for m in cm.output))