devlens-mcp 0.1.0 → 0.1.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.
- package/README.md +2 -7
- package/SKILL.md +2 -2
- package/dist/index.js +27 -12
- package/package.json +16 -4
package/README.md
CHANGED
|
@@ -17,18 +17,13 @@ DevLens keeps a single Chromium instance alive for the duration of your Claude C
|
|
|
17
17
|
|
|
18
18
|
## Setup
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
npm install -g devlens-mcp
|
|
22
|
-
npx playwright install chromium
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Then in your project root:
|
|
20
|
+
In your project root:
|
|
26
21
|
|
|
27
22
|
```bash
|
|
28
23
|
npx devlens-mcp init
|
|
29
24
|
```
|
|
30
25
|
|
|
31
|
-
This creates `.mcp.json`, `.claude/skills/devlens.md`, and `devlens.config.ts
|
|
26
|
+
This creates `.mcp.json`, `.claude/skills/devlens.md`, and `devlens.config.ts`, and installs the Chromium browser automatically.
|
|
32
27
|
|
|
33
28
|
Edit `devlens.config.ts` to map your source files to dev server routes, then restart Claude Code.
|
|
34
29
|
|
package/SKILL.md
CHANGED
|
@@ -16,12 +16,12 @@ You have access to four visual inspection tools: `dl_warmup`, `dl_capture`, `dl_
|
|
|
16
16
|
## Rules — follow these automatically, no user prompting needed
|
|
17
17
|
|
|
18
18
|
### Rule 1: Warmup at session start
|
|
19
|
-
The first time you are about to edit any file
|
|
19
|
+
The first time you are about to edit any frontend file (`.tsx`, `.ts`, `.css`, `.vue`, `.svelte`), call `dl_warmup` first.
|
|
20
20
|
This pre-warms the browser so subsequent captures cost ~50ms instead of ~500ms.
|
|
21
21
|
Only call it once per session.
|
|
22
22
|
|
|
23
23
|
### Rule 2: Capture after every frontend file write
|
|
24
|
-
After every `Edit` or `Write` to a file matching
|
|
24
|
+
After every `Edit` or `Write` to a file matching `**/*.tsx`, `**/*.vue`, `**/*.svelte`, or `**/*.css`:
|
|
25
25
|
1. Call `dl_capture` with either `route` (if you know it) or `filePath` (auto-resolved from devlens.config.ts)
|
|
26
26
|
2. Add `selector` if you only changed a specific component (keeps the image small and fast)
|
|
27
27
|
3. Look at the returned image — does it look right?
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// src/cli/init.ts
|
|
4
4
|
import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs";
|
|
5
5
|
import { resolve, join } from "path";
|
|
6
|
+
import { spawnSync } from "child_process";
|
|
6
7
|
var SKILL_CONTENT = `---
|
|
7
8
|
name: devlens
|
|
8
9
|
description: Real-time visual QA for frontend development. Auto-invoked when editing frontend files.
|
|
@@ -103,15 +104,19 @@ async function runInit() {
|
|
|
103
104
|
} else {
|
|
104
105
|
console.log(" \u2713 devlens.config.ts already exists \u2014 skipped");
|
|
105
106
|
}
|
|
107
|
+
console.log(" Installing Chromium browser...");
|
|
108
|
+
const result = spawnSync("npx", ["playwright", "install", "chromium"], { stdio: "inherit", shell: true });
|
|
109
|
+
if (result.status === 0) {
|
|
110
|
+
console.log(" \u2713 Chromium installed");
|
|
111
|
+
} else {
|
|
112
|
+
console.warn(' \u26A0 Chromium install failed \u2014 run "npx playwright install chromium" manually');
|
|
113
|
+
}
|
|
106
114
|
console.log("");
|
|
107
|
-
console.log("DevLens initialized. Next
|
|
108
|
-
console.log("");
|
|
109
|
-
console.log(" 1. Install Chromium (one-time):");
|
|
110
|
-
console.log(" npx playwright install chromium");
|
|
115
|
+
console.log("DevLens initialized. Next step:");
|
|
111
116
|
console.log("");
|
|
112
|
-
console.log("
|
|
117
|
+
console.log(" 1. Edit devlens.config.ts to map your pages to routes");
|
|
113
118
|
console.log("");
|
|
114
|
-
console.log("
|
|
119
|
+
console.log(" 2. Restart Claude Code");
|
|
115
120
|
console.log("");
|
|
116
121
|
console.log(" Claude will then automatically screenshot your UI after every file write.");
|
|
117
122
|
}
|
|
@@ -414,16 +419,24 @@ async function startServer() {
|
|
|
414
419
|
]
|
|
415
420
|
}));
|
|
416
421
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
417
|
-
const { name
|
|
422
|
+
const { name } = req.params;
|
|
423
|
+
const args = req.params.arguments ?? {};
|
|
424
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
425
|
+
const num = (v) => typeof v === "number" ? v : void 0;
|
|
418
426
|
try {
|
|
419
427
|
if (name === "dl_warmup") {
|
|
420
|
-
const result = await warmup(args.devServerUrl, config);
|
|
428
|
+
const result = await warmup(str(args.devServerUrl), config);
|
|
421
429
|
return {
|
|
422
430
|
content: [{ type: "text", text: `Warmed up in ${result.durationMs}ms \u2014 ${result.url}` }]
|
|
423
431
|
};
|
|
424
432
|
}
|
|
425
433
|
if (name === "dl_capture") {
|
|
426
|
-
const input =
|
|
434
|
+
const input = {
|
|
435
|
+
route: str(args.route),
|
|
436
|
+
filePath: str(args.filePath),
|
|
437
|
+
selector: str(args.selector),
|
|
438
|
+
waitMs: num(args.waitMs)
|
|
439
|
+
};
|
|
427
440
|
const result = await capture(input, config);
|
|
428
441
|
if ("error" in result) {
|
|
429
442
|
return { content: [{ type: "text", text: `Error: ${result.error}` }] };
|
|
@@ -437,11 +450,12 @@ async function startServer() {
|
|
|
437
450
|
return { content };
|
|
438
451
|
}
|
|
439
452
|
if (name === "dl_diff") {
|
|
440
|
-
const
|
|
453
|
+
const route = str(args.route) ?? "";
|
|
454
|
+
const selector = str(args.selector);
|
|
441
455
|
const result = await diff(route, selector, diffStore, config);
|
|
442
456
|
if ("error" in result) return { content: [{ type: "text", text: `Error: ${result.error}` }] };
|
|
443
457
|
const content = [
|
|
444
|
-
{ type: "text", text: result.diff ? `Changed: ${result.diff.changedPercent}% (${result.diff.changedPixels} pixels)` : result.message
|
|
458
|
+
{ type: "text", text: result.diff ? `Changed: ${result.diff.changedPercent}% (${result.diff.changedPixels} pixels)` : "message" in result && typeof result.message === "string" ? result.message : "Baseline set." }
|
|
445
459
|
];
|
|
446
460
|
if (result.diff?.diffImageBase64) {
|
|
447
461
|
content.push({ type: "image", data: result.diff.diffImageBase64, mimeType: "image/png" });
|
|
@@ -449,7 +463,8 @@ async function startServer() {
|
|
|
449
463
|
return { content };
|
|
450
464
|
}
|
|
451
465
|
if (name === "dl_snapshot") {
|
|
452
|
-
const
|
|
466
|
+
const route = str(args.route) ?? "";
|
|
467
|
+
const selector = str(args.selector);
|
|
453
468
|
const result = await snapshot(route, selector, config);
|
|
454
469
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
455
470
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devlens-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Real-time visual feedback plugin for Claude Code frontend development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"bin": {
|
|
8
14
|
"devlens-mcp": "dist/index.js"
|
|
9
15
|
},
|
|
@@ -26,19 +32,25 @@
|
|
|
26
32
|
"dev": "tsup --watch",
|
|
27
33
|
"test": "vitest run",
|
|
28
34
|
"test:watch": "vitest",
|
|
29
|
-
"start": "node dist/index.js"
|
|
35
|
+
"start": "node dist/index.js",
|
|
36
|
+
"lint": "eslint src --ext .ts",
|
|
37
|
+
"format": "prettier --write src"
|
|
30
38
|
},
|
|
31
39
|
"dependencies": {
|
|
32
40
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
33
41
|
"minimatch": "^10.2.5",
|
|
34
42
|
"pixelmatch": "^6.0.0",
|
|
35
43
|
"playwright": "^1.45.0",
|
|
36
|
-
"pngjs": "^7.0.0"
|
|
37
|
-
"zod": "^3.22.0"
|
|
44
|
+
"pngjs": "^7.0.0"
|
|
38
45
|
},
|
|
39
46
|
"devDependencies": {
|
|
40
47
|
"@types/node": "^20.0.0",
|
|
41
48
|
"@types/pngjs": "^6.0.0",
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
50
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
51
|
+
"eslint": "^8.57.0",
|
|
52
|
+
"eslint-config-prettier": "^9.0.0",
|
|
53
|
+
"prettier": "^3.0.0",
|
|
42
54
|
"tsup": "^8.0.0",
|
|
43
55
|
"typescript": "^5.4.0",
|
|
44
56
|
"vitest": "^1.6.0"
|