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.
- package/.gitignore.default +59 -0
- package/.mcp.json +35 -0
- package/.opencode/codex.codex +1 -1
- package/.opencode/commands/dependency-audit.md +3 -3
- package/.opencode/commands/summary-logger.md +4 -4
- package/.opencode/enforcer-config.json +2 -2
- package/.opencode/hooks/post-commit +1 -1
- package/.opencode/init.sh +10 -4
- package/.plugin/plugin.json +11 -0
- package/AGENTS-consumer.md +46 -0
- package/AGENTS.md +10 -10
- package/LICENSE +1 -1
- package/README.md +8 -8
- package/SKILLS.md +140 -0
- package/dist/.gitignore.default +59 -0
- package/dist/AGENTS.md +10 -10
- package/dist/CHANGELOG.md +34 -0
- package/dist/LICENSE +1 -1
- package/dist/README.md +8 -8
- package/dist/SKILLS.md +140 -0
- package/dist/agents/index.js +3 -1
- package/dist/agents/registry.js +13 -0
- package/dist/analytics/routing-refiner.js +1 -1
- package/dist/architect/architectural-integrity.js +3 -3
- package/dist/benchmark/performance-benchmark.js +4 -3
- package/dist/cli/commands/antigravity-status.js +23 -23
- package/dist/cli/commands/credible-init.js +1 -1
- package/dist/cli/commands/hermes-install.js +12 -4
- package/dist/cli/commands/mcp-install.js +1 -1
- package/dist/cli/commands/openclaw-install.js +10 -1
- package/dist/cli/commands/opencode-install.js +22 -0
- package/dist/cli/commands/plugin-commands.js +2 -2
- package/dist/cli/commands/publish-agent.js +5 -5
- package/dist/cli/commands/skill-install.js +39 -2
- package/dist/cli/commands/status.js +8 -3
- package/dist/cli/commands/storyteller.js +2 -2
- package/dist/cli/index.js +17 -11
- package/dist/cli/server.js +4 -4
- package/dist/core/activity-logger.js +4 -4
- package/dist/core/agent-spawn-gate.js +1 -1
- package/dist/core/boot-orchestrator.js +30 -31
- package/dist/core/bridge.mjs +26 -26
- package/dist/core/codex-formatter.js +2 -3
- package/dist/core/codex-injector.js +7 -7
- package/dist/core/config-loader.js +3 -2
- package/dist/core/config-paths.js +38 -31
- package/dist/core/context-loader.js +5 -5
- package/dist/core/features-config.js +1 -1
- package/dist/core/index.js +2 -2
- package/dist/core/logging-config.js +7 -7
- package/dist/core/orchestrator.js +2 -2
- package/dist/core/system-prompt-generator.js +2 -2
- package/dist/core/xray-activation.js +42 -41
- package/dist/delegation/agent-delegator.js +3 -3
- package/dist/enforcement/enforcer-tools.js +4 -4
- package/dist/enforcement/loaders/codex-loader.js +2 -2
- package/dist/execution/opencode-cli-invoker.js +1 -1
- package/dist/governance/governance-service.js +1 -1
- package/dist/governance/llm-governance-provider.js +223 -0
- package/dist/index.js +6 -6
- package/dist/inference/deploy-verifier.js +1 -1
- package/dist/inference/inference-cycle.js +16 -16
- package/dist/integrations/base/README.md +1 -1
- package/dist/integrations/enforcement-gate.js +2 -2
- package/dist/integrations/governance/index.js +1 -1
- package/dist/integrations/grok/grok-cli.js +20 -6
- package/dist/integrations/grok/hooks/pre-tool-use.js +3 -3
- package/dist/integrations/grok/plugin/0xray/.mcp.json +16 -4
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +2 -2
- package/dist/integrations/hermes-agent/__init__.py +90 -79
- package/dist/integrations/hermes-agent/after-install.md +14 -14
- package/dist/integrations/hermes-agent/bridge.mjs +60 -25
- package/dist/integrations/hermes-agent/plugin.yaml +5 -5
- package/dist/integrations/hermes-agent/schemas.py +61 -12
- package/dist/integrations/hermes-agent/test_plugin.py +124 -124
- package/dist/integrations/hermes-agent/tools.py +129 -25
- package/dist/integrations/openclaw/README.md +6 -6
- package/dist/integrations/openclaw/config.js +1 -1
- package/dist/integrations/openclaw/index.js +2 -2
- package/dist/integrations/openclaw/types.js +3 -3
- package/dist/integrations/plugins/plugin-integration.js +1 -1
- package/dist/integrations/plugins/plugin-registry.js +3 -3
- package/dist/mcps/architect-tools.server.js +3 -2
- package/dist/mcps/auto-format.server.js +2 -2
- package/dist/mcps/boot-orchestrator.server.js +2 -2
- package/dist/mcps/config/config-loader.js +40 -9
- package/dist/mcps/config/server-config-registry.js +10 -10
- package/dist/mcps/connection/mcp-connection.js +1 -1
- package/dist/mcps/enforcer-tools.server.js +2 -2
- package/dist/mcps/estimation.server.js +3 -2
- package/dist/mcps/framework-compliance-audit.server.js +2 -2
- package/dist/mcps/framework-help.server.js +9 -8
- package/dist/mcps/governance.server.js +8 -4
- package/dist/mcps/knowledge-skills/api-design.server.js +3 -2
- package/dist/mcps/knowledge-skills/architecture-patterns.server.js +3 -2
- package/dist/mcps/knowledge-skills/bug-triage-specialist.server.js +3 -3
- package/dist/mcps/knowledge-skills/code-analyzer.server.js +2 -2
- package/dist/mcps/knowledge-skills/code-review.server.js +14 -39
- package/dist/mcps/knowledge-skills/content-creator.server.js +3 -3
- package/dist/mcps/knowledge-skills/database-design.server.js +2 -2
- package/dist/mcps/knowledge-skills/devops-deployment.server.js +3 -2
- package/dist/mcps/knowledge-skills/git-workflow.server.js +3 -2
- package/dist/mcps/knowledge-skills/growth-strategist.server.js +3 -3
- package/dist/mcps/knowledge-skills/log-monitor.server.js +3 -3
- package/dist/mcps/knowledge-skills/mobile-development.server.js +3 -3
- package/dist/mcps/knowledge-skills/multimodal-looker.server.js +3 -3
- package/dist/mcps/knowledge-skills/performance-optimization.server.js +2 -2
- package/dist/mcps/knowledge-skills/project-analysis.server.js +2 -2
- package/dist/mcps/knowledge-skills/refactoring-strategies.server.js +3 -2
- package/dist/mcps/knowledge-skills/security-audit.server.js +14 -44
- package/dist/mcps/knowledge-skills/seo-consultant.server.js +3 -2
- package/dist/mcps/knowledge-skills/session-management.server.js +3 -2
- package/dist/mcps/knowledge-skills/skill-invocation.server.js +105 -3
- package/dist/mcps/knowledge-skills/strategist.server.js +3 -3
- package/dist/mcps/knowledge-skills/tech-writer.server.js +3 -3
- package/dist/mcps/knowledge-skills/testing-best-practices.server.js +3 -2
- package/dist/mcps/knowledge-skills/testing-strategy.server.js +2 -2
- package/dist/mcps/knowledge-skills/ui-ux-design.server.js +5 -4
- package/dist/mcps/lint.server.js +4 -4
- package/dist/mcps/mcp-client.js +5 -5
- package/dist/mcps/model-health-check.server.js +3 -2
- package/dist/mcps/orchestrator/server.js +4 -3
- package/dist/mcps/orchestrator.server.js +2 -1
- package/dist/mcps/performance-analysis.server.js +2 -2
- package/dist/mcps/processor-pipeline.server.js +3 -2
- package/dist/mcps/registry.json +1 -1
- package/dist/mcps/researcher.server.js +14 -39
- package/dist/mcps/security-scan.server.js +2 -2
- package/dist/mcps/simulation/server-simulations.js +3 -3
- package/dist/mcps/state-manager.server.js +5 -5
- package/dist/metrics/agent-metrics.js +1 -1
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +4 -4
- package/dist/orchestrator/intelligent-commit-batcher.js +2 -3
- package/dist/orchestrator/multi-agent-orchestration-coordinator.js +9 -9
- package/dist/orchestrator/orchestrator.js +9 -8
- package/dist/orchestrator/universal-registry-bridge.js +4 -4
- package/dist/postprocessor/PostProcessor.js +9 -9
- package/dist/postprocessor/services/RegressionAnalysisService.js +4 -4
- package/dist/postprocessor/triggers/GitHookTrigger.js +18 -18
- package/dist/processors/implementations/agents-md-validation-processor.js +2 -2
- package/dist/processors/implementations/inference-improvement-processor.js +1 -2
- package/dist/processors/implementations/log-protection-processor.js +2 -2
- package/dist/processors/implementations/publish-preflight-processor.js +2 -3
- package/dist/processors/implementations/regression-testing-processor.js +2 -2
- package/dist/processors/implementations/session-summary-processor.js +2 -2
- package/dist/processors/implementations/storytelling-trigger-processor.js +1 -2
- package/dist/public/about.html +6 -6
- package/dist/public/enterprise.html +1 -1
- package/dist/public/features.html +2 -2
- package/dist/public/index.html +4 -4
- package/dist/reporting/report-formatter.js +1 -1
- package/dist/scripts/activate-kernel-pipeline.js +4 -4
- package/dist/scripts/pre-command +1 -1
- package/dist/services/inference-tuner.js +2 -2
- package/dist/skills/api-design/SKILL.md +1 -1
- package/dist/skills/architect-tools/SKILL.md +1 -1
- package/dist/skills/architecture-patterns/SKILL.md +1 -1
- package/dist/skills/auto-format/SKILL.md +1 -1
- package/dist/skills/boot-orchestrator/SKILL.md +1 -1
- package/dist/skills/bug-triage/SKILL.md +1 -1
- package/dist/skills/code-analyzer/SKILL.md +1 -1
- package/dist/skills/code-review/SKILL.md +1 -1
- package/dist/skills/enforcer/SKILL.md +1 -1
- package/dist/skills/framework-compliance-audit/SKILL.md +1 -1
- package/dist/skills/git-workflow/SKILL.md +1 -1
- package/dist/skills/hermes-agent/SKILL.md +42 -42
- package/dist/skills/inference-improve/SKILL.md +1 -1
- package/dist/skills/lint/SKILL.md +1 -1
- package/dist/skills/log-monitor/SKILL.md +1 -1
- package/dist/skills/model-health-check/SKILL.md +1 -1
- package/dist/skills/multimodal-looker/SKILL.md +1 -1
- package/dist/skills/orchestrator/SKILL.md +1 -1
- package/dist/skills/performance-analysis/SKILL.md +1 -1
- package/dist/skills/performance-optimization/SKILL.md +1 -1
- package/dist/skills/processor-pipeline/SKILL.md +1 -1
- package/dist/skills/project-analysis/SKILL.md +1 -1
- package/dist/skills/refactoring-strategies/SKILL.md +1 -1
- package/dist/skills/registry.json +1 -1
- package/dist/skills/researcher/SKILL.md +1 -1
- package/dist/skills/security-audit/SKILL.md +1 -1
- package/dist/skills/security-scan/SKILL.md +1 -1
- package/dist/skills/session-management/SKILL.md +1 -1
- package/dist/skills/state-manager/SKILL.md +1 -1
- package/dist/skills/storyteller/SKILL.md +1 -1
- package/dist/skills/testing-best-practices/SKILL.md +1 -1
- package/dist/skills/testing-strategy/SKILL.md +1 -1
- package/dist/skills/ui-ux-design/SKILL.md +1 -1
- package/dist/state/index.js +1 -1
- package/dist/state/state-manager.js +3 -4
- package/dist/utils/import-resolver.js +1 -1
- package/dist/utils/path-resolver.js +2 -2
- package/dist/utils/token-manager.js +1 -1
- package/dist/validation/estimation-validator.js +1 -1
- package/package.json +10 -3
- package/scripts/hooks/pre-command +1 -1
- package/scripts/hooks/run-hook.js +2 -2
- package/scripts/node/auto-reflection-generator.mjs +30 -36
- package/scripts/node/govern-reflection.mjs +1 -1
- package/scripts/node/postinstall.cjs +37 -2
- package/scripts/node/pre-publish-guard.js +13 -9
- package/scripts/node/setup-xai-oauth.mjs +244 -0
- package/scripts/node/universal-version-manager.js +8 -8
- package/scripts/node/version-manager.mjs +1 -1
- package/src/integrations/grok/plugin/0xray/.mcp.json +16 -4
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +2 -2
- package/src/mcps/architect-tools.server.ts +2 -2
- package/src/mcps/auto-format.server.ts +2 -2
- package/src/mcps/boot-orchestrator.server.ts +2 -2
- package/src/mcps/config/__tests__/config-loader.test.ts +29 -33
- package/src/mcps/config/__tests__/server-config-registry.test.ts +12 -12
- package/src/mcps/config/config-loader.ts +50 -11
- package/src/mcps/config/server-config-registry.ts +10 -10
- package/src/mcps/connection/mcp-connection.ts +1 -1
- package/src/mcps/enforcer-tools.server.ts +2 -2
- package/src/mcps/estimation.server.ts +3 -2
- package/src/mcps/framework-compliance-audit.server.ts +2 -2
- package/src/mcps/framework-help.server.ts +9 -8
- package/src/mcps/governance.server.ts +8 -3
- package/src/mcps/knowledge-skills/api-design.server.ts +3 -2
- package/src/mcps/knowledge-skills/architecture-patterns.server.ts +2 -2
- package/src/mcps/knowledge-skills/bug-triage-specialist.server.ts +2 -2
- package/src/mcps/knowledge-skills/code-analyzer.server.ts +2 -2
- package/src/mcps/knowledge-skills/code-review.server.ts +21 -36
- package/src/mcps/knowledge-skills/content-creator.server.ts +3 -3
- package/src/mcps/knowledge-skills/database-design.server.ts +2 -2
- package/src/mcps/knowledge-skills/devops-deployment.server.ts +3 -2
- package/src/mcps/knowledge-skills/git-workflow.server.ts +3 -2
- package/src/mcps/knowledge-skills/growth-strategist.server.ts +3 -3
- package/src/mcps/knowledge-skills/log-monitor.server.ts +3 -3
- package/src/mcps/knowledge-skills/mobile-development.server.ts +3 -3
- package/src/mcps/knowledge-skills/multimodal-looker.server.ts +3 -3
- package/src/mcps/knowledge-skills/performance-optimization.server.ts +2 -2
- package/src/mcps/knowledge-skills/project-analysis.server.ts +2 -2
- package/src/mcps/knowledge-skills/refactoring-strategies.server.ts +3 -2
- package/src/mcps/knowledge-skills/security-audit.server.ts +21 -40
- package/src/mcps/knowledge-skills/seo-consultant.server.ts +3 -2
- package/src/mcps/knowledge-skills/session-management.server.ts +3 -2
- package/src/mcps/knowledge-skills/skill-invocation.server.ts +113 -3
- package/src/mcps/knowledge-skills/strategist.server.ts +3 -3
- package/src/mcps/knowledge-skills/tech-writer.server.ts +3 -3
- package/src/mcps/knowledge-skills/testing-best-practices.server.ts +3 -2
- package/src/mcps/knowledge-skills/testing-strategy.server.ts +2 -2
- package/src/mcps/knowledge-skills/ui-ux-design.server.ts +5 -4
- package/src/mcps/lint.server.ts +4 -4
- package/src/mcps/mcp-client.ts +5 -5
- package/src/mcps/model-health-check.server.ts +2 -2
- package/src/mcps/orchestrator/server.ts +4 -3
- package/src/mcps/orchestrator.server.ts +2 -1
- package/src/mcps/performance-analysis.server.ts +2 -2
- package/src/mcps/processor-pipeline.server.ts +3 -2
- package/src/mcps/registry.json +1 -1
- package/src/mcps/researcher.server.ts +21 -36
- package/src/mcps/security-scan.server.ts +2 -2
- package/src/mcps/simulation/server-simulations.ts +3 -3
- package/src/mcps/state-manager.server.ts +5 -5
- package/src/opencode/codex.codex +1 -1
- package/src/opencode/commands/dependency-audit.md +3 -3
- package/src/opencode/commands/summary-logger.md +4 -4
- package/src/opencode/enforcer-config.json +2 -2
- package/src/skills/api-design/SKILL.md +1 -1
- package/src/skills/architect-tools/SKILL.md +1 -1
- package/src/skills/architecture-patterns/SKILL.md +1 -1
- package/src/skills/auto-format/SKILL.md +1 -1
- package/src/skills/boot-orchestrator/SKILL.md +1 -1
- package/src/skills/bug-triage/SKILL.md +1 -1
- package/src/skills/code-analyzer/SKILL.md +1 -1
- package/src/skills/code-review/SKILL.md +1 -1
- package/src/skills/enforcer/SKILL.md +1 -1
- package/src/skills/framework-compliance-audit/SKILL.md +1 -1
- package/src/skills/git-workflow/SKILL.md +1 -1
- package/src/skills/hermes-agent/SKILL.md +42 -42
- package/src/skills/inference-improve/SKILL.md +1 -1
- package/src/skills/lint/SKILL.md +1 -1
- package/src/skills/log-monitor/SKILL.md +1 -1
- package/src/skills/model-health-check/SKILL.md +1 -1
- package/src/skills/multimodal-looker/SKILL.md +1 -1
- package/src/skills/orchestrator/SKILL.md +1 -1
- package/src/skills/performance-analysis/SKILL.md +1 -1
- package/src/skills/performance-optimization/SKILL.md +1 -1
- package/src/skills/processor-pipeline/SKILL.md +1 -1
- package/src/skills/project-analysis/SKILL.md +1 -1
- package/src/skills/refactoring-strategies/SKILL.md +1 -1
- package/src/skills/registry.json +1 -1
- package/src/skills/researcher/SKILL.md +1 -1
- package/src/skills/security-audit/SKILL.md +1 -1
- package/src/skills/security-scan/SKILL.md +1 -1
- package/src/skills/session-management/SKILL.md +1 -1
- package/src/skills/state-manager/SKILL.md +1 -1
- package/src/skills/storyteller/SKILL.md +1 -1
- package/src/skills/testing-best-practices/SKILL.md +1 -1
- package/src/skills/testing-strategy/SKILL.md +1 -1
- package/src/skills/ui-ux-design/SKILL.md +1 -1
- package/xray/agents_template.md +46 -0
- package/xray/codex.json +598 -0
- package/dist/plugin/xray-codex-injection.js +0 -857
- /package/dist/integrations/openclaw/hooks/{strray-hooks.js → xray-hooks.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Xray Hermes Plugin — full framework pipeline integration.
|
|
2
2
|
|
|
3
3
|
Mirrors the OpenCode xray-codex-injection.ts behavior:
|
|
4
4
|
1. Captures ALL tool calls and logs to disk
|
|
@@ -29,32 +29,31 @@ except ImportError:
|
|
|
29
29
|
schemas = importlib.import_module("schemas")
|
|
30
30
|
tools = importlib.import_module("tools")
|
|
31
31
|
|
|
32
|
-
logger = logging.getLogger("
|
|
32
|
+
logger = logging.getLogger("xray-hermes")
|
|
33
33
|
|
|
34
34
|
# ── Paths ─────────────────────────────────────────────────────
|
|
35
35
|
|
|
36
36
|
PLUGIN_DIR = Path(__file__).resolve().parent
|
|
37
37
|
BRIDGE_PATH = PLUGIN_DIR / "bridge.mjs"
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
def _find_project_root() -> Path:
|
|
40
|
+
"""Walk up from cwd to find project root."""
|
|
41
|
+
cwd = Path.cwd()
|
|
42
|
+
home = Path.home()
|
|
43
|
+
override = os.environ.get("XRAY_PROJECT_ROOT")
|
|
44
|
+
if override:
|
|
45
|
+
return Path(override).resolve()
|
|
46
|
+
d = cwd
|
|
47
|
+
while True:
|
|
48
|
+
if (d / "node_modules" / "0xray" / "package.json").exists():
|
|
47
49
|
return d
|
|
48
|
-
|
|
49
|
-
if (d / ".opencode" / "strray" / "features.json").exists():
|
|
50
|
+
if (d / ".xray" / "features.json").exists():
|
|
50
51
|
return d
|
|
51
|
-
# package.json but not home dir
|
|
52
52
|
if d != home and (d / "package.json").exists():
|
|
53
53
|
return d
|
|
54
54
|
d = d.parent
|
|
55
55
|
if d == d.parent:
|
|
56
56
|
break
|
|
57
|
-
|
|
58
57
|
return cwd
|
|
59
58
|
|
|
60
59
|
PROJECT_ROOT = _find_project_root()
|
|
@@ -79,20 +78,20 @@ _TOOL_AGENT_MAP = {
|
|
|
79
78
|
"delegate_task": ("orchestrator", "delegation"),
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
# Tools where
|
|
81
|
+
# Tools where Xray has a better alternative
|
|
83
82
|
# terminal: only nudge when the command looks lint/security/search related
|
|
84
|
-
|
|
85
|
-
"search_files": "Use
|
|
83
|
+
_BETTER_WITH_XRAY = {
|
|
84
|
+
"search_files": "Use mcp_xray_researcher_search_codebase for code pattern searches",
|
|
86
85
|
}
|
|
87
86
|
|
|
88
87
|
# Patterns that suggest the terminal command should use an MCP tool instead
|
|
89
88
|
_TERMINAL_NUDGE_PATTERNS = {
|
|
90
|
-
"grep": "Use
|
|
91
|
-
"rg ": "Use
|
|
92
|
-
"eslint": "Use
|
|
93
|
-
"npx eslint": "Use
|
|
94
|
-
"npm audit": "Use
|
|
95
|
-
"yarn audit": "Use
|
|
89
|
+
"grep": "Use mcp_xray_researcher_search_codebase instead of grep",
|
|
90
|
+
"rg ": "Use mcp_xray_researcher_search_codebase instead of ripgrep",
|
|
91
|
+
"eslint": "Use mcp_xray_lint_lint instead of raw eslint",
|
|
92
|
+
"npx eslint": "Use mcp_xray_lint_lint instead of raw eslint",
|
|
93
|
+
"npm audit": "Use mcp_xray_security_scan_security_scan instead of npm audit",
|
|
94
|
+
"yarn audit": "Use mcp_xray_security_scan_security_scan instead of yarn audit",
|
|
96
95
|
"find ": "Use search_files(target='files') instead of find",
|
|
97
96
|
"sed ": "Use patch tool instead of sed",
|
|
98
97
|
"awk ": "Use patch tool instead of awk",
|
|
@@ -108,7 +107,7 @@ _session_stats = {
|
|
|
108
107
|
"session_id": None,
|
|
109
108
|
"code_operations": 0,
|
|
110
109
|
"total_tool_calls": 0,
|
|
111
|
-
"
|
|
110
|
+
"xray_mcp_calls": 0,
|
|
112
111
|
"native_tool_calls": 0,
|
|
113
112
|
"quality_gate_runs": 0,
|
|
114
113
|
"quality_gate_blocks": 0,
|
|
@@ -184,8 +183,8 @@ def _call_bridge(command: dict, timeout: int = 10) -> dict:
|
|
|
184
183
|
|
|
185
184
|
# ── Hook: pre_tool_call ───────────────────────────────────────
|
|
186
185
|
|
|
187
|
-
def
|
|
188
|
-
return tool_name.startswith("
|
|
186
|
+
def _is_xray_mcp(tool_name: str) -> bool:
|
|
187
|
+
return tool_name.startswith("mcp_xray_")
|
|
189
188
|
|
|
190
189
|
|
|
191
190
|
def _on_pre_tool_call(tool_name: str, args: dict, task_id: str, **kwargs):
|
|
@@ -195,17 +194,17 @@ def _on_pre_tool_call(tool_name: str, args: dict, task_id: str, **kwargs):
|
|
|
195
194
|
1. Track stats
|
|
196
195
|
2. Log tool-start event to disk
|
|
197
196
|
3. For code-producing tools: run quality gate + pre-processors via bridge
|
|
198
|
-
4. For non-code tools: nudge if
|
|
197
|
+
4. For non-code tools: nudge if Xray alternative exists
|
|
199
198
|
"""
|
|
200
199
|
_session_stats["total_tool_calls"] += 1
|
|
201
200
|
|
|
202
201
|
# Log start event
|
|
203
202
|
_log_tool_event("start", tool_name, args)
|
|
204
203
|
|
|
205
|
-
#
|
|
206
|
-
if
|
|
207
|
-
_session_stats["
|
|
208
|
-
_log_to_file("activity.log", f"[quality-gate] SKIP
|
|
204
|
+
# Xray MCP tools — track but don't interfere
|
|
205
|
+
if _is_xray_mcp(tool_name):
|
|
206
|
+
_session_stats["xray_mcp_calls"] += 1
|
|
207
|
+
_log_to_file("activity.log", f"[quality-gate] SKIP [xray-mcp]: {tool_name}")
|
|
209
208
|
return
|
|
210
209
|
|
|
211
210
|
# delegate_task: snapshot working tree so post_hook can validate changes
|
|
@@ -252,7 +251,7 @@ def _on_pre_tool_call(tool_name: str, args: dict, task_id: str, **kwargs):
|
|
|
252
251
|
_log_to_file("activity.log",
|
|
253
252
|
f"[quality-gate] BLOCKED: tool={tool_name} violations={violation_msg}")
|
|
254
253
|
logger.warning(
|
|
255
|
-
"[
|
|
254
|
+
"[xray] Quality gate BLOCKED %s: %s",
|
|
256
255
|
tool_name, violation_msg,
|
|
257
256
|
)
|
|
258
257
|
else:
|
|
@@ -277,10 +276,10 @@ def _on_pre_tool_call(tool_name: str, args: dict, task_id: str, **kwargs):
|
|
|
277
276
|
f"[bridge] ERROR in pre-process: {bridge_result.get('error', 'unknown')}")
|
|
278
277
|
return
|
|
279
278
|
|
|
280
|
-
# Non-code tools: nudge for
|
|
281
|
-
if tool_name in
|
|
282
|
-
tip =
|
|
283
|
-
logger.info("[
|
|
279
|
+
# Non-code tools: nudge for Xray alternatives
|
|
280
|
+
if tool_name in _BETTER_WITH_XRAY:
|
|
281
|
+
tip = _BETTER_WITH_XRAY[tool_name]
|
|
282
|
+
logger.info("[xray] Tip: %s — %s", tool_name, tip)
|
|
284
283
|
_log_to_file("activity.log",
|
|
285
284
|
f"[nudge] {tool_name}: {tip}")
|
|
286
285
|
|
|
@@ -290,7 +289,7 @@ def _on_pre_tool_call(tool_name: str, args: dict, task_id: str, **kwargs):
|
|
|
290
289
|
if isinstance(cmd, str):
|
|
291
290
|
for pattern, tip in _TERMINAL_NUDGE_PATTERNS.items():
|
|
292
291
|
if pattern in cmd:
|
|
293
|
-
logger.info("[
|
|
292
|
+
logger.info("[xray] Tip: %s — %s", tool_name, tip)
|
|
294
293
|
_log_to_file("activity.log",
|
|
295
294
|
f"[nudge] {tool_name}: {tip}")
|
|
296
295
|
break
|
|
@@ -378,14 +377,14 @@ def _on_post_tool_call(tool_name: str, args: dict, result, task_id: str, **kwarg
|
|
|
378
377
|
if calls - _last_tune_tool_call_count >= _INFERENCE_TUNE_INTERVAL:
|
|
379
378
|
_last_tune_tool_call_count = calls
|
|
380
379
|
logger.info(
|
|
381
|
-
"[
|
|
380
|
+
"[xray] Triggering inference tuning cycle (tool call #%d)", calls
|
|
382
381
|
)
|
|
383
382
|
_log_to_file("activity.log",
|
|
384
383
|
f"[inference-tune] auto-cycle at tool call #{calls}")
|
|
385
384
|
try:
|
|
386
385
|
_run_inference_tune()
|
|
387
386
|
except Exception as e:
|
|
388
|
-
logger.warning("[
|
|
387
|
+
logger.warning("[xray] Inference tuning failed: %s", e)
|
|
389
388
|
|
|
390
389
|
|
|
391
390
|
# ── Hook: session_start ───────────────────────────────────────
|
|
@@ -394,7 +393,7 @@ def _on_session_start(session_id: str, platform: str, **kwargs):
|
|
|
394
393
|
"""Fires when a new session starts. Resets stats, logs to disk."""
|
|
395
394
|
_session_stats["started_at"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
396
395
|
_session_stats["session_id"] = session_id
|
|
397
|
-
for key in ("code_operations", "total_tool_calls", "
|
|
396
|
+
for key in ("code_operations", "total_tool_calls", "xray_mcp_calls",
|
|
398
397
|
"native_tool_calls", "quality_gate_runs", "quality_gate_blocks",
|
|
399
398
|
"pre_processor_runs", "post_processor_runs",
|
|
400
399
|
"bridge_calls", "bridge_errors",
|
|
@@ -406,23 +405,23 @@ def _on_session_start(session_id: str, platform: str, **kwargs):
|
|
|
406
405
|
_ensure_log_dir()
|
|
407
406
|
_log_to_file("activity.log",
|
|
408
407
|
f"[session-start] session={session_id} platform={platform}")
|
|
409
|
-
logger.info("[
|
|
408
|
+
logger.info("[xray] Session %s started on %s", session_id, platform)
|
|
410
409
|
|
|
411
410
|
|
|
412
411
|
# ── Slash command ─────────────────────────────────────────────
|
|
413
412
|
|
|
414
|
-
def
|
|
415
|
-
"""Slash command handler: /
|
|
413
|
+
def _xray_command(args: str) -> str:
|
|
414
|
+
"""Slash command handler: /xray [status|stats|help]"""
|
|
416
415
|
cmd = (args or "status").strip().lower()
|
|
417
416
|
|
|
418
417
|
if cmd == "stats":
|
|
419
418
|
return (
|
|
420
|
-
f"
|
|
419
|
+
f"Xray Session Stats\n"
|
|
421
420
|
f" Session: {_session_stats['session_id'] or 'N/A'}\n"
|
|
422
421
|
f" Started: {_session_stats['started_at'] or 'N/A'}\n"
|
|
423
422
|
f" Tool calls: {_session_stats['total_tool_calls']}\n"
|
|
424
423
|
f" Code operations: {_session_stats['code_operations']}\n"
|
|
425
|
-
f"
|
|
424
|
+
f" Xray MCP: {_session_stats['xray_mcp_calls']}\n"
|
|
426
425
|
f" Native tools: {_session_stats['native_tool_calls']}\n"
|
|
427
426
|
f" Quality gate runs: {_session_stats['quality_gate_runs']}\n"
|
|
428
427
|
f" Quality gate blocks: {_session_stats['quality_gate_blocks']}\n"
|
|
@@ -437,19 +436,19 @@ def _strray_command(args: str) -> str:
|
|
|
437
436
|
|
|
438
437
|
if cmd == "help":
|
|
439
438
|
return (
|
|
440
|
-
"
|
|
441
|
-
" /
|
|
442
|
-
" /
|
|
443
|
-
" /
|
|
439
|
+
"Xray Commands:\n"
|
|
440
|
+
" /xray status — Plugin and framework health\n"
|
|
441
|
+
" /xray stats — Session pipeline statistics\n"
|
|
442
|
+
" /xray help — This message"
|
|
444
443
|
)
|
|
445
444
|
|
|
446
445
|
# Default: status (calls bridge health)
|
|
447
446
|
bridge_result = _call_bridge({"command": "health"}, timeout=10)
|
|
448
447
|
if "error" in bridge_result:
|
|
449
|
-
return f"
|
|
448
|
+
return f"Xray plugin loaded. Bridge: {bridge_result['error']}"
|
|
450
449
|
|
|
451
450
|
return (
|
|
452
|
-
f"
|
|
451
|
+
f"Xray Hermes Plugin Status\n"
|
|
453
452
|
f" Framework: {bridge_result.get('framework', 'unknown')}\n"
|
|
454
453
|
f" Version: {bridge_result.get('version', 'unknown')}\n"
|
|
455
454
|
f" Quality Gate: {'ready' if bridge_result.get('components', {}).get('qualityGate') else 'not loaded'}\n"
|
|
@@ -518,13 +517,13 @@ def _record_tool_outcome(tool_name: str, args: dict, success: bool):
|
|
|
518
517
|
with open(_OUTCOMES_PATH, "w") as f:
|
|
519
518
|
json.dump(outcomes, f, indent=2)
|
|
520
519
|
except Exception as e:
|
|
521
|
-
logger.debug("[
|
|
520
|
+
logger.debug("[xray] outcome recording failed: %s", e)
|
|
522
521
|
|
|
523
522
|
|
|
524
523
|
# ── Inference tuning (auto-calibration) ────────────────────────
|
|
525
524
|
|
|
526
525
|
def _run_inference_tune():
|
|
527
|
-
"""Shell out to
|
|
526
|
+
"""Shell out to 0xray inference:tuner --run-once.
|
|
528
527
|
|
|
529
528
|
Runs in a background thread so it doesn't block the tool call pipeline.
|
|
530
529
|
The tuner reads routing outcomes, runs the analytics pipeline, and
|
|
@@ -560,7 +559,7 @@ def _run_inference_tune():
|
|
|
560
559
|
# ── Registration ──────────────────────────────────────────────
|
|
561
560
|
|
|
562
561
|
# ── Subagent (delegate_task) enforcement ────────────────────
|
|
563
|
-
# Subagents bypass all
|
|
562
|
+
# Subagents bypass all Xray hooks because they run in isolated
|
|
564
563
|
# contexts. We enforce by snapshotting the working tree before dispatch
|
|
565
564
|
# and validating all changed files after return.
|
|
566
565
|
|
|
@@ -647,7 +646,7 @@ def _validate_subagent_changes(task_id: str, **kwargs):
|
|
|
647
646
|
f"[subagent-validate] BLOCKED: {rel_path} "
|
|
648
647
|
f"violations={'; '.join(str(v) for v in violations[:3])}")
|
|
649
648
|
logger.warning(
|
|
650
|
-
"[
|
|
649
|
+
"[xray] Subagent BLOCKED %s: %s",
|
|
651
650
|
rel_path, violations[:3],
|
|
652
651
|
)
|
|
653
652
|
else:
|
|
@@ -659,28 +658,40 @@ def register(ctx):
|
|
|
659
658
|
"""Wire schemas to handlers and register lifecycle hooks."""
|
|
660
659
|
# ── Register tools ────────────────────────────────────────
|
|
661
660
|
ctx.register_tool(
|
|
662
|
-
name="
|
|
663
|
-
toolset="
|
|
664
|
-
schema=schemas.
|
|
665
|
-
handler=tools.
|
|
661
|
+
name="xray_validate",
|
|
662
|
+
toolset="xray-hermes",
|
|
663
|
+
schema=schemas.XRAY_VALIDATE,
|
|
664
|
+
handler=tools.xray_validate,
|
|
665
|
+
)
|
|
666
|
+
ctx.register_tool(
|
|
667
|
+
name="xray_codex_check",
|
|
668
|
+
toolset="xray-hermes",
|
|
669
|
+
schema=schemas.XRAY_CODEX_CHECK,
|
|
670
|
+
handler=tools.xray_codex_check,
|
|
671
|
+
)
|
|
672
|
+
ctx.register_tool(
|
|
673
|
+
name="xray_health",
|
|
674
|
+
toolset="xray-hermes",
|
|
675
|
+
schema=schemas.XRAY_HEALTH,
|
|
676
|
+
handler=tools.xray_health,
|
|
666
677
|
)
|
|
667
678
|
ctx.register_tool(
|
|
668
|
-
name="
|
|
669
|
-
toolset="
|
|
670
|
-
schema=schemas.
|
|
671
|
-
handler=tools.
|
|
679
|
+
name="xray_hooks",
|
|
680
|
+
toolset="xray-hermes",
|
|
681
|
+
schema=schemas.XRAY_HOOKS,
|
|
682
|
+
handler=tools.xray_hooks,
|
|
672
683
|
)
|
|
673
684
|
ctx.register_tool(
|
|
674
|
-
name="
|
|
675
|
-
toolset="
|
|
676
|
-
schema=schemas.
|
|
677
|
-
handler=tools.
|
|
685
|
+
name="xray_skill_install",
|
|
686
|
+
toolset="xray-hermes",
|
|
687
|
+
schema=schemas.XRAY_SKILL_INSTALL,
|
|
688
|
+
handler=tools.xray_skill_install,
|
|
678
689
|
)
|
|
679
690
|
ctx.register_tool(
|
|
680
|
-
name="
|
|
681
|
-
toolset="
|
|
682
|
-
schema=schemas.
|
|
683
|
-
handler=tools.
|
|
691
|
+
name="xray_skill_registry",
|
|
692
|
+
toolset="xray-hermes",
|
|
693
|
+
schema=schemas.XRAY_SKILL_REGISTRY,
|
|
694
|
+
handler=tools.xray_skill_registry,
|
|
684
695
|
)
|
|
685
696
|
|
|
686
697
|
# ── Register hooks ────────────────────────────────────────
|
|
@@ -691,28 +702,28 @@ def register(ctx):
|
|
|
691
702
|
try:
|
|
692
703
|
ctx.register_hook("on_session_start", _on_session_start)
|
|
693
704
|
except (AttributeError, TypeError):
|
|
694
|
-
logger.debug("[
|
|
705
|
+
logger.debug("[xray] on_session_start hook not yet available")
|
|
695
706
|
|
|
696
707
|
# ── Register slash command ────────────────────────────────
|
|
697
708
|
try:
|
|
698
709
|
ctx.register_command(
|
|
699
|
-
name="
|
|
700
|
-
handler=
|
|
701
|
-
description="
|
|
710
|
+
name="xray",
|
|
711
|
+
handler=_xray_command,
|
|
712
|
+
description="Xray status, stats, hooks, and enforcement info",
|
|
702
713
|
args_hint="[status|stats|help]",
|
|
703
714
|
aliases=("sr",),
|
|
704
715
|
)
|
|
705
716
|
except (AttributeError, TypeError):
|
|
706
|
-
logger.debug("[
|
|
717
|
+
logger.debug("[xray] Slash command registration not yet available")
|
|
707
718
|
|
|
708
719
|
# ── Bootstrap ─────────────────────────────────────────────
|
|
709
720
|
_ensure_log_dir()
|
|
710
721
|
_log_to_file("activity.log",
|
|
711
|
-
f"[plugin-loaded]
|
|
712
|
-
f"
|
|
722
|
+
f"[plugin-loaded] Xray Hermes Plugin v2.2 — "
|
|
723
|
+
f"6 tools, 2 hooks, subagent enforcement, bridge={BRIDGE_PATH.exists()}")
|
|
713
724
|
|
|
714
725
|
logger.info(
|
|
715
|
-
"[
|
|
726
|
+
"[xray] Plugin v2.2 loaded: 6 tools, 2 hooks, "
|
|
716
727
|
"subagent enforcement active, bridge=%s",
|
|
717
728
|
BRIDGE_PATH.exists(),
|
|
718
729
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Xray Hermes Plugin Installed
|
|
2
2
|
|
|
3
3
|
**Restart your Hermes session** for the plugin to take effect.
|
|
4
4
|
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
| Component | Description |
|
|
8
8
|
|-----------|-------------|
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
9
|
+
| `xray_validate` | Pre-commit validation with quality gates |
|
|
10
|
+
| `xray_codex_check` | Code review against 60 Codex error-prevention rules |
|
|
11
|
+
| `xray_health` | Framework health check |
|
|
12
|
+
| `xray_hooks` | Git hooks management (install, uninstall, list, status) |
|
|
13
13
|
| `pre_tool_call` hook | Quality gates + nudges before every tool call |
|
|
14
14
|
| `post_tool_call` hook | Post-processors after every tool call |
|
|
15
15
|
|
|
@@ -27,24 +27,24 @@ The plugin can install git hooks for automated validation:
|
|
|
27
27
|
### Install Hooks
|
|
28
28
|
|
|
29
29
|
```
|
|
30
|
-
/
|
|
30
|
+
/xray hooks install
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Or use the tool directly:
|
|
34
34
|
```
|
|
35
|
-
|
|
35
|
+
xray_hooks(action="install")
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
### Check Hook Status
|
|
39
39
|
|
|
40
40
|
```
|
|
41
|
-
/
|
|
41
|
+
/xray hooks status
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
### Uninstall Hooks
|
|
45
45
|
|
|
46
46
|
```
|
|
47
|
-
/
|
|
47
|
+
/xray hooks uninstall
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
## Quick Test
|
|
@@ -52,20 +52,20 @@ strray_hooks(action="install")
|
|
|
52
52
|
After restarting Hermes, try:
|
|
53
53
|
|
|
54
54
|
```
|
|
55
|
-
/
|
|
56
|
-
/
|
|
55
|
+
/xray status
|
|
56
|
+
/xray stats
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Or use the tools directly — `
|
|
59
|
+
Or use the tools directly — `xray_health` will confirm the bridge is connected.
|
|
60
60
|
|
|
61
61
|
## MCP Servers
|
|
62
62
|
|
|
63
|
-
The plugin works alongside
|
|
63
|
+
The plugin works alongside Xray's MCP servers (if configured in your
|
|
64
64
|
Hermes config). The plugin's native tools provide offline/offline-first
|
|
65
65
|
validation, while MCP servers offer deeper framework integration.
|
|
66
66
|
|
|
67
67
|
## Files
|
|
68
68
|
|
|
69
|
-
The plugin lives at `~/.hermes/plugins/
|
|
69
|
+
The plugin lives at `~/.hermes/plugins/xray-hermes/`. It is auto-updated
|
|
70
70
|
when you run `npm install` or `npm update` in a project with `0xray`
|
|
71
71
|
as a dependency.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Xray Framework Bridge for Hermes Agent
|
|
5
5
|
*
|
|
6
|
-
* Provides direct access to
|
|
6
|
+
* Provides direct access to Xray framework components
|
|
7
7
|
* (ProcessorManager, QualityGate, StateManager) from Python.
|
|
8
8
|
* Uses JSON stdin/stdout protocol for IPC.
|
|
9
9
|
*
|
|
@@ -44,7 +44,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
44
44
|
|
|
45
45
|
// ── Framework components (lazy-loaded) ───────────────────────
|
|
46
46
|
let ProcessorManager = null;
|
|
47
|
-
let
|
|
47
|
+
let XrayStateManager = null;
|
|
48
48
|
let featuresConfigLoader = null;
|
|
49
49
|
let runQualityGateWithLogging = null;
|
|
50
50
|
let enforcerValidators = null;
|
|
@@ -53,12 +53,12 @@ let frameworkLoadAttempted = false;
|
|
|
53
53
|
|
|
54
54
|
// ── Project root detection ───────────────────────────────────
|
|
55
55
|
function findProjectRoot() {
|
|
56
|
-
const envHome = process.env.
|
|
56
|
+
const envHome = process.env.XRAY_HOME;
|
|
57
57
|
if (envHome && existsSync(join(envHome, "package.json"))) return envHome;
|
|
58
58
|
|
|
59
59
|
const candidates = [
|
|
60
60
|
process.cwd(),
|
|
61
|
-
join(homedir(), "dev", "
|
|
61
|
+
join(homedir(), "dev", "xray"),
|
|
62
62
|
join(dirname(__dirname), "..", "..", "..", ".."), // plugin dir -> project root
|
|
63
63
|
];
|
|
64
64
|
|
|
@@ -67,7 +67,7 @@ function findProjectRoot() {
|
|
|
67
67
|
const pkgPath = join(dir, "package.json");
|
|
68
68
|
if (existsSync(pkgPath)) {
|
|
69
69
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
70
|
-
if (pkg.name === "
|
|
70
|
+
if (pkg.name === "0xray" || pkg.dependencies?.["0xray"] || pkg.name === "0xray" || pkg.dependencies?.["0xray"]) {
|
|
71
71
|
return dir;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -149,11 +149,11 @@ async function loadFramework(projectRoot) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
if (!
|
|
152
|
+
if (!XrayStateManager) {
|
|
153
153
|
const smPath = join(distDir, "state", "state-manager.js");
|
|
154
154
|
if (existsSync(smPath)) {
|
|
155
155
|
const sm = await import(smPath);
|
|
156
|
-
|
|
156
|
+
XrayStateManager = sm.XrayStateManager;
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
@@ -230,15 +230,15 @@ async function runQualityGateCheck(context, projectRoot, logDir) {
|
|
|
230
230
|
|
|
231
231
|
// ── Processor Pipeline ───────────────────────────────────────
|
|
232
232
|
async function runProcessors(tool, args, phase, projectRoot, logDir) {
|
|
233
|
-
if (!ProcessorManager || !
|
|
233
|
+
if (!ProcessorManager || !XrayStateManager) {
|
|
234
234
|
return { ran: false, reason: "framework modules not loaded" };
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
const logger = new BridgeLogger(logDir);
|
|
238
238
|
|
|
239
239
|
try {
|
|
240
|
-
const stateDir = join(projectRoot, ".
|
|
241
|
-
const stateManager = new
|
|
240
|
+
const stateDir = join(projectRoot, ".xray", "state");
|
|
241
|
+
const stateManager = new XrayStateManager(stateDir);
|
|
242
242
|
const processorManager = new ProcessorManager(stateManager);
|
|
243
243
|
|
|
244
244
|
// Register processors matching OpenCode plugin
|
|
@@ -353,7 +353,7 @@ async function handleHealth(input) {
|
|
|
353
353
|
const components = {
|
|
354
354
|
qualityGate: !!runQualityGateWithLogging,
|
|
355
355
|
processorManager: !!ProcessorManager,
|
|
356
|
-
stateManager: !!
|
|
356
|
+
stateManager: !!XrayStateManager,
|
|
357
357
|
featuresConfig: !!featuresConfigLoader,
|
|
358
358
|
};
|
|
359
359
|
|
|
@@ -563,7 +563,7 @@ function handleHooks(input, projectRoot) {
|
|
|
563
563
|
const { action, hooks } = input;
|
|
564
564
|
const hookTypes = hooks || ["pre-commit", "post-commit", "pre-push", "post-push"];
|
|
565
565
|
const gitHooksDir = join(projectRoot, ".git", "hooks");
|
|
566
|
-
const
|
|
566
|
+
const xrayHooksDir = join(projectRoot, "hooks");
|
|
567
567
|
|
|
568
568
|
if (!existsSync(gitHooksDir)) {
|
|
569
569
|
return { error: "Not a git repository — no .git/hooks directory" };
|
|
@@ -575,14 +575,14 @@ function handleHooks(input, projectRoot) {
|
|
|
575
575
|
if (action === "list" || action === "status") {
|
|
576
576
|
for (const hookName of hookTypes) {
|
|
577
577
|
const gitHook = join(gitHooksDir, hookName);
|
|
578
|
-
const
|
|
578
|
+
const xrayHook = join(xrayHooksDir, hookName);
|
|
579
579
|
|
|
580
580
|
if (!existsSync(gitHook)) {
|
|
581
581
|
result.missing.push(hookName);
|
|
582
582
|
} else {
|
|
583
583
|
try {
|
|
584
584
|
const content = readFileSync(gitHook, "utf-8");
|
|
585
|
-
if (content.includes("
|
|
585
|
+
if (content.includes("Xray") || content.includes("xray") || false || content.includes("run-hook.js")) {
|
|
586
586
|
result.managed.push(hookName);
|
|
587
587
|
} else {
|
|
588
588
|
result.external.push(hookName);
|
|
@@ -592,8 +592,8 @@ function handleHooks(input, projectRoot) {
|
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
-
// Check if
|
|
596
|
-
if (!existsSync(
|
|
595
|
+
// Check if xray source hook exists
|
|
596
|
+
if (!existsSync(xrayHook)) {
|
|
597
597
|
result.stale.push(hookName);
|
|
598
598
|
}
|
|
599
599
|
}
|
|
@@ -603,7 +603,7 @@ function handleHooks(input, projectRoot) {
|
|
|
603
603
|
action,
|
|
604
604
|
hooks: result,
|
|
605
605
|
gitHooksDir,
|
|
606
|
-
|
|
606
|
+
xrayHooksDir,
|
|
607
607
|
};
|
|
608
608
|
}
|
|
609
609
|
|
|
@@ -614,7 +614,7 @@ function handleHooks(input, projectRoot) {
|
|
|
614
614
|
const errors = [];
|
|
615
615
|
|
|
616
616
|
for (const hookName of hookTypes) {
|
|
617
|
-
const src = join(
|
|
617
|
+
const src = join(xrayHooksDir, hookName);
|
|
618
618
|
const dst = join(gitHooksDir, hookName);
|
|
619
619
|
|
|
620
620
|
if (!existsSync(src)) {
|
|
@@ -623,11 +623,11 @@ function handleHooks(input, projectRoot) {
|
|
|
623
623
|
}
|
|
624
624
|
|
|
625
625
|
try {
|
|
626
|
-
// Backup existing non-
|
|
626
|
+
// Backup existing non-xray hooks
|
|
627
627
|
if (existsSync(dst)) {
|
|
628
628
|
const content = readFileSync(dst, "utf-8");
|
|
629
|
-
if (!content.includes("
|
|
630
|
-
renameSync(dst, `${dst}.
|
|
629
|
+
if (!content.includes("Xray") && !content.includes("xray") && !false && !content.includes("run-hook.js")) {
|
|
630
|
+
renameSync(dst, `${dst}.xray-backup`);
|
|
631
631
|
} else {
|
|
632
632
|
unlinkSync(dst);
|
|
633
633
|
}
|
|
@@ -659,15 +659,15 @@ function handleHooks(input, projectRoot) {
|
|
|
659
659
|
|
|
660
660
|
for (const hookName of hookTypes) {
|
|
661
661
|
const dst = join(gitHooksDir, hookName);
|
|
662
|
-
const backup = `${dst}.
|
|
662
|
+
const backup = `${dst}.xray-backup`;
|
|
663
663
|
|
|
664
664
|
if (!existsSync(dst)) continue;
|
|
665
665
|
|
|
666
666
|
try {
|
|
667
667
|
const content = readFileSync(dst, "utf-8");
|
|
668
|
-
const
|
|
668
|
+
const isXray = content.includes("Xray") || content.includes("xray") || false || content.includes("run-hook.js");
|
|
669
669
|
|
|
670
|
-
if (
|
|
670
|
+
if (isXray || lstatSync(dst).isSymbolicLink()) {
|
|
671
671
|
unlinkSync(dst);
|
|
672
672
|
|
|
673
673
|
// Restore backup if exists
|
|
@@ -743,8 +743,37 @@ async function handleApply(input, projectRoot, logDir) {
|
|
|
743
743
|
// ── Known commands for positional-arg mode ──────────────────
|
|
744
744
|
const KNOWN_COMMANDS = new Set([
|
|
745
745
|
"health", "stats", "pre-process", "post-process", "validate", "codex-check", "hooks", "govern", "apply",
|
|
746
|
+
"skill-install", "skill-registry",
|
|
746
747
|
]);
|
|
747
748
|
|
|
749
|
+
// ── Skill management ──────────────────────────────────────────
|
|
750
|
+
|
|
751
|
+
async function handleSkillInstall(command, projectRoot, logDir) {
|
|
752
|
+
try {
|
|
753
|
+
const source = command.source;
|
|
754
|
+
if (!source) {
|
|
755
|
+
return { error: "Missing 'source' — specify a source name or git URL" };
|
|
756
|
+
}
|
|
757
|
+
const { execSync } = await import("child_process");
|
|
758
|
+
const output = execSync(`npx 0xray skill:install "${source}"`, { cwd: projectRoot, timeout: 120000 }).toString();
|
|
759
|
+
return { status: "ok", output: output.trim() };
|
|
760
|
+
} catch (error) {
|
|
761
|
+
return { error: `Skill install failed: ${error.message || error}` };
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
async function handleSkillRegistry(command, projectRoot, logDir) {
|
|
766
|
+
try {
|
|
767
|
+
const action = command.action || "list";
|
|
768
|
+
const { execSync } = await import("child_process");
|
|
769
|
+
const args = command.source ? `"${command.source}"` : "";
|
|
770
|
+
const output = execSync(`npx 0xray skill:registry ${action} ${args}`, { cwd: projectRoot, timeout: 30000 }).toString();
|
|
771
|
+
return { status: "ok", output: output.trim() };
|
|
772
|
+
} catch (error) {
|
|
773
|
+
return { error: `Skill registry command failed: ${error.message || error}` };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
748
777
|
// ── Main ─────────────────────────────────────────────────────
|
|
749
778
|
async function main() {
|
|
750
779
|
// Parse --cwd flag and detect positional command arg
|
|
@@ -836,6 +865,12 @@ async function main() {
|
|
|
836
865
|
case "apply":
|
|
837
866
|
response = await handleApply(command, projectRoot, logDir);
|
|
838
867
|
break;
|
|
868
|
+
case "skill-install":
|
|
869
|
+
response = await handleSkillInstall(command, projectRoot, logDir);
|
|
870
|
+
break;
|
|
871
|
+
case "skill-registry":
|
|
872
|
+
response = await handleSkillRegistry(command, projectRoot, logDir);
|
|
873
|
+
break;
|
|
839
874
|
default:
|
|
840
875
|
response = { error: `Unknown command: ${cmd}` };
|
|
841
876
|
}
|