0xray 4.0.8 → 4.0.10

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.
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * validate-release-docs.mjs — Blocking guard for release doc freshness.
4
4
  *
5
- * Ensures README, AGENTS, SKILLS, CHANGELOG, Docusaurus headers, and required
6
- * guides match package.json version and live framework counts before tag/publish.
5
+ * Ensures README, AGENTS, SKILLS, llms.txt, CHANGELOG, Docusaurus headers, and
6
+ * required guides match package.json version and mill law before tag/publish.
7
7
  *
8
8
  * Usage:
9
9
  * npx @0xray/foundry docs-check
@@ -42,6 +42,24 @@ const REQUIRED_GUIDES = [
42
42
  'docs-site/docs/agents/README.md',
43
43
  ];
44
44
 
45
+ function millLawLagErrors(content) {
46
+ const errors = [];
47
+ if (/Syncs \*\*45 framework skills\*\*/.test(content) || /syncs all 45 skills/i.test(content)) {
48
+ errors.push('lags mill law: default plant is mill+inspect, not 45-skill sync');
49
+ }
50
+ if (/SKILLS\.md \+ 45 skills → platform skill directories/.test(content)) {
51
+ errors.push('lags mill law: postinstall must not sync 45 skills as default plant');
52
+ }
53
+ return errors;
54
+ }
55
+
56
+ function millPlantMentionErrors(content) {
57
+ if (!content.includes('mill') || !content.includes('inspect')) {
58
+ return ['missing mill+inspect plant'];
59
+ }
60
+ return [];
61
+ }
62
+
45
63
  const ROOT_DOC_CHECKS = [
46
64
  {
47
65
  rel: 'README.md',
@@ -62,6 +80,14 @@ const ROOT_DOC_CHECKS = [
62
80
  if (!content.includes('install-bridges') && !content.includes('installAllBridges')) {
63
81
  errors.push('missing install-bridges / installAllBridges reference');
64
82
  }
83
+ if (!content.includes('llms.txt')) {
84
+ errors.push('missing llms.txt agent-map reference');
85
+ }
86
+ if (!content.includes('shopPlant') && !content.includes('shop-extract')) {
87
+ errors.push('missing shop plant / shopPlant');
88
+ }
89
+ errors.push(...millPlantMentionErrors(content));
90
+ errors.push(...millLawLagErrors(content));
65
91
  return errors;
66
92
  },
67
93
  },
@@ -81,6 +107,11 @@ const ROOT_DOC_CHECKS = [
81
107
  if (!content.includes('SKILLS.md')) {
82
108
  errors.push('missing SKILLS.md link');
83
109
  }
110
+ if (!content.includes('llms.txt')) {
111
+ errors.push('missing llms.txt agent-map reference');
112
+ }
113
+ errors.push(...millPlantMentionErrors(content));
114
+ errors.push(...millLawLagErrors(content));
84
115
  return errors;
85
116
  },
86
117
  },
@@ -100,6 +131,11 @@ const ROOT_DOC_CHECKS = [
100
131
  if (!content.includes('AsideContext')) {
101
132
  errors.push('missing AsideContext section');
102
133
  }
134
+ if (!content.includes('shopPlant') && !content.includes('shop-extract')) {
135
+ errors.push('missing shop plant / shopPlant');
136
+ }
137
+ errors.push(...millPlantMentionErrors(content));
138
+ errors.push(...millLawLagErrors(content));
103
139
  return errors;
104
140
  },
105
141
  },
