@uxlint-net/uxlint 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/bin/uxlint.js +116 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @uxlint-net/uxlint
|
|
2
|
+
|
|
3
|
+
Audit any website's UX the way a design-literate reviewer would — contrast, tap targets, type scale,
|
|
4
|
+
colour discipline, copy clarity, scan patterns, resilience — and get a concrete fix for every finding.
|
|
5
|
+
|
|
6
|
+
Built to sit in a coding agent's loop: an agent writes UI it cannot see, and this is how it looks.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npx @uxlint-net/uxlint audit --base http://localhost:5173 # audit a running site
|
|
10
|
+
npx @uxlint-net/uxlint mcp # run the MCP server (stdio)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Use it from an agent
|
|
14
|
+
|
|
15
|
+
Add it as an MCP server. In Claude Code:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
claude mcp add uxlint -- npx -y @uxlint-net/uxlint mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or in any client that reads a JSON config:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"uxlint": { "command": "npx", "args": ["-y", "@uxlint-net/uxlint", "mcp"] }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The agent gets `audit_url` (audit and get findings with fixes), `verify_fix` (re-check one rule on one
|
|
32
|
+
page in ~2s), `ux_guidance` (the idiomatic pattern for an area, before you change UI) and `get_shot`
|
|
33
|
+
(the annotated screenshot of a finding).
|
|
34
|
+
|
|
35
|
+
## What this package is
|
|
36
|
+
|
|
37
|
+
A launcher, not the tool. uxlint is a single compiled Rust binary; this package downloads the build for
|
|
38
|
+
your platform from the matching GitHub release, verifies the checksum published beside it, caches it
|
|
39
|
+
under `~/.cache/uxlint`, and hands over. The version you install is the version you get —
|
|
40
|
+
`npx @uxlint-net/uxlint@0.1.26` runs exactly that binary.
|
|
41
|
+
|
|
42
|
+
It drives a Chrome or Chromium you already have (no browser download, no Node runtime for the audit
|
|
43
|
+
itself). macOS and Linux, x64 and arm64. `CHROME=/path/to/chrome` if yours lives somewhere unusual.
|
|
44
|
+
|
|
45
|
+
## Privacy
|
|
46
|
+
|
|
47
|
+
The capture script is compiled into the binary and the source is public, so what runs in your pages is
|
|
48
|
+
fixed by the version you installed and can't be changed at run time. `uxlint audit --dry-run <dir>`
|
|
49
|
+
writes the exact payload to a folder so you can read it before anything is uploaded. See
|
|
50
|
+
[Privacy & trust](https://github.com/uxlint-net/uxlint-cli#privacy--trust).
|
|
51
|
+
|
|
52
|
+
- Docs: <https://uxlint.net/docs>
|
|
53
|
+
- Source: <https://github.com/uxlint-net/uxlint-cli>
|
package/bin/uxlint.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `npx uxlint …` — the npm face of the uxlint CLI.
|
|
3
|
+
//
|
|
4
|
+
// uxlint is a compiled Rust binary, but nearly every MCP client, editor and directory assumes a
|
|
5
|
+
// one-line `npx` command with no prior install. This package is that line. It ships no binary of its
|
|
6
|
+
// own: it fetches the one published for this platform from the GitHub release matching its own
|
|
7
|
+
// version, verifies the checksum we publish beside it, caches it, and hands over.
|
|
8
|
+
//
|
|
9
|
+
// Two rules govern everything here:
|
|
10
|
+
//
|
|
11
|
+
// 1. NOTHING may be written to stdout. Under `uxlint mcp` stdout is the JSON-RPC channel, and one
|
|
12
|
+
// stray line of progress makes the server look broken to its client. Every message goes to
|
|
13
|
+
// stderr, which clients show as logs.
|
|
14
|
+
// 2. The version is pinned to this package's own version, so `npx uxlint@0.1.26` runs exactly the
|
|
15
|
+
// 0.1.26 binary. An npm install that silently drifted to a newer CLI would make the two version
|
|
16
|
+
// numbers a lie, and /v1/me tells clients which CLI to run.
|
|
17
|
+
//
|
|
18
|
+
// No dependencies, on purpose: a launcher that fetches a signed artifact should not itself pull a
|
|
19
|
+
// tree of packages. `tar` is invoked from PATH (present on macOS and Linux, the platforms we build).
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const { spawn, spawnSync } = require('node:child_process');
|
|
23
|
+
const crypto = require('node:crypto');
|
|
24
|
+
const fs = require('node:fs');
|
|
25
|
+
const os = require('node:os');
|
|
26
|
+
const path = require('node:path');
|
|
27
|
+
|
|
28
|
+
const { version } = require('../package.json');
|
|
29
|
+
const REPO = 'uxlint-net/uxlint-cli';
|
|
30
|
+
|
|
31
|
+
const log = (msg) => process.stderr.write(`uxlint: ${msg}\n`);
|
|
32
|
+
|
|
33
|
+
/** The release asset for this machine, or null if we don't publish one for it. */
|
|
34
|
+
function assetName() {
|
|
35
|
+
const arch = { x64: 'x64', arm64: 'arm64' }[process.arch];
|
|
36
|
+
const plat = { linux: 'linux', darwin: 'macos' }[process.platform];
|
|
37
|
+
return arch && plat ? `uxlint-${plat}-${arch}.tar.gz` : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Follows redirects — GitHub release downloads always redirect to object storage. */
|
|
41
|
+
async function fetchBuffer(url, redirects = 5) {
|
|
42
|
+
const res = await fetch(url, { redirect: 'follow' });
|
|
43
|
+
if (!res.ok) throw new Error(`${res.status} ${res.statusText} for ${url}`);
|
|
44
|
+
return Buffer.from(await res.arrayBuffer());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function install(dir, asset) {
|
|
48
|
+
const base = `https://github.com/${REPO}/releases/download/v${version}/${asset}`;
|
|
49
|
+
log(`downloading ${asset} v${version} (once)`);
|
|
50
|
+
const [tarball, shaFile] = await Promise.all([
|
|
51
|
+
fetchBuffer(base),
|
|
52
|
+
fetchBuffer(`${base}.sha256`).catch(() => null) // absent → we say so rather than pretend
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
// Verify against the checksum published beside the artifact. This is the same file the shell
|
|
56
|
+
// installer checks; skipping it silently would make `npx` the weakest way to install uxlint.
|
|
57
|
+
if (shaFile) {
|
|
58
|
+
const want = shaFile.toString('utf8').trim().split(/\s+/)[0];
|
|
59
|
+
const got = crypto.createHash('sha256').update(tarball).digest('hex');
|
|
60
|
+
if (want !== got) {
|
|
61
|
+
throw new Error(`checksum mismatch for ${asset}\n published ${want}\n downloaded ${got}`);
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
log(`warning: no published checksum for ${asset} — proceeding unverified`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
68
|
+
const tmp = path.join(dir, asset);
|
|
69
|
+
fs.writeFileSync(tmp, tarball);
|
|
70
|
+
const untar = spawnSync('tar', ['xzf', tmp, '-C', dir], { stdio: ['ignore', 'ignore', 'inherit'] });
|
|
71
|
+
fs.unlinkSync(tmp);
|
|
72
|
+
if (untar.status !== 0) throw new Error('could not extract the release archive (is `tar` on PATH?)');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function main() {
|
|
76
|
+
const asset = assetName();
|
|
77
|
+
if (!asset) {
|
|
78
|
+
log(`no published build for ${process.platform}/${process.arch}.`);
|
|
79
|
+
log('supported: macOS and Linux on x64 or arm64. See https://uxlint.net/docs/cli');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Versioned cache: a new package version fetches a new binary, and old ones stay put rather than
|
|
84
|
+
// being overwritten under a running process.
|
|
85
|
+
const cache = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
86
|
+
const dir = path.join(cache, 'uxlint', 'bin', version);
|
|
87
|
+
const bin = path.join(dir, 'uxlint');
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(bin)) {
|
|
90
|
+
try {
|
|
91
|
+
await install(dir, asset);
|
|
92
|
+
fs.chmodSync(bin, 0o755);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
log(`install failed: ${err.message}`);
|
|
95
|
+
log('you can install it directly instead: curl -fsSL https://uxlint.net/install.sh | sh');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Hand over completely: same argv, same streams, same exit code. `npx uxlint mcp` is then
|
|
101
|
+
// indistinguishable from running the binary, which is what an MCP client needs.
|
|
102
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: 'inherit' });
|
|
103
|
+
child.on('error', (err) => {
|
|
104
|
+
log(`could not run ${bin}: ${err.message}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
107
|
+
child.on('exit', (code, signal) => {
|
|
108
|
+
if (signal) process.kill(process.pid, signal);
|
|
109
|
+
else process.exit(code ?? 0);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
main().catch((err) => {
|
|
114
|
+
log(String((err && err.message) || err));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uxlint-net/uxlint",
|
|
3
|
+
"mcpName": "io.github.uxlint-net/uxlint",
|
|
4
|
+
"version": "0.1.26",
|
|
5
|
+
"description": "Audit any website's UX the way a design-literate reviewer would — contrast, tap targets, type scale, copy, scan patterns, resilience — with a concrete fix for every finding. Runs in the Chrome you already have; built for coding agents (MCP).",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ux",
|
|
8
|
+
"design",
|
|
9
|
+
"accessibility",
|
|
10
|
+
"a11y",
|
|
11
|
+
"audit",
|
|
12
|
+
"mcp",
|
|
13
|
+
"mcp-server",
|
|
14
|
+
"modelcontextprotocol",
|
|
15
|
+
"lighthouse",
|
|
16
|
+
"code-review"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://uxlint.net",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/uxlint-net/uxlint-cli.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/uxlint-net/uxlint-cli/issues"
|
|
25
|
+
},
|
|
26
|
+
"license": "BUSL-1.1",
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "uxlint",
|
|
29
|
+
"url": "https://uxlint.net"
|
|
30
|
+
},
|
|
31
|
+
"type": "commonjs",
|
|
32
|
+
"bin": {
|
|
33
|
+
"uxlint": "bin/uxlint.js"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"bin/uxlint.js",
|
|
37
|
+
"README.md"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"os": [
|
|
43
|
+
"darwin",
|
|
44
|
+
"linux"
|
|
45
|
+
],
|
|
46
|
+
"cpu": [
|
|
47
|
+
"x64",
|
|
48
|
+
"arm64"
|
|
49
|
+
]
|
|
50
|
+
}
|