0xray 3.0.14 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/.gitignore.default +59 -0
  2. package/.mcp.json +35 -0
  3. package/.opencode/codex.codex +1 -1
  4. package/.opencode/commands/dependency-audit.md +3 -3
  5. package/.opencode/commands/summary-logger.md +4 -4
  6. package/.opencode/enforcer-config.json +2 -2
  7. package/.opencode/hooks/post-commit +1 -1
  8. package/.opencode/init.sh +10 -4
  9. package/.plugin/plugin.json +11 -0
  10. package/AGENTS-consumer.md +46 -0
  11. package/AGENTS.md +10 -10
  12. package/LICENSE +1 -1
  13. package/README.md +8 -8
  14. package/SKILLS.md +140 -0
  15. package/dist/.gitignore.default +59 -0
  16. package/dist/AGENTS.md +10 -10
  17. package/dist/CHANGELOG.md +34 -0
  18. package/dist/LICENSE +1 -1
  19. package/dist/README.md +8 -8
  20. package/dist/SKILLS.md +140 -0
  21. package/dist/agents/index.js +3 -1
  22. package/dist/agents/registry.js +13 -0
  23. package/dist/analytics/routing-refiner.js +1 -1
  24. package/dist/architect/architectural-integrity.js +3 -3
  25. package/dist/benchmark/performance-benchmark.js +4 -3
  26. package/dist/cli/commands/antigravity-status.js +23 -23
  27. package/dist/cli/commands/credible-init.js +1 -1
  28. package/dist/cli/commands/hermes-install.js +12 -4
  29. package/dist/cli/commands/mcp-install.js +1 -1
  30. package/dist/cli/commands/openclaw-install.js +10 -1
  31. package/dist/cli/commands/opencode-install.js +22 -0
  32. package/dist/cli/commands/plugin-commands.js +2 -2
  33. package/dist/cli/commands/publish-agent.js +5 -5
  34. package/dist/cli/commands/skill-install.js +39 -2
  35. package/dist/cli/commands/status.js +8 -3
  36. package/dist/cli/commands/storyteller.js +2 -2
  37. package/dist/cli/index.js +17 -11
  38. package/dist/cli/server.js +4 -4
  39. package/dist/core/activity-logger.js +4 -4
  40. package/dist/core/agent-spawn-gate.js +1 -1
  41. package/dist/core/boot-orchestrator.js +30 -31
  42. package/dist/core/bridge.mjs +26 -26
  43. package/dist/core/codex-formatter.js +2 -3
  44. package/dist/core/codex-injector.js +7 -7
  45. package/dist/core/config-loader.js +3 -2
  46. package/dist/core/config-paths.js +38 -31
  47. package/dist/core/context-loader.js +5 -5
  48. package/dist/core/features-config.js +1 -1
  49. package/dist/core/index.js +2 -2
  50. package/dist/core/logging-config.js +7 -7
  51. package/dist/core/orchestrator.js +2 -2
  52. package/dist/core/system-prompt-generator.js +2 -2
  53. package/dist/core/xray-activation.js +42 -41
  54. package/dist/delegation/agent-delegator.js +3 -3
  55. package/dist/enforcement/enforcer-tools.js +4 -4
  56. package/dist/enforcement/loaders/codex-loader.js +2 -2
  57. package/dist/execution/opencode-cli-invoker.js +1 -1
  58. package/dist/governance/governance-service.js +1 -1
  59. package/dist/governance/llm-governance-provider.js +223 -0
  60. package/dist/index.js +6 -6
  61. package/dist/inference/deploy-verifier.js +1 -1
  62. package/dist/inference/inference-cycle.js +16 -16
  63. package/dist/integrations/base/README.md +1 -1
  64. package/dist/integrations/enforcement-gate.js +2 -2
  65. package/dist/integrations/governance/index.js +1 -1
  66. package/dist/integrations/grok/grok-cli.js +20 -6
  67. package/dist/integrations/grok/hooks/pre-tool-use.js +3 -3
  68. package/dist/integrations/grok/plugin/0xray/.mcp.json +16 -4
  69. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +2 -2
  70. package/dist/integrations/hermes-agent/__init__.py +90 -79
  71. package/dist/integrations/hermes-agent/after-install.md +14 -14
  72. package/dist/integrations/hermes-agent/bridge.mjs +60 -25
  73. package/dist/integrations/hermes-agent/plugin.yaml +5 -5
  74. package/dist/integrations/hermes-agent/schemas.py +61 -12
  75. package/dist/integrations/hermes-agent/test_plugin.py +124 -124
  76. package/dist/integrations/hermes-agent/tools.py +129 -25
  77. package/dist/integrations/openclaw/README.md +6 -6
  78. package/dist/integrations/openclaw/config.js +1 -1
  79. package/dist/integrations/openclaw/index.js +2 -2
  80. package/dist/integrations/openclaw/types.js +3 -3
  81. package/dist/integrations/plugins/plugin-integration.js +1 -1
  82. package/dist/integrations/plugins/plugin-registry.js +3 -3
  83. package/dist/mcps/architect-tools.server.js +3 -2
  84. package/dist/mcps/auto-format.server.js +2 -2
  85. package/dist/mcps/boot-orchestrator.server.js +2 -2
  86. package/dist/mcps/config/config-loader.js +40 -9
  87. package/dist/mcps/config/server-config-registry.js +10 -10
  88. package/dist/mcps/connection/mcp-connection.js +1 -1
  89. package/dist/mcps/enforcer-tools.server.js +2 -2
  90. package/dist/mcps/estimation.server.js +3 -2
  91. package/dist/mcps/framework-compliance-audit.server.js +2 -2
  92. package/dist/mcps/framework-help.server.js +9 -8
  93. package/dist/mcps/governance.server.js +8 -4
  94. package/dist/mcps/knowledge-skills/api-design.server.js +3 -2
  95. package/dist/mcps/knowledge-skills/architecture-patterns.server.js +3 -2
  96. package/dist/mcps/knowledge-skills/bug-triage-specialist.server.js +3 -3
  97. package/dist/mcps/knowledge-skills/code-analyzer.server.js +2 -2
  98. package/dist/mcps/knowledge-skills/code-review.server.js +14 -39
  99. package/dist/mcps/knowledge-skills/content-creator.server.js +3 -3
  100. package/dist/mcps/knowledge-skills/database-design.server.js +2 -2
  101. package/dist/mcps/knowledge-skills/devops-deployment.server.js +3 -2
  102. package/dist/mcps/knowledge-skills/git-workflow.server.js +3 -2
  103. package/dist/mcps/knowledge-skills/growth-strategist.server.js +3 -3
  104. package/dist/mcps/knowledge-skills/log-monitor.server.js +3 -3
  105. package/dist/mcps/knowledge-skills/mobile-development.server.js +3 -3
  106. package/dist/mcps/knowledge-skills/multimodal-looker.server.js +3 -3
  107. package/dist/mcps/knowledge-skills/performance-optimization.server.js +2 -2
  108. package/dist/mcps/knowledge-skills/project-analysis.server.js +2 -2
  109. package/dist/mcps/knowledge-skills/refactoring-strategies.server.js +3 -2
  110. package/dist/mcps/knowledge-skills/security-audit.server.js +14 -44
  111. package/dist/mcps/knowledge-skills/seo-consultant.server.js +3 -2
  112. package/dist/mcps/knowledge-skills/session-management.server.js +3 -2
  113. package/dist/mcps/knowledge-skills/skill-invocation.server.js +105 -3
  114. package/dist/mcps/knowledge-skills/strategist.server.js +3 -3
  115. package/dist/mcps/knowledge-skills/tech-writer.server.js +3 -3
  116. package/dist/mcps/knowledge-skills/testing-best-practices.server.js +3 -2
  117. package/dist/mcps/knowledge-skills/testing-strategy.server.js +2 -2
  118. package/dist/mcps/knowledge-skills/ui-ux-design.server.js +5 -4
  119. package/dist/mcps/lint.server.js +4 -4
  120. package/dist/mcps/mcp-client.js +5 -5
  121. package/dist/mcps/model-health-check.server.js +3 -2
  122. package/dist/mcps/orchestrator/server.js +4 -3
  123. package/dist/mcps/orchestrator.server.js +2 -1
  124. package/dist/mcps/performance-analysis.server.js +2 -2
  125. package/dist/mcps/processor-pipeline.server.js +3 -2
  126. package/dist/mcps/registry.json +1 -1
  127. package/dist/mcps/researcher.server.js +14 -39
  128. package/dist/mcps/security-scan.server.js +2 -2
  129. package/dist/mcps/simulation/server-simulations.js +3 -3
  130. package/dist/mcps/state-manager.server.js +5 -5
  131. package/dist/metrics/agent-metrics.js +1 -1
  132. package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +4 -4
  133. package/dist/orchestrator/intelligent-commit-batcher.js +2 -3
  134. package/dist/orchestrator/multi-agent-orchestration-coordinator.js +9 -9
  135. package/dist/orchestrator/orchestrator.js +9 -8
  136. package/dist/orchestrator/universal-registry-bridge.js +4 -4
  137. package/dist/postprocessor/PostProcessor.js +9 -9
  138. package/dist/postprocessor/services/RegressionAnalysisService.js +4 -4
  139. package/dist/postprocessor/triggers/GitHookTrigger.js +18 -18
  140. package/dist/processors/implementations/agents-md-validation-processor.js +2 -2
  141. package/dist/processors/implementations/inference-improvement-processor.js +1 -2
  142. package/dist/processors/implementations/log-protection-processor.js +2 -2
  143. package/dist/processors/implementations/publish-preflight-processor.js +2 -3
  144. package/dist/processors/implementations/regression-testing-processor.js +2 -2
  145. package/dist/processors/implementations/session-summary-processor.js +2 -2
  146. package/dist/processors/implementations/storytelling-trigger-processor.js +1 -2
  147. package/dist/public/about.html +6 -6
  148. package/dist/public/enterprise.html +1 -1
  149. package/dist/public/features.html +2 -2
  150. package/dist/public/index.html +4 -4
  151. package/dist/reporting/report-formatter.js +1 -1
  152. package/dist/scripts/activate-kernel-pipeline.js +4 -4
  153. package/dist/scripts/pre-command +1 -1
  154. package/dist/services/inference-tuner.js +2 -2
  155. package/dist/skills/api-design/SKILL.md +1 -1
  156. package/dist/skills/architect-tools/SKILL.md +1 -1
  157. package/dist/skills/architecture-patterns/SKILL.md +1 -1
  158. package/dist/skills/auto-format/SKILL.md +1 -1
  159. package/dist/skills/boot-orchestrator/SKILL.md +1 -1
  160. package/dist/skills/bug-triage/SKILL.md +1 -1
  161. package/dist/skills/code-analyzer/SKILL.md +1 -1
  162. package/dist/skills/code-review/SKILL.md +1 -1
  163. package/dist/skills/enforcer/SKILL.md +1 -1
  164. package/dist/skills/framework-compliance-audit/SKILL.md +1 -1
  165. package/dist/skills/git-workflow/SKILL.md +1 -1
  166. package/dist/skills/hermes-agent/SKILL.md +42 -42
  167. package/dist/skills/inference-improve/SKILL.md +1 -1
  168. package/dist/skills/lint/SKILL.md +1 -1
  169. package/dist/skills/log-monitor/SKILL.md +1 -1
  170. package/dist/skills/model-health-check/SKILL.md +1 -1
  171. package/dist/skills/multimodal-looker/SKILL.md +1 -1
  172. package/dist/skills/orchestrator/SKILL.md +1 -1
  173. package/dist/skills/performance-analysis/SKILL.md +1 -1
  174. package/dist/skills/performance-optimization/SKILL.md +1 -1
  175. package/dist/skills/processor-pipeline/SKILL.md +1 -1
  176. package/dist/skills/project-analysis/SKILL.md +1 -1
  177. package/dist/skills/refactoring-strategies/SKILL.md +1 -1
  178. package/dist/skills/registry.json +1 -1
  179. package/dist/skills/researcher/SKILL.md +1 -1
  180. package/dist/skills/security-audit/SKILL.md +1 -1
  181. package/dist/skills/security-scan/SKILL.md +1 -1
  182. package/dist/skills/session-management/SKILL.md +1 -1
  183. package/dist/skills/state-manager/SKILL.md +1 -1
  184. package/dist/skills/storyteller/SKILL.md +1 -1
  185. package/dist/skills/testing-best-practices/SKILL.md +1 -1
  186. package/dist/skills/testing-strategy/SKILL.md +1 -1
  187. package/dist/skills/ui-ux-design/SKILL.md +1 -1
  188. package/dist/state/index.js +1 -1
  189. package/dist/state/state-manager.js +3 -4
  190. package/dist/utils/import-resolver.js +1 -1
  191. package/dist/utils/path-resolver.js +2 -2
  192. package/dist/utils/token-manager.js +1 -1
  193. package/dist/validation/estimation-validator.js +1 -1
  194. package/package.json +10 -3
  195. package/scripts/hooks/pre-command +1 -1
  196. package/scripts/hooks/run-hook.js +2 -2
  197. package/scripts/node/auto-reflection-generator.mjs +30 -36
  198. package/scripts/node/govern-reflection.mjs +1 -1
  199. package/scripts/node/postinstall.cjs +37 -2
  200. package/scripts/node/pre-publish-guard.js +13 -9
  201. package/scripts/node/setup-xai-oauth.mjs +244 -0
  202. package/scripts/node/universal-version-manager.js +8 -8
  203. package/scripts/node/version-manager.mjs +1 -1
  204. package/src/integrations/grok/plugin/0xray/.mcp.json +16 -4
  205. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +2 -2
  206. package/src/mcps/architect-tools.server.ts +2 -2
  207. package/src/mcps/auto-format.server.ts +2 -2
  208. package/src/mcps/boot-orchestrator.server.ts +2 -2
  209. package/src/mcps/config/__tests__/config-loader.test.ts +29 -33
  210. package/src/mcps/config/__tests__/server-config-registry.test.ts +12 -12
  211. package/src/mcps/config/config-loader.ts +50 -11
  212. package/src/mcps/config/server-config-registry.ts +10 -10
  213. package/src/mcps/connection/mcp-connection.ts +1 -1
  214. package/src/mcps/enforcer-tools.server.ts +2 -2
  215. package/src/mcps/estimation.server.ts +3 -2
  216. package/src/mcps/framework-compliance-audit.server.ts +2 -2
  217. package/src/mcps/framework-help.server.ts +9 -8
  218. package/src/mcps/governance.server.ts +8 -3
  219. package/src/mcps/knowledge-skills/api-design.server.ts +3 -2
  220. package/src/mcps/knowledge-skills/architecture-patterns.server.ts +2 -2
  221. package/src/mcps/knowledge-skills/bug-triage-specialist.server.ts +2 -2
  222. package/src/mcps/knowledge-skills/code-analyzer.server.ts +2 -2
  223. package/src/mcps/knowledge-skills/code-review.server.ts +21 -36
  224. package/src/mcps/knowledge-skills/content-creator.server.ts +3 -3
  225. package/src/mcps/knowledge-skills/database-design.server.ts +2 -2
  226. package/src/mcps/knowledge-skills/devops-deployment.server.ts +3 -2
  227. package/src/mcps/knowledge-skills/git-workflow.server.ts +3 -2
  228. package/src/mcps/knowledge-skills/growth-strategist.server.ts +3 -3
  229. package/src/mcps/knowledge-skills/log-monitor.server.ts +3 -3
  230. package/src/mcps/knowledge-skills/mobile-development.server.ts +3 -3
  231. package/src/mcps/knowledge-skills/multimodal-looker.server.ts +3 -3
  232. package/src/mcps/knowledge-skills/performance-optimization.server.ts +2 -2
  233. package/src/mcps/knowledge-skills/project-analysis.server.ts +2 -2
  234. package/src/mcps/knowledge-skills/refactoring-strategies.server.ts +3 -2
  235. package/src/mcps/knowledge-skills/security-audit.server.ts +21 -40
  236. package/src/mcps/knowledge-skills/seo-consultant.server.ts +3 -2
  237. package/src/mcps/knowledge-skills/session-management.server.ts +3 -2
  238. package/src/mcps/knowledge-skills/skill-invocation.server.ts +113 -3
  239. package/src/mcps/knowledge-skills/strategist.server.ts +3 -3
  240. package/src/mcps/knowledge-skills/tech-writer.server.ts +3 -3
  241. package/src/mcps/knowledge-skills/testing-best-practices.server.ts +3 -2
  242. package/src/mcps/knowledge-skills/testing-strategy.server.ts +2 -2
  243. package/src/mcps/knowledge-skills/ui-ux-design.server.ts +5 -4
  244. package/src/mcps/lint.server.ts +4 -4
  245. package/src/mcps/mcp-client.ts +5 -5
  246. package/src/mcps/model-health-check.server.ts +2 -2
  247. package/src/mcps/orchestrator/server.ts +4 -3
  248. package/src/mcps/orchestrator.server.ts +2 -1
  249. package/src/mcps/performance-analysis.server.ts +2 -2
  250. package/src/mcps/processor-pipeline.server.ts +3 -2
  251. package/src/mcps/registry.json +1 -1
  252. package/src/mcps/researcher.server.ts +21 -36
  253. package/src/mcps/security-scan.server.ts +2 -2
  254. package/src/mcps/simulation/server-simulations.ts +3 -3
  255. package/src/mcps/state-manager.server.ts +5 -5
  256. package/src/opencode/codex.codex +1 -1
  257. package/src/opencode/commands/dependency-audit.md +3 -3
  258. package/src/opencode/commands/summary-logger.md +4 -4
  259. package/src/opencode/enforcer-config.json +2 -2
  260. package/src/skills/api-design/SKILL.md +1 -1
  261. package/src/skills/architect-tools/SKILL.md +1 -1
  262. package/src/skills/architecture-patterns/SKILL.md +1 -1
  263. package/src/skills/auto-format/SKILL.md +1 -1
  264. package/src/skills/boot-orchestrator/SKILL.md +1 -1
  265. package/src/skills/bug-triage/SKILL.md +1 -1
  266. package/src/skills/code-analyzer/SKILL.md +1 -1
  267. package/src/skills/code-review/SKILL.md +1 -1
  268. package/src/skills/enforcer/SKILL.md +1 -1
  269. package/src/skills/framework-compliance-audit/SKILL.md +1 -1
  270. package/src/skills/git-workflow/SKILL.md +1 -1
  271. package/src/skills/hermes-agent/SKILL.md +42 -42
  272. package/src/skills/inference-improve/SKILL.md +1 -1
  273. package/src/skills/lint/SKILL.md +1 -1
  274. package/src/skills/log-monitor/SKILL.md +1 -1
  275. package/src/skills/model-health-check/SKILL.md +1 -1
  276. package/src/skills/multimodal-looker/SKILL.md +1 -1
  277. package/src/skills/orchestrator/SKILL.md +1 -1
  278. package/src/skills/performance-analysis/SKILL.md +1 -1
  279. package/src/skills/performance-optimization/SKILL.md +1 -1
  280. package/src/skills/processor-pipeline/SKILL.md +1 -1
  281. package/src/skills/project-analysis/SKILL.md +1 -1
  282. package/src/skills/refactoring-strategies/SKILL.md +1 -1
  283. package/src/skills/registry.json +1 -1
  284. package/src/skills/researcher/SKILL.md +1 -1
  285. package/src/skills/security-audit/SKILL.md +1 -1
  286. package/src/skills/security-scan/SKILL.md +1 -1
  287. package/src/skills/session-management/SKILL.md +1 -1
  288. package/src/skills/state-manager/SKILL.md +1 -1
  289. package/src/skills/storyteller/SKILL.md +1 -1
  290. package/src/skills/testing-best-practices/SKILL.md +1 -1
  291. package/src/skills/testing-strategy/SKILL.md +1 -1
  292. package/src/skills/ui-ux-design/SKILL.md +1 -1
  293. package/xray/agents_template.md +46 -0
  294. package/xray/codex.json +598 -0
  295. package/dist/plugin/xray-codex-injection.js +0 -857
  296. /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 Xray 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
