cssprobe-cli 0.2.26 → 0.3.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.
- package/AGENTS.md +29 -4
- package/CHANGELOG.md +11 -0
- package/README.md +84 -1
- package/dist/browser/interactions.d.ts +48 -0
- package/dist/browser/interactions.js +88 -0
- package/dist/cli/commands.js +97 -0
- package/dist/cli/program.js +78 -152
- package/dist/cli/session-ops.d.ts +44 -0
- package/dist/cli/session-ops.js +111 -0
- package/dist/daemon/daemon.js +102 -6
- package/dist/daemon/flowRunner.d.ts +62 -0
- package/dist/daemon/flowRunner.js +155 -0
- package/dist/engine/evalSerializer.d.ts +1 -0
- package/dist/engine/evalSerializer.js +43 -0
- package/dist/help.json +84 -1
- package/dist/installer/index.d.ts +2 -0
- package/dist/installer/index.js +6 -1
- package/dist/installer/mcp.d.ts +14 -0
- package/dist/installer/mcp.js +274 -0
- package/dist/installer/skill-template.js +43 -0
- package/dist/installer/toml.d.ts +20 -0
- package/dist/installer/toml.js +98 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.js +174 -0
- package/dist/mcp/server-instructions.d.ts +1 -0
- package/dist/mcp/server-instructions.js +27 -0
- package/dist/mcp/tools.d.ts +261 -0
- package/dist/mcp/tools.js +238 -0
- package/dist/utils/screenshot.d.ts +1 -0
- package/dist/utils/screenshot.js +58 -0
- package/llms.txt +2 -1
- package/package.json +2 -1
package/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`cssprobe-cli` — Runtime CSS probe for layout/scroll/overflow/color/font inspection with session-based browser management. Entrypoint: `bin/cssprobe-cli.js` → `require('../dist/index')`.
|
|
6
6
|
Build: `npm run build` (= `tsc` + `esbuild` collector IIFE bundle + `esbuild` daemon entry + `tsx scripts/generate-help.ts`).
|
|
7
|
-
Test: `npm test` (= `tsx --test tests/*.test.ts` — Node.js built-in test runner,
|
|
7
|
+
Test: `npm test` (= `tsx --test tests/*.test.ts` — Node.js built-in test runner, 108 tests).
|
|
8
8
|
Dependencies: `playwright-core`, `zod`. Dev: `typescript`, `@types/node`, `tsx`, `esbuild`.
|
|
9
9
|
|
|
10
10
|
## Architecture
|
|
@@ -21,19 +21,23 @@ src/
|
|
|
21
21
|
├── daemon/
|
|
22
22
|
│ ├── session.ts # Session management (single session, Unix socket)
|
|
23
23
|
│ ├── daemon.ts # Daemon process: browser lifecycle + command execution
|
|
24
|
+
│ ├── flowRunner.ts # Flow script loader/runner (run command): dynamic import, API injection, console capture
|
|
24
25
|
│ └── daemonEntry.ts # Daemon entry point (spawned as child process)
|
|
25
26
|
├── engine/
|
|
26
27
|
│ ├── types.ts # Shared types: Snapshot, TreeNode, Finding, Confidence
|
|
27
28
|
│ ├── collector.ts # Browser-side: DOM traversal, computed styles, declared values
|
|
28
29
|
│ ├── analyzer.ts # Node-side: pure functions, confidence model, pattern detection
|
|
30
|
+
│ ├── evalSerializer.ts # Self-contained serializer for eval results (runs in-page via JSHandle.evaluate)
|
|
29
31
|
│ └── renderer.ts # Markdown / JSON rendering
|
|
30
32
|
├── browser/
|
|
31
|
-
│
|
|
33
|
+
│ ├── launcher.ts # Playwright wrapper: launch, navigate, inject collector
|
|
34
|
+
│ └── interactions.ts # Interaction primitives: click/hover/type/wait-for (shared by daemon + run API)
|
|
32
35
|
├── config/
|
|
33
36
|
│ ├── config.ts # Config loader (CLI → env → ~/.cssprobe-clirc profiles)
|
|
34
37
|
│ └── helpGenerator.ts # Build-time: Zod schemas → dist/help.json
|
|
35
38
|
└── utils/
|
|
36
|
-
|
|
39
|
+
├── socketConnection.ts # Socket communication (adapted from playwright-cli)
|
|
40
|
+
└── screenshot.ts # Screenshot data-URL → file saving (shared by CLI + flow runner)
|
|
37
41
|
```
|
|
38
42
|
|
|
39
43
|
## Key Patterns
|
|
@@ -73,11 +77,32 @@ src/
|
|
|
73
77
|
- `resize <width> <height>` — Resize browser viewport
|
|
74
78
|
- `eval <expression>` — Evaluate JavaScript (browser context)
|
|
75
79
|
- Returns raw string values (not JSON-quoted) for string results
|
|
80
|
+
- DOM nodes / circular structures are auto-serialized (descriptors, `[Circular]`, `[Function: name]`, depth cap 6)
|
|
76
81
|
- `playwright <call>` — Execute Playwright API call (Node.js context)
|
|
77
82
|
- Returns `{ ok: true, value: null, note: ... }` when the call returns no value; throws a clear error message on failure
|
|
78
83
|
- `screenshot` — Take a screenshot
|
|
79
84
|
- `--out <path>` — Output file path (default: `~/.cssprobe-cli/screenshots/<timestamp>.png`)
|
|
80
85
|
|
|
86
|
+
### Interaction
|
|
87
|
+
- `click <selector>` — Real mouse click (auto-scroll + actionability wait, works with custom React components)
|
|
88
|
+
- `--nth <n>` — 1-based match index (default: first match; multiple matches without `--nth` fail — strict)
|
|
89
|
+
- `--force` — skip actionability checks
|
|
90
|
+
- `--timeout <ms>` — action timeout (default 30000)
|
|
91
|
+
- `hover <selector>` — Real mouse hover (`--nth` / `--force` / `--timeout` as above)
|
|
92
|
+
- `type <selector> <text>` — Type text
|
|
93
|
+
- Default: `fill()` (replaces the value); `--sequential` for real key events (custom editors), `--clear` clears first
|
|
94
|
+
- `--press <key>` — press a key after typing (e.g. `Enter`)
|
|
95
|
+
- `--delay <ms>` — keystroke delay for `--sequential`
|
|
96
|
+
- `wait-for` — Wait for a condition (three mutually exclusive modes)
|
|
97
|
+
- `wait-for <selector>` — selector state: `--state visible|hidden|attached|detached` (default visible)
|
|
98
|
+
- `wait-for --js "<predicate>"` — wait until a JS predicate is truthy
|
|
99
|
+
- `wait-for --network-idle` — wait until the page is network-idle
|
|
100
|
+
- `--timeout <ms>` — default 30000; timeout errors name the target
|
|
101
|
+
- `run <file>` — Run a multi-step flow script inside the daemon
|
|
102
|
+
- Script default-exports an async function receiving the API object: `page`, `click`, `hover`, `type`, `waitFor`, `inspect`, `screenshot`, `log` (CJS also supported)
|
|
103
|
+
- `--timeout <ms>` — whole-script guard (default 300000, 0 = unlimited); failure exits 1 and keeps the session alive
|
|
104
|
+
- `screenshot` relative paths resolve against the script's directory; logs collected and returned
|
|
105
|
+
|
|
81
106
|
### State
|
|
82
107
|
- `state-import [file]` — Import cookies from Netscape format
|
|
83
108
|
- `--out <file>` — Output state file path (default: `~/.cssprobe-cli/states/imported.json`)
|
|
@@ -151,6 +176,6 @@ cssprobe-cli close
|
|
|
151
176
|
| Output | Single markdown string | Markdown (default) / JSON (--json) |
|
|
152
177
|
| Config | `window.__DOM_REPORT_CFG` global + eval two-step | CLI flags → env → rc file, one command |
|
|
153
178
|
| ROOT_SELECTOR | Manual or eval probe | `--auto-root` auto-detection |
|
|
154
|
-
| Tests | None | 45 unit tests (command, analyzer, output, renderer) |
|
|
179
|
+
| Tests | None | 45+ unit tests (command, analyzer, output, renderer) |
|
|
155
180
|
| Build | Regex stripping CJS module.exports | esbuild IIFE + CJS bundle, clean build |
|
|
156
181
|
| Session | None (blocking commands) | Session-based daemon architecture |
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes are documented on the [GitHub Releases](https://github.com/mack-peng/cssprobe-cli/releases) page.
|
|
4
4
|
|
|
5
|
+
## [0.3.0] - 2026-09-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- MCP server (`cssprobe-cli mcp`, stdio): 10 tools — open / inspect / tree / layout / findings / eval / screenshot / inject_css / close / status
|
|
9
|
+
- `mcp-install` / `mcp-uninstall`: auto-configure MCP clients (Claude Code, Cursor, Codex CLI, opencode, Gemini CLI, Hermes)
|
|
10
|
+
- `mcpName` (`io.github.mack-peng/cssprobe-cli`) for the official MCP Registry
|
|
11
|
+
- README MCP section; `llms.txt` MCP pointer
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Session commands (open/close/status/inspect/…) now share one implementation between CLI and MCP (`src/cli/session-ops.ts`); behavior unchanged
|
|
15
|
+
|
|
5
16
|
## [0.2.26] - 2026-09-16
|
|
6
17
|
|
|
7
18
|
### Added
|
package/README.md
CHANGED
|
@@ -119,6 +119,30 @@ cssprobe-cli inspect body --json
|
|
|
119
119
|
cssprobe-cli inspect body --json | jq '.findings[] | {id, confidence, message}'
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### 4. Use as an MCP Server (AI clients)
|
|
123
|
+
|
|
124
|
+
cssprobe-cli ships an MCP server (stdio), so AI clients call it as a native tool — no shell needed:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cssprobe-cli mcp-install # auto-detect and configure installed clients
|
|
128
|
+
cssprobe-cli mcp-install --target=claude,cursor,codex
|
|
129
|
+
cssprobe-cli mcp-install --local # write project-scoped config
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Manual configuration for any MCP client:
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"mcpServers": {
|
|
137
|
+
"cssprobe-cli": { "command": "cssprobe-cli", "args": ["mcp"] }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Exposes 10 tools: `cssprobe_open`, `cssprobe_inspect`, `cssprobe_tree`, `cssprobe_layout`,
|
|
143
|
+
`cssprobe_findings`, `cssprobe_eval`, `cssprobe_screenshot`, `cssprobe_inject_css`,
|
|
144
|
+
`cssprobe_close`, `cssprobe_status`. Remove with `cssprobe-cli mcp-uninstall`.
|
|
145
|
+
|
|
122
146
|
---
|
|
123
147
|
|
|
124
148
|
## Commands
|
|
@@ -158,10 +182,24 @@ cssprobe-cli inspect body --json | jq '.findings[] | {id, confidence, message}'
|
|
|
158
182
|
| Command | Description |
|
|
159
183
|
|---------|-------------|
|
|
160
184
|
| `resize <width> <height>` | Resize browser viewport |
|
|
161
|
-
| `eval <expression>` | Evaluate JavaScript (browser context) |
|
|
185
|
+
| `eval <expression>` | Evaluate JavaScript (browser context); DOM nodes are auto-serialized |
|
|
162
186
|
| `playwright <call>` | Execute Playwright API (Node.js context) |
|
|
163
187
|
| `screenshot` | Take screenshot (saves to ~/.cssprobe-cli/screenshots/, --out to override) |
|
|
164
188
|
|
|
189
|
+
### Interaction
|
|
190
|
+
|
|
191
|
+
| Command | Description |
|
|
192
|
+
|---------|-------------|
|
|
193
|
+
| `click <selector>` | Real mouse click (auto-scroll + actionability wait; works with custom React components) |
|
|
194
|
+
| `hover <selector>` | Real mouse hover |
|
|
195
|
+
| `type <selector> <text>` | Fill text (replaces value); `--press Enter` to submit; `--sequential` for real key events |
|
|
196
|
+
| `wait-for <selector>` | Wait for selector state (`--state visible\|hidden\|attached\|detached`, `--timeout ms`) |
|
|
197
|
+
| `wait-for --js "<predicate>"` | Wait until a JS predicate is truthy |
|
|
198
|
+
| `wait-for --network-idle` | Wait until the page is network-idle |
|
|
199
|
+
| `run <file>` | Run a multi-step flow script |
|
|
200
|
+
|
|
201
|
+
`click` / `hover` support `--nth N` (1-based, default first match), `--force` (skip actionability checks) and `--timeout ms`. Without `--nth`, multiple matches fail (strict) to prevent wrong-target clicks.
|
|
202
|
+
|
|
165
203
|
### State
|
|
166
204
|
|
|
167
205
|
| Command | Description |
|
|
@@ -182,6 +220,51 @@ cssprobe-cli inspect body --json | jq '.findings[] | {id, confidence, message}'
|
|
|
182
220
|
|
|
183
221
|
---
|
|
184
222
|
|
|
223
|
+
## Multi-step Flows
|
|
224
|
+
|
|
225
|
+
The interaction commands drive a page step by step; `run` turns a sequence into a versionable script:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
cssprobe-cli open https://example.com/editor
|
|
229
|
+
cssprobe-cli wait-for .editor-canvas
|
|
230
|
+
cssprobe-cli click ".layout-switch" --nth 1
|
|
231
|
+
cssprobe-cli type ".block-title input" "Hello" --press Enter
|
|
232
|
+
cssprobe-cli inspect .block-grid --json
|
|
233
|
+
cssprobe-cli close
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For repeatable flows, write a script and run it with one command. The script runs inside the daemon session (full Playwright `page` access):
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
// flow.js
|
|
240
|
+
export default async ({ page, click, waitFor, type, inspect, screenshot, log }) => {
|
|
241
|
+
await page.goto('https://example.com/editor', { waitUntil: 'domcontentloaded' });
|
|
242
|
+
await waitFor('.editor-canvas', { state: 'visible', timeout: 10000 });
|
|
243
|
+
|
|
244
|
+
await click('.layout-switch', { nth: 1 });
|
|
245
|
+
await waitFor({ js: "() => document.body.dataset.layout === 'two-col'" });
|
|
246
|
+
|
|
247
|
+
await type('.block-title input', 'Hello', { press: 'Enter', sequential: true, clear: true });
|
|
248
|
+
await waitFor({ networkIdle: true });
|
|
249
|
+
|
|
250
|
+
const { findings } = await inspect('.block-grid', { format: 'json' });
|
|
251
|
+
log('findings:', findings.length);
|
|
252
|
+
await screenshot({ path: './after-layout-switch.png' });
|
|
253
|
+
|
|
254
|
+
return { findings: findings.length };
|
|
255
|
+
};
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
cssprobe-cli run ./flow.js # --timeout ms for the whole script (default 300000, 0 = unlimited)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Scripts must `default-export` an async function receiving the API object: `page`, `click`, `hover`, `type`, `waitFor`, `inspect`, `screenshot`, `log`. `screenshot` relative paths resolve against the script's directory; logs (`log()` and `console.*`) are collected and returned with the result. On failure the command exits `1`, prints the collected logs, and keeps the session alive.
|
|
263
|
+
|
|
264
|
+
> Scripts are executed by the local daemon with full Node privileges — only run trusted scripts.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
185
268
|
## Confidence Model
|
|
186
269
|
|
|
187
270
|
Every finding carries a confidence level:
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Page } from 'playwright-core';
|
|
2
|
+
export interface ElementDescriptor {
|
|
3
|
+
tag: string;
|
|
4
|
+
classes: string[];
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
export interface TargetResult {
|
|
8
|
+
matches: number;
|
|
9
|
+
nth: number;
|
|
10
|
+
descriptor: ElementDescriptor | null;
|
|
11
|
+
}
|
|
12
|
+
export type WaitState = 'visible' | 'hidden' | 'attached' | 'detached';
|
|
13
|
+
export type WaitTarget = {
|
|
14
|
+
kind: 'selector';
|
|
15
|
+
selector: string;
|
|
16
|
+
state: WaitState;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'js';
|
|
19
|
+
expression: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'networkIdle';
|
|
22
|
+
};
|
|
23
|
+
export declare function clickElement(page: Page, selector: string, opts?: {
|
|
24
|
+
nth?: number;
|
|
25
|
+
force?: boolean;
|
|
26
|
+
timeout?: number;
|
|
27
|
+
}): Promise<TargetResult>;
|
|
28
|
+
export declare function hoverElement(page: Page, selector: string, opts?: {
|
|
29
|
+
nth?: number;
|
|
30
|
+
force?: boolean;
|
|
31
|
+
timeout?: number;
|
|
32
|
+
}): Promise<TargetResult>;
|
|
33
|
+
export interface TypeOptions {
|
|
34
|
+
press?: string;
|
|
35
|
+
sequential?: boolean;
|
|
36
|
+
clear?: boolean;
|
|
37
|
+
delay?: number;
|
|
38
|
+
nth?: number;
|
|
39
|
+
timeout?: number;
|
|
40
|
+
}
|
|
41
|
+
export declare function typeInto(page: Page, selector: string, text: string, opts?: TypeOptions): Promise<void>;
|
|
42
|
+
export declare function describeWaitTarget(target: WaitTarget): string;
|
|
43
|
+
export declare function resolveWaitTarget(selector: string | undefined, opts: {
|
|
44
|
+
js?: string;
|
|
45
|
+
networkIdle?: boolean;
|
|
46
|
+
state?: string;
|
|
47
|
+
}): WaitTarget;
|
|
48
|
+
export declare function waitForCondition(page: Page, target: WaitTarget, timeout: number): Promise<number>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Interaction primitives shared by daemon commands and the run-script API.
|
|
3
|
+
// Strict mode is preserved when no nth is given: multiple matches fail the action.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.clickElement = clickElement;
|
|
6
|
+
exports.hoverElement = hoverElement;
|
|
7
|
+
exports.typeInto = typeInto;
|
|
8
|
+
exports.describeWaitTarget = describeWaitTarget;
|
|
9
|
+
exports.resolveWaitTarget = resolveWaitTarget;
|
|
10
|
+
exports.waitForCondition = waitForCondition;
|
|
11
|
+
const DEFAULT_TIMEOUT = 30_000;
|
|
12
|
+
function resolveLocator(page, selector, nth) {
|
|
13
|
+
const base = page.locator(selector);
|
|
14
|
+
return nth ? base.nth(nth - 1) : base;
|
|
15
|
+
}
|
|
16
|
+
async function describeTarget(locator) {
|
|
17
|
+
return await locator
|
|
18
|
+
.evaluate(el => ({
|
|
19
|
+
tag: el.tagName.toLowerCase(),
|
|
20
|
+
classes: Array.from(el.classList),
|
|
21
|
+
text: (el.textContent ?? '').trim().slice(0, 60),
|
|
22
|
+
}))
|
|
23
|
+
.catch(() => null);
|
|
24
|
+
}
|
|
25
|
+
async function interactTarget(page, selector, opts, action) {
|
|
26
|
+
const base = page.locator(selector);
|
|
27
|
+
const locator = resolveLocator(page, selector, opts.nth);
|
|
28
|
+
const matches = await base.count();
|
|
29
|
+
const descriptor = await describeTarget(locator);
|
|
30
|
+
await action(locator, { force: opts.force, timeout: opts.timeout ?? DEFAULT_TIMEOUT });
|
|
31
|
+
return { matches, nth: opts.nth ?? 1, descriptor };
|
|
32
|
+
}
|
|
33
|
+
async function clickElement(page, selector, opts = {}) {
|
|
34
|
+
return interactTarget(page, selector, opts, (locator, o) => locator.click({ force: o.force, timeout: o.timeout }));
|
|
35
|
+
}
|
|
36
|
+
async function hoverElement(page, selector, opts = {}) {
|
|
37
|
+
return interactTarget(page, selector, opts, (locator, o) => locator.hover({ force: o.force, timeout: o.timeout }));
|
|
38
|
+
}
|
|
39
|
+
async function typeInto(page, selector, text, opts = {}) {
|
|
40
|
+
const locator = resolveLocator(page, selector, opts.nth);
|
|
41
|
+
const timeout = opts.timeout ?? DEFAULT_TIMEOUT;
|
|
42
|
+
if (opts.sequential) {
|
|
43
|
+
if (opts.clear)
|
|
44
|
+
await locator.fill('', { timeout });
|
|
45
|
+
await locator.pressSequentially(text, { delay: opts.delay ?? 0, timeout });
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
await locator.fill(text, { timeout });
|
|
49
|
+
}
|
|
50
|
+
if (opts.press)
|
|
51
|
+
await locator.press(opts.press, { timeout });
|
|
52
|
+
}
|
|
53
|
+
function describeWaitTarget(target) {
|
|
54
|
+
switch (target.kind) {
|
|
55
|
+
case 'selector':
|
|
56
|
+
return `${target.selector} (state: ${target.state})`;
|
|
57
|
+
case 'js':
|
|
58
|
+
return `JS predicate: ${target.expression.slice(0, 80)}`;
|
|
59
|
+
case 'networkIdle':
|
|
60
|
+
return 'network idle';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function resolveWaitTarget(selector, opts) {
|
|
64
|
+
const modeCount = [selector, opts.js, opts.networkIdle].filter(Boolean).length;
|
|
65
|
+
if (modeCount !== 1)
|
|
66
|
+
throw new Error('wait-for requires exactly one of: <selector>, --js "<expression>", --network-idle');
|
|
67
|
+
if (selector)
|
|
68
|
+
return { kind: 'selector', selector, state: (opts.state ?? 'visible') };
|
|
69
|
+
if (opts.js)
|
|
70
|
+
return { kind: 'js', expression: opts.js };
|
|
71
|
+
return { kind: 'networkIdle' };
|
|
72
|
+
}
|
|
73
|
+
async function waitForCondition(page, target, timeout) {
|
|
74
|
+
const started = Date.now();
|
|
75
|
+
switch (target.kind) {
|
|
76
|
+
case 'selector':
|
|
77
|
+
await page.waitForSelector(target.selector, { state: target.state, timeout });
|
|
78
|
+
break;
|
|
79
|
+
case 'js':
|
|
80
|
+
await page.waitForFunction(target.expression, null, { timeout });
|
|
81
|
+
break;
|
|
82
|
+
case 'networkIdle':
|
|
83
|
+
await page.waitForLoadState('networkidle', { timeout });
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
return Date.now() - started;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=interactions.js.map
|
package/dist/cli/commands.js
CHANGED
|
@@ -157,6 +157,75 @@ const playwrightCmd = (0, command_1.declareCommand)({
|
|
|
157
157
|
call: z.string().describe('Playwright API call, e.g. "page.setViewportSize({width: 1280, height: 720})"'),
|
|
158
158
|
}),
|
|
159
159
|
});
|
|
160
|
+
// ── interaction ──
|
|
161
|
+
const click = (0, command_1.declareCommand)({
|
|
162
|
+
name: 'click',
|
|
163
|
+
category: 'core',
|
|
164
|
+
description: 'Click an element with real mouse events (works with custom React components)',
|
|
165
|
+
args: z.object({
|
|
166
|
+
selector: z.string().describe('CSS selector'),
|
|
167
|
+
}),
|
|
168
|
+
options: z.object({
|
|
169
|
+
nth: numberArg.optional().describe('1-based match index (default: first match)'),
|
|
170
|
+
force: z.boolean().optional().describe('skip actionability checks'),
|
|
171
|
+
timeout: numberArg.optional().describe('action timeout in ms (default 30000)'),
|
|
172
|
+
}),
|
|
173
|
+
});
|
|
174
|
+
const hover = (0, command_1.declareCommand)({
|
|
175
|
+
name: 'hover',
|
|
176
|
+
category: 'core',
|
|
177
|
+
description: 'Hover an element with real mouse events',
|
|
178
|
+
args: z.object({
|
|
179
|
+
selector: z.string().describe('CSS selector'),
|
|
180
|
+
}),
|
|
181
|
+
options: z.object({
|
|
182
|
+
nth: numberArg.optional().describe('1-based match index (default: first match)'),
|
|
183
|
+
force: z.boolean().optional().describe('skip actionability checks'),
|
|
184
|
+
timeout: numberArg.optional().describe('action timeout in ms (default 30000)'),
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
const typeCmd = (0, command_1.declareCommand)({
|
|
188
|
+
name: 'type',
|
|
189
|
+
category: 'core',
|
|
190
|
+
description: 'Type text into an element (fill by default; --sequential for real key events)',
|
|
191
|
+
args: z.object({
|
|
192
|
+
selector: z.string().describe('CSS selector'),
|
|
193
|
+
text: z.string().describe('text to type'),
|
|
194
|
+
}),
|
|
195
|
+
options: z.object({
|
|
196
|
+
press: z.string().optional().describe('key to press after typing (e.g. Enter, Tab, Control+A)'),
|
|
197
|
+
sequential: z.boolean().optional().describe('type with real key events (for custom editors)'),
|
|
198
|
+
clear: z.boolean().optional().describe('clear existing content before sequential typing'),
|
|
199
|
+
delay: numberArg.optional().describe('delay between keystrokes in ms (--sequential, default 0)'),
|
|
200
|
+
nth: numberArg.optional().describe('1-based match index (default: first match)'),
|
|
201
|
+
timeout: numberArg.optional().describe('action timeout in ms (default 30000)'),
|
|
202
|
+
}),
|
|
203
|
+
});
|
|
204
|
+
const waitForCmd = (0, command_1.declareCommand)({
|
|
205
|
+
name: 'wait-for',
|
|
206
|
+
category: 'core',
|
|
207
|
+
description: 'Wait for a condition: selector state, JS predicate, or network idle',
|
|
208
|
+
args: z.object({
|
|
209
|
+
selector: z.string().optional().describe('CSS selector to wait for'),
|
|
210
|
+
}),
|
|
211
|
+
options: z.object({
|
|
212
|
+
state: z.enum(['visible', 'hidden', 'attached', 'detached']).optional().describe('selector state to wait for (default visible)'),
|
|
213
|
+
js: z.string().optional().describe('JS predicate, e.g. "() => document.querySelector(\'.x\')?.offsetHeight > 100"'),
|
|
214
|
+
'network-idle': z.boolean().optional().describe('wait until the page is network-idle'),
|
|
215
|
+
timeout: numberArg.optional().describe('timeout in ms (default 30000)'),
|
|
216
|
+
}),
|
|
217
|
+
});
|
|
218
|
+
const runCmd = (0, command_1.declareCommand)({
|
|
219
|
+
name: 'run',
|
|
220
|
+
category: 'core',
|
|
221
|
+
description: 'Run a multi-step flow script (default-exports an async function receiving the API object)',
|
|
222
|
+
args: z.object({
|
|
223
|
+
file: z.string().describe('flow script path (.js / .mjs / .cjs)'),
|
|
224
|
+
}),
|
|
225
|
+
options: z.object({
|
|
226
|
+
timeout: numberArg.optional().describe('whole-script timeout in ms (default 300000, 0 = unlimited)'),
|
|
227
|
+
}),
|
|
228
|
+
});
|
|
160
229
|
// ── state management ──
|
|
161
230
|
const stateImport = (0, command_1.declareCommand)({
|
|
162
231
|
name: 'state-import',
|
|
@@ -247,6 +316,30 @@ const skillUninstall = (0, command_1.declareCommand)({
|
|
|
247
316
|
path: z.string().optional().describe('custom base directory'),
|
|
248
317
|
}),
|
|
249
318
|
});
|
|
319
|
+
// ── mcp ──
|
|
320
|
+
const mcp = (0, command_1.declareCommand)({
|
|
321
|
+
name: 'mcp',
|
|
322
|
+
category: 'core',
|
|
323
|
+
description: 'Start MCP server (stdio transport) for AI clients',
|
|
324
|
+
});
|
|
325
|
+
const mcpInstall = (0, command_1.declareCommand)({
|
|
326
|
+
name: 'mcp-install',
|
|
327
|
+
category: 'core',
|
|
328
|
+
description: 'Install MCP server config into AI client configs',
|
|
329
|
+
options: z.object({
|
|
330
|
+
target: z.string().optional().describe('client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)'),
|
|
331
|
+
local: z.boolean().optional().describe('write project-local config instead of global'),
|
|
332
|
+
}),
|
|
333
|
+
});
|
|
334
|
+
const mcpUninstall = (0, command_1.declareCommand)({
|
|
335
|
+
name: 'mcp-uninstall',
|
|
336
|
+
category: 'core',
|
|
337
|
+
description: 'Remove MCP server config from AI client configs',
|
|
338
|
+
options: z.object({
|
|
339
|
+
target: z.string().optional().describe('client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)'),
|
|
340
|
+
local: z.boolean().optional().describe('remove project-local config instead of global'),
|
|
341
|
+
}),
|
|
342
|
+
});
|
|
250
343
|
// ── export ──
|
|
251
344
|
const commandsArray = [
|
|
252
345
|
// session management
|
|
@@ -255,12 +348,16 @@ const commandsArray = [
|
|
|
255
348
|
inspect, tree, layout, findings, injectCss,
|
|
256
349
|
// browser
|
|
257
350
|
resize, evalCmd, playwrightCmd, screenshot,
|
|
351
|
+
// interaction
|
|
352
|
+
click, hover, typeCmd, waitForCmd, runCmd,
|
|
258
353
|
// state
|
|
259
354
|
stateImport, stateSave,
|
|
260
355
|
// config
|
|
261
356
|
configShow, configSet, configList, configUse, configNew, configPath,
|
|
262
357
|
// skill
|
|
263
358
|
skillInstall, skillUninstall,
|
|
359
|
+
// mcp
|
|
360
|
+
mcp, mcpInstall, mcpUninstall,
|
|
264
361
|
];
|
|
265
362
|
exports.commands = Object.fromEntries(commandsArray.map(cmd => [cmd.name, cmd]));
|
|
266
363
|
exports.inspectCommand = inspect;
|