0xray 4.1.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/.gitignore.default +4 -1
  2. package/.grok-plugin/plugin.json +1 -1
  3. package/.opencode/skills/orchestrator/SKILL.md +1 -7
  4. package/.xray/features.json +4 -1
  5. package/.xray/features.schema.json +8 -0
  6. package/AGENTS-consumer.md +1 -1
  7. package/AGENTS.md +2 -2
  8. package/README.md +2 -2
  9. package/dist/.gitignore.default +4 -1
  10. package/dist/AGENTS.md +2 -2
  11. package/dist/CHANGELOG.md +27 -1
  12. package/dist/README.md +2 -2
  13. package/dist/cli/commands/opencode-install.js +3 -9
  14. package/dist/cli/commands/skill-install.js +10 -5
  15. package/dist/cli/goggles-status.d.ts +2 -5
  16. package/dist/cli/goggles-status.js +5 -53
  17. package/dist/cli/index.js +2 -2
  18. package/dist/enforcement/enforcer-tools.d.ts +1 -1
  19. package/dist/enforcement/enforcer-tools.js +1 -1
  20. package/dist/integrations/cursor/README.md +1 -1
  21. package/dist/integrations/cursor/hooks/pre-tool-use.js +4 -0
  22. package/dist/integrations/grok/grok-cli.js +5 -7
  23. package/dist/integrations/grok/hooks/grok-hook-utils.js +4 -1
  24. package/dist/integrations/grok/hooks/pre-tool-use.js +4 -18
  25. package/dist/integrations/grok/hooks/session-start.js +4 -2
  26. package/dist/integrations/hermes-agent/bridge.mjs +4 -1
  27. package/dist/integrations/hooks/goggles-mcp.mjs +20 -196
  28. package/dist/integrations/hooks/goggles-pipeline.mjs +606 -616
  29. package/dist/integrations/hooks/goggles-planes.json +88 -40
  30. package/dist/integrations/hooks/plates.cjs +84 -0
  31. package/dist/integrations/hooks/pre-tool-speak.mjs +2 -4
  32. package/dist/integrations/hooks/skill-seek.mjs +115 -0
  33. package/dist/integrations/hooks/station-hook-runtime.cjs +4 -1
  34. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +4 -4
  35. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +10 -1
  36. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  37. package/dist/memory-routing/plates.d.ts +1 -1
  38. package/dist/memory-routing/plates.js +14 -0
  39. package/dist/nucleus/thin-dispatch.d.ts +1 -1
  40. package/dist/nucleus/thin-dispatch.js +1 -1
  41. package/dist/nucleus/work-freshness.mjs +10 -0
  42. package/dist/plugin/xray-codex-injection.js +3 -1
  43. package/dist/skills/orchestrator/SKILL.md +1 -7
  44. package/docs-site/docs/plates/asides.md +35 -0
  45. package/docs-site/docs/plates/confer.md +35 -0
  46. package/docs-site/docs/plates/consumer-mcp.md +35 -0
  47. package/docs-site/docs/plates/delegation-gate.md +35 -0
  48. package/docs-site/docs/plates/dynamo.md +35 -0
  49. package/docs-site/docs/plates/foundry.md +35 -0
  50. package/docs-site/docs/plates/goggles.md +8 -8
  51. package/docs-site/docs/plates/ground.md +35 -0
  52. package/docs-site/docs/plates/hermes.md +35 -0
  53. package/docs-site/docs/plates/index.md +12 -5
  54. package/docs-site/docs/plates/inference-cycle.md +35 -0
  55. package/docs-site/docs/plates/lens-gate.md +6 -6
  56. package/docs-site/docs/plates/openclaw.md +35 -0
  57. package/docs-site/docs/plates/opencode.md +35 -0
  58. package/docs-site/docs/plates/repertoire.md +35 -0
  59. package/docs-site/docs/plates/synthesis.md +35 -0
  60. package/docs-site/docs/plates/temperament.md +35 -0
  61. package/docs-site/docs/plates/trail-state.md +1 -1
  62. package/package.json +1 -1
  63. package/scripts/foundry/README.md +3 -1
  64. package/scripts/foundry/mint-suit.cjs +120 -2
  65. package/scripts/foundry/plant/skills/mill/SKILL.md +1 -1
  66. package/scripts/foundry/reconcile-version.mjs +10 -3
  67. package/scripts/foundry/release.mjs +1 -1
  68. package/scripts/foundry/validate-release-docs.mjs +2 -2
  69. package/scripts/foundry/version-manager.mjs +12 -4
  70. package/scripts/node/install-bridges.cjs +63 -34
  71. package/scripts/node/pack-tmp-suit-proof.mjs +23 -4
  72. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +10 -1
  73. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  74. package/src/skills/orchestrator/SKILL.md +1 -7
  75. package/xray/features.json +4 -1
  76. package/xray/features.schema.json +8 -0
@@ -29,7 +29,10 @@ AGENTS.md
29
29
  SKILLS.md
30
30
 
31
31
  # Runtime state (xray inference workflows, session state)