+ """Xray 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"], "xray-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
 
@@ -656,9 +656,9 @@ class TestLiveBridge(unittest.TestCase):
656
656
  if not bridge_path.exists():
657
657
  self.skipTest("bridge.mjs not built yet")
658
658
 
659
- # 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
659
+ # Use xray dev repo as project root (has package.json)
660
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "xray")
661
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
662
662
 
663
663
  r = subprocess.run(
664
664
  ["node", str(bridge_path), "health", "--cwd", cwd],
@@ -675,8 +675,8 @@ class TestLiveBridge(unittest.TestCase):
675
675
  if not bridge_path.exists():
676
676
  self.skipTest("bridge.mjs not built yet")
677
677
 
678
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
679
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
678
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "xray")
679
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
680
680
 
681
681
  r = subprocess.run(
682
682
  ["node", str(bridge_path), "stats", "--cwd", cwd],
@@ -692,8 +692,8 @@ class TestLiveBridge(unittest.TestCase):
692
692
  if not bridge_path.exists():
693
693
  self.skipTest("bridge.mjs not built yet")
694
694
 
695
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
696
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
695
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "xray")
696
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
697
697
 
698
698
  r = subprocess.run(
699
699
  ["node", str(bridge_path), "validate", "--cwd", cwd,
@@ -710,8 +710,8 @@ class TestLiveBridge(unittest.TestCase):
710
710
  if not bridge_path.exists():
711
711
  self.skipTest("bridge.mjs not built yet")
712
712
 
713
- strray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "stringray")
714
- cwd = strray_root if Path(strray_root).exists() else PLUGIN_DIR
713
+ xray_root = str(Path(PLUGIN_DIR).parent.parent.parent / "dev" / "xray")
714
+ cwd = xray_root if Path(xray_root).exists() else PLUGIN_DIR
715
715
 
716
716
  r = subprocess.run(
717
717
  ["node", str(bridge_path), "validate", "--cwd", cwd,
@@ -739,32 +739,32 @@ class TestBridgeErrorPaths(unittest.TestCase):
739
739
  r = tools_mod._bridge_call({"command": "health"})
740
740
  self.assertIn("error", r)
741
741
 
742
- def test_bridge_generic_exception_in_run_strray(self):
743
- """_run_strray catches non-standard exceptions."""
742
+ def test_bridge_generic_exception_in_run_xray(self):
743
+ """_run_xray catches non-standard exceptions."""
744
744
  with patch("subprocess.run", side_effect=RuntimeError("unexpected")):
745
- r = json.loads(tools_mod._run_strray(["health"]))
745
+ r = json.loads(tools_mod._run_xray(["health"]))
746
746
  self.assertIn("unexpected", r["error"])
747
747
 
748
748
  def test_validate_cli_fallback_error(self):
749
- """strray_validate CLI fallback when CLI returns an error JSON."""
749
+ """xray_validate CLI fallback when CLI returns an error JSON."""
750
750
  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"]}))
751
+ with patch.object(tools_mod, "_run_xray", return_value='{"error": "validation failed"}'):
752
+ r = json.loads(tools_mod.xray_validate({"files": ["a.ts"]}))
753
753
  # CLI error path returns raw result without "via" key
754
754
  self.assertIn("error", r)
755
755
 
756
756
  def test_codex_check_static_fallback(self):
757
- """strray_codex_check with code but bridge down falls back to static analysis."""
757
+ """xray_codex_check with code but bridge down falls back to static analysis."""
758
758
  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"}))
759
+ r = json.loads(tools_mod.xray_codex_check({"code": "console.log(1)", "operation": "create"}))
760
760
  self.assertEqual(r["via"], "static")
761
761
  self.assertIn("basic analysis", r["note"])
762
762
 
763
763
  def test_codex_check_cli_health_error(self):
764
- """strray_codex_check no-code path: bridge error + CLI also errors."""
764
+ """xray_codex_check no-code path: bridge error + CLI also errors."""
765
765
  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"}))
766
+ with patch.object(tools_mod, "_run_xray", return_value='{"error": "0xray not found"}'):
767
+ r = json.loads(tools_mod.xray_codex_check({"operation": "create"}))
768
768
  self.assertIn("error", r)
769
769
 
770
770
 
@@ -850,17 +850,17 @@ class TestPreToolCallEdgeCases(unittest.TestCase):
850
850
  self.assertEqual(pi._session_stats["code_operations"], 1, f"{tool} should be a code tool")
851
851
 
852
852
  @patch.object(pi, "_call_bridge")
853
- def test_unknown_tool_not_strray_mcp(self, mock_bridge):
853
+ def test_unknown_tool_not_xray_mcp(self, mock_bridge):
854
854
  """Unknown tools should be treated as native tools."""
855
855
  pi._on_pre_tool_call("some_random_tool", {}, "t1")
856
856
  self.assertEqual(pi._session_stats["native_tool_calls"], 1)
857
857
  mock_bridge.assert_not_called()
858
858
 
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")
859
+ def test_xray_validate_tool_not_treated_as_mcp(self):
860
+ """xray_validate is a native tool, not an MCP tool."""
861
+ pi._on_pre_tool_call("xray_validate", {}, "t1")
862
862
  self.assertEqual(pi._session_stats["native_tool_calls"], 1)
863
- self.assertEqual(pi._session_stats["strray_mcp_calls"], 0)
863
+ self.assertEqual(pi._session_stats["xray_mcp_calls"], 0)
864
864
 
865
865
  @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
866
  def test_pre_processor_partial_failure(self, mock_bridge):
@@ -875,12 +875,12 @@ class TestSlashCommandEdgeCases(unittest.TestCase):
875
875
  def test_unknown_command_defaults_to_status(self):
876
876
  """Unknown args default to status."""
877
877
  with patch.object(pi, "_call_bridge", return_value={"framework": "loaded", "version": "1.0", "components": {}}) as m:
878
- pi._strray_command("something-random")
878
+ pi._xray_command("something-random")
879
879
  m.assert_called_once_with({"command": "health"}, timeout=10)
880
880
 
881
881
  def test_case_insensitive(self):
882
882
  """Command arg is lowercased."""
883
- o = pi._strray_command(" STATS ")
883
+ o = pi._xray_command(" STATS ")
884
884
  self.assertIn("Session", o)
885
885
 
886
886
 
@@ -945,8 +945,8 @@ class TestBridgeHelperTimeoutDefault(unittest.TestCase):
945
945
  self.assertEqual(m.call_args[1]["timeout"], 5)
946
946
 
947
947
 
948
- class TestStrrayHooksTool(unittest.TestCase):
949
- """Tests for the strray_hooks tool."""
948
+ class TestXrayHooksTool(unittest.TestCase):
949
+ """Tests for the xray_hooks tool."""
950
950
 
951
951
  def test_list_via_bridge(self):
952
952
  """list action uses bridge when available."""
@@ -954,7 +954,7 @@ class TestStrrayHooksTool(unittest.TestCase):
954
954
  "status": "ok", "action": "list",
955
955
  "hooks": {"managed": ["pre-commit"], "missing": [], "external": [], "stale": []},
956
956
  }) as m:
957
- r = json.loads(tools_mod.strray_hooks({"action": "list"}))
957
+ r = json.loads(tools_mod.xray_hooks({"action": "list"}))
958
958
  self.assertEqual(r["status"], "ok")
959
959
  self.assertEqual(r["via"], "bridge")
960
960
  m.assert_called_once()
@@ -968,7 +968,7 @@ class TestStrrayHooksTool(unittest.TestCase):
968
968
  "status": "ok", "action": "install", "installed": ["pre-commit", "post-commit"],
969
969
  "skipped": [], "errors": [],
970
970
  }) as m:
971
- r = json.loads(tools_mod.strray_hooks({"action": "install"}))
971
+ r = json.loads(tools_mod.xray_hooks({"action": "install"}))
972
972
  self.assertEqual(r["via"], "bridge")
973
973
  self.assertEqual(len(r["result"]["installed"]), 2)
974
974
 
@@ -977,14 +977,14 @@ class TestStrrayHooksTool(unittest.TestCase):
977
977
  with patch.object(tools_mod, "_bridge_call", return_value={
978
978
  "status": "ok", "action": "uninstall", "removed": ["pre-commit"], "restored": [],
979
979
  }) as m:
980
- r = json.loads(tools_mod.strray_hooks({"action": "uninstall"}))
980
+ r = json.loads(tools_mod.xray_hooks({"action": "uninstall"}))
981
981
  self.assertEqual(r["via"], "bridge")
982
982
 
983
983
  def test_bridge_error_fallback(self):
984
984
  """Falls back to file-based when bridge errors."""
985
985
  with patch.object(tools_mod, "_bridge_call", return_value={"error": "node not found"}):
986
986
  # Without a real git repo, should return error
987
- r = json.loads(tools_mod.strray_hooks({"action": "list"}))
987
+ r = json.loads(tools_mod.xray_hooks({"action": "list"}))
988
988
  self.assertIn("via", r)
989
989
 
990
990
  def test_specific_hooks(self):
@@ -993,7 +993,7 @@ class TestStrrayHooksTool(unittest.TestCase):
993
993
  "status": "ok", "action": "list",
994
994
  "hooks": {"managed": [], "missing": ["pre-commit"], "external": [], "stale": []},
995
995
  }) as m:
996
- tools_mod.strray_hooks({"action": "list", "hooks": ["pre-commit"]})
996
+ tools_mod.xray_hooks({"action": "list", "hooks": ["pre-commit"]})
997
997
  call_cmd = m.call_args[0][0]
998
998
  self.assertEqual(call_cmd["hooks"], ["pre-commit"])
999
999
 
@@ -1003,7 +1003,7 @@ class TestStrrayHooksTool(unittest.TestCase):
1003
1003
  "status": "ok", "action": "status",
1004
1004
  "hooks": {"managed": [], "missing": [], "external": [], "stale": []},
1005
1005
  }) as m:
1006
- r = json.loads(tools_mod.strray_hooks({"action": "status"}))
1006
+ r = json.loads(tools_mod.xray_hooks({"action": "status"}))
1007
1007
  self.assertEqual(r["status"], "ok")
1008
1008
  m.assert_called_once()
1009
1009
 
@@ -1013,23 +1013,23 @@ class TestStrrayHooksTool(unittest.TestCase):
1013
1013
  "status": "ok", "action": "list",
1014
1014
  "hooks": {"managed": [], "missing": [], "external": [], "stale": []},
1015
1015
  }) as m:
1016
- tools_mod.strray_hooks({})
1016
+ tools_mod.xray_hooks({})
1017
1017
  call_cmd = m.call_args[0][0]
1018
1018
  self.assertEqual(call_cmd["action"], "list")
1019
1019
 
1020
1020
 
1021
- class TestStrrayHooksSchema(unittest.TestCase):
1022
- """Tests for the STRRAY_HOOKS schema."""
1021
+ class TestXrayHooksSchema(unittest.TestCase):
1022
+ """Tests for the XRAY_HOOKS schema."""
1023
1023
 
1024
1024
  def test_schema_has_required_fields(self):
1025
- s = schemas.STRRAY_HOOKS
1026
- self.assertEqual(s["name"], "strray_hooks")
1025
+ s = schemas.XRAY_HOOKS
1026
+ self.assertEqual(s["name"], "xray_hooks")
1027
1027
  self.assertIn("action", s["parameters"]["properties"])
1028
1028
  self.assertIn("hooks", s["parameters"]["properties"])
1029
1029
  self.assertIn("action", s["parameters"]["required"])
1030
1030
 
1031
1031
  def test_action_enum(self):
1032
- s = schemas.STRRAY_HOOKS
1032
+ s = schemas.XRAY_HOOKS
1033
1033
  action = s["parameters"]["properties"]["action"]
1034
1034
  self.assertIn("install", action["enum"])
1035
1035
  self.assertIn("uninstall", action["enum"])
@@ -1037,7 +1037,7 @@ class TestStrrayHooksSchema(unittest.TestCase):
1037
1037
  self.assertIn("status", action["enum"])
1038
1038
 
1039
1039
  def test_hooks_enum(self):
1040
- s = schemas.STRRAY_HOOKS
1040
+ s = schemas.XRAY_HOOKS
1041
1041
  hooks = s["parameters"]["properties"]["hooks"]
1042
1042
  self.assertIn("pre-commit", hooks["items"]["enum"])
1043
1043
  self.assertIn("post-commit", hooks["items"]["enum"])
@@ -1045,7 +1045,7 @@ class TestStrrayHooksSchema(unittest.TestCase):
1045
1045
  self.assertIn("post-push", hooks["items"]["enum"])
1046
1046
 
1047
1047
  def test_description_mentions_hooks(self):
1048
- s = schemas.STRRAY_HOOKS
1048
+ s = schemas.XRAY_HOOKS
1049
1049
  self.assertIn("git hooks", s["description"])
1050
1050
 
1051
1051
 
@@ -1057,21 +1057,21 @@ class TestRegisterIntegrationV2_1(unittest.TestCase):
1057
1057
  pi.register(ctx)
1058
1058
  names = [c[1]["name"] for c in ctx.register_tool.call_args_list]
1059
1059
  self.assertEqual(set(names), {
1060
- "strray_validate", "strray_codex_check",
1061
- "strray_health", "strray_hooks",
1060
+ "xray_validate", "xray_codex_check",
1061
+ "xray_health", "xray_hooks",
1062
1062
  })
1063
1063
 
1064
- def test_strray_hooks_schema_wired(self):
1064
+ def test_xray_hooks_schema_wired(self):
1065
1065
  ctx = MagicMock()
1066
1066
  pi.register(ctx)
1067
1067
  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)
1068
+ self.assertIs(sm["xray_hooks"], schemas.XRAY_HOOKS)
1069
1069
 
1070
- def test_strray_hooks_handler_wired(self):
1070
+ def test_xray_hooks_handler_wired(self):
1071
1071
  ctx = MagicMock()
1072
1072
  pi.register(ctx)
1073
1073
  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)
1074
+ self.assertIs(hm["xray_hooks"], tools_mod.xray_hooks)
1075
1075
 
1076
1076
  def test_registers_two_hooks(self):
1077
1077
  ctx = MagicMock()
@@ -1090,7 +1090,7 @@ class TestRegisterIntegrationV2_1(unittest.TestCase):
1090
1090
 
1091
1091
  def test_v2_2_log_message(self):
1092
1092
  ctx = MagicMock()
1093
- with self.assertLogs("strray-hermes", level="INFO") as cm:
1093
+ with self.assertLogs("xray-hermes", level="INFO") as cm:
1094
1094
  pi.register(ctx)
1095
1095
  self.assertTrue(any("v2.2" in m for m in cm.output))
1096
1096
  self.assertTrue(any("4 tools" in m for m in cm.output))