0xray 4.0.35 → 4.0.36
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/.grok-plugin/plugin.json +1 -1
- package/.xray/features.json +1 -1
- package/dist/CHANGELOG.md +10 -0
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
- package/dist/integrations/hooks/goggles-pipeline.mjs +59 -23
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/package.json +2 -1
- package/scripts/mjs/run-grok-hook.mjs +50 -0
- package/scripts/node/install-bridges.cjs +3 -3
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/xray/features.json +1 -1
package/.grok-plugin/plugin.json
CHANGED
package/.xray/features.json
CHANGED
package/dist/CHANGELOG.md
CHANGED
|
@@ -42,6 +42,16 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
|
|
|
42
42
|
- Archive root `integrations/` costume SKILL dump → `docs/archive/integrations-costume/`. Live wear is `src/integrations`.
|
|
43
43
|
- Archive root `commands/` stale copy → `docs/archive/commands-copy/`. Live wear is `src/opencode/commands`.
|
|
44
44
|
|
|
45
|
+
## [4.0.36] - 2026-10-02
|
|
46
|
+
|
|
47
|
+
### 🔄 Changes
|
|
48
|
+
|
|
49
|
+
### 🔎 Other Changes
|
|
50
|
+
- Merge pull request #178 from 0xRayAI/fix/goggles-robust (21c8f30d9)
|
|
51
|
+
- fix(goggles): hold the stop through search, read, and a dist wipe (abe603265)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
45
55
|
## [4.0.35] - 2026-10-02
|
|
46
56
|
|
|
47
57
|
### 🔄 Changes
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
8
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" pre-tool-use.js",
|
|
9
9
|
"timeout": 30
|
|
10
10
|
}
|
|
11
11
|
]
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"hooks": [
|
|
17
17
|
{
|
|
18
18
|
"type": "command",
|
|
19
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
19
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js",
|
|
20
20
|
"timeout": 30
|
|
21
21
|
}
|
|
22
22
|
]
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
30
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
30
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=user_prompt_submit",
|
|
31
31
|
"timeout": 30
|
|
32
32
|
}
|
|
33
33
|
]
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"hooks": [
|
|
39
39
|
{
|
|
40
40
|
"type": "command",
|
|
41
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
41
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" post-tool-use.js",
|
|
42
42
|
"timeout": 30
|
|
43
43
|
}
|
|
44
44
|
]
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"hooks": [
|
|
50
50
|
{
|
|
51
51
|
"type": "command",
|
|
52
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
52
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=pre_compact",
|
|
53
53
|
"timeout": 30
|
|
54
54
|
}
|
|
55
55
|
]
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"hooks": [
|
|
61
61
|
{
|
|
62
62
|
"type": "command",
|
|
63
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
63
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=post_compact",
|
|
64
64
|
"timeout": 30
|
|
65
65
|
}
|
|
66
66
|
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Goggles MCP. Stdio. Server name `goggles`.
|
|
3
3
|
* Tools: look, status_lens. Same organ as the CLI.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
6
6
|
import { basename, join, resolve } from 'node:path';
|
|
7
7
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
8
8
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -328,9 +328,16 @@ async function main() {
|
|
|
328
328
|
await server.connect(transport);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
function sameInvoked(argvPath, modulePath) {
|
|
332
|
+
try {
|
|
333
|
+
return realpathSync(argvPath) === realpathSync(modulePath);
|
|
334
|
+
} catch {
|
|
335
|
+
return resolve(argvPath) === resolve(modulePath);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
331
339
|
const thisFile = fileURLToPath(import.meta.url);
|
|
332
|
-
|
|
333
|
-
if (invoked && resolve(thisFile) === invoked) {
|
|
340
|
+
if (process.argv[1] && sameInvoked(process.argv[1], thisFile)) {
|
|
334
341
|
main().catch((err) => {
|
|
335
342
|
process.stderr.write(`goggles MCP failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
336
343
|
process.exit(1);
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* A named plane returns its view. Field pipes fill what they can see.
|
|
4
4
|
* The view keeps every field for the next look.
|
|
5
5
|
* A held plane stays quiet. A leave is denied.
|
|
6
|
-
* On this host a search is grep
|
|
6
|
+
* On this host a search is grep, glob, or a shell that runs one.
|
|
7
|
+
* The lens file stays open. One later read may go on, then the next read stops.
|
|
7
8
|
*/
|
|
8
9
|
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
9
10
|
import { createRequire } from 'node:module';
|
|
@@ -73,9 +74,12 @@ export function organStop(held, action) {
|
|
|
73
74
|
const text = String(action?.text || '');
|
|
74
75
|
const paths = (action?.paths || []).map(String).filter(Boolean);
|
|
75
76
|
const tool = String(action?.tool || '');
|
|
76
|
-
|
|
77
|
+
const spoken = [text, ...paths].join('\n');
|
|
78
|
+
if (/docs-site\/docs\/plates\/[a-z0-9-]+\.md/i.test(spoken)) {
|
|
77
79
|
return organDeny(`The reading is ${plane}. That drawing is not the plane.`);
|
|
78
80
|
}
|
|
81
|
+
const otherFile = otherPlaneFile(plane, spoken);
|
|
82
|
+
if (otherFile) return organDeny(`The reading is ${plane}. The action is ${otherFile}.`);
|
|
79
83
|
const stripped = withoutPaths(text);
|
|
80
84
|
const others = acceptedPlanes().filter((name) => name !== plane && planeWord(name, stripped));
|
|
81
85
|
const namesThis = planeWord(plane, stripped);
|
|
@@ -256,10 +260,10 @@ function cardView(plane, scope, flavor, root) {
|
|
|
256
260
|
teardown: plane.teardown || '',
|
|
257
261
|
worn: plane.worn || '',
|
|
258
262
|
};
|
|
263
|
+
view.exam = examinePlane(plane, root).text;
|
|
259
264
|
if (flavor === 'triage') {
|
|
260
265
|
const filled = new Set(filledOf(plane));
|
|
261
266
|
view.empty = FIELD_ORDER.filter((name) => !filled.has(name));
|
|
262
|
-
view.exam = examinePlane(plane, root).text;
|
|
263
267
|
}
|
|
264
268
|
return view;
|
|
265
269
|
}
|
|
@@ -284,6 +288,8 @@ function formatCardText(view) {
|
|
|
284
288
|
if (view.empty.length) lines.push(`Empty: ${view.empty.join(', ')}`);
|
|
285
289
|
lines.push(view.exam);
|
|
286
290
|
text = `${text}\n${lines.join('\n')}`;
|
|
291
|
+
} else if (typeof view.exam === 'string' && view.exam.startsWith('Drift:')) {
|
|
292
|
+
text = `${text}\n${view.exam}`;
|
|
287
293
|
}
|
|
288
294
|
return text;
|
|
289
295
|
}
|
|
@@ -305,6 +311,8 @@ export function formatCardPane(view) {
|
|
|
305
311
|
if (view.flavor === 'triage') {
|
|
306
312
|
if (Array.isArray(view.empty) && view.empty.length) rows.push(['Empty', view.empty.join(', ')]);
|
|
307
313
|
if (view.exam) rows.push(['', view.exam]);
|
|
314
|
+
} else if (typeof view.exam === 'string' && view.exam.startsWith('Drift:')) {
|
|
315
|
+
rows.push(['', view.exam]);
|
|
308
316
|
}
|
|
309
317
|
const body = rows.map(([label, value]) => (label ? `${label.padEnd(8)} ${value}` : value).trimEnd());
|
|
310
318
|
const title = `${view.flavor} · ${view.plane}`;
|
|
@@ -686,6 +694,33 @@ export function suitHint(text) {
|
|
|
686
694
|
|
|
687
695
|
const DEEP_SEARCH = /search_codebase|find_implementation|get_documentation/i;
|
|
688
696
|
const HOLDS_ON_NAME = new Set(['dichotomy', 'syncopate', 'synthesis']);
|
|
697
|
+
const SHELL_SEARCH = /(?:^|&&|\|\||[;&|`(\n])\s*(?:(?:\/[\w.+/-]+\/)|(?:npx\s+))?(?:rg|grep|ag|ack|fd|find)\b|\bgit\s+grep\b/;
|
|
698
|
+
|
|
699
|
+
function shellSearches(text) {
|
|
700
|
+
return SHELL_SEARCH.test(String(text || ''));
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function mappedPlaneFiles(name) {
|
|
704
|
+
try {
|
|
705
|
+
const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
|
|
706
|
+
const entry = data[name];
|
|
707
|
+
if (!entry || typeof entry !== 'object') return [];
|
|
708
|
+
const candidates = [entry.file, entry.worn, entry.skills];
|
|
709
|
+
if (Array.isArray(entry.files)) candidates.push(...entry.files);
|
|
710
|
+
return candidates.filter((file) => typeof file === 'string' && file.includes('.') && file.includes('/'));
|
|
711
|
+
} catch {
|
|
712
|
+
return [];
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function otherPlaneFile(held, spoken) {
|
|
717
|
+
const text = String(spoken || '');
|
|
718
|
+
for (const name of acceptedPlanes()) {
|
|
719
|
+
if (name === held) continue;
|
|
720
|
+
if (mappedPlaneFiles(name).some((file) => text.includes(file))) return name;
|
|
721
|
+
}
|
|
722
|
+
return '';
|
|
723
|
+
}
|
|
689
724
|
|
|
690
725
|
function mapFiles() {
|
|
691
726
|
try {
|
|
@@ -715,8 +750,8 @@ function researchCall(toolName, text) {
|
|
|
715
750
|
const tool = String(toolName || '');
|
|
716
751
|
const spoken = String(text || '');
|
|
717
752
|
if (/researcher|explorer|deep[- ]?research/i.test(tool) || DEEP_SEARCH.test(tool)) return true;
|
|
718
|
-
if (/bash|shell/i.test(tool) && (/researcher|explorer|deep[- ]?research/i.test(spoken) || DEEP_SEARCH.test(spoken))) return true;
|
|
719
|
-
if (/^grep$/i.test(tool)) return true;
|
|
753
|
+
if (/bash|shell/i.test(tool) && (/researcher|explorer|deep[- ]?research/i.test(spoken) || DEEP_SEARCH.test(spoken) || shellSearches(spoken))) return true;
|
|
754
|
+
if (/^(grep|glob)$/i.test(tool)) return true;
|
|
720
755
|
return /^read_file$/i.test(tool) && !opensLensFile(spoken);
|
|
721
756
|
}
|
|
722
757
|
|
|
@@ -727,16 +762,17 @@ function passPath(root) {
|
|
|
727
762
|
function loadReadPass(root) {
|
|
728
763
|
try {
|
|
729
764
|
const data = JSON.parse(readFileSync(passPath(root), 'utf8'));
|
|
730
|
-
|
|
765
|
+
if (!data || data.read !== true) return null;
|
|
766
|
+
return data.spent === true ? 'spent' : 'armed';
|
|
731
767
|
} catch {
|
|
732
|
-
return
|
|
768
|
+
return null;
|
|
733
769
|
}
|
|
734
770
|
}
|
|
735
771
|
|
|
736
|
-
function saveReadPass(root) {
|
|
772
|
+
function saveReadPass(root, spent) {
|
|
737
773
|
const file = passPath(root);
|
|
738
774
|
mkdirSync(dirname(file), { recursive: true });
|
|
739
|
-
writeFileSync(file, `${JSON.stringify({ read: true })}\n`);
|
|
775
|
+
writeFileSync(file, `${JSON.stringify({ read: true, spent: Boolean(spent) })}\n`);
|
|
740
776
|
}
|
|
741
777
|
|
|
742
778
|
/** One plane: the files the suit continues with. A look with no plane stops. Two planes stay quiet. */
|
|
@@ -771,15 +807,20 @@ function stampPlane(root, id) {
|
|
|
771
807
|
}
|
|
772
808
|
}
|
|
773
809
|
|
|
774
|
-
/** A search stays stopped. The lens file stays open. One later read may go on. */
|
|
810
|
+
/** A search stays stopped. The lens file stays open. One later read may go on, then the next read stops. */
|
|
775
811
|
export function lensBeforeResearch(root, toolName, text) {
|
|
776
812
|
const spoken = String(text || '');
|
|
777
813
|
if (!researchCall(toolName, spoken)) return null;
|
|
778
814
|
const names = CARD_PLANES.filter((name) => intentWords(spoken).includes(name));
|
|
779
815
|
if (/^read_file$/i.test(String(toolName || ''))) {
|
|
780
|
-
if (loadReadPass(root))
|
|
781
|
-
|
|
782
|
-
|
|
816
|
+
if (loadReadPass(root) === 'armed') {
|
|
817
|
+
saveReadPass(root, true);
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
if (loadReadPass(root) !== 'spent') {
|
|
821
|
+
if (names.length === 1) stampPlane(root, names[0]);
|
|
822
|
+
saveReadPass(root, false);
|
|
823
|
+
}
|
|
783
824
|
return { gate: 'lens', decision: 'deny', reason: lensPage(root, names) };
|
|
784
825
|
}
|
|
785
826
|
if (names.length === 1) stampPlane(root, names[0]);
|
|
@@ -825,22 +866,17 @@ function seenFields(plane, root) {
|
|
|
825
866
|
};
|
|
826
867
|
}
|
|
827
868
|
|
|
828
|
-
function
|
|
829
|
-
if (key === 'files')
|
|
830
|
-
|
|
831
|
-
return Array.isArray(stored.files) ? stored.files.map(String) : [];
|
|
832
|
-
}
|
|
833
|
-
if (seen[key]) return seen[key];
|
|
834
|
-
return stored[key] || '';
|
|
869
|
+
function freshField(seen, key) {
|
|
870
|
+
if (key === 'files') return Array.isArray(seen.files) ? seen.files.map(String) : [];
|
|
871
|
+
return seen[key] || '';
|
|
835
872
|
}
|
|
836
873
|
|
|
837
874
|
export function growPlane(id, platesDir, root) {
|
|
838
875
|
const seen = seenFields(assemblePlane(id, platesDir), sourceRoot(platesDir));
|
|
839
876
|
const views = loadViews(root);
|
|
840
|
-
const stored = views[id] && typeof views[id] === 'object' ? views[id] : {};
|
|
841
877
|
const grown = { id };
|
|
842
|
-
for (const key of VIEW_KEYS) grown[key] =
|
|
843
|
-
if (id === 'ground') grown.from =
|
|
878
|
+
for (const key of VIEW_KEYS) grown[key] = freshField(seen, key);
|
|
879
|
+
if (id === 'ground') grown.from = '';
|
|
844
880
|
views[id] = {};
|
|
845
881
|
for (const key of VIEW_KEYS) views[id][key] = grown[key];
|
|
846
882
|
saveViews(root, views);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0xray",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.36",
|
|
4
4
|
"description": "Multi-agent orchestration and Codex governance for OpenCode, Hermes, Grok Build, and OpenClaw",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"license": "MIT",
|
|
@@ -171,6 +171,7 @@
|
|
|
171
171
|
"scripts/mjs/verify-four-floor-compact.mjs",
|
|
172
172
|
"scripts/mjs/run-repertoire-mcp.mjs",
|
|
173
173
|
"scripts/mjs/run-goggles-mcp.mjs",
|
|
174
|
+
"scripts/mjs/run-grok-hook.mjs",
|
|
174
175
|
"scripts/mjs/verify-delegation-gate-core.mjs",
|
|
175
176
|
"scripts/mjs/delegation-gate-fixture.mjs",
|
|
176
177
|
"src/opencode/",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Runs a Grok hook from dist, or from src when a rebuild has removed dist.
|
|
4
|
+
* The hook command stays on this file, which prebuild does not delete.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
7
|
+
import { dirname, join } from 'node:path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const root = join(here, '..', '..');
|
|
12
|
+
|
|
13
|
+
export function resolveGrokHook(packageRoot, name) {
|
|
14
|
+
const base = String(name || '');
|
|
15
|
+
if (!base || base.includes('/') || base.includes('..') || !base.endsWith('.js')) return '';
|
|
16
|
+
const candidates = [
|
|
17
|
+
join(packageRoot, 'dist', 'integrations', 'grok', 'hooks', base),
|
|
18
|
+
join(packageRoot, 'src', 'integrations', 'grok', 'hooks', base),
|
|
19
|
+
];
|
|
20
|
+
return candidates.find((file) => existsSync(file)) || '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function allow() {
|
|
24
|
+
process.stdout.write('{"decision":"allow"}\n');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function main() {
|
|
29
|
+
const name = process.argv[2];
|
|
30
|
+
const hit = resolveGrokHook(root, name);
|
|
31
|
+
if (!hit) allow();
|
|
32
|
+
const extra = process.argv.slice(3);
|
|
33
|
+
process.argv = [process.argv[0], hit, ...extra];
|
|
34
|
+
try {
|
|
35
|
+
await import(pathToFileURL(hit).href);
|
|
36
|
+
} catch {
|
|
37
|
+
allow();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function samePath(left, right) {
|
|
42
|
+
try {
|
|
43
|
+
return realpathSync(left) === realpathSync(right);
|
|
44
|
+
} catch {
|
|
45
|
+
return left === right;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const invoked = Boolean(process.argv[1]) && samePath(process.argv[1], fileURLToPath(import.meta.url));
|
|
50
|
+
if (invoked) await main();
|
|
@@ -228,10 +228,10 @@ function mergeOpencodeJson(targetDir, packageRoot, log) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
function grokHookShellCommand(packageRoot, scriptName, extraArgs) {
|
|
231
|
-
const script = path.join(packageRoot, "
|
|
231
|
+
const script = path.join(packageRoot, "scripts", "mjs", "run-grok-hook.mjs");
|
|
232
232
|
const extra = extraArgs ? ` ${extraArgs}` : "";
|
|
233
233
|
// JSON.stringify quotes so spaces/$ in install paths still exec (Grok fail-opens on crash).
|
|
234
|
-
return `XRAY_AI_PATH=${JSON.stringify(packageRoot)} node ${JSON.stringify(script)}${extra}`;
|
|
234
|
+
return `XRAY_AI_PATH=${JSON.stringify(packageRoot)} node ${JSON.stringify(script)} ${scriptName}${extra}`;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
function patchGrokHookEntry(hook, packageRoot, targetDir, scriptName, extraArgs) {
|
|
@@ -260,7 +260,7 @@ function ensureGrokHookEvent(hooks, eventName) {
|
|
|
260
260
|
function patchGrokHooks(pluginDir, packageRoot, targetDir, log, label) {
|
|
261
261
|
const hooksDir = path.join(pluginDir, "hooks");
|
|
262
262
|
const hooksPath = path.join(hooksDir, "hooks.json");
|
|
263
|
-
const hookScript = path.join(packageRoot, "
|
|
263
|
+
const hookScript = path.join(packageRoot, "scripts", "mjs", "run-grok-hook.mjs");
|
|
264
264
|
if (!fs.existsSync(hooksPath)) {
|
|
265
265
|
if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
|
|
266
266
|
if (!fs.existsSync(hooksPath)) return;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
8
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" pre-tool-use.js",
|
|
9
9
|
"timeout": 30
|
|
10
10
|
}
|
|
11
11
|
]
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"hooks": [
|
|
17
17
|
{
|
|
18
18
|
"type": "command",
|
|
19
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
19
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js",
|
|
20
20
|
"timeout": 30
|
|
21
21
|
}
|
|
22
22
|
]
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
30
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
30
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=user_prompt_submit",
|
|
31
31
|
"timeout": 30
|
|
32
32
|
}
|
|
33
33
|
]
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"hooks": [
|
|
39
39
|
{
|
|
40
40
|
"type": "command",
|
|
41
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
41
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" post-tool-use.js",
|
|
42
42
|
"timeout": 30
|
|
43
43
|
}
|
|
44
44
|
]
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"hooks": [
|
|
50
50
|
{
|
|
51
51
|
"type": "command",
|
|
52
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
52
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=pre_compact",
|
|
53
53
|
"timeout": 30
|
|
54
54
|
}
|
|
55
55
|
]
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"hooks": [
|
|
61
61
|
{
|
|
62
62
|
"type": "command",
|
|
63
|
-
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/
|
|
63
|
+
"command": "XRAY_AI_PATH=\"${XRAY_AI_PATH:-node_modules/0xray}\" node \"${XRAY_AI_PATH:-node_modules/0xray}/scripts/mjs/run-grok-hook.mjs\" session-start.js --hook-event=post_compact",
|
|
64
64
|
"timeout": 30
|
|
65
65
|
}
|
|
66
66
|
]
|