@@ -113,6 +149,37 @@ const ROOT_DOC_CHECKS = [
113
149
  if (!content.includes(`**${counts.skills} skills**`)) {
114
150
  errors.push(`skill count stale (expected **${counts.skills} skills**)`);
115
151
  }
152
+ errors.push(...millPlantMentionErrors(content));
153
+ errors.push(...millLawLagErrors(content));
154
+ return errors;
155
+ },
156
+ },
157
+ {
158
+ rel: 'llms.txt',
159
+ validate: (content) => {
160
+ const errors = [];
161
+ if (kernelLineHasPatchStamp(content)) {
162
+ errors.push('kernel header must be era (major.minor), not a patch stamp');
163
+ }
164
+ if (!content.includes('mill') || !content.includes('inspect')) {
165
+ errors.push('missing mill+inspect plant');
166
+ }
167
+ if (!content.includes('shopPlant') && !content.includes('shop-extract')) {
168
+ errors.push('missing hangar / shopPlant coexistence');
169
+ }
170
+ if (!content.includes('69')) {
171
+ errors.push('missing Codex 69');
172
+ }
173
+ if (!content.includes('xray-governance') || !content.includes('xray-orchestrator')) {
174
+ errors.push('missing 7 MCP agent map');
175
+ }
176
+ if (!content.toLowerCase().includes('costume')) {
177
+ errors.push('missing no-costume-dump law');
178
+ }
179
+ if (/^## Complete catalog/m.test(content) || content.includes('`api-design` · `architect-tools`')) {
180
+ errors.push('llms.txt must not costume-dump the 45-skill catalog');
181
+ }
182
+ errors.push(...millLawLagErrors(content));
116
183
  return errors;
117
184
  },
118
185
  },
@@ -348,6 +348,7 @@ export function getReleaseArtifactPaths(baseDir = resolveMillRoot()) {
348
348
  'AGENTS.md',
349
349
  'AGENTS-consumer.md',
350
350
  'SKILLS.md',
351
+ 'llms.txt',
351
352
  '.grok-plugin/plugin.json',
352
353
  'docs/README.md',
353
354
  'docs-site/docs/index.md',
@@ -366,6 +367,9 @@ export function getReleaseArtifactPaths(baseDir = resolveMillRoot()) {
366
367
  'docs-site/docs/architecture/GROK_GUIDE.md',
367
368
  'docs-site/sidebars.ts',
368
369
  'xray/features.json',
370
+ '.xray/features.json',
371
+ 'docs/PIPELINE-FACET-SNAPSHOT.json',
372
+ 'src/integrations/openclaw/plugin/xray-pre-tool/package.json',
369
373
  ];
370
374
  return candidates.filter((rel) => fs.existsSync(path.join(baseDir, rel)));
371
375
  }
@@ -39,6 +39,17 @@ function resolveRepertoireMcp(targetDir) {
39
39
  const candidates = [
40
40
  path.join(targetDir, "node_modules", "@0xray", "repertoire", "dist", "mcp", "server.js"),
41
41
  path.join(targetDir, "vendor", "@0xray", "repertoire", "dist", "mcp", "server.js"),
42
+ path.join(
43
+ targetDir,
44
+ "node_modules",
45
+ "0xray",
46
+ "vendor",
47
+ "@0xray",
48
+ "repertoire",
49
+ "dist",
50
+ "mcp",
51
+ "server.js",
52
+ ),
42
53
  isRepertoirePackageRoot(selfRoot) ? path.join(selfRoot, "dist", "mcp", "server.js") : null,
43
54
  isRepertoirePackageRoot(siblingRoot) ? path.join(siblingRoot, "dist", "mcp", "server.js") : null,
44
55
  ];
@@ -66,8 +77,20 @@ function resolveRepertoireProvider(targetDir) {
66
77
  "provider",
67
78
  "memory-routing-provider.js",
68
79
  );
80
+ const packedVendorProvider = path.join(
81
+ targetDir,
82
+ "node_modules",
83
+ "0xray",
84
+ "vendor",
85
+ "@0xray",
86
+ "repertoire",
87
+ "dist",
88
+ "provider",
89
+ "memory-routing-provider.js",
90
+ );
69
91
  if (fs.existsSync(nmProvider)) return nmProvider;
70
92
  if (fs.existsSync(vendorProvider)) return vendorProvider;
93
+ if (fs.existsSync(packedVendorProvider)) return packedVendorProvider;
71
94
  if (isRepertoirePackageRoot(siblingRoot) && fs.existsSync(siblingProvider)) return siblingProvider;
72
95
  return null;
73
96
  }