32
- .xray/
32
+ .xray/*
33
+ !.xray/state/
34
+ .xray/state/*
35
+ !.xray/state/LENS.md
33
36
  .strray/
34
37
  strray/
35
38
  xray/inference/
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "4.1.0",
3
+ "version": "4.1.1",
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",
@@ -57,13 +57,7 @@ When `multi_agent_orchestration.lead_dev_mode` is true in `features.json` (defau
57
57
 
58
58
  The command is `0xray look`. `0xray goggles` is the same organ. A pipe keeps the line card. A terminal shows that card as a pane. Chat seats without hooks use the `goggles` MCP tools `look` and `status_lens`. Same organ.
59
59
 
60
- Kind 0 is actuality. It compares the list in the file to the same six names in the code. It speaks only when those lists differ. A match is quiet.
61
-
62
- Kind 1 names one plane: dichotomy, syncopate, synthesis, or loop. The lens holds that plane. An action on that plane is quiet. An action on another plane, or on a pipeline drawing, is stopped. A pipeline drawing is not a scope stop. Ground is not a scope stop. Scope, when named, is ecosystem, part, one flow, or one artifact, and an action wider than that scope is stopped. One kind and one scope level at a time. Two planes, calling, ground, and a pipeline name do not arm the lens. `outer` without one plane is `Name one plane.` After 1 is empty. Nothing is picked.
63
-
64
- Naming a plane, `digest`, or `triage` returns that plane's view. The fields are from, digest, plate, entry, exit, files, skills, setup, teardown, and worn. Each field has a pipe. The pipe writes what it can see from the plate or the tree, and the view keeps that field. A field the pipe cannot see yet stays on the view for the next look. The rows are ground, routing, house, boot, governance, memory-recall, orchestration, processor, and reporting. Those nine are not Kind 0. The mill path is `scripts/foundry`. `triage` names the fields still open and whether the row holds. Zoom after the card is the same ladder: ecosystem, part, one flow, one artifact.
65
-
66
- The hold lasts for the work that named the plane. A sentence that does not say look or name does not arm it and does not drop it. Work that says goggles clears the hold and stays quiet. When a tool names one card plane, the suit adds that plane's file as a hint.
60
+ A session starts on dev. The boot payload includes that card from `.xray/state/LENS.md`. The planes are dev, integration, domain, and ecosystem. A search inside the current card runs. Any other search returns the current card and does not run. Opening a file on the current card adds nothing. Opening a file on another plane switches to that card and the open proceeds. A file on no card stays on the current plane and returns that card. Compact writes that same card back. A thing on that plane has its own card. One card has its own plate and can name more. A missing plate names the stamp-plate skill. Stamp one plate for that card. The cards and plates that stay are what the next agent reads. Saying goggles clears a stale card line on the station.
67
61
 
68
62
  ## Hooks (rewired, not new MCPs)
69
63
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./features.schema.json",
3
- "version": "4.1.0",
3
+ "version": "4.1.1",
4
4
  "description": "0xRay Framework - Unified Feature Configuration",
5
5
  "suit_temperament": {
6
6
  "profile": "auto",
@@ -12,6 +12,9 @@
12
12
  "generic": "guided"
13
13
  }
14
14
  },
15
+ "goggles": {
16
+ "enabled": true
17
+ },
15
18
  "token_optimization": {
16
19
  "enabled": true,
17
20
  "max_context_tokens": 20000,
@@ -132,6 +132,14 @@
132
132
  }
133
133
  }
134
134
  },
135
+ "goggles": {
136
+ "type": "object",
137
+ "description": "The suit keeps goggles on. enabled false fails wear and doctor.",
138
+ "properties": {
139
+ "enabled": { "type": "boolean" }
140
+ },
141
+ "required": ["enabled"]
142
+ },
135
143
  "synthesis": {
136
144
  "type": "object",
137
145
  "properties": {
@@ -25,7 +25,7 @@ A peer lead continues from this card + Repertoire + the `orchestrator` skill. Du
25
25
  `npx 0xray wear` writes the consumer suit:
26
26
 
27
27
  - Copies this file → **`AGENTS.md`** in your project root
28
- - Fastens mill plant (`mill` + `inspect`) then overlays **your** `src/skills` (same name wins). 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 package declares (blip-inspect, blip-vibe, blip-looker, sound-inspect, sound-mixer live in the mill). `npx @0xray/foundry sound mix` / `sound render` / `blip render` are CLI shims (0.1.11+). `npx @0xray/foundry inspect` runs mill-exported inspect; on a sound seat the last bed receipt; on a blip seat the last 4.44s receipt. Chat is not a receipt. Does **not** dump 45/42 costume unless `foundry.json` `"costume": true`. Does **not** write root **`SKILLS.md`**
28
+ - Fastens mill plant (`mill` + `inspect`) then overlays **your** `src/skills` (same name wins). 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 package declares (blip-inspect, blip-vibe, blip-looker, sound-inspect, sound-mixer live in the mill). `npx @0xray/foundry sound mix` / `sound render` / `blip render` are CLI shims (0.1.11+). `npx @0xray/foundry inspect` runs mill-exported inspect; on a sound seat the last bed receipt; on a blip seat the last 4.44s receipt. Chat is not a receipt. The mill suit also wears every catalog skill and every role file. A blip or sound seat fastens that plant only. A name outside the fastened set still fails inspect. Does **not** write root **`SKILLS.md`**
29
29
  - Factory shop plant (`shop-extract`, `shop-witness`, `shop-pin` from groover-hangar) is first-class with mill plant. Extra shops: `foundry.json` `"shopPlant"`. Not costume.
30
30
  - Seeds **`.gitignore`** (from template, if absent)
31
31
  - Deploys **`.xray/`** (`codex.json`, `features.json`, `config.json`) then overlays **your** mill SSOT (`xray/codex.json`, `xray/features.json`, `src/skills`, `src/opencode/agents`; remap with `foundry.json`)
package/AGENTS.md CHANGED
@@ -24,7 +24,7 @@ Before other work, Read `.xray/state/STATION.md`. Compaction and host change are
24
24
  - **External Governance** — Dynamo Solar SSOT, 69-term Codex. Judgement stays on.
25
25
  - **Autonomous Engine** — thinDispatch 7-flow, AsideContext, confidence gate
26
26
 
27
- Agents are declared in `src/opencode/agents/*.yml` — the YML SSOT. Skills live in `src/skills/*/SKILL.md` — see [SKILLS.md](SKILLS.md) for the exo catalog. Consumer default plant is **mill + inspect**, not a 45-skill sync (`AGENTS-consumer.md` → `AGENTS.md`). Factory hangar shops coexist via shop plant. See [llms.txt](llms.txt).
27
+ Agents are declared in `src/opencode/agents/*.yml` — the YML SSOT. Skills live in `src/skills/*/SKILL.md` — see [SKILLS.md](SKILLS.md) for the exo catalog. Consumer default plant is **mill + inspect**. The mill suit also wears every catalog skill and every role file (`AGENTS-consumer.md` → `AGENTS.md`). Factory hangar shops coexist via shop plant. See [llms.txt](llms.txt).
28
28
 
29
29
  ## Consumer MCP surface (7 servers)
30
30
 
@@ -49,7 +49,7 @@ Governance deliberation: **code-review**, **security-audit**, **researcher** wit
49
49
  `npx 0xray wear` runs `installAllBridges()` from `install-bridges.cjs`:
50
50
 
51
51
  1. `AGENTS-consumer.md` → `AGENTS.md` (does **not** write consumer-root `SKILLS.md`)
52
- 2. Fasten mill plant (`mill` + `inspect`). 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 package declares (looker, vibe, mixer, blip-inspect, blip-vibe, blip-looker, sound-inspect, sound-mixer live in the mill). Not a 45-skill costume dump unless `foundry.json` `"costume": true`
52
+ 2. Fasten mill plant (`mill` + `inspect`). 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 package declares (looker, vibe, mixer, blip-inspect, blip-vibe, blip-looker, sound-inspect, sound-mixer live in the mill). The mill suit also wears every catalog skill and every OpenCode role file. A blip or sound seat fastens that plant only. A name outside the fastened set still fails inspect.
53
53
  3. `.gitignore.default` → `.gitignore` (if absent)
54
54
  4. `.xray/` config (`codex.json`, `features.json`, `config.json`) then overlay their plant
55
55
  5. `.mcp.json` (7 servers)
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **4.1** — a suit that survives the context window
6
6
 
7
- Bifrost is the live board. Goggles reads one named plane.
7
+ Bifrost is the live board. Goggles views one plane. A search runs inside that card.
8
8
 
9
9
  0xRay is a three-subsystem autonomous engine. The suit is the wear. Inference remembers. External Governance judges. The Autonomous Engine runs the work. The host you already run wears it: Grok, OpenCode, Hermes, OpenClaw, or Cursor. Memory, judgement, and payments come on with the suit. When the window closes, the next session wakes with the job, the page, and the laws.
10
10
 
@@ -59,7 +59,7 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
59
59
 
60
60
  | Version | Highlights |
61
61
  |---------|------------|
62
- | **4.1.0** | Bifrost is the collector board. A cloud agent is a board row. A stuck card's L2 nudge goes to the CoS role. `live-inbox` requires `LIVE_URL`, `SEAT_KEYS_DIR`, and `LIVE_CURSORS`. Goggles reads one named plane. |
62
+ | **4.1.0** | Bifrost is the collector board. A cloud agent is a board row. A stuck card's L2 nudge goes to the CoS role. `live-inbox` requires `LIVE_URL`, `SEAT_KEYS_DIR`, and `LIVE_CURSORS`. Goggles views one plane. The card is in LENS.md. A search runs inside that card. |
63
63
  | **4.0.43** | Confer completes only on a real PASS. Cursor gate events fail closed, except EACCES when a cloud wrapper cannot write dest. Gate C cannot be skipped. |
64
64
  | **4.0.42** | Confer votes. Burst runs the swarm. |
65
65
  | **4.0.41** | Wake cascade stays on the station module. A fresh wear fastens mill and inspect. Goggles records the worn package path. The live page plays the mesh in the browser. |
@@ -29,7 +29,10 @@ AGENTS.md
29
29
  SKILLS.md
30
30
 
31
31
  # Runtime state (xray inference workflows, session state)
32
- .xray/
32
+ .xray/*
33
+ !.xray/state/
34
+ .xray/state/*
35
+ !.xray/state/LENS.md
33
36
  .strray/
34
37
  strray/
35
38
  xray/inference/
package/dist/AGENTS.md CHANGED
@@ -24,7 +24,7 @@ Before other work, Read `.xray/state/STATION.md`. Compaction and host change are
24
24
  - **External Governance** — Dynamo Solar SSOT, 69-term Codex. Judgement stays on.
25
25
  - **Autonomous Engine** — thinDispatch 7-flow, AsideContext, confidence gate
26
26
 
27
- Agents are declared in `src/opencode/agents/*.yml` — the YML SSOT. Skills live in `src/skills/*/SKILL.md` — see [SKILLS.md](SKILLS.md) for the exo catalog. Consumer default plant is **mill + inspect**, not a 45-skill sync (`AGENTS-consumer.md` → `AGENTS.md`). Factory hangar shops coexist via shop plant. See [llms.txt](llms.txt).
27
+ Agents are declared in `src/opencode/agents/*.yml` — the YML SSOT. Skills live in `src/skills/*/SKILL.md` — see [SKILLS.md](SKILLS.md) for the exo catalog. Consumer default plant is **mill + inspect**. The mill suit also wears every catalog skill and every role file (`AGENTS-consumer.md` → `AGENTS.md`). Factory hangar shops coexist via shop plant. See [llms.txt](llms.txt).
28
28
 
29
29
  ## Consumer MCP surface (7 servers)
30
30
 
@@ -49,7 +49,7 @@ Governance deliberation: **code-review**, **security-audit**, **researcher** wit
49
49
  `npx 0xray wear` runs `installAllBridges()` from `install-bridges.cjs`:
50
50
 
51
51
  1. `AGENTS-consumer.md` → `AGENTS.md` (does **not** write consumer-root `SKILLS.md`)
52
- 2. Fasten mill plant (`mill` + `inspect`). 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 package declares (looker, vibe, mixer, blip-inspect, blip-vibe, blip-looker, sound-inspect, sound-mixer live in the mill). Not a 45-skill costume dump unless `foundry.json` `"costume": true`
52
+ 2. Fasten mill plant (`mill` + `inspect`). 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 package declares (looker, vibe, mixer, blip-inspect, blip-vibe, blip-looker, sound-inspect, sound-mixer live in the mill). The mill suit also wears every catalog skill and every OpenCode role file. A blip or sound seat fastens that plant only. A name outside the fastened set still fails inspect.
53
53
  3. `.gitignore.default` → `.gitignore` (if absent)
54
54
  4. `.xray/` config (`codex.json`, `features.json`, `config.json`) then overlay their plant
55
55
  5. `.mcp.json` (7 servers)
package/dist/CHANGELOG.md CHANGED
@@ -6,7 +6,33 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- Groundwork before the 4.1.0 publish. The notes under [4.1.0] are the cut. npm latest is still 4.0.42.
9
+ ## [4.1.1] - 2026-10-18
10
+
11
+ Work on main after 4.1.0.
12
+
13
+ ### Wear
14
+
15
+ The mill suit wears every catalog skill and every role file when costume is off. The mill plant stays `mill` and `inspect`. A blip or sound seat fastens that plant only. A name outside the fastened set still fails inspect. (#379)
16
+
17
+ ### Goggles
18
+
19
+ The lens file is `.xray/state/LENS.md`. The planes are dev, integration, domain, and ecosystem. A search runs only inside the current card. Any other search returns that card and does not run. A path switches the card. (#371)
20
+
21
+ Each card lists three to five skills in hand order. That list is the skills seek. The first skill that fits wins. If none fit, plate recall returns a skill name or declines. The seek does not call a model. A stored lens that still points at the catalog is rewritten to that list before the seek. Wear and doctor fail when a fastened suit turns goggles off. (#390)
22
+
23
+ ### Live inbox
24
+
25
+ A seat can ack an inbox line after the pull has merged or the line was dropped. (#375)
26
+
27
+ ### Docs
28
+
29
+ Four broken repository links on the docs site are fixed. (#383)
30
+
31
+ The grok-bot notes cover the memory packet, the duplicate-agent guard, and no local builds. (#378)
32
+
33
+ ### Skills
34
+
35
+ `draw-stamped-card` and the bifrost-storyboard recipe are in the catalog. (#370)
10
36
 
11
37
  ## [4.1.0] - 2026-10-15
12
38
 
package/dist/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **4.1** — a suit that survives the context window
6
6
 
7
- Bifrost is the live board. Goggles reads one named plane.
7
+ Bifrost is the live board. Goggles views one plane. A search runs inside that card.
8
8
 
9
9
  0xRay is a three-subsystem autonomous engine. The suit is the wear. Inference remembers. External Governance judges. The Autonomous Engine runs the work. The host you already run wears it: Grok, OpenCode, Hermes, OpenClaw, or Cursor. Memory, judgement, and payments come on with the suit. When the window closes, the next session wakes with the job, the page, and the laws.
10
10
 
@@ -59,7 +59,7 @@ Docs: [guides/autonomy-command](docs-site/docs/guides/autonomy-command.md) · Sk
59
59
 
60
60
  | Version | Highlights |
61
61
  |---------|------------|
62
- | **4.1.0** | Bifrost is the collector board. A cloud agent is a board row. A stuck card's L2 nudge goes to the CoS role. `live-inbox` requires `LIVE_URL`, `SEAT_KEYS_DIR`, and `LIVE_CURSORS`. Goggles reads one named plane. |
62
+ | **4.1.0** | Bifrost is the collector board. A cloud agent is a board row. A stuck card's L2 nudge goes to the CoS role. `live-inbox` requires `LIVE_URL`, `SEAT_KEYS_DIR`, and `LIVE_CURSORS`. Goggles views one plane. The card is in LENS.md. A search runs inside that card. |
63
63
  | **4.0.43** | Confer completes only on a real PASS. Cursor gate events fail closed, except EACCES when a cloud wrapper cannot write dest. Gate C cannot be skipped. |
64
64
  | **4.0.42** | Confer votes. Burst runs the swarm. |
65
65
  | **4.0.41** | Wake cascade stays on the station module. A fresh wear fastens mill and inspect. Goggles records the worn package path. The live page plays the mesh in the browser. |
@@ -75,7 +75,7 @@ function copyDir(src, dest, relPath = "") {
75
75
  async function installForOpencode(options = {}) {
76
76
  const { frameworkLogger } = await import('../../core/framework-logger.js');
77
77
  const { syncBuiltinSkills } = await import('./skill-install.js');
78
- const { mintAfterWear, wantsCostume } = await import('./foundry-mint-wear.js');
78
+ const { mintAfterWear } = await import('./foundry-mint-wear.js');
79
79
  const wiring = loadWiring();
80
80
  frameworkLogger.log('opencode-integration', 'install-start', 'info', { options });
81
81
  const opencodeSource = path.join(__dirname, '..', '..', '..', '.opencode');
@@ -109,14 +109,8 @@ async function installForOpencode(options = {}) {
109
109
  console.log('Use --force to reinstall.');
110
110
  }
111
111
  else {
112
- if (wantsCostume(process.cwd())) {
113
- copyDir(opencodeSource, opencodeDest);
114
- frameworkLogger.log('opencode-integration', 'opencode-copied', 'info', { destination: opencodeDest });
115
- }
116
- else {
117
- fs.mkdirSync(opencodeDest, { recursive: true });
118
- frameworkLogger.log('opencode-integration', 'mill-plant-plugin', 'info', { destination: opencodeDest });
119
- }
112
+ copyDir(opencodeSource, opencodeDest);
113
+ frameworkLogger.log('opencode-integration', 'opencode-copied', 'info', { destination: opencodeDest });
120
114
  const pluginSource = path.join(__dirname, '..', '..', '..', 'dist', 'plugin', 'xray-codex-injection.js');
121
115
  const pluginDest = path.join(opencodeDest, 'plugin', 'xray-codex-injection.js');
122
116
  if (fs.existsSync(pluginSource)) {
@@ -1,7 +1,6 @@
1
1
  import { existsSync, readdirSync, readFileSync, mkdirSync, writeFileSync, cpSync, rmSync, statSync, unlinkSync, copyFileSync } from "fs";
2
2
  import { join, basename, dirname } from "path";
3
3
  import { fileURLToPath } from "url";
4
- import { createRequire } from "module";
5
4
  import { execSync } from "child_process";
6
5
  function getLocalRegistryPath() {
7
6
  return join(process.cwd(), ".xray", "skill-registry.json");
@@ -485,9 +484,6 @@ export async function skillRegistryCommand(action, args) {
485
484
  // Each platform's install command calls this to copy framework skills.
486
485
  export function syncBuiltinSkills(targetSkillsDir, packageRoot) {
487
486
  const root = packageRoot ?? join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
488
- const millSuit = createRequire(import.meta.url)(join(root, "scripts/foundry/mint-suit.cjs"));
489
- if (!millSuit.wantsCostume(process.cwd()))
490
- return 0;
491
487
  const candidateDirs = [
492
488
  join(root, "dist", "skills"),
493
489
  join(root, "src", "skills"),
@@ -500,7 +496,16 @@ export function syncBuiltinSkills(targetSkillsDir, packageRoot) {
500
496
  if (!existsSync(targetSkillsDir))
501
497
  mkdirSync(targetSkillsDir, { recursive: true });
502
498
  for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
503
- if (!entry.isDirectory())
499
+ let linkedDir = false;
500
+ if (entry.isSymbolicLink()) {
501
+ try {
502
+ linkedDir = statSync(join(sourceDir, entry.name)).isDirectory();
503
+ }
504
+ catch {
505
+ linkedDir = false;
506
+ }
507
+ }
508
+ if (!entry.isDirectory() && !linkedDir)
504
509
  continue;
505
510
  const skillMd = join(sourceDir, entry.name, "SKILL.md");
506
511
  if (!existsSync(skillMd))
@@ -1,13 +1,10 @@
1
1
  /**
2
2
  * Goggles on status and health.
3
- * Organ present is not Kind 0. Quiet is a match. No phone-home.
3
+ * The line is whether the organ is worn.
4
4
  */
5
5
  export type GogglesStatus = {
6
6
  organPresent: boolean;
7
- kind0Text: string;
8
- kind0Quiet: boolean;
9
- kind0Label: string;
10
7
  organLine: string;
11
8
  };
12
- export declare function probeGoggles(packageRoot: string, cwd: string): GogglesStatus;
9
+ export declare function probeGoggles(packageRoot: string, _cwd: string): GogglesStatus;
13
10
  export declare function formatGogglesStatus(probe: GogglesStatus): string[];
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Goggles on status and health.
3
- * Organ present is not Kind 0. Quiet is a match. No phone-home.
3
+ * The line is whether the organ is worn.
4
4
  */
5
- import { existsSync, readFileSync } from "node:fs";
5
+ import { existsSync } from "node:fs";
6
6
  import { join } from "node:path";
7
- const WORN_PLANES = ["dichotomy", "syncopate", "synthesis", "digest", "triage", "loop"];
8
7
  const ORGAN_PAIRS = [
9
8
  ["dist/integrations/hooks/goggles-pipeline.mjs", "dist/integrations/hooks/goggles-planes.json"],
10
9
  ["src/integrations/hooks/goggles-pipeline.mjs", "src/integrations/hooks/goggles-planes.json"],
@@ -17,60 +16,13 @@ function organPair(packageRoot) {
17
16
  }
18
17
  return null;
19
18
  }
20
- function lensText(cwd) {
21
- const file = join(cwd, ".xray", "state", "LENS.md");
22
- if (!existsSync(file))
23
- return { found: false, text: "" };
24
- try {
25
- return { found: true, text: readFileSync(file, "utf8").trim() };
26
- }
27
- catch {
28
- return { found: false, text: "" };
29
- }
30
- }
31
- function mapDrift(packageRoot, planesRel) {
32
- try {
33
- const data = JSON.parse(readFileSync(join(packageRoot, planesRel), "utf8"));
34
- const map = Array.isArray(data.planes) ? data.planes.map((name) => String(name)) : [];
35
- const same = map.length === WORN_PLANES.length && map.every((name, index) => name === WORN_PLANES[index]);
36
- return same ? "" : "Actuality. Drift: worn is not the map.";
37
- }
38
- catch {
39
- return "";
40
- }
41
- }
42
- export function probeGoggles(packageRoot, cwd) {
43
- const pair = organPair(packageRoot);
44
- const organPresent = pair !== null;
45
- const lens = lensText(cwd);
46
- let kind0Text = "";
47
- let kind0Label = "(no LENS yet)";
48
- let kind0Quiet = false;
49
- if (lens.found) {
50
- kind0Text = lens.text;
51
- if (!lens.text || lens.text === "quiet (match)") {
52
- kind0Quiet = true;
53
- kind0Label = "quiet (match)";
54
- }
55
- else {
56
- kind0Label = lens.text;
57
- }
58
- }
59
- else if (pair) {
60
- const drift = mapDrift(packageRoot, pair[1]);
61
- if (drift) {
62
- kind0Text = drift;
63
- kind0Label = drift;
64
- }
65
- }
19
+ export function probeGoggles(packageRoot, _cwd) {
20
+ const organPresent = organPair(packageRoot) !== null;
66
21
  return {
67
22
  organPresent,
68
- kind0Text,
69
- kind0Quiet,
70
- kind0Label,
71
23
  organLine: organPresent ? "Goggles: worn" : "Goggles: not found",
72
24
  };
73
25
  }
74
26
  export function formatGogglesStatus(probe) {
75
- return [probe.organLine, `Kind 0: ${probe.kind0Label}`];
27
+ return [probe.organLine];
76
28
  }
package/dist/cli/index.js CHANGED
@@ -79,7 +79,7 @@ program
79
79
  process.exit(1);
80
80
  }
81
81
  });
82
- const LOOK_HELP = "Kind 0 is quiet on a match. One outer is a reading. digest and triage return a card.";
82
+ const LOOK_HELP = "dev, integration, domain, or ecosystem. The card is in LENS.md.";
83
83
  function gogglesScript() {
84
84
  const built = join(packageRoot, "dist", "integrations", "hooks", "goggles-pipeline.mjs");
85
85
  const source = join(packageRoot, "src", "integrations", "hooks", "goggles-pipeline.mjs");
@@ -104,7 +104,7 @@ function runGoggles(args) {
104
104
  }
105
105
  program
106
106
  .command("look")
107
- .argument("[args...]", "kind 0, one outer, or digest/triage and one card plane")
107
+ .argument("[args...]", "one card plane")
108
108
  .description(LOOK_HELP)
109
109
  .action((args) => {
110
110
  runGoggles(args);
@@ -22,7 +22,7 @@ export declare function preProcessAndRoute(operation: string, context: RuleValid
22
22
  export declare const FOUNDRY_EXO_CANNOT_SHIP = "Exo cannot ship. Use: node scripts/node/release.mjs [patch|minor|major]";
23
23
  /**
24
24
  * Release keyword used to run version-manager + git add -A + npm publish from the exo.
25
- * That path is refused. Canonical mill: reconcile → artifacts-only → gate → PUT → tag.
25
+ * That path is refused. Canonical mill: scripts/foundry/release.mjs.
26
26
  */
27
27
  export declare function executeReleaseWorkflow(operation: string, _context: RuleValidationContext, jobId: string, _routing: RoutingRecommendation): Promise<EnforcementResult>;
28
28
  export interface EnforcementResult {
@@ -203,7 +203,7 @@ function buildTaskDescription(operation, context) {
203
203
  export const FOUNDRY_EXO_CANNOT_SHIP = "Exo cannot ship. Use: node scripts/node/release.mjs [patch|minor|major]";
204
204
  /**
205
205
  * Release keyword used to run version-manager + git add -A + npm publish from the exo.
206
- * That path is refused. Canonical mill: reconcile → artifacts-only → gate → PUT → tag.
206
+ * That path is refused. Canonical mill: scripts/foundry/release.mjs.
207
207
  */
208
208
  export async function executeReleaseWorkflow(operation, _context, jobId, _routing) {
209
209
  await frameworkLogger.log("enforcer-tools", "release-workflow-refused", "error", {
@@ -34,7 +34,7 @@ Install the package first (`npm install`), then `npx 0xray wear`. Postinstall wr
34
34
 
35
35
  | Hook | Stdin | Stdout |
36
36
  |------|--------|--------|
37
- | `preToolUse` | Cursor tool event (`tool_name`, `tool_input`, `cwd`, `workspace_roots`, `conversation_id`) | `{ "permission": "allow" \| "deny" }` plus `user_message` / `agent_message` on deny |
37
+ | `preToolUse` | Cursor tool event (`tool_name`, `tool_input`, `cwd`, `workspace_roots`, `conversation_id`) | `{ "permission": "allow" \| "deny" }` plus `user_message` / `agent_message` when the hook has something to say |
38
38
  | `preCompact` | `{ trigger, context_tokens, conversation_id, generation_id, … }` | `{ "user_message": "…" }` after Station write. Receipt keeps `conversation_id` + `generation_id`. That id is enough to resume. |
39
39
  | `afterFileEdit` | file event | `{}` — boots Station only |
40
40
 
@@ -48,6 +48,7 @@ function finish(root, permission, reason, hint, toolName, extra = {}) {
48
48
  }
49
49
  if (hint && permission === 'allow') {
50
50
  out.user_message = hint;
51
+ out.agent_message = hint;
51
52
  }
52
53
  if (extra.hookSpecificOutput) out.hookSpecificOutput = extra.hookSpecificOutput;
53
54
  console.log(JSON.stringify(out));
@@ -115,6 +116,9 @@ async function main() {
115
116
  if (lensStop?.decision === 'deny') {
116
117
  finish(gateRoot, 'deny', lensStop.reason, null, toolName, { gate: lensStop.gate });
117
118
  }
119
+ if (lensStop?.decision === 'allow' && lensStop.reason) {
120
+ finish(gateRoot, 'allow', lensStop.reason, lensStop.reason, toolName, { gate: lensStop.gate });
121
+ }
118
122
  const route = repertoireRoute([toolName, cmd, content].filter(Boolean).join(' '));
119
123
 
120
124
  if (isShellTool(toolName) && cmd) {
@@ -80,17 +80,15 @@ export async function installForGrokCLI(options = {}) {
80
80
  frameworkLogger.log('grok-integration', 'plugin-copied', 'info', { destination: dest });
81
81
  console.log(`\x1b[32m✓ Copied Grok plugin to ${dest}\x1b[0m`);
82
82
  }
83
- if (millSuit.wantsCostume(targetDir)) {
84
- const skillsCopied = syncBuiltinSkills(path.join(dest, 'skills'));
85
- if (skillsCopied > 0) {
86
- console.log(`\x1b[32m✓ Synced ${skillsCopied} builtin skills to Grok plugin\x1b[0m`);
87
- }
88
- frameworkLogger.log('grok-integration', 'skills-synced', 'info', { count: skillsCopied });
83
+ const skillsCopied = syncBuiltinSkills(path.join(dest, 'skills'));
84
+ if (skillsCopied > 0) {
85
+ console.log(`\x1b[32m✓ Synced ${skillsCopied} builtin skills to Grok plugin\x1b[0m`);
89
86
  }
87
+ frameworkLogger.log('grok-integration', 'skills-synced', 'info', { count: skillsCopied });
90
88
  pinGrokPluginToInstalledDist(dest, packageRoot, targetDir);
91
89
  wearGrokHookCommands(dest, packageRoot, targetDir);
92
90
  }
93
- if (isolated && millSuit.wantsCostume(targetDir)) {
91
+ if (isolated) {
94
92
  const home = millSuit.processHome(env);
95
93
  const globalCopied = syncBuiltinSkills(path.join(home, '.grok', 'skills'));
96
94
  if (globalCopied > 0) {
@@ -28,7 +28,7 @@ import {
28
28
  retainCompactFields,
29
29
  writeStationMarkdown,
30
30
  } from '../../hooks/station-hook-runtime.mjs';
31
- import { handCard } from '../../hooks/goggles-pipeline.mjs';
31
+ import { handCard, planeCard } from '../../hooks/goggles-pipeline.mjs';
32
32
 
33
33
  export {
34
34
  checkPendingDelegationGate,
@@ -315,6 +315,7 @@ export function buildSessionBootPayload(root, source = '0xray/grok-session-start
315
315
  const existing = readExistingBoot(root);
316
316
  const heat = applyStationHeat(root, extra.host || 'grok', extra, existing);
317
317
  const compactHold = retainCompactFields(existing, extra);
318
+ const shown = planeCard(root);
318
319
  return {
319
320
  lead_dev_mode: features.lead_dev_mode,
320
321
  no_new_surface: features.no_new_surface,
@@ -348,6 +349,8 @@ export function buildSessionBootPayload(root, source = '0xray/grok-session-start
348
349
  hook: source,
349
350
  source,
350
351
  timestamp: new Date().toISOString(),
352
+ plane: shown.plane,
353
+ card: shown.card,
351
354
  };
352
355
  }
353
356
 
@@ -21,7 +21,7 @@ import {
21
21
  workspaceRoot,
22
22
  } from './grok-hook-utils.js';
23
23
  import { appendHookActivity } from './grok-hook-activity.js';
24
- import { cardStop, lensBeforeResearch } from '../../hooks/goggles-pipeline.mjs';
24
+ import { lensBeforeResearch } from '../../hooks/goggles-pipeline.mjs';
25
25
  import { repertoireDecisionFields, repertoireRoute } from '../../hooks/pre-tool-speak.mjs';
26
26
 
27
27
  export { repertoireDecisionFields };
@@ -81,28 +81,14 @@ async function main() {
81
81
  if (extraBlock) finish(eventRoot, 'deny', extraBlock, null, toolName);
82
82
  }
83
83
 
84
- const stop = cardStop(
85
- eventRoot,
86
- toolName,
87
- [cmd, content, toolInput.pattern, toolInput.query, ...(ctx.paths || [])].filter(Boolean).join('\n'),
88
- ctx.paths || [],
89
- toolInput,
90
- );
91
- if (stop?.decision === 'deny') {
92
- finish(eventRoot, 'deny', stop.reason, null, toolName, { gate: stop.gate });
93
- }
94
- if (stop?.decision === 'allow') {
95
- finish(eventRoot, 'allow', null, null, toolName, {
96
- gate: stop.gate,
97
- hookSpecificOutput: stop.hookSpecificOutput,
98
- });
99
- }
100
-
101
84
  const spoken = [cmd, content, toolInput.pattern, toolInput.query, ...(ctx.paths || [])].filter(Boolean).join('\n');
102
85
  const lensStop = lensBeforeResearch(eventRoot, toolName, spoken);
103
86
  if (lensStop?.decision === 'deny') {
104
87
  finish(eventRoot, 'deny', lensStop.reason, null, toolName, { gate: lensStop.gate });
105
88
  }
89
+ if (lensStop?.decision === 'allow' && lensStop.reason) {
90
+ finish(eventRoot, 'allow', lensStop.reason, lensStop.reason, toolName, { gate: lensStop.gate });
91
+ }
106
92
 
107
93
  const route = repertoireRoute([toolName, cmd, content].filter(Boolean).join(' '));
108
94
 
@@ -24,7 +24,7 @@ import {
24
24
  runInferenceImprovementLight,
25
25
  } from '../../hooks/pipeline-hook-runtime.mjs';
26
26
  import { maintainLens } from '../../hooks/goggles-pipeline.mjs';
27
- import { readSavedFreshness, refreshFreshness } from '../../../nucleus/work-freshness.mjs';
27
+ import { freshnessNeedsRefresh, readSavedFreshness, refreshFreshness } from '../../../nucleus/work-freshness.mjs';
28
28
 
29
29
  function extractIntent(event) {
30
30
  return event.prompt || event.userMessage || event.user_prompt || event.compactContext || null;
@@ -137,7 +137,9 @@ async function main() {
137
137
  let freshnessLine = null;
138
138
  if (!compact) {
139
139
  try {
140
- freshnessLine = HOOK_EVENT === 'session_start'
140
+ const refresh = HOOK_EVENT === 'session_start'
141
+ || (HOOK_EVENT === 'user_prompt_submit' && freshnessNeedsRefresh(eventRoot));
142
+ freshnessLine = refresh
141
143
  ? refreshFreshness(eventRoot).freshnessLine
142
144
  : readSavedFreshness(eventRoot);
143
145
  } catch {
@@ -920,7 +920,7 @@ async function handleDelegationGate(command, projectRoot, logDir) {
920
920
  const spoken = `${tool} ${JSON.stringify(args)}`.slice(0, 500);
921
921
  const { lensDecision, repertoireRoute } = await import("../hooks/pre-tool-speak.mjs");
922
922
  const lens = lensDecision(projectRoot, tool, spoken);
923
- if (lens) {
923
+ if (lens?.decision === "deny") {
924
924
  return {
925
925
  allow: false,
926
926
  phase: "pre",
@@ -928,6 +928,9 @@ async function handleDelegationGate(command, projectRoot, logDir) {
928
928
  reason: lens.reason,
929
929
  };
930
930
  }
931
+ if (lens?.decision === "allow" && lens.reason) {
932
+ return { phase: "pre", ...outcome, gate: "lens", reason: lens.reason };
933
+ }
931
934
  const route = repertoireRoute(spoken);
932
935
  return { phase: "pre", ...outcome, ...(route || {}) };
933
936
  } catch (error) {