0xray 4.0.1 → 4.0.2

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 (55) hide show
  1. package/.xray/features.json +1 -1
  2. package/AGENTS-consumer.md +4 -4
  3. package/AGENTS.md +3 -3
  4. package/README.md +1 -1
  5. package/SKILLS.md +1 -1
  6. package/dist/AGENTS.md +3 -3
  7. package/dist/CHANGELOG.md +36 -0
  8. package/dist/README.md +1 -1
  9. package/dist/SKILLS.md +1 -1
  10. package/dist/cli/commands/foundry-mint-wear.d.ts +2 -0
  11. package/dist/cli/commands/foundry-mint-wear.js +14 -0
  12. package/dist/cli/commands/hermes-install.js +2 -0
  13. package/dist/cli/commands/openclaw-install.js +2 -0
  14. package/dist/cli/commands/opencode-install.js +2 -0
  15. package/dist/enforcement/enforcer-tools.d.ts +7 -0
  16. package/dist/enforcement/enforcer-tools.js +18 -109
  17. package/dist/integrations/grok/grok-cli.js +2 -0
  18. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  19. package/dist/processors/implementations/version-compliance-processor.d.ts +3 -34
  20. package/dist/processors/implementations/version-compliance-processor.js +8 -135
  21. package/package.json +15 -14
  22. package/scripts/foundry/CHANGELOG.md +34 -0
  23. package/scripts/foundry/LICENSE +21 -0
  24. package/scripts/foundry/README.md +67 -0
  25. package/scripts/foundry/ci-monitor.mjs +115 -0
  26. package/scripts/foundry/cli.js +2 -0
  27. package/scripts/foundry/cli.mjs +55 -0
  28. package/scripts/foundry/docs-build.mjs +38 -0
  29. package/scripts/foundry/hooks.mjs +36 -0
  30. package/scripts/foundry/mill-root.mjs +62 -0
  31. package/scripts/foundry/mint-suit.cjs +313 -0
  32. package/scripts/foundry/mint.mjs +33 -0
  33. package/scripts/foundry/package.json +53 -0
  34. package/scripts/foundry/reconcile-version.mjs +150 -0
  35. package/scripts/foundry/release-gate.mjs +100 -0
  36. package/scripts/foundry/release.mjs +266 -0
  37. package/scripts/foundry/validate-release-docs.mjs +316 -0
  38. package/scripts/foundry/version-manager.mjs +444 -0
  39. package/scripts/node/ci-cd-auto-fix.cjs +12 -257
  40. package/scripts/node/consumer-install-smoke.mjs +2 -0
  41. package/scripts/node/enforce-version-compliance.sh +5 -20
  42. package/scripts/node/enforce-version-compliance.ts +13 -113
  43. package/scripts/node/foundry-shim.mjs +19 -0
  44. package/scripts/node/github-actions-monitor.cjs +13 -18
  45. package/scripts/node/postinstall.cjs +36 -1
  46. package/scripts/node/reconcile-version.mjs +3 -142
  47. package/scripts/node/release-gate.mjs +3 -74
  48. package/scripts/node/release.mjs +3 -171
  49. package/scripts/node/validate-release-docs.mjs +3 -285
  50. package/scripts/node/version-manager.mjs +3 -738
  51. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  52. package/xray/features.json +1 -1
  53. package/scripts/node/release.js +0 -17
  54. package/scripts/node/sync-versions.mjs +0 -18
  55. package/scripts/node/universal-version-manager.js +0 -1043
@@ -5,10 +5,9 @@
5
5
  * commits or publishes. Integrates with 0xRay's processor pipeline.
6
6
  *
7
7
  * Rules Enforced:
8
- * 1. Universal Version Manager MUST be 1 ahead of NPM published version
9
- * 2. package.json SHOULD match UVM (warning if not)
10
- * 3. Source files MUST be synchronized to UVM version
11
- * 4. README SHOULD reference current version
8
+ * 1. package.json version is the SSOT (semver)
9
+ * 2. Mill SSOT is package.json via scripts/foundry/reconcile-version.mjs
10
+ * 3. Do not bump or publish from this processor (foundry mill ships)
12
11
  *
