0xray 4.0.27 → 4.0.29

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.
@@ -21,6 +21,7 @@ credentials.json
21
21
  *.key
22
22
 
23
23
  # 0xray suit artifacts (postinstall / bridge install)
24
+ .xray/state/cursor-hook-wear/
24
25
  .grok/
25
26
  .mcp.json
26
27
  opencode.json
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "4.0.27",
3
+ "version": "4.0.29",
4
4
  "description": "0xRay suit for Grok, Hermes, OpenClaw, OpenCode: mill+inspect, Codex, 7 MCP servers. Costume 45/42 is opt-in foundry, not the default wear.",
5
5
  "author": {
6
6
  "name": "0xRay AI",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./features.schema.json",
3
- "version": "4.0.27",
3
+ "version": "4.0.29",
4
4
  "description": "0xRay Framework - Unified Feature Configuration",
5
5
  "suit_temperament": {
6
6
  "profile": "auto",
package/README.md CHANGED
@@ -52,7 +52,9 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
52
52
  | Version | Highlights |
53
53
  |---------|------------|
54
54
  | **Unreleased** | Fasten is **foundry-plant/0**: builtin plant is mill+inspect only. `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill declares (looker / vibe / mixer live in the mill). Nested mill `@0xray/foundry@0.1.12`. CLI shims `foundry blip\|sound` remain (0.1.11+). Shop plant first-class (`shop-extract`, `shop-witness`, `shop-pin` + `foundry.json` `shopPlant`). |
55
- | **4.0.27** | Host compact proof needs a generation id and a numeric usage percent, and the same preCompact receipt is mirrored to nested consumer wear roots. Memory scoring: [docs/memory-scoring.md](docs/memory-scoring.md) (orchestrator links it). Companion `@0xray/grok-bot` 0.1.6: general page vs `house/` overlay, `templates/house/`, `grok-bot house init`, doctor `GROK_BOT_HOUSE` or walk-up. Not published. |
55
+ | **4.0.29** | `npm install` only creates the relative `node_modules/@0xray/repertoire` link to the vendored repertoire and prints `npx 0xray wear`. Run `npx 0xray wear` after install. Wear writes only the consumer project's `.cursor/hooks.json` (installed dist hooks, beside user entries); `npx 0xray unwear` restores the snapshot. Companion `@0xray/grok-bot` 0.1.8. Released 2026-09-28. |
56
+ | **4.0.28** | MCP servers start through `scripts/node/mcp-launch.cjs` and only see PATH, HOME, XRAY_ROOT, plus names on their keep list. Tokens such as NPM_TOKEN are not inherited. User env values stay out of argv. Postinstall keeps user edits to AGENTS.md, .mcp.json, and opencode.json. `house init` does not copy EXAMPLE.md; doctor FAILs while `house/EXAMPLE.md` exists. Inspect adds a machinePlugin check. The Cursor hook defaults XRAY_ROOT to the suit. Companion `@0xray/grok-bot` 0.1.7. Released 2026-09-27. |
57
+ | **4.0.27** | Host compact proof needs a generation id and a numeric usage percent, and the same preCompact receipt is mirrored to nested consumer wear roots. Memory scoring: [docs/memory-scoring.md](docs/memory-scoring.md) (orchestrator links it). Companion `@0xray/grok-bot` 0.1.6: general page vs `house/` overlay, `templates/house/`, `grok-bot house init`, doctor `GROK_BOT_HOUSE` or walk-up. Released 2026-09-26. |
56
58
  | **4.0.9** | Mill target: postinstall does not mill npm global prefix or `_npx` as a consumer. Isolated HOME skips machine `~/.grok`. |
57
59
  | **4.0.0** | Exo + temperament. Constitution always on; ceremony by host. Repertoire organ on (vendored 0.2). Station card survives compact/host-swap. **On npm.** [vision](docs-site/docs/architecture/v4-vision.md) · [now](docs-site/docs/architecture/v4-now.md) |
58
60
  | **3.4.1** | Unified `install-bridges.cjs` on postinstall — OpenCode, Grok, Hermes, OpenClaw in one pass. All 7 MCPs servers via `npx -y 0xray mcp <cmd>` (no `dist/` paths). Canonical `release.mjs` pipeline. |
@@ -64,7 +66,7 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
64
66
 
65
67
  ### Consolidations
66
68
 
67
- - **One consumer install path** — `postinstall.cjs` → `installAllBridges()` replaces scattered per-platform setup. Mill target is the consumer project, not npm global prefix or `_npx`.
69
+ - **Install, then wear, then setup where needed** — `npm install` runs `postinstall.cjs`, which writes nothing and prints one line: `npx 0xray wear`. `wear` and `unwear` set up or remove the Cursor suit. `npx 0xray setup` writes `.mcp.json` and the OpenCode, Grok, Hermes, and OpenClaw bridges (`install-bridges.cjs`); git hooks only with `--git-hooks`. Mill target is the consumer project, not npm global prefix or `_npx`.
68
70
  - **7-server MCP surface** — `.mcp.json` SSOT; Grok plugin and all bridges share `XRAY_MCP_SERVERS`.
69
71
  - **Dev vs consumer AGENTS** — `AGENTS.md` (framework) vs `AGENTS-consumer.md` (copied to consumer projects on install).
70
72
  - **Release pipeline** — `npm run release:patch|minor|major` → reconcile → gate → artifacts → tag → publish.
@@ -21,6 +21,7 @@ credentials.json
21
21
  *.key
22
22
 
23
23
  # 0xray suit artifacts (postinstall / bridge install)
24
+ .xray/state/cursor-hook-wear/
24
25
  .grok/
25
26
  .mcp.json
26
27
  opencode.json
package/dist/CHANGELOG.md CHANGED
@@ -41,6 +41,22 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
41
41
  - Archive root `integrations/` costume SKILL dump → `docs/archive/integrations-costume/`. Live wear is `src/integrations`.
42
42
  - Archive root `commands/` stale copy → `docs/archive/commands-copy/`. Live wear is `src/opencode/commands`.
43
43
 
44
+ ## [4.0.29] - 2026-09-28
45
+
46
+ Next patch after npm `4.0.28`. Released 2026-09-28.
47
+
48
+ `npm install` now only creates the relative `node_modules/@0xray/repertoire` link to the vendored repertoire and prints one line: `npx 0xray wear`. It no longer writes the suit. Run `npx 0xray wear` after install. Wear writes only the consumer project's `.cursor/hooks.json`, pointing at the installed package's dist hooks, and adds them beside existing user entries. It snapshots the pre-wear file under `.xray/state/cursor-hook-wear/`, and `npx 0xray unwear` restores it. `npx 0xray setup` writes `.mcp.json` and the OpenCode, Grok, Hermes, and OpenClaw bridges. Companion package `@0xray/grok-bot` is unchanged at 0.1.8.
49
+
50
+ ---
51
+
52
+ ## [4.0.28] - 2026-09-26
53
+
54
+ Next patch after npm `4.0.27`. Released 2026-09-27.
55
+
56
+ MCP servers start through `scripts/node/mcp-launch.cjs` and only see PATH, HOME, and XRAY_ROOT, plus the names on their keep list. Tokens such as NPM_TOKEN are no longer inherited. User env values stay out of argv. Postinstall keeps user edits to AGENTS.md, .mcp.json, and opencode.json. `house init` no longer copies EXAMPLE.md, and doctor FAILs while `house/EXAMPLE.md` exists. Inspect has a new machinePlugin check. The Cursor hook defaults XRAY_ROOT to the suit directory. Companion package `@0xray/grok-bot@0.1.7`.
57
+
58
+ ---
59
+
44
60
  ## [4.0.27] - 2026-09-25
45
61
 
46
62
  Next patch after npm `4.0.26`. Released 2026-09-26.
package/dist/README.md CHANGED
@@ -52,7 +52,9 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
52
52
  | Version | Highlights |
53
53
  |---------|------------|
54
54
  | **Unreleased** | Fasten is **foundry-plant/0**: builtin plant is mill+inspect only. `foundry.json` `"plant": "@0xray/blip"` (aliases `"plant": "blip"` / `"plant": "sound"`) fastens whatever that mill declares (looker / vibe / mixer live in the mill). Nested mill `@0xray/foundry@0.1.12`. CLI shims `foundry blip\|sound` remain (0.1.11+). Shop plant first-class (`shop-extract`, `shop-witness`, `shop-pin` + `foundry.json` `shopPlant`). |
55
- | **4.0.27** | Host compact proof needs a generation id and a numeric usage percent, and the same preCompact receipt is mirrored to nested consumer wear roots. Memory scoring: [docs/memory-scoring.md](docs/memory-scoring.md) (orchestrator links it). Companion `@0xray/grok-bot` 0.1.6: general page vs `house/` overlay, `templates/house/`, `grok-bot house init`, doctor `GROK_BOT_HOUSE` or walk-up. Not published. |
55
+ | **4.0.29** | `npm install` only creates the relative `node_modules/@0xray/repertoire` link to the vendored repertoire and prints `npx 0xray wear`. Run `npx 0xray wear` after install. Wear writes only the consumer project's `.cursor/hooks.json` (installed dist hooks, beside user entries); `npx 0xray unwear` restores the snapshot. Companion `@0xray/grok-bot` 0.1.8. Released 2026-09-28. |
56
+ | **4.0.28** | MCP servers start through `scripts/node/mcp-launch.cjs` and only see PATH, HOME, XRAY_ROOT, plus names on their keep list. Tokens such as NPM_TOKEN are not inherited. User env values stay out of argv. Postinstall keeps user edits to AGENTS.md, .mcp.json, and opencode.json. `house init` does not copy EXAMPLE.md; doctor FAILs while `house/EXAMPLE.md` exists. Inspect adds a machinePlugin check. The Cursor hook defaults XRAY_ROOT to the suit. Companion `@0xray/grok-bot` 0.1.7. Released 2026-09-27. |
57
+ | **4.0.27** | Host compact proof needs a generation id and a numeric usage percent, and the same preCompact receipt is mirrored to nested consumer wear roots. Memory scoring: [docs/memory-scoring.md](docs/memory-scoring.md) (orchestrator links it). Companion `@0xray/grok-bot` 0.1.6: general page vs `house/` overlay, `templates/house/`, `grok-bot house init`, doctor `GROK_BOT_HOUSE` or walk-up. Released 2026-09-26. |
56
58
  | **4.0.9** | Mill target: postinstall does not mill npm global prefix or `_npx` as a consumer. Isolated HOME skips machine `~/.grok`. |
57
59
  | **4.0.0** | Exo + temperament. Constitution always on; ceremony by host. Repertoire organ on (vendored 0.2). Station card survives compact/host-swap. **On npm.** [vision](docs-site/docs/architecture/v4-vision.md) · [now](docs-site/docs/architecture/v4-now.md) |
58
60
  | **3.4.1** | Unified `install-bridges.cjs` on postinstall — OpenCode, Grok, Hermes, OpenClaw in one pass. All 7 MCPs servers via `npx -y 0xray mcp <cmd>` (no `dist/` paths). Canonical `release.mjs` pipeline. |
@@ -64,7 +66,7 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
64
66
 
65
67
  ### Consolidations
66
68
 
67
- - **One consumer install path** — `postinstall.cjs` → `installAllBridges()` replaces scattered per-platform setup. Mill target is the consumer project, not npm global prefix or `_npx`.
69
+ - **Install, then wear, then setup where needed** — `npm install` runs `postinstall.cjs`, which writes nothing and prints one line: `npx 0xray wear`. `wear` and `unwear` set up or remove the Cursor suit. `npx 0xray setup` writes `.mcp.json` and the OpenCode, Grok, Hermes, and OpenClaw bridges (`install-bridges.cjs`); git hooks only with `--git-hooks`. Mill target is the consumer project, not npm global prefix or `_npx`.
68
70
  - **7-server MCP surface** — `.mcp.json` SSOT; Grok plugin and all bridges share `XRAY_MCP_SERVERS`.
69
71
  - **Dev vs consumer AGENTS** — `AGENTS.md` (framework) vs `AGENTS-consumer.md` (copied to consumer projects on install).
70
72
  - **Release pipeline** — `npm run release:patch|minor|major` → reconcile → gate → artifacts → tag → publish.
package/dist/cli/index.js CHANGED
@@ -45,52 +45,71 @@ program
45
45
  .name("0xray")
46
46
  .description("xray: Bulletproof AI orchestration with systematic error prevention")
47
47
  .version(version);
48
- function runSetup() {
49
- const setupScript = join(packageRoot, "scripts", "node", "setup.cjs");
50
- validateScriptPath(setupScript, "setup script");
51
- execSync(`node "${setupScript}"`, { stdio: "inherit", cwd: process.cwd() });
48
+ function runNodeScript(scriptName, args = []) {
49
+ const script = join(packageRoot, "scripts", "node", scriptName);
50
+ validateScriptPath(script, scriptName);
51
+ const quoted = [script, ...args].map((part) => `"${part}"`).join(" ");
52
+ execSync(`node ${quoted}`, { stdio: "inherit", cwd: process.cwd() });
52
53
  }
53
54
  program
54
55
  .command("install")
55
56
  .description("Install xray framework in the current project")
56
- .action(async () => {
57
- frameworkLogger.log('cli', 'install-start', 'info', { message: 'Installing xray framework...' });
57
+ .action(() => {
58
+ frameworkLogger.log('cli', 'install', 'info', { message: 'nothing written' });
59
+ process.stdout.write("nothing written\n");
60
+ });
61
+ program
62
+ .command("setup")
63
+ .description("Set up .mcp.json and the OpenCode, Grok, Hermes, and OpenClaw bridges")
64
+ .option("--git-hooks", "also install git hooks")
65
+ .action((opts) => {
66
+ const args = ["setup"];
67
+ if (opts.gitHooks)
68
+ args.push("--git-hooks");
58
69
  try {
59
- const postinstallScript = join(packageRoot, "scripts", "node", "postinstall.cjs");
60
- validateScriptPath(postinstallScript, "postinstall script");
61
- execSync(`node "${postinstallScript}"`, { stdio: "inherit", cwd: process.cwd() });
62
- frameworkLogger.log('cli', 'install-success', 'info', { message: 'xray framework installed' });
63
- // UX banner kept for user visibility post-install (non-removable per exception)
64
- console.log("✅ xray framework installed!");
65
- console.log("💡 Run 'npx xray setup' for full configuration (hooks, Hermes, symlinks)");
70
+ runNodeScript("install-bridges.cjs", args);
66
71
  }
67
72
  catch (error) {
68
- frameworkLogger.log('cli', 'install-error', 'error', { error: error instanceof Error ? error.message : String(error) });
69
- console.error("❌ Installation failed:", error instanceof Error ? error.message : String(error));
73
+ frameworkLogger.log("cli", "setup-error", "error", {
74
+ error: error instanceof Error ? error.message : String(error),
75
+ });
70
76
  process.exit(1);
71
77
  }
72
78
  });
73
79
  program
74
- .command("setup")
75
- .description("Full framework setup: hooks, Hermes integration, symlinks, MCP paths")
76
- .action(runSetup);
80
+ .command("wear")
81
+ .description("Wear the 0xray suit in this git checkout")
82
+ .action(() => {
83
+ try {
84
+ runNodeScript("wear-cursor-hooks.cjs");
85
+ }
86
+ catch (error) {
87
+ frameworkLogger.log('cli', 'wear-error', 'error', {
88
+ error: error instanceof Error ? error.message : String(error),
89
+ });
90
+ process.exit(1);
91
+ }
92
+ });
77
93
  program
78
- .command("init")
79
- .description("Initialize xray configuration in the current project")
80
- .action(async () => {
81
- console.log("🚀 xray CLI: Initializing configuration...");
94
+ .command("unwear")
95
+ .description("Remove the 0xray suit and restore the pre-wear hooks file")
96
+ .action(() => {
82
97
  try {
83
- const postinstallScript = join(packageRoot, "scripts", "node", "postinstall.cjs");
84
- validateScriptPath(postinstallScript, "postinstall script");
85
- execSync(`node "${postinstallScript}"`, { stdio: "inherit", cwd: process.cwd() });
86
- runSetup();
87
- console.log("✅ xray configuration initialized!");
98
+ runNodeScript("unwear-cursor-hooks.cjs");
88
99
  }
89
100
  catch (error) {
90
- console.error("❌ Initialization failed:", error instanceof Error ? error.message : String(error));
101
+ frameworkLogger.log('cli', 'unwear-error', 'error', {
102
+ error: error instanceof Error ? error.message : String(error),
103
+ });
91
104
  process.exit(1);
92
105
  }
93
106
  });
107
+ program
108
+ .command("init")
109
+ .description("Initialize xray configuration in the current project")
110
+ .action(() => {
111
+ process.stdout.write("nothing written\n");
112
+ });
94
113
  program
95
114
  .command("status")
96
115
  .description("Show comprehensive xray framework status")
@@ -341,22 +360,7 @@ program
341
360
  console.log("===========================");
342
361
  console.log("");
343
362
  try {
344
- console.log("Running postinstall setup to restore configuration...");
345
- // Run the postinstaller script (same as install command)
346
- const postinstallScript = join(packageRoot, "scripts", "node", "postinstall.cjs");
347
- // SECURITY: Validate script path before execution
348
- validateScriptPath(postinstallScript, "postinstall script");
349
- execSync(`node "${postinstallScript}"`, {
350
- stdio: "inherit",
351
- cwd: process.cwd(),
352
- });
353
- console.log("");
354
- console.log("🎉 Framework configuration restored successfully!");
355
- console.log("");
356
- console.log("💡 Next steps:");
357
- console.log(" • Restart OpenCode to load the restored configuration");
358
- console.log(" • Run: npx xray health (to verify everything works)");
359
- console.log(" • Try: @security-auditor scan this project");
363
+ process.stdout.write("nothing written\n");
360
364
  }
361
365
  catch (error) {
362
366
  console.error("❌ Fix command failed:", error instanceof Error ? error.message : String(error));
@@ -451,6 +455,10 @@ program
451
455
  }
452
456
  // Check configuration - check for opencode.json or .xray/ (min compat .xray/ fallback for prior 0xRay consumer runtime per Scope Rule; plain xray primary)
453
457
  const cwd = process.cwd();
458
+ const wearMeta = path.join(cwd, ".xray", "state", "cursor-hook-wear", "meta.json");
459
+ if (!fs.existsSync(wearMeta)) {
460
+ process.stdout.write("not worn\n");
461
+ }
454
462
  const opencodeConfigPath = path.join(cwd, "opencode.json");
455
463
  const xrayDir = getConfigDir(cwd);
456
464
  const opencodeExists = fs.existsSync(opencodeConfigPath);
@@ -10,10 +10,25 @@ Repertoire is preferred (vendored, dest = named laws). Station + Codex gates sti
10
10
  2. Cursor Cloud Agent clones the repo and loads **project** hooks. Desktop Agent Chat does the same when the folder is the workspace.
11
11
  3. Hook names are camelCase: `preToolUse`, `preCompact`, `afterFileEdit`, plus Cloud `beforeShellExecution` / `beforeReadFile`. Command is a **relative** `.cursor/hooks/*.sh` — Cloud execs argv[0] without a shell. Do not put `XRAY_AI_PATH=` in `hooks.json`. Stdin is JSON; stdout is JSON.
12
12
  4. First `preToolUse` or `afterFileEdit` writes `.xray/state/session-boot.json` + `.xray/state/STATION.md`. After a compact, **Read STATION.md**. The host does not inject it. A multi-repo Cloud wrapper (`/agent` with `repos/xray`) is not a dest heat root, even if a leftover wrapper card exists. Heat the mill under `repos/` and the Read path. Dest `EACCES` fail-opens — a wired hook must not deny every tool.
13
- 5. `install-bridges` also fastens the daemon workspace root when it can see `repos/xray` above the checkout, then pokes local `ReloadAgentSkills` so a mid-session wear binds this daemon. Command stays a relative `.cursor/hooks/*.sh`.
13
+ 5. When the package has no dist hook scripts yet, `install-bridges` fastens the daemon workspace root if it can see `repos/xray` above the checkout. A consumer install that already has dist scripts writes only that consumer project directory. `ReloadAgentSkills` still runs so a mid-session wear can bind this daemon. Dogfood commands stay a relative `.cursor/hooks/*.sh`.
14
14
  6. `preCompact` is observational (cannot block). Cloud **does** fire it ([hooks.md](https://cursor.com/docs/hooks.md) support matrix: Yes) once hooks are bound and the host actually compacts. Stdout is only `{ "user_message": "…" }` — that is a notice, not a spawn. The spawn id is stdin `conversation_id` (same `bc-`) plus Task's returned agent id. Resume that id. Do not cold-start a twin. Station merge keeps ticket / seed / Durable / unfinished keys.
15
15
 
16
- Copy the consumer template from `src/integrations/cursor/hooks/hooks.json` and the sibling `.sh` runners. `xray-cloud-hook.sh` finds mill JS at `XRAY_AI_PATH`, `../xray`, or `node_modules/0xray` (dist then src). This exo repo wears `src/integrations/cursor/hooks/*.js` so a cloud can run before `npm run build`.
16
+ Copy the consumer template from `src/integrations/cursor/hooks/hooks.json` and the sibling `.sh` runners. This exo repo's own `.cursor/hooks.json` stays on `.cursor/hooks/*.sh` so dogfood can run `src/integrations/cursor/hooks/*.js` before `npm run build`.
17
+
18
+ Consumer wear (`installCursorBridge` / `wearCursorHooks`, including `npm run wear` in a suited bench) writes only that consumer project's `.cursor/hooks.json`. It does not rewrite a parent checkout's committed hooks, `CURSOR_PROJECT_DIR`, or a `repos/xray` directory above the project. Those outer paths are written only when `wearCursorHooks` is called with `{ outerRoots: true }`, and each one is printed as `cursor-wear: wrote outer hooks at <path>`. The five shipped events (`preToolUse`, `preCompact`, `afterFileEdit`, `beforeShellExecution`, `beforeReadFile`) are added beside existing user entries, unless that event already runs `xray-cloud-hook.sh` (the committed `.cursor/hooks/*.sh` runners). A suited bench that already commits those runners is left byte for byte. The new command, when one is added, is a relative path to the installed package:
19
+
20
+ `node_modules/0xray/dist/integrations/cursor/hooks/<event>.sh`
21
+
22
+ Wear does not copy scripts into `.cursor/hooks/`. A user's `.cursor/hooks/<name>.sh` stays as it is, including when the name matches a shipped script, and wear adds the dist command beside it. A `hooks.json` that contains `//` comments is merged in place. A duplicate `hooks` key, or any other file that cannot be parsed safely, makes wear stop and write nothing.
23
+
24
+ Install the package first (`npm install`), then `npx 0xray wear`. Postinstall writes nothing. Wearing again writes the same bytes and takes a fresh snapshot every time. The pre-wear copy is `<project>/.xray/state/cursor-hook-wear/` (gitignored, and left out of the npm package), so removing `node_modules` does not delete it. When wear creates that folder and the path is not already ignored, it appends the path to `$GIT_DIR/info/exclude`. Wear stores the user bytes, never an already-worn `hooks.json`. Restore with `npx 0xray unwear` (or `node node_modules/0xray/scripts/node/unwear-cursor-hooks.cjs`). Unwear prints `restored` only when the file bytes match that snapshot exactly. If the file changed or cannot be parsed, unwear exits non-zero, says what differs, and leaves the snapshot in place. Wear refuses, and writes nothing, when git is missing, the directory is not a work tree, `GIT_DIR` is bare, or `hooks.json` cannot be parsed.
25
+
26
+ `xray-cloud-hook.sh` resolution order (first file that exists wins). Each run appends one `cursor-hook-invoke.log` line with `js=` set to the absolute path it executes:
27
+
28
+ 1. Consumer `node_modules/0xray/dist/integrations/cursor/hooks/${JS}`, walking upward from the script directory, then from cwd. A directory whose `package.json` name is `0xray` is skipped, so a factory checkout does not prefer its own `node_modules` or a nested example over `src/`.
29
+ 2. Sibling `${JS}` when the runner itself lives under `node_modules/0xray`.
30
+ 3. `$XRAY_AI_PATH` `src/` then `dist/` (explicit mill).
31
+ 4. Dogfood `src/` at cwd, `../xray`, `repos/xray`, and `$CURSOR_PROJECT_DIR/repos/xray`, then dist at cwd, then `cwd/node_modules/0xray`.
17
32
 
18
33
  ## Contracts
19
34
 
@@ -64,3 +79,7 @@ echo '{"hook_event_name":"preCompact","trigger":"auto","context_tokens":1}' \
64
79
  ```
65
80
 
66
81
  Path C seed + receipt: `examples/cursor-cloud-compact/`. Suited Arm S (real usage, no FILL): `examples/killer-dual/`.
82
+
83
+ ## Known limits
84
+
85
+ `findLocalExecDaemon` reads the local exec-daemon auth token from `/proc/<pid>/cmdline` (`scripts/node/install-bridges.cjs:927`–`:935`). That read needs a separate security review. Wear does not change it.
@@ -1,6 +1,19 @@
1
1
  #!/bin/sh
2
2
  # Cloud-safe Cursor hook runner. Command in hooks.json must be a relative path.
3
3
  # Do not put env-assignment in hooks.json — Cloud execs argv[0] without a shell.
4
+ #
5
+ # Resolution order (first existing file wins):
6
+ # 1. Consumer install. Walk upward from this script's directory, then from cwd.
7
+ # Use node_modules/0xray/dist/integrations/cursor/hooks/${JS}.
8
+ # Skip a directory whose package.json "name" is "0xray" (the factory repo).
9
+ # That keeps dogfood on src/ and prefers a consumer install over a parent src/.
10
+ # 2. Sibling ${JS} when this runner lives under node_modules/0xray (installed dist).
11
+ # 3. XRAY_AI_PATH, if set: src then dist (explicit mill, dogfood path shape).
12
+ # 4. Dogfood repo source: cwd/src, cwd/../xray/src, cwd/repos/xray/src,
13
+ # ${CURSOR_PROJECT_DIR}/repos/xray/src, then those roots' dist/, then
14
+ # cwd/node_modules/0xray (dist then src).
15
+ # Consumer wear points hooks.json at the installed dist *.sh so step 1/2 run
16
+ # even when Cursor's cwd is a parent factory checkout.
4
17
  set -eu
5
18
 
6
19
  EVENT="${1:-preToolUse}"
@@ -11,7 +24,7 @@ REL=""
11
24
  pick_mill() {
12
25
  mill="$1"
13
26
  rel="$2"
14
- if [ -f "${mill}/${rel}" ]; then
27
+ if [ -n "$mill" ] && [ -f "${mill}/${rel}" ]; then
15
28
  MILL="$mill"
16
29
  REL="$rel"
17
30
  return 0
@@ -19,15 +32,54 @@ pick_mill() {
19
32
  return 1
20
33
  }
21
34
 
22
- if [ -n "${XRAY_AI_PATH:-}" ]; then
35
+ is_xray_package() {
36
+ pkg="$1/package.json"
37
+ [ -f "$pkg" ] || return 1
38
+ grep -q '"name"[[:space:]]*:[[:space:]]*"0xray"' "$pkg" 2>/dev/null || return 1
39
+ return 0
40
+ }
41
+
42
+ pick_consumer_from() {
43
+ dir="$1"
44
+ [ -n "$dir" ] || return 1
45
+ while [ -n "$dir" ]; do
46
+ if ! is_xray_package "$dir"; then
47
+ if pick_mill "$dir/node_modules/0xray" "dist/integrations/cursor/hooks/${JS}"; then
48
+ return 0
49
+ fi
50
+ fi
51
+ parent="$(dirname -- "$dir")"
52
+ if [ "$parent" = "$dir" ]; then
53
+ break
54
+ fi
55
+ dir="$parent"
56
+ done
57
+ return 1
58
+ }
59
+
60
+ HERE="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
61
+
62
+ pick_consumer_from "$HERE" || pick_consumer_from "$(pwd)" || true
63
+
64
+ if [ -z "$MILL" ]; then
65
+ case "$HERE" in
66
+ */node_modules/0xray/*)
67
+ pick_mill "$HERE" "${JS}" || true
68
+ ;;
69
+ esac
70
+ fi
71
+
72
+ if [ -z "$MILL" ] && [ -n "${XRAY_AI_PATH:-}" ]; then
23
73
  pick_mill "${XRAY_AI_PATH}" "src/integrations/cursor/hooks/${JS}" || \
24
74
  pick_mill "${XRAY_AI_PATH}" "dist/integrations/cursor/hooks/${JS}" || true
25
75
  fi
76
+
26
77
  if [ -z "$MILL" ]; then
27
78
  pick_mill "$(pwd)" "src/integrations/cursor/hooks/${JS}" || \
28
79
  pick_mill "$(pwd)/../xray" "src/integrations/cursor/hooks/${JS}" || \
29
80
  pick_mill "$(pwd)/repos/xray" "src/integrations/cursor/hooks/${JS}" || \
30
81
  pick_mill "${CURSOR_PROJECT_DIR:-}/repos/xray" "src/integrations/cursor/hooks/${JS}" || \
82
+ pick_mill "$(pwd)" "dist/integrations/cursor/hooks/${JS}" || \
31
83
  pick_mill "$(pwd)/node_modules/0xray" "dist/integrations/cursor/hooks/${JS}" || \
32
84
  pick_mill "$(pwd)/node_modules/0xray" "src/integrations/cursor/hooks/${JS}" || true
33
85
  fi
@@ -36,14 +88,19 @@ if [ -n "$MILL" ] && [ -d "$MILL" ]; then
36
88
  MILL="$(cd "$MILL" && pwd)"
37
89
  fi
38
90
 
91
+ JS_ABS=""
92
+ if [ -n "$MILL" ] && [ -n "$REL" ] && [ -f "${MILL}/${REL}" ]; then
93
+ JS_ABS="$(CDPATH= cd -- "$(dirname -- "${MILL}/${REL}")" && pwd)/$(basename -- "${REL}")"
94
+ fi
95
+
39
96
  NODE_BIN="$(command -v node 2>/dev/null || true)"
40
97
 
41
98
  log_invoke() {
42
99
  dest="$1"
43
100
  mkdir -p "${dest}/.xray/state" 2>/dev/null || true
44
101
  {
45
- printf 'ts=%s event=%s cwd=%s mill=%s node=%s\n' \
46
- "$(date -Iseconds)" "$EVENT" "$(pwd)" "${MILL:-MISSING}" "${NODE_BIN:-MISSING}"
102
+ printf 'ts=%s event=%s cwd=%s mill=%s node=%s js=%s\n' \
103
+ "$(date -Iseconds)" "$EVENT" "$(pwd)" "${MILL:-MISSING}" "${NODE_BIN:-MISSING}" "${JS_ABS:-MISSING}"
47
104
  } >> "${dest}/.xray/state/cursor-hook-invoke.log" 2>/dev/null || true
48
105
  }
49
106
 
@@ -59,5 +116,5 @@ fi
59
116
 
60
117
  export XRAY_HOOK_EVENT="$EVENT"
61
118
  export XRAY_AI_PATH="$MILL"
62
- export XRAY_ROOT="$MILL"
119
+ export XRAY_ROOT="${XRAY_ROOT:-$(pwd)}"
63
120
  exec "$NODE_BIN" "${MILL}/${REL}"
@@ -14,6 +14,7 @@ const PLATE_IDS = Object.freeze([
14
14
  "processor",
15
15
  "reporting",
16
16
  "memory-recall",
17
+ "house",
17
18
  ]);
18
19
 
19
20
  /** Id token, then extra phrases. Equal top scores recall nothing. */
@@ -61,6 +62,12 @@ const CUES = {
61
62
  [/recall(?:s|ed|ing)?\s+(?:a\s+|one\s+|the\s+)?plate/i, 5],
62
63
  [/stamp(?:ed|ing)?\s+plates?/i, 4],
63
64
  ],
65
+ house: [
66
+ [/house init/i, 4],
67
+ [/HOUSE\.md/i, 4],
68
+ [/GROK_BOT_HOUSE/i, 4],
69
+ [/setup-house/i, 4],
70
+ ],
64
71
  };
65
72
 
66
73
  function platesDir() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xray-pre-tool",
3
- "version": "4.0.27",
3
+ "version": "4.0.29",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "0xRay constitution + temperament PreToolUse for OpenClaw",
@@ -1,4 +1,4 @@
1
- export declare const PLATE_IDS: readonly ["routing", "governance", "boot", "orchestration", "processor", "reporting", "memory-recall"];
1
+ export declare const PLATE_IDS: readonly ["routing", "governance", "boot", "orchestration", "processor", "reporting", "memory-recall", "house"];
2
2
  export type PlateId = (typeof PLATE_IDS)[number];
3
3
  export interface PlateStamp {
4
4
  id: PlateId;
@@ -13,6 +13,7 @@ export const PLATE_IDS = [
13
13
  'processor',
14
14
  'reporting',
15
15
  'memory-recall',
16
+ 'house',
16
17
  ];
17
18
  function isPlateId(value) {
18
19
  return PLATE_IDS.includes(value);
@@ -0,0 +1,50 @@
1
+ ---
2
+ title: House
3
+ sidebar_label: House
4
+ ---
5
+
6
+ # House
7
+
8
+ One team, one folder. `grok-bot house init` copies `HOUSE.md`, `WAVEBOARD.md`, and `ATTENTION_STATE.md` into `./house`. It does not copy `EXAMPLE.md`. It refuses when a target file is already there. Doctor reads `HOUSE.md` through `GROK_BOT_HOUSE`, or by walking up when that variable is unset, and FAILs with `house/EXAMPLE.md exists — delete it` when that file is present. A wake reads the station, then the board files in `house/`.
9
+
10
+ ```
11
+ ┌──────────────────────────────────────────────────────────────┐
12
+ │ INPUT LAYER │
13
+ │ │
14
+ │ npx @0xray/grok-bot house init │
15
+ │ copies HOUSE.md, WAVEBOARD.md, ATTENTION_STATE.md only │
16
+ │ refuses if a target file is already there │
17
+ └──────────────────────────────┬───────────────────────────────┘
18
+ v
19
+ ┌──────────────────────────────┼───────────────────────────────┐
20
+ │ PROCESSING LAYER v │
21
+ │ │
22
+ │ ┌──────────────────────────────────────────────────────┐ │
23
+ │ │ HOUSE.md — six headings │ │
24
+ │ │ owner · seats · public voice · allowed · ask first │ │
25
+ │ │ board: WAVEBOARD.md + ATTENTION_STATE.md │ │
26
+ │ │ the owner approves Allowed before it counts │ │
27
+ │ └───────────────────────────┬──────────────────────────┘ │
28
+ │ v │
29
+ │ ┌──────────────────────────────────────────────────────┐ │
30
+ │ │ grok-bot doctor │ │
31
+ │ │ GROK_BOT_HOUSE, or walk up for house/HOUSE.md │ │
32
+ │ │ a set path that is missing warns and does not walk │ │
33
+ │ │ a line that starts with (example) fails │ │
34
+ │ │ house/EXAMPLE.md exists — delete it │ │
35
+ │ └───────────────────────────┬──────────────────────────┘ │
36
+ └──────────────────────────────┼───────────────────────────────┘
37
+ v
38
+ ┌──────────────────────────────┼───────────────────────────────┐
39
+ │ OUTPUT LAYER v │
40
+ │ │
41
+ │ read the station, then house/WAVEBOARD.md │
42
+ │ and house/ATTENTION_STATE.md │
43
+ │ a missing board file uses templates/house/ │
44
+ │ one card, one owner │
45
+ │ change the house when the owner says a rule twice │
46
+ └──────────────────────────────────────────────────────────────┘
47
+
48
+ ```
49
+
50
+ stamped · 0xray · @0xray/grok-bot 0.1.7
@@ -13,4 +13,4 @@ The station card stays the short subsystem table. That table is the index still
13
13
 
14
14
  The markdown file is the source of truth for that stamp. When the pipeline changes, edit the plate. A worn project may hold a copy at `.xray/state/plates/<id>.md`. The copy is written only when that file is missing, so a local edit stays.
15
15
 
16
- [Routing](./routing.md) · [Governance](./governance.md) · [Boot](./boot.md) · [Orchestration](./orchestration.md) · [Processor](./processor.md) · [Reporting](./reporting.md) · [Memory recall](./memory-recall.md)
16
+ [Routing](./routing.md) · [Governance](./governance.md) · [Boot](./boot.md) · [Orchestration](./orchestration.md) · [Processor](./processor.md) · [Reporting](./reporting.md) · [Memory recall](./memory-recall.md) · [House](./house.md)
package/llms.txt CHANGED
@@ -12,6 +12,8 @@ Default consumer wear is **mill plant** (`mill` + `inspect`), not a 45-skill / 4
12
12
  - Memory scoring rules: [docs/memory-scoring.md](docs/memory-scoring.md). The orchestrator links that file. It does not copy the rules.
13
13
  - Host `preCompact` writes the hook line and per-session receipt on the suit mill and on nested consumer wear roots (a bench with local `node_modules/0xray`). A counted cut needs a non-empty generation id and a numeric usage percent.
14
14
  - `@0xray/grok-bot`: the general page is `grok-bot/OP-PROC.md`. The team overlay is `house/` and is not in that npm pack. `templates/house/` ships. `grok-bot house init` copies the template and refuses to overwrite. Doctor uses `GROK_BOT_HOUSE` when set, otherwise walks up from the working directory for `house/HOUSE.md`.
15
+ - MCP servers start through `scripts/node/mcp-launch.cjs`. They see PATH, HOME, and XRAY_ROOT, plus the names on their keep list. Tokens such as NPM_TOKEN are not inherited. User env values stay out of argv. Postinstall keeps user edits to AGENTS.md, `.mcp.json`, and `opencode.json`. `house init` does not copy EXAMPLE.md. Doctor FAILs while `house/EXAMPLE.md` exists. Inspect has a machinePlugin check. The Cursor hook defaults XRAY_ROOT to the suit directory.
16
+ - `npm install` only creates the relative `node_modules/@0xray/repertoire` link to the vendored repertoire and prints `npx 0xray wear`. Run `npx 0xray wear` after install; it writes only the consumer project's `.cursor/hooks.json`. `npx 0xray unwear` restores the pre-wear snapshot.
15
17
 
16
18
  - `npx @0xray/foundry mint --skip-live` — re-fasten mill plant, then inspect.
17
19
  - `npx @0xray/foundry inspect --skip-live` — plant-vs-worn, receipt, CI. Chat is not a receipt.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "4.0.27",
3
+ "version": "4.0.29",
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",
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Mill inspect organ. Runs the six plant checks plus observational harness.
3
+ * Mill inspect organ. Runs the plant checks, a machinePlugin check, and observational harness.
4
4
  * Not an 8th MCP. Not PPE. Does not fasten hooks or Repertoire.
5
5
  *
6
6
  * npx @0xray/foundry inspect [--skip-live] [--go] [--go-out PATH]
@@ -515,7 +515,7 @@ export function packagesToProbe(root) {
515
515
  });
516
516
  }
517
517
 
518
- function checkIsolatedHome(root, env = process.env, machine = mint.machineHome()) {
518
+ export function checkIsolatedHome(root, env = process.env, machine = mint.machineHome()) {
519
519
  const isolated = isIsolatedHome(env, machine);
520
520
  const machinePlugin = machineGrokPluginDir(machine);
521
521
  const dests = mint.resolveGrokPluginDests(root, env, machine);
@@ -542,6 +542,50 @@ function checkIsolatedHome(root, env = process.env, machine = mint.machineHome()
542
542
  };
543
543
  }
544
544
 
545
+ function isXrayPluginName(name) {
546
+ return name === "0xray" || name.startsWith("0xray");
547
+ }
548
+
549
+ /** 0xray plugin dirs under the machine HOME's grok plugin folder. */
550
+ export function machineXrayPluginPaths(machine = mint.machineHome()) {
551
+ const pluginsDir = path.join(machine, ".grok", "plugins");
552
+ if (!fs.existsSync(pluginsDir)) return [];
553
+ let names;
554
+ try {
555
+ names = fs.readdirSync(pluginsDir);
556
+ } catch {
557
+ return [];
558
+ }
559
+ return names
560
+ .filter((name) => isXrayPluginName(name))
561
+ .sort()
562
+ .map((name) => path.join(pluginsDir, name));
563
+ }
564
+
565
+ /**
566
+ * `isolated` stays HOME != machine home. This check is whether a machine-level
567
+ * 0xray Grok plugin exists. Overall inspect ok is false when this check fails.
568
+ */
569
+ export function checkMachinePlugin(machine = mint.machineHome()) {
570
+ const machinePlugin = machineGrokPluginDir(machine);
571
+ const hits = machineXrayPluginPaths(machine);
572
+ if (hits.length > 0) {
573
+ return {
574
+ id: "machinePlugin",
575
+ ok: false,
576
+ machinePlugin,
577
+ hits,
578
+ detail: `machine-level ${hits.join(", ")} exists — delete it`,
579
+ };
580
+ }
581
+ return {
582
+ id: "machinePlugin",
583
+ ok: true,
584
+ machinePlugin,
585
+ hits: [],
586
+ };
587
+ }
588
+
545
589
  export async function inspectSuit(root, opts = {}) {
546
590
  const millRoot = opts.millRoot || millPackageDir();
547
591
  const fetchFn = opts.fetch || fetch;
@@ -584,6 +628,7 @@ export async function inspectSuit(root, opts = {}) {
584
628
  }
585
629
 
586
630
  checks.push(checkIsolatedHome(root, env, machineHome));
631
+ checks.push(checkMachinePlugin(machineHome));
587
632
  checks.push(checkHarness(root, requireHarness));
588
633
 
589
634
  const failed = checks.filter((c) => c.ok === false);