@@ -15,6 +15,7 @@ import os from "os";
15
15
  import path from "path";
16
16
  import { fileURLToPath } from "url";
17
17
  import { createRequire } from "module";
18
+ import { proveSuitAfterInstall } from "./pack-tmp-suit-proof.mjs";
18
19
 
19
20
  const require = createRequire(import.meta.url);
20
21
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -39,6 +40,64 @@ function run(cmd, cwd, { inherit = false } = {}) {
39
40
  return inherit ? "" : String(result).trim();
40
41
  }
41
42
 
43
+ function assertPackedPackageHasNoFileDeps(tarballPath) {
44
+ const packedJson = run(`tar -xOf ${JSON.stringify(tarballPath)} package/package.json`);
45
+ const packed = JSON.parse(packedJson);
46
+ const buckets = ["dependencies", "optionalDependencies", "peerDependencies"];
47
+ for (const bucket of buckets) {
48
+ const entries = packed[bucket] || {};
49
+ for (const [name, spec] of Object.entries(entries)) {
50
+ if (String(spec).startsWith("file:")) {
51
+ throw new Error(
52
+ `packed package.json must not ship file: ${bucket} ${name}=${spec} (consumers resolve file: from their root)`,
53
+ );
54
+ }
55
+ }
56
+ }
57
+ if (!String(packedJson).includes("vendor/@0xray/repertoire/")) {
58
+ const listing = run(`tar -tzf ${JSON.stringify(tarballPath)}`);
59
+ if (!listing.includes("package/vendor/@0xray/repertoire/package.json")) {
60
+ throw new Error("packed tarball missing vendor/@0xray/repertoire/package.json");
61
+ }
62
+ }
63
+ console.log(" ✅ packed package.json has no file: deps; vendor organ is in the tarball");
64
+ }
65
+
66
+ function assertOrganRequire(tmpRoot) {
67
+ const consumerRequire = createRequire(path.join(tmpRoot, "package.json"));
68
+ let organPkg;
69
+ try {
70
+ organPkg = consumerRequire("@0xray/repertoire/package.json");
71
+ } catch (err) {
72
+ throw new Error(`require('@0xray/repertoire') failed: ${err.message}`);
73
+ }
74
+ if (organPkg.name !== "@0xray/repertoire") {
75
+ throw new Error(`required organ name ${organPkg.name}`);
76
+ }
77
+ if (organPkg.version !== "0.2.0") {
78
+ throw new Error(`required organ version ${organPkg.version}`);
79
+ }
80
+ if (fs.existsSync(path.join(tmpRoot, "vendor"))) {
81
+ throw new Error("consumer install must not create or require a consumer-root vendor/ tree");
82
+ }
83
+ console.log(" ✅ require('@0xray/repertoire') from consumer cwd (no consumer vendor/)");
84
+ }
85
+
86
+ function writeBareConsumerManifest(tmpRoot) {
87
+ fs.writeFileSync(
88
+ path.join(tmpRoot, "package.json"),
89
+ `${JSON.stringify(
90
+ {
91
+ name: "xray-consumer-test",
92
+ version: "0.0.1",
93
+ private: true,
94
+ },
95
+ null,
96
+ 2,
97
+ )}\n`,
98
+ );
99
+ }
100
+
42
101
  function assertFreshInstallDefaults(tmpRoot, version) {
43
102
  const consumerFeaturesPath = path.join(tmpRoot, ".xray", "features.json");
44
103
  if (!fs.existsSync(consumerFeaturesPath)) {
@@ -188,25 +247,30 @@ function main() {
188
247
  throw new Error(`Tarball not found: ${tarballPath}`);
189
248
  }
190
249
 
191
- const consumerPkg = {
192
- name: "0xray-smoke-consumer",
193
- private: true,
194
- version: "1.0.0",
195
- dependencies: {
196
- "0xray": `file:${tarballPath}`,
197
- },
198
- };
199
- fs.writeFileSync(
200
- path.join(tmpRoot, "package.json"),
201
- JSON.stringify(consumerPkg, null, 2) + "\n",
202
- );
250
+ assertPackedPackageHasNoFileDeps(tarballPath);
251
+
252
+ writeBareConsumerManifest(tmpRoot);
203
253
 
204
- console.log("📥 npm install (runs postinstall)...");
205
- run("npm install", tmpRoot, { inherit: true });
254
+ console.log("📥 npm install tarball into bare consumer (exact ENOENT repro)...");
255
+ run(`npm install ${JSON.stringify(tarballPath)}`, tmpRoot, { inherit: true });
256
+
257
+ const installLinksRoot = fs.mkdtempSync(path.join(os.tmpdir(), "0xray-smoke-links-"));
258
+ writeBareConsumerManifest(installLinksRoot);
259
+ console.log("📥 npm install tarball --install-links (file: ENOENT path)...");
260
+ run(`npm install ${JSON.stringify(tarballPath)} --install-links`, installLinksRoot, {
261
+ inherit: true,
262
+ });
263
+ assertOrganRequire(installLinksRoot);
264
+ try {
265
+ fs.rmSync(installLinksRoot, { recursive: true, force: true });
266
+ } catch {
267
+ /* best-effort */
268
+ }
206
269
 
207
270
  const nmRoot = path.join(tmpRoot, "node_modules", "0xray");
208
271
  const requiredFiles = [
209
272
  "scripts/node/install-bridges.cjs",
273
+ "scripts/node/wear-vendored-repertoire.cjs",
210
274
  "scripts/node/postinstall.cjs",
211
275
  "scripts/foundry/mint-suit.cjs",
212
276
  "scripts/foundry/hooks.mjs",
@@ -274,6 +338,7 @@ function main() {
274
338
  console.log(` ✅ .mcp.json has ${XRAY_MCP_NAMES.length} npx MCP servers`);
275
339
 
276
340
  assertFreshInstallDefaults(tmpRoot, version);
341
+ assertOrganRequire(tmpRoot);
277
342
 
278
343
  const schemaInPackage = path.join(nmRoot, "xray", "features.schema.json");
279
344
  if (!fs.existsSync(schemaInPackage)) {
@@ -283,6 +348,9 @@ function main() {
283
348
 
284
349
  runUpgradeMergeSmoke(tmpRoot, nmRoot, version);
285
350
 
351
+ console.log("\n📋 Pack → tmp mill fasten + hangar shop plant (not costume)...");
352
+ proveSuitAfterInstall(tmpRoot, path.join(nmRoot, "scripts", "foundry"));
353
+
286
354
  console.log("\n✅ Consumer install smoke passed\n");
287
355
  } catch (err) {
288
356
  console.error(`\n❌ Consumer smoke failed: ${err.message}\n`);
@@ -9,6 +9,7 @@ const path = require("path");
9
9
  const os = require("os");
10
10
  const { execSync } = require("child_process");
11
11
  const { wantsCostume, isIsolatedHome, machineHome } = require("../foundry/mint-suit.cjs");
12
+ const { wearVendoredRepertoire } = require("./wear-vendored-repertoire.cjs");
12
13
  const {
13
14
  wireHermesBridge,
14
15
  wireOpencodeBridge,
@@ -27,23 +28,44 @@ const SKIP_DIRS = new Set(["node_modules", "logs"]);
27
28
  const MERGE_FILES = new Set(["enforcer-config.json"]);
28
29
  const KEEP_IF_EXISTS = new Set([".yml", ".yaml", ".md"]);
29
30
 
31
+ function isInstallPrefixTarget(dir) {
32
+ const n = path.resolve(dir);
33
+ const norm = n.replace(/\\/g, "/");
34
+ if (norm.includes("/_npx/") || norm.endsWith("/_npx")) return true;
35
+ if (!fs.existsSync(path.join(n, "package.json"))) return true;
36
+ return false;
37
+ }
38
+
30
39
  function resolveConsumerTargetDir(packageRoot, fallbackDir) {
31
40
  const resolved = path.resolve(packageRoot);
41
+ const fallback = path.resolve(
42
+ fallbackDir || process.env.INIT_CWD || process.env.PWD || process.cwd(),
43
+ );
32
44
  const inNodeModules =
33
45
  resolved.includes(`${path.sep}node_modules${path.sep}`) ||
34
46
  resolved.endsWith(`${path.sep}node_modules`);
35
47
 
48
+ let candidate;
36
49
  if (!inNodeModules) {
37
- return fallbackDir || process.env.PWD || process.cwd();
50
+ candidate = fallback;
51
+ } else {
52
+ let current = resolved;
53
+ while (path.basename(current) !== "node_modules") {
54
+ const parent = path.dirname(current);
55
+ if (parent === current) {
56
+ candidate = fallback;
57
+ break;
58
+ }
59
+ current = parent;
60
+ }
61
+ if (!candidate) candidate = path.dirname(current);
38
62
  }
39
63
 
40
- let current = resolved;
41
- while (path.basename(current) !== "node_modules") {
42
- const parent = path.dirname(current);
43
- if (parent === current) return fallbackDir || process.cwd();
44
- current = parent;
64
+ if (isInstallPrefixTarget(candidate)) {
65
+ if (!isInstallPrefixTarget(fallback)) return fallback;
66
+ return resolved;
45
67
  }
46
- return path.dirname(current);
68
+ return candidate;
47
69
  }
48
70
 
49
71
  function isConsumerInstall(packageRoot, targetDir) {
@@ -721,6 +743,8 @@ function installAllBridges(opts) {
721
743
  /* noop */
722
744
  });
723
745
 
746
+ wearVendoredRepertoire(packageRoot, targetDir, log);
747
+
724
748
  if (!isConsumerInstall(packageRoot, targetDir)) {
725
749
  log("install-bridges", "framework dogfood wear", "info");
726
750
  installFrameworkDogfoodWear(packageRoot, log);
@@ -744,6 +768,7 @@ function installAllBridges(opts) {
744
768
  module.exports = {
745
769
  installAllBridges,
746
770
  resolveConsumerTargetDir,
771
+ isInstallPrefixTarget,
747
772
  syncBuiltinSkills,
748
773
  isConsumerInstall,
749
774
  deployXrayConfig,
@@ -757,4 +782,5 @@ module.exports = {
757
782
  isEphemeralInstallRoot,
758
783
  isIsolatedHome,
759
784
  installFrameworkDogfoodWear,
785
+ wearVendoredRepertoire,
760
786
  };
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * pack-tmp-suit-proof.mjs — Clean-room pack → tmp install → mill fasten → hangar plant → inspect.
4
+ *
5
+ * Asserts (Task B):
6
+ * 1. `npm install` of the packed 0xray tgz works
7
+ * 2. `foundry mint --skip-live` fastens mill+inspect (not 45/42 costume)
8
+ * 3. groover-hangar factory shops + inspect do not costume-dump
9
+ *
10
+ * Usage:
11
+ * node scripts/node/pack-tmp-suit-proof.mjs
12
+ * npm run pack:tmp-proof
13
+ *
14
+ * Playwright / UI: n/a (mill CLI).
15
+ */
16
+
17
+ import { execSync, spawnSync } from "node:child_process";
18
+ import fs from "node:fs";
19
+ import os from "node:os";
20
+ import path from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ const repoRoot = path.resolve(__dirname, "../..");
25
+
26
+ export const FACTORY_SHOP_SKILLS = ["shop-extract", "shop-witness", "shop-pin"];
27
+ export const MILL_PLANT_SKILLS = ["mill", "inspect"];
28
+
29
+ function run(cmd, cwd, { inherit = false } = {}) {
30
+ const result = execSync(cmd, {
31
+ cwd,
32
+ encoding: inherit ? undefined : "utf-8",
33
+ stdio: inherit ? "inherit" : "pipe",
34
+ });
35
+ return inherit ? "" : String(result).trim();
36
+ }
37
+
38
+ export function foundryCliPath(millRoot) {
39
+ return path.join(millRoot, "cli.js");
40
+ }
41
+
42
+ export function runFoundryCli(millRoot, consumerRoot, args) {
43
+ const isolatedHome = path.join(consumerRoot, ".proof-home");
44
+ fs.mkdirSync(isolatedHome, { recursive: true });
45
+ return spawnSync(process.execPath, [foundryCliPath(millRoot), ...args], {
46
+ cwd: consumerRoot,
47
+ encoding: "utf8",
48
+ env: {
49
+ ...process.env,
50
+ FOUNDRY_ROOT: consumerRoot,
51
+ HOME: isolatedHome,
52
+ },
53
+ });
54
+ }
55
+
56
+ export function lastJsonObject(text) {
57
+ const src = String(text || "");
58
+ for (let end = src.length - 1; end >= 0; end -= 1) {
59
+ if (src[end] !== "}") continue;
60
+ let depth = 0;
61
+ for (let start = end; start >= 0; start -= 1) {
62
+ const ch = src[start];
63
+ if (ch === "}") depth += 1;
64
+ else if (ch === "{") {
65
+ depth -= 1;
66
+ if (depth === 0) {
67
+ try {
68
+ return JSON.parse(src.slice(start, end + 1));
69
+ } catch {
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return null;
77
+ }
78
+
79
+ export function plantHangarFactoryShops(consumerRoot) {
80
+ for (const name of FACTORY_SHOP_SKILLS) {
81
+ const dir = path.join(consumerRoot, ".opencode", "skills", name);
82
+ fs.mkdirSync(dir, { recursive: true });
83
+ fs.writeFileSync(path.join(dir, "SKILL.md"), `groover-hangar plant ${name}\n`);
84
+ }
85
+ }
86
+
87
+ export function wornSkillNames(consumerRoot) {
88
+ const skillsDir = path.join(consumerRoot, ".opencode", "skills");
89
+ if (!fs.existsSync(skillsDir)) return [];
90
+ return fs.readdirSync(skillsDir).filter((name) =>
91
+ fs.existsSync(path.join(skillsDir, name, "SKILL.md")),
92
+ );
93
+ }
94
+
95
+ export function assertFastenedMillPlant(consumerRoot) {
96
+ const inventoryPath = path.join(consumerRoot, ".xray", "foundry-inventory.json");
97
+ if (!fs.existsSync(inventoryPath)) {
98
+ throw new Error("foundry-inventory.json missing — mill did not fasten");
99
+ }
100
+ const inventory = JSON.parse(fs.readFileSync(inventoryPath, "utf8"));
101
+ if (inventory.costume === true) {
102
+ throw new Error("inventory.costume is true — costume dump is not default mill plant");
103
+ }
104
+ const millSkills = inventory.millPlant?.skills || [];
105
+ for (const name of MILL_PLANT_SKILLS) {
106
+ if (!millSkills.includes(name)) {
107
+ throw new Error(`mill plant missing ${name} (got ${millSkills.join(",")})`);
108
+ }
109
+ if (!fs.existsSync(path.join(consumerRoot, ".opencode", "skills", name, "SKILL.md"))) {
110
+ throw new Error(`worn mill plant missing ${name}`);
111
+ }
112
+ }
113
+ const foundry = path.join(consumerRoot, "foundry.json");
114
+ if (fs.existsSync(foundry)) {
115
+ const extra = JSON.parse(fs.readFileSync(foundry, "utf8"));
116
+ if (extra.costume === true) {
117
+ throw new Error("foundry.json costume:true is not the mill+inspect path");
118
+ }
119
+ }
120
+ const worn = wornSkillNames(consumerRoot);
121
+ const extras = worn.filter(
122
+ (name) => !MILL_PLANT_SKILLS.includes(name) && !FACTORY_SHOP_SKILLS.includes(name),
123
+ );
124
+ if (extras.length > 0) {
125
+ throw new Error(`costume dump extras before hangar plant: ${extras.join(",")}`);
126
+ }
127
+ return inventory;
128
+ }
129
+
130
+ export function proveSuitAfterInstall(consumerRoot, millRoot) {
131
+ if (!fs.existsSync(foundryCliPath(millRoot))) {
132
+ throw new Error(`foundry CLI missing at ${foundryCliPath(millRoot)}`);
133
+ }
134
+
135
+ console.log(" ⚒️ foundry mint --skip-live (fasten mill+inspect)...");
136
+ const mint = runFoundryCli(millRoot, consumerRoot, ["mint", "--skip-live"]);
137
+ if (mint.status !== 0) {
138
+ throw new Error(`foundry mint --skip-live failed:\n${mint.stdout}\n${mint.stderr}`);
139
+ }
140
+ const inventory = assertFastenedMillPlant(consumerRoot);
141
+ if (inventory.suit !== "fastened" && inventory.suit !== "overlay") {
142
+ throw new Error(`expected fastened/overlay suit, got ${inventory.suit}`);
143
+ }
144
+ console.log(` ✅ mint fastened mill+inspect (suit=${inventory.suit}, costume=${inventory.costume === true})`);
145
+
146
+ console.log(" 🏭 groover-hangar plant factory shops (not costume)...");
147
+ plantHangarFactoryShops(consumerRoot);
148
+ for (const name of FACTORY_SHOP_SKILLS) {
149
+ if (!fs.existsSync(path.join(consumerRoot, ".opencode", "skills", name, "SKILL.md"))) {
150
+ throw new Error(`hangar plant missing ${name}`);
151
+ }
152
+ }
153
+
154
+ console.log(" 🔍 foundry inspect --skip-live (shop plant must not costume-dump)...");
155
+ const inspect = runFoundryCli(millRoot, consumerRoot, ["inspect", "--skip-live"]);
156
+ const report = lastJsonObject(inspect.stdout);
157
+ if (inspect.status !== 0 || !report?.ok) {
158
+ throw new Error(
159
+ `inspect failed after hangar plant (costume dump?):\n${inspect.stdout}\n${inspect.stderr}`,
160
+ );
161
+ }
162
+ const plantVsWorn = (report.checks || []).find((c) => c.id === "plant-vs-worn");
163
+ if (!plantVsWorn?.ok) {
164
+ throw new Error(`plant-vs-worn failed: ${JSON.stringify(plantVsWorn)}`);
165
+ }
166
+ const shopPlant = plantVsWorn.shopPlant || [];
167
+ for (const name of FACTORY_SHOP_SKILLS) {
168
+ if (!shopPlant.includes(name)) {
169
+ throw new Error(`inspect shopPlant missing ${name} (got ${shopPlant.join(",")})`);
170
+ }
171
+ }
172
+ if (Array.isArray(plantVsWorn.extraSkills) && plantVsWorn.extraSkills.length > 0) {
173
+ throw new Error(`inspect extraSkills after hangar plant: ${plantVsWorn.extraSkills.join(",")}`);
174
+ }
175
+ const after = assertFastenedMillPlant(consumerRoot);
176
+ if (after.costume === true) {
177
+ throw new Error("costume flipped true after hangar plant");
178
+ }
179
+ console.log(" ✅ hangar factory shops + inspect OK (costume false, no dump)");
180
+ return { inventory: after, report };
181
+ }
182
+
183
+ export function packXrayTgz(xrayRoot = repoRoot) {
184
+ const packOut = run("npm pack --silent", xrayRoot);
185
+ const tarball = packOut.split("\n").filter(Boolean).pop();
186
+ const tarballPath = path.join(xrayRoot, tarball);
187
+ if (!fs.existsSync(tarballPath)) {
188
+ throw new Error(`Tarball not found: ${tarballPath}`);
189
+ }
190
+ return tarballPath;
191
+ }
192
+
193
+ export function writeBareConsumerManifest(tmpRoot) {
194
+ fs.writeFileSync(
195
+ path.join(tmpRoot, "package.json"),
196
+ `${JSON.stringify({ name: "xray-pack-tmp-proof", version: "0.0.1", private: true }, null, 2)}\n`,
197
+ );
198
+ }
199
+
200
+ export function runPackTmpProof(xrayRoot = repoRoot) {
201
+ console.log("\n=== Pack → tmp suit proof ===\n");
202
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "0xray-pack-tmp-proof-"));
203
+ let tarballPath = null;
204
+ try {
205
+ console.log("📦 npm pack...");
206
+ tarballPath = packXrayTgz(xrayRoot);
207
+ writeBareConsumerManifest(tmpRoot);
208
+ console.log("📥 npm install tgz into clean tmp...");
209
+ run(`npm install ${JSON.stringify(tarballPath)}`, tmpRoot, { inherit: true });
210
+ const millRoot = path.join(tmpRoot, "node_modules", "0xray", "scripts", "foundry");
211
+ proveSuitAfterInstall(tmpRoot, millRoot);
212
+ console.log("\n✅ Pack → tmp suit proof passed (Playwright n/a — mill CLI)\n");
213
+ return { tmpRoot, tarballPath };
214
+ } catch (err) {
215
+ console.error(`\n❌ Pack → tmp suit proof failed: ${err.message}\n`);
216
+ throw err;
217
+ } finally {
218
+ if (tarballPath) {
219
+ try {
220
+ fs.unlinkSync(tarballPath);
221
+ } catch {
222
+ /* best-effort */
223
+ }
224
+ }
225
+ try {
226
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
227
+ } catch {
228
+ /* best-effort */
229
+ }
230
+ }
231
+ }
232
+
233
+ const isMain =
234
+ process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
235
+
236
+ if (isMain) {
237
+ try {
238
+ runPackTmpProof();
239
+ } catch {
240
+ process.exit(1);
241
+ }
242
+ }
@@ -120,7 +120,10 @@ module.exports = {
120
120
 
121
121
  if (require.main === module) {
122
122
  const packageRoot = path.join(__dirname, "..", "..");
123
- const targetDir = resolveConsumerTargetDir(packageRoot, process.env.PWD || process.cwd());
123
+ const targetDir = resolveConsumerTargetDir(
124
+ packageRoot,
125
+ process.env.INIT_CWD || process.env.PWD || process.cwd(),
126
+ );
124
127
  try {
125
128
  runPostinstall(packageRoot, targetDir);
126
129
  } catch {
@@ -11,7 +11,10 @@ const {
11
11
  isConsumerInstall,
12
12
  deployXrayConfig,
13
13
  } = require("./install-bridges.cjs");
14
- let targetDir = resolveConsumerTargetDir(packageRoot, process.env.PWD || process.cwd());
14
+ let targetDir = resolveConsumerTargetDir(
15
+ packageRoot,
16
+ process.env.INIT_CWD || process.env.PWD || process.cwd(),
17
+ );
15
18
 
16
19
  const resolvedPackage = path.resolve(packageRoot);
17
20
  const resolvedTarget = path.resolve(targetDir);