13
12
  * @processor_type pre
14
13
  * @priority 25 (high - runs early, after preValidate, before errorBoundary)
@@ -100,53 +99,13 @@ export class VersionComplianceProcessor {
100
99
  this.errors = [];
101
100
  this.warnings = [];
102
101
  this.fixes = [];
103
- // Get all versions
104
102
  const npmVersion = await this.getNpmVersion();
105
- const uvmVersion = this.getUvmVersion();
106
103
  const pkgVersion = this.getPackageVersion();
107
- // Rule 1: UVM MUST be 1 ahead of NPM
108
- if (npmVersion !== "NOT_PUBLISHED" && npmVersion !== "ERROR") {
109
- const npmParsed = this.parseVersion(npmVersion);
110
- const uvmParsed = this.parseVersion(uvmVersion);
111
- const expectedUvm = this.incrementPatch(npmParsed);
112
- if (uvmVersion !== this.formatVersion(expectedUvm)) {
113
- this.errors.push(`Version manager not 1 ahead of npm (NPM: ${npmVersion}, UVM: ${uvmVersion}, Expected: ${this.formatVersion(expectedUvm)})`);
114
- this.fixes.push({
115
- type: "update-uvm",
116
- description: `Update UVM to ${this.formatVersion(expectedUvm)}`,
117
- command: "node scripts/node/universal-version-manager.js",
118
- autoFixable: false, // Requires manual edit
119
- });
120
- }
121
- }
122
- // Rule 2: package.json SHOULD match UVM
123
- if (pkgVersion !== uvmVersion) {
124
- this.warnings.push(`package.json version (${pkgVersion}) doesn't match UVM (${uvmVersion}) - run "npm version [patch|minor|major]" to sync`);
125
- }
126
- // Rule 3: Source files MUST be synchronized
127
- const sourceVersion = this.getSourceVersion();
128
- if (sourceVersion && sourceVersion !== uvmVersion) {
129
- this.errors.push(`Source files not synchronized to UVM version (${sourceVersion} vs ${uvmVersion})`);
130
- this.fixes.push({
131
- type: "sync-source",
132
- description: "Synchronize source files to UVM version",
133
- command: "node scripts/node/universal-version-manager.js",
134
- autoFixable: true,
135
- });
136
- }
137
- // Rule 4: README SHOULD reference current version
138
- const readmeVersion = this.getReadmeVersion();
139
- if (readmeVersion &&
140
- readmeVersion !== uvmVersion &&
141
- readmeVersion !== pkgVersion) {
142
- this.warnings.push(`README version (${readmeVersion}) may be outdated (UVM: ${uvmVersion}, package: ${pkgVersion})`);
143
- this.fixes.push({
144
- type: "update-readme",
145
- description: "Update README version references",
146
- command: "node scripts/node/universal-version-manager.js",
147
- autoFixable: true,
148
- });
104
+ const uvmVersion = "FROZEN";
105
+ if (!/^\d+\.\d+\.\d+/.test(pkgVersion)) {
106
+ this.errors.push(`package.json version is not semver (${pkgVersion})`);
149
107
  }
108
+ this.warnings.push("SSOT is package.json via scripts/foundry/reconcile-version.mjs");
150
109
  return {
151
110
  compliant: this.errors.length === 0,
152
111
  npmVersion,
@@ -172,29 +131,6 @@ export class VersionComplianceProcessor {
172
131
  return "NOT_PUBLISHED";
173
132
  }
174
133
  }
175
- /**
176
- * Get Universal Version Manager version
177
- */
178
- getUvmVersion() {
179
- try {
180
- const uvmPath = path.join(this.projectRoot, "scripts", "node", "universal-version-manager.js");
181
- if (!fs.existsSync(uvmPath)) {
182
- return "NOT_FOUND";
183
- }
184
- const content = fs.readFileSync(uvmPath, "utf-8");
185
- // Match version in OFFICIAL_VERSIONS.framework.version format
186
- const match = content.match(/OFFICIAL_VERSIONS\s*=\s*\{[\s\S]*?framework:\s*\{[\s\S]*?version:\s*["']([^"']+)["']/);
187
- if (match && match[1]) {
188
- return match[1];
189
- }
190
- // Fallback: grep for version pattern
191
- const fallbackMatch = content.match(/version:\s*["'](\d+\.\d+\.\d+)["']/);
192
- return fallbackMatch && fallbackMatch[1] ? fallbackMatch[1] : "NOT_FOUND";
193
- }
194
- catch {
195
- return "ERROR";
196
- }
197
- }
198
134
  /**
199
135
  * Get package.json version
200
136
  */
@@ -211,69 +147,6 @@ export class VersionComplianceProcessor {
211
147
  return "ERROR";
212
148
  }
213
149
  }
214
- /**
215
- * Get version from source files
216
- */
217
- getSourceVersion() {
218
- try {
219
- const cliPath = path.join(this.projectRoot, "src", "cli", "index.ts");
220
- if (!fs.existsSync(cliPath)) {
221
- return null;
222
- }
223
- const content = fs.readFileSync(cliPath, "utf-8");
224
- const match = content.match(/\.version\(["']([^"']+)["']\)/);
225
- return match && match[1] ? match[1] : null;
226
- }
227
- catch {
228
- return null;
229
- }
230
- }
231
- /**
232
- * Get version from README
233
- */
234
- getReadmeVersion() {
235
- try {
236
- const readmePath = path.join(this.projectRoot, "README.md");
237
- if (!fs.existsSync(readmePath)) {
238
- return null;
239
- }
240
- const content = fs.readFileSync(readmePath, "utf-8");
241
- // Match vX.Y.Z or X.Y.Z patterns - but exclude IP addresses (127.x.x)
242
- // Look for version in badges, headers, or specific patterns
243
- const match = content.match(/v?(1\.\d+\.\d+)/);
244
- return match && match[1] ? match[1] : null;
245
- }
246
- catch {
247
- return null;
248
- }
249
- }
250
- /**
251
- * Parse version string into components
252
- */
253
- parseVersion(version) {
254
- const parts = version.split(".");
255
- return {
256
- major: parseInt(parts[0] || "0", 10),
257
- minor: parseInt(parts[1] || "0", 10),
258
- patch: parseInt(parts[2] || "0", 10),
259
- raw: version,
260
- };
261
- }
262
- /**
263
- * Format version components back to string
264
- */
265
- formatVersion(version) {
266
- return `${version.major}.${version.minor}.${version.patch}`;
267
- }
268
- /**
269
- * Increment patch version
270
- */
271
- incrementPatch(version) {
272
- return {
273
- ...version,
274
- patch: version.patch + 1,
275
- };
276
- }
277
150
  /**
278
151
  * Auto-fix version compliance issues
279
152
  */
@@ -318,7 +191,7 @@ export class VersionComplianceProcessor {
318
191
  "============================",
319
192
  "",
320
193
  `NPM Published: ${result.npmVersion}`,
321
- `Version Manager: ${result.uvmVersion}`,
194
+ `UVM: ${result.uvmVersion}`,
322
195
  `package.json: ${result.pkgVersion}`,
323
196
  "",
324
197
  result.compliant ? "✅ COMPLIANT" : "❌ NON-COMPLIANT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "0xray",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
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",
@@ -61,8 +61,8 @@
61
61
  },
62
62
  "scripts": {
63
63
  "prepublishOnly": "node scripts/node/reconcile-version.mjs --check && npm run prepare-consumer && npm run build:all && find dist -name '*.js.map' -o -name '*.d.ts.map' | xargs rm -f 2>/dev/null || true",
64
- "version:bump": "node scripts/node/version-manager.mjs",
65
- "version": "node scripts/node/version-manager.mjs",
64
+ "version:bump": "node scripts/foundry/reconcile-version.mjs",
65
+ "version": "node scripts/foundry/version-manager.mjs",
66
66
  "prebuild": "rm -rf dist tsconfig.tsbuildinfo tsconfig.*.tsbuildinfo",
67
67
  "build": "tsc && chmod +x dist/cli/index.js && mkdir -p dist/public dist/scripts && cp -r public/* dist/public/ && cp scripts/hooks/pre-command dist/scripts/ && cp scripts/hooks/pre-command.mjs dist/scripts/ && cp README.md AGENTS.md SKILLS.md CHANGELOG.md LICENSE .gitignore.default dist/ && find src -name '*.mjs' ! -path '*/__tests__/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done && for dir in skills integrations mcps; do find src/$dir -type f ! -name '*.ts' ! -path '*/.pytest_cache/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done; done && mkdir -p dist/plugin && (find dist/plugin -name '*codex-injection*.js' -exec cp {} dist/plugin/xray-codex-injection.js \\; 2>/dev/null || true) && cp -r src/opencode/ .opencode/ && mkdir -p .opencode/skills && cp -R src/skills/. .opencode/skills/ && mkdir -p .xray && for f in codex.json features.json features.schema.json config.json; do cp xray/$f .xray/$f 2>/dev/null || true; done",
68
68
  "build:all": "npm run build",
@@ -112,20 +112,20 @@
112
112
  "build:run": "node scripts/build/utils.js build",
113
113
  "build:clean": "node scripts/build/utils.js clean",
114
114
  "build:verify": "node scripts/build/utils.js verify",
115
- "enforce:versions": "bash scripts/node/enforce-version-compliance.sh",
116
- "version:sync": "node scripts/node/version-manager.mjs --artifacts-only",
117
- "release": "node scripts/node/release.mjs patch",
118
- "release:minor": "node scripts/node/release.mjs minor",
119
- "release:major": "node scripts/node/release.mjs major",
120
- "release:dry": "node scripts/node/release.mjs patch --dry-run",
121
- "version:reconcile": "node scripts/node/reconcile-version.mjs",
115
+ "enforce:versions": "node scripts/foundry/validate-release-docs.mjs",
116
+ "version:sync": "node scripts/foundry/version-manager.mjs --artifacts-only",
117
+ "release": "node scripts/foundry/release.mjs patch",
118
+ "release:minor": "node scripts/foundry/release.mjs minor",
119
+ "release:major": "node scripts/foundry/release.mjs major",
120
+ "release:dry": "node scripts/foundry/release.mjs patch --dry-run",
121
+ "version:reconcile": "node scripts/foundry/reconcile-version.mjs",
122
122
  "consumer:smoke": "node scripts/node/consumer-install-smoke.mjs",
123
- "release:gate": "node scripts/node/release-gate.mjs",
124
- "release:docs-check": "node scripts/node/validate-release-docs.mjs",
123
+ "release:gate": "node scripts/foundry/release-gate.mjs",
124
+ "release:docs-check": "node scripts/foundry/validate-release-docs.mjs",
125
125
  "pre-publish-guard": "node scripts/node/pre-publish-guard.js",
126
126
  "safe-publish": "npm run prepare-consumer && npm run build",
127
- "release:npm": "npm run release:gate && npm run safe-publish && npm publish --access public",
128
- "release:patch": "node scripts/node/release.mjs patch",
127
+ "release:npm": "node scripts/foundry/release.mjs --publish-only",
128
+ "release:patch": "node scripts/foundry/release.mjs patch",
129
129
  "verify:orchestrator-behavior": "npm run build && node scripts/mjs/verify-orchestrator-behavior.mjs",
130
130
  "verify:grok-delegation-gate": "npm run build && node scripts/mjs/verify-grok-delegation-gate.mjs && node scripts/mjs/verify-grok-synthesis-gate.mjs",
131
131
  "verify:grok-synthesis-gate": "npm run build && node scripts/mjs/verify-grok-synthesis-gate.mjs",
@@ -146,6 +146,7 @@
146
146
  "files": [
147
147
  "dist/",
148
148
  "scripts/node/",
149
+ "scripts/foundry/",
149
150
  "scripts/integrations/",
150
151
  "scripts/helpers/",
151
152
  "scripts/hooks/",
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ ## [0.1.4] - 2026-09-04
4
+
5
+ `npx @0xray/foundry hooks` on a published mill uses the worn garment
6
+ `node_modules/0xray/scripts/hooks/install-hooks.cjs`. Dogfood still uses
7
+ `scripts/hooks` next to `scripts/foundry`. Missing installer fails honestly
8
+ (no spawn of `node_modules/@0xray/hooks/...`).
9
+
10
+ ## [0.1.3] - 2026-09-04
11
+
12
+ Pages homepage is a baseUrl meta refresh to `/docs/`, not a client redirect to `/docs/introduction` (org-root 404).
13
+
14
+ Docusaurus is mill: `npx @0xray/foundry docs-build` (exo only). GitHub Pages no longer clobbers the generated homepage with a stale 3.x `static/index.html`.
15
+
16
+ ## [0.1.2] - 2026-09-04
17
+
18
+ Mill owns GitHub CI, git hooks, and CI report. `npx @0xray/foundry ci` reports Actions (no auto-push). `npx @0xray/foundry hooks` installs pre/post git hooks. `0xRay CI/CD` is the mill gate on main.
19
+
20
+ ## [0.1.1] - 2026-09-04
21
+
22
+ Live `release` (bump/commit/push/publish of the milled cwd) requires `--i-mean-it` or `FOUNDRY_RELEASE=1`. `--dry-run` stays free. Packed mill tarball mints overlay. Stamp keeps `[Unreleased]` and inserts the version under it.
23
+
24
+ ## [0.1.0] - 2026-09-03
25
+
26
+ First mill package. Overlay their constitution, features, skills, and agents onto **project** hangers. Stamp/gate/docs-check mill `FOUNDRY_ROOT` or cwd. Not the worn exo.
27
+
28
+ Publish this package from this directory:
29
+
30
+ ```bash
31
+ cd scripts/foundry && npm publish --access public
32
+ ```
33
+
34
+ Do not `npm publish` from the 0xray repo root (that is `0xray@4.0.1`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Xray Framework Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,67 @@
1
+ # @0xray/foundry
2
+
3
+ The **mill**, not the worn exo. Reconcile, stamp JSON/CHANGELOG, verify docs, gate, publish, overlay their suit params.
4
+
5
+ PPE (`evaluatePreToolGate`) stays worn. The mill plants **their** constitution, features, skills, and agents onto the hanger. It is not a fifth MCP. The suit is [`0xray`](https://www.npmjs.com/package/0xray).
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm i -D @0xray/foundry
11
+ ```
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ npx @0xray/foundry reconcile [patch|minor|major] [--apply] [--check]
17
+ npx @0xray/foundry stamp
18
+ npx @0xray/foundry docs-check
19
+ npx @0xray/foundry docs-build
20
+ npx @0xray/foundry gate [--verify-only]
21
+ npx @0xray/foundry release [patch|minor|major] --dry-run
22
+ npx @0xray/foundry release [patch|minor|major] --i-mean-it
23
+ npx @0xray/foundry release --publish-only --dry-run
24
+ npx @0xray/foundry release --publish-only --i-mean-it
25
+ npx @0xray/foundry mint
26
+ npx @0xray/foundry ci [--commit SHA] [--report]
27
+ npx @0xray/foundry hooks
28
+ ```
29
+
30
+ `release` without `--dry-run` requires `--i-mean-it` or `FOUNDRY_RELEASE=1`. It is a bump/commit/push/publish bot for the **milled cwd**, not a casual command.
31
+
32
+ `FOUNDRY_ROOT` overrides cwd (the repo being milled).
33
+
34
+ `gate` is build + test + docs-check. `docs-build` runs Docusaurus on the 0xray exo (`docs-site/`); stranger mills skip. `ci` reports GitHub Actions (no auto-push). `hooks` installs git pre/post hooks. GitHub `0xRay CI/CD` is the mill gate on `main`. `Deploy Docs` is the mill Pages put.
35
+
36
+ `docs-check` and `gate` run **full** 0xRay corpus only when the milled `package.json` name is `0xray` and `docs-site/` exists. Otherwise **light**: `package.json` + CHANGELOG.
37
+
38
+ `hooks` installs git hooks via `scripts/hooks/install-hooks.cjs` (dogfood) or `node_modules/0xray/scripts/hooks/install-hooks.cjs` (published mill + worn garment). It does not look for `@0xray/hooks`.
39
+
40
+ ## Suit params (`mint`)
41
+
42
+ After the garment is on the hanger, overlay their plant (defaults; remap in `foundry.json` or `.xray/foundry.json`):
43
+
44
+ | Facet | Default plant | Hanger |
45
+ |---|---|---|
46
+ | Constitution | `xray/codex.json` | `.xray/codex.json` |
47
+ | Features / temperament | `xray/features.json` | `.xray/features.json` |
48
+ | Config | `xray/config.json` | `.xray/config.json` |
49
+ | Skills | `src/skills/<name>/SKILL.md` | `.opencode/skills/` and project `.grok/plugins/0xray/skills` if that dir exists |
50
+ | Agents | `src/opencode/agents/*.yml` | `.opencode/agents/` (OpenCode hangar; other floors do not read mill YML agents) |
51
+ | Agents card | `xray/AGENTS.md` | `AGENTS.md` if mill-managed |
52
+
53
+ **Mill-fill is law:** their keys/files win; mill names they did not plant stay unless they overlay the same name. JSON facets always merge. Skill overlay is project hangers only. CLI re-wear calls mint **once** after mill copies.
54
+
55
+ ## Publish this mill
56
+
57
+ From **this directory**, not the 0xray repo root:
58
+
59
+ ```bash
60
+ cd scripts/foundry && npm publish --access public
61
+ ```
62
+
63
+ Repo-root `npm publish` is `0xray`. `npx @0xray/foundry release --i-mean-it` publishes the **milled cwd** package (their `publishConfig`; `--access public` only for `0xray` and `@0xray/foundry`).
64
+
65
+ ## 0xRay exo
66
+
67
+ The mill lives in `scripts/foundry/` inside [0xRayAI/xray](https://github.com/0xRayAI/xray). `scripts/node/*.mjs` wrappers are shims onto this package.
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Mill CI monitor. Reports GitHub Actions for the milled cwd.
4
+ * Not PPE. Not an auto-push bot.
5
+ *
6
+ * npx @0xray/foundry ci [--commit SHA] [--report]
7
+ */
8
+
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { resolveMillRoot } from "./mill-root.mjs";
13
+
14
+ const args = process.argv.slice(2);
15
+ const commitIdx = args.indexOf("--commit");
16
+ const shaArg = commitIdx >= 0 ? args[commitIdx + 1] : "";
17
+ const sha = (shaArg && !shaArg.startsWith("-") ? shaArg : process.env.GITHUB_SHA || "").trim();
18
+ const root = resolveMillRoot();
19
+ const reportPath = path.join(root, ".opencode", "logs", "ci-cd-monitor-report.json");
20
+
21
+ function writeReport(report) {
22
+ fs.mkdirSync(path.dirname(reportPath), { recursive: true });
23
+ fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
24
+ }
25
+
26
+ function emptyReport(status, reason) {
27
+ return {
28
+ ci_status: status,
29
+ health_score: status === "healthy" || status === "unknown" ? 100 : 0,
30
+ issues: [],
31
+ reason,
32
+ sha: sha || null,
33
+ generatedAt: new Date().toISOString(),
34
+ };
35
+ }
36
+
37
+ async function main() {
38
+ const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || "";
39
+ const repo = process.env.GITHUB_REPOSITORY || "";
40
+ if (!token || !repo.includes("/")) {
41
+ const report = emptyReport(
42
+ "unknown",
43
+ "no GITHUB_TOKEN or GITHUB_REPOSITORY — mill CI monitor skipped",
44
+ );
45
+ writeReport(report);
46
+ process.stdout.write("SUCCESS: mill CI monitor skipped (no GitHub token)\n");
47
+ process.stdout.write("No failed jobs\n");
48
+ return 0;
49
+ }
50
+
51
+ const url = new URL(`https://api.github.com/repos/${repo}/actions/runs`);
52
+ url.searchParams.set("per_page", "50");
53
+ if (sha) url.searchParams.set("head_sha", sha);
54
+
55
+ const res = await fetch(url, {
56
+ headers: {
57
+ Authorization: `Bearer ${token}`,
58
+ Accept: "application/vnd.github+json",
59
+ "X-GitHub-Api-Version": "2022-11-28",
60
+ "User-Agent": "0xray-foundry-ci-monitor",
61
+ },
62
+ });
63
+ if (!res.ok) {
64
+ const report = emptyReport("warning", `GitHub API ${res.status}`);
65
+ writeReport(report);
66
+ process.stderr.write(`mill ci: GitHub API ${res.status}\n`);
67
+ return 1;
68
+ }
69
+ const body = await res.json();
70
+ const runs = Array.isArray(body.workflow_runs) ? body.workflow_runs : [];
71
+ const failed = runs.filter((run) => {
72
+ const conclusion = String(run.conclusion || "");
73
+ return conclusion === "failure" || conclusion === "timed_out" || conclusion === "startup_failure";
74
+ });
75
+ const issues = failed.map((run) => ({
76
+ name: run.name || run.display_title || "workflow",
77
+ conclusion: run.conclusion,
78
+ html_url: run.html_url,
79
+ head_sha: run.head_sha,
80
+ }));
81
+ const uniqueFails = new Set(issues.map((i) => i.name)).size;
82
+ const health = Math.max(0, 100 - uniqueFails * 20);
83
+ const ci_status = uniqueFails === 0 ? "healthy" : uniqueFails >= 3 ? "critical" : "warning";
84
+ const report = {
85
+ ci_status,
86
+ health_score: health,
87
+ issues,
88
+ sha: sha || null,
89
+ run_count: runs.length,
90
+ generatedAt: new Date().toISOString(),
91
+ };
92
+ writeReport(report);
93
+
94
+ if (uniqueFails === 0) {
95
+ process.stdout.write("SUCCESS: All workflows passed\n");
96
+ process.stdout.write("No failed jobs\n");
97
+ return 0;
98
+ }
99
+ process.stdout.write(`FAILURE: ${issues.map((i) => i.name).join(", ")}\n`);
100
+ return 1;
101
+ }
102
+
103
+ const isMain =
104
+ process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
105
+
106
+ if (isMain) {
107
+ main()
108
+ .then((code) => process.exit(code))
109
+ .catch((err) => {
110
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
111
+ process.exit(1);
112
+ });
113
+ }
114
+
115
+ export { main };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "./cli.mjs";
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @0xray/foundry CLI — mill, not exo. No fifth MCP.
4
+ *
5
+ * npx @0xray/foundry <reconcile|stamp|gate|release|docs-check|docs-build|mint|ci|hooks> [...args]
6
+ */
7
+
8
+ import { spawnSync } from "node:child_process";
9
+ import { millScript, resolveMillRoot } from "./mill-root.mjs";
10
+
11
+ const COMMANDS = {
12
+ reconcile: { script: "reconcile-version.mjs", preset: [] },
13
+ stamp: { script: "version-manager.mjs", preset: ["--artifacts-only"] },
14
+ gate: { script: "release-gate.mjs", preset: [] },
15
+ release: { script: "release.mjs", preset: [] },
16
+ "docs-check": { script: "validate-release-docs.mjs", preset: [] },
17
+ "docs-build": { script: "docs-build.mjs", preset: [] },
18
+ mint: { script: "mint.mjs", preset: [] },
19
+ ci: { script: "ci-monitor.mjs", preset: [] },
20
+ hooks: { script: "hooks.mjs", preset: [] },
21
+ };
22
+
23
+ const HELP =
24
+ "Usage: npx @0xray/foundry <reconcile|stamp|gate|release|docs-check|docs-build|mint|ci|hooks> [...args]\n" +
25
+ "\n" +
26
+ "Mill, not exo. FOUNDRY_ROOT overrides cwd (the repo being milled).\n" +
27
+ "release bumps/commits/pushes/publishes the milled cwd — pass --dry-run or --i-mean-it (or FOUNDRY_RELEASE=1).\n" +
28
+ "Mint overlays their plant onto project hangers (mill-fill). PPE stays worn.\n" +
29
+ "gate is build+test. docs-build is Docusaurus on the 0xray exo. ci reports GitHub Actions (no auto-push). hooks installs git pre/post hooks.\n";
30
+
31
+ const cmd = process.argv[2];
32
+ const rest = process.argv.slice(3);
33
+
34
+ if (cmd === "--help" || cmd === "-h" || cmd === "help") {
35
+ process.stdout.write(HELP);
36
+ process.exit(0);
37
+ }
38
+
39
+ if (!cmd || !COMMANDS[cmd]) {
40
+ process.stderr.write(HELP);
41
+ process.exit(1);
42
+ }
43
+
44
+ const spec = COMMANDS[cmd];
45
+ const root = resolveMillRoot();
46
+ const result = spawnSync(
47
+ process.execPath,
48
+ [millScript(spec.script), ...spec.preset, ...rest],
49
+ {
50
+ stdio: "inherit",
51
+ cwd: root,
52
+ env: { ...process.env, FOUNDRY_ROOT: root },
53
+ },
54
+ );
55
+ process.exit(result.status === null ? 1 : result.status);
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Mill Docusaurus build. Full corpus only on the 0xray exo (docs-site/).
4
+ * Stranger mills skip. Not PPE. Not an 8th MCP.
5
+ *
6
+ * npx @0xray/foundry docs-build
7
+ */
8
+
9
+ import { spawnSync } from "node:child_process";
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { isXrayExoRepo, resolveMillRoot } from "./mill-root.mjs";
14
+
15
+ function run(cmd, args, cwd) {
16
+ const result = spawnSync(cmd, args, { cwd, stdio: "inherit", env: process.env });
17
+ return result.status === null ? 1 : result.status;
18
+ }
19
+
20
+ function main() {
21
+ const root = resolveMillRoot();
22
+ if (!isXrayExoRepo(root)) {
23
+ process.stdout.write("docs-build: skipped (not 0xray exo with docs-site)\n");
24
+ process.exit(0);
25
+ }
26
+ const docs = path.join(root, "docs-site");
27
+ if (!fs.existsSync(path.join(docs, "node_modules"))) {
28
+ const install = run("npm", ["ci"], docs);
29
+ if (install !== 0) process.exit(install);
30
+ }
31
+ process.exit(run("npm", ["run", "build"], docs));
32
+ }
33
+
34
+ const isMain =
35
+ process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
36
+ if (isMain) {
37
+ main();
38
+ }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Mill git hooks: install pre-commit / post-commit / pre-push / post-push.
4
+ * Wear-time installer lives at scripts/hooks/install-hooks.cjs (dogfood)
5
+ * or node_modules/0xray/scripts/hooks/install-hooks.cjs (published mill).
6
+ *
7
+ * npx @0xray/foundry hooks
8
+ */
9
+
10
+ import { spawnSync } from "node:child_process";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { resolveHookInstaller, resolveMillRoot } from "./mill-root.mjs";
14
+
15
+ function main() {
16
+ const root = resolveMillRoot();
17
+ const installer = resolveHookInstaller(root);
18
+ if (!installer) {
19
+ process.stderr.write(
20
+ "mill hooks: no install-hooks.cjs (dogfood scripts/hooks or node_modules/0xray/scripts/hooks)\n",
21
+ );
22
+ process.exit(1);
23
+ }
24
+ const result = spawnSync(process.execPath, [installer, ...process.argv.slice(2)], {
25
+ stdio: "inherit",
26
+ cwd: root,
27
+ env: { ...process.env, FOUNDRY_ROOT: root },
28
+ });
29
+ process.exit(result.status === null ? 1 : result.status);
30
+ }
31
+
32
+ const isMain =
33
+ process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
34
+ if (isMain) {
35
+ main();
36
+ }