cssprobe-cli 0.2.26 → 0.3.0

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/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
@@ -247,6 +247,30 @@ const skillUninstall = (0, command_1.declareCommand)({
247
247
  path: z.string().optional().describe('custom base directory'),
248
248
  }),
249
249
  });
250
+ // ── mcp ──
251
+ const mcp = (0, command_1.declareCommand)({
252
+ name: 'mcp',
253
+ category: 'core',
254
+ description: 'Start MCP server (stdio transport) for AI clients',
255
+ });
256
+ const mcpInstall = (0, command_1.declareCommand)({
257
+ name: 'mcp-install',
258
+ category: 'core',
259
+ description: 'Install MCP server config into AI client configs',
260
+ options: z.object({
261
+ target: z.string().optional().describe('client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)'),
262
+ local: z.boolean().optional().describe('write project-local config instead of global'),
263
+ }),
264
+ });
265
+ const mcpUninstall = (0, command_1.declareCommand)({
266
+ name: 'mcp-uninstall',
267
+ category: 'core',
268
+ description: 'Remove MCP server config from AI client configs',
269
+ options: z.object({
270
+ target: z.string().optional().describe('client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)'),
271
+ local: z.boolean().optional().describe('remove project-local config instead of global'),
272
+ }),
273
+ });
250
274
  // ── export ──
251
275
  const commandsArray = [
252
276
  // session management
@@ -261,6 +285,8 @@ const commandsArray = [
261
285
  configShow, configSet, configList, configUse, configNew, configPath,
262
286
  // skill
263
287
  skillInstall, skillUninstall,
288
+ // mcp
289
+ mcp, mcpInstall, mcpUninstall,
264
290
  ];
265
291
  exports.commands = Object.fromEntries(commandsArray.map(cmd => [cmd.name, cmd]));
266
292
  exports.inspectCommand = inspect;
@@ -13,6 +13,8 @@ const commands_1 = require("./commands");
13
13
  const output_1 = require("./output");
14
14
  const config_1 = require("../config/config");
15
15
  const session_1 = require("../daemon/session");
16
+ const session_ops_1 = require("./session-ops");
17
+ const mcp_1 = require("../mcp");
16
18
  const globalOptions = ['json', 'raw', 'help', 'h', 'version', 'v', 'p', 'profile'];
17
19
  const booleanGlobalOptions = ['help', 'json', 'raw', 'version', 'v', 'h'];
18
20
  async function program() {
@@ -37,8 +39,13 @@ async function program() {
37
39
  return;
38
40
  if (handleSkillCommands(commandName, command, rawArgs, output))
39
41
  return;
42
+ if (handleMcpInstallCommands(commandName, command, rawArgs, output))
43
+ return;
40
44
  // Session-based commands
41
45
  switch (commandName) {
46
+ case 'mcp':
47
+ await (0, mcp_1.runMcpServer)();
48
+ return;
42
49
  case 'open':
43
50
  await handleOpen(command, rawArgs, output);
44
51
  return;
@@ -111,12 +118,8 @@ async function handleOpen(command, args, output) {
111
118
  try {
112
119
  const cmdArgs = splitArgs(args);
113
120
  const parsed = (0, command_1.parseCommand)(command, cmdArgs);
114
- const url = parsed.url || 'about:blank';
115
- const browser = parsed.browser || 'chromium';
116
- const headed = !!parsed.headed;
117
- const viewportStr = parsed.viewport;
118
- const state = parsed.state;
119
121
  // Parse viewport
122
+ const viewportStr = parsed.viewport;
120
123
  let viewport;
121
124
  if (viewportStr) {
122
125
  const parts = viewportStr.split('x');
@@ -132,90 +135,41 @@ async function handleOpen(command, args, output) {
132
135
  }
133
136
  viewport = { width, height };
134
137
  }
135
- // Start daemon
136
- const { pid } = await session_1.Session.startDaemon({
137
- browser,
138
- headed,
138
+ const result = await (0, session_ops_1.openSession)({
139
+ url: parsed.url,
140
+ browser: parsed.browser,
141
+ headed: !!parsed.headed,
139
142
  viewport,
140
- state,
141
- _: ['open', url],
142
- }, 'open');
143
- // Wait for the daemon to be ready (poll session file instead of fixed delay)
144
- const session = await waitForSession(5000);
145
- if (!session)
146
- throw new Error('Daemon failed to become ready within 5s. Check the daemon log for errors.');
147
- await session.run({ _: ['goto', url] });
148
- const result = {
149
- sessionId: 'default',
150
- pid,
151
- url,
152
- viewport: viewport || { width: 1280, height: 720 },
153
- };
143
+ state: parsed.state,
144
+ });
154
145
  if (output.json) {
155
146
  console.log(JSON.stringify(result, null, 2));
156
147
  }
157
148
  else {
158
- console.log(`Browser opened at ${url}`);
159
- console.log(`Session ID: default`);
160
- console.log(`PID: ${pid}`);
161
- console.log(`Viewport: ${viewport?.width || 1280}x${viewport?.height || 720}`);
149
+ console.log(`Browser opened at ${result.url}`);
150
+ console.log(`Session ID: ${result.sessionId}`);
151
+ console.log(`PID: ${result.pid}`);
152
+ console.log(`Viewport: ${result.viewport.width}x${result.viewport.height}`);
162
153
  }
163
154
  }
164
155
  catch (e) {
165
156
  output.error(e instanceof Error ? e.message : String(e));
166
157
  }
167
158
  }
168
- async function waitForSession(timeoutMs) {
169
- const deadline = Date.now() + timeoutMs;
170
- while (Date.now() < deadline) {
171
- const session = await (0, session_1.loadSession)();
172
- if (session && await session.canConnect())
173
- return session;
174
- await new Promise(resolve => setTimeout(resolve, 100));
175
- }
176
- return undefined;
177
- }
178
159
  async function handleClose(command, args, output) {
179
160
  try {
180
161
  const cmdArgs = splitArgs(args);
181
162
  const parsed = (0, command_1.parseCommand)(command, cmdArgs);
182
- if (parsed.all) {
183
- const sessions = await (0, session_1.listAllSessions)();
184
- if (sessions.length === 0) {
185
- output.error('No active sessions found.');
186
- return;
187
- }
188
- const results = [];
189
- for (const session of sessions) {
190
- try {
191
- await session.stop();
192
- results.push({ sessionId: session.name, closed: true });
193
- }
194
- catch (e) {
195
- results.push({ sessionId: session.name, closed: false, error: e.message });
196
- }
197
- }
198
- if (output.json) {
199
- console.log(JSON.stringify({ closed: results }, null, 2));
200
- }
201
- else {
202
- const closedCount = results.filter(r => r.closed).length;
203
- console.log(`Closed ${closedCount} session(s).`);
204
- }
163
+ const result = await (0, session_ops_1.closeSession)(!!parsed.all);
164
+ if (output.json) {
165
+ console.log(JSON.stringify(parsed.all ? { closed: result.sessions } : { closed: true, sessionId: 'default' }, null, 2));
166
+ }
167
+ else if (parsed.all) {
168
+ const closedCount = result.sessions.filter(r => r.closed).length;
169
+ console.log(`Closed ${closedCount} session(s).`);
205
170
  }
206
171
  else {
207
- const session = await (0, session_1.loadSession)();
208
- if (!session) {
209
- output.error('No active session. Run: cssprobe-cli open <url>');
210
- return;
211
- }
212
- await session.stop();
213
- if (output.json) {
214
- console.log(JSON.stringify({ closed: true, sessionId: 'default' }, null, 2));
215
- }
216
- else {
217
- console.log('Browser closed.');
218
- }
172
+ console.log('Browser closed.');
219
173
  }
220
174
  }
221
175
  catch (e) {
@@ -224,32 +178,19 @@ async function handleClose(command, args, output) {
224
178
  }
225
179
  async function handleStatus(output) {
226
180
  try {
227
- const session = await (0, session_1.loadSession)();
228
- if (!session) {
229
- const result = { sessionId: 'default', alive: false };
230
- if (output.json) {
231
- console.log(JSON.stringify(result, null, 2));
232
- }
233
- else {
234
- console.log('No active session.');
235
- }
236
- return;
237
- }
238
- const canConnect = await session.canConnect();
239
- const result = {
240
- sessionId: 'default',
241
- alive: canConnect,
242
- config: session.config,
243
- };
181
+ const result = await (0, session_ops_1.getSessionStatus)();
244
182
  if (output.json) {
245
183
  console.log(JSON.stringify(result, null, 2));
184
+ return;
246
185
  }
247
- else {
248
- console.log(`Session: default`);
249
- console.log(`Status: ${canConnect ? 'alive' : 'dead'}`);
250
- if (canConnect) {
251
- console.log(`Browser: ${session.config.browser.browserName}`);
252
- }
186
+ if (!result.config) {
187
+ console.log('No active session.');
188
+ return;
189
+ }
190
+ console.log(`Session: default`);
191
+ console.log(`Status: ${result.alive ? 'alive' : 'dead'}`);
192
+ if (result.alive) {
193
+ console.log(`Browser: ${result.config.browser.browserName}`);
253
194
  }
254
195
  }
255
196
  catch (e) {
@@ -258,49 +199,12 @@ async function handleStatus(output) {
258
199
  }
259
200
  async function handleSessionCommand(commandName, command, args, output) {
260
201
  try {
261
- const session = await (0, session_1.loadSession)();
262
- if (!session) {
263
- output.error('No active session. Run: cssprobe-cli open <url>');
264
- return;
265
- }
266
202
  const cmdArgs = splitArgs(args);
267
203
  const parsed = (0, command_1.parseCommand)(command, cmdArgs);
268
- // Build command args for daemon
269
- const daemonArgs = [commandName];
270
- // Add positional args
271
- if (command.args) {
272
- const argsSchema = command.args;
273
- const argNames = Object.keys(argsSchema.shape);
274
- for (const name of argNames) {
275
- if (parsed[name] !== undefined) {
276
- daemonArgs.push(String(parsed[name]));
277
- }
278
- }
279
- }
280
- // Add options
281
- if (command.options) {
282
- const optionsSchema = command.options;
283
- const optionNames = Object.keys(optionsSchema.shape);
284
- for (const name of optionNames) {
285
- if (parsed[name] !== undefined) {
286
- if (typeof parsed[name] === 'boolean') {
287
- if (parsed[name])
288
- daemonArgs.push(`--${name}`);
289
- }
290
- else {
291
- daemonArgs.push(`--${name}=${parsed[name]}`);
292
- }
293
- }
294
- }
295
- }
296
- // Run command in session
297
- const runArgs = { ...cmdArgs, _: daemonArgs };
298
- if (args.json)
299
- runArgs.json = true;
300
- const result = await session.run(runArgs);
204
+ const result = await (0, session_ops_1.runSessionCommand)(commandName, command, parsed, !!args.json);
301
205
  // Screenshot: save the base64 data URL to a file and report the path
302
206
  if (commandName === 'screenshot' && typeof result.text === 'string' && result.text.startsWith('data:image/png;base64,')) {
303
- const saved = saveScreenshotFile(result.text, parsed.out);
207
+ const saved = (0, session_ops_1.saveScreenshotFile)(result.text, parsed.out);
304
208
  if (output.json) {
305
209
  console.log(JSON.stringify({ saved }, null, 2));
306
210
  }
@@ -315,24 +219,6 @@ async function handleSessionCommand(commandName, command, args, output) {
315
219
  output.error(e instanceof Error ? e.message : String(e));
316
220
  }
317
221
  }
318
- function saveScreenshotFile(dataUrl, out) {
319
- const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
320
- const buffer = Buffer.from(base64, 'base64');
321
- const resolvedOut = out
322
- ? path_1.default.resolve(out)
323
- : (() => {
324
- const dir = path_1.default.join(os_1.default.homedir(), '.cssprobe-cli', 'screenshots');
325
- if (!fs_1.default.existsSync(dir))
326
- fs_1.default.mkdirSync(dir, { recursive: true });
327
- const ts = new Date().toISOString().replace(/[:.]/g, '-');
328
- return path_1.default.join(dir, `screenshot-${ts}.png`);
329
- })();
330
- const dir = path_1.default.dirname(resolvedOut);
331
- if (!fs_1.default.existsSync(dir))
332
- fs_1.default.mkdirSync(dir, { recursive: true });
333
- fs_1.default.writeFileSync(resolvedOut, buffer);
334
- return resolvedOut;
335
- }
336
222
  // ── config commands ──
337
223
  function handleConfigCommands(commandName, command, args, output) {
338
224
  try {
@@ -426,6 +312,27 @@ function handleSkillCommands(commandName, command, args, output) {
426
312
  }
427
313
  return false;
428
314
  }
315
+ // ── mcp-install commands ──
316
+ function handleMcpInstallCommands(commandName, command, args, output) {
317
+ if (commandName !== 'mcp-install' && commandName !== 'mcp-uninstall')
318
+ return false;
319
+ const targetFlag = args.target || 'auto';
320
+ const loc = args.local ? 'local' : 'global';
321
+ try {
322
+ const result = commandName === 'mcp-install'
323
+ ? (0, installer_1.installMcpConfig)(loc, targetFlag)
324
+ : (0, installer_1.uninstallMcpConfig)(loc, targetFlag);
325
+ const summary = result.files.map(f => `${f.agent}: ${f.action} ${f.path}`);
326
+ for (const note of result.notes)
327
+ summary.push(`note: ${note}`);
328
+ console.log(output.format({ files: summary }));
329
+ return true;
330
+ }
331
+ catch (e) {
332
+ output.error(e instanceof Error ? e.message : String(e));
333
+ }
334
+ return false;
335
+ }
429
336
  function parseNetscapeCookies(content) {
430
337
  const cookies = [];
431
338
  const lines = content.split('\n');
@@ -0,0 +1,43 @@
1
+ import { Session } from '../daemon/session';
2
+ import type { SessionConfig } from '../daemon/session';
3
+ import type { AnyCommandSchema } from './command';
4
+ export interface OpenOptions {
5
+ url?: string;
6
+ browser?: string;
7
+ headed?: boolean;
8
+ viewport?: {
9
+ width: number;
10
+ height: number;
11
+ };
12
+ state?: string;
13
+ }
14
+ export interface OpenResult {
15
+ sessionId: string;
16
+ pid?: number;
17
+ url: string;
18
+ viewport: {
19
+ width: number;
20
+ height: number;
21
+ };
22
+ }
23
+ export declare function openSession(opts: OpenOptions): Promise<OpenResult>;
24
+ export declare function waitForSession(timeoutMs: number): Promise<Session | undefined>;
25
+ export interface CloseResult {
26
+ sessions: Array<{
27
+ sessionId: string;
28
+ closed: boolean;
29
+ error?: string;
30
+ }>;
31
+ }
32
+ export declare function closeSession(all?: boolean): Promise<CloseResult>;
33
+ export interface StatusResult {
34
+ sessionId: string;
35
+ alive: boolean;
36
+ config?: SessionConfig;
37
+ }
38
+ export declare function getSessionStatus(): Promise<StatusResult>;
39
+ export declare function buildDaemonArgs(commandName: string, command: AnyCommandSchema, parsed: Record<string, unknown>): string[];
40
+ export declare function runSessionCommand(commandName: string, command: AnyCommandSchema, parsed: Record<string, unknown>, json?: boolean): Promise<{
41
+ text: string;
42
+ }>;
43
+ export declare function saveScreenshotFile(dataUrl: string, out?: string): string;
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.openSession = openSession;
37
+ exports.waitForSession = waitForSession;
38
+ exports.closeSession = closeSession;
39
+ exports.getSessionStatus = getSessionStatus;
40
+ exports.buildDaemonArgs = buildDaemonArgs;
41
+ exports.runSessionCommand = runSessionCommand;
42
+ exports.saveScreenshotFile = saveScreenshotFile;
43
+ const fs = __importStar(require("fs"));
44
+ const os = __importStar(require("os"));
45
+ const path = __importStar(require("path"));
46
+ const session_1 = require("../daemon/session");
47
+ async function openSession(opts) {
48
+ const url = opts.url || 'about:blank';
49
+ const browser = opts.browser || 'chromium';
50
+ const { pid } = await session_1.Session.startDaemon({
51
+ browser,
52
+ headed: !!opts.headed,
53
+ viewport: opts.viewport,
54
+ state: opts.state,
55
+ _: ['open', url],
56
+ }, 'open');
57
+ const session = await waitForSession(5000);
58
+ if (!session)
59
+ throw new Error('Daemon failed to become ready within 5s. Check the daemon log for errors.');
60
+ await session.run({ _: ['goto', url] });
61
+ return {
62
+ sessionId: 'default',
63
+ pid,
64
+ url,
65
+ viewport: opts.viewport || { width: 1280, height: 720 },
66
+ };
67
+ }
68
+ async function waitForSession(timeoutMs) {
69
+ const deadline = Date.now() + timeoutMs;
70
+ while (Date.now() < deadline) {
71
+ const session = await (0, session_1.loadSession)();
72
+ if (session && await session.canConnect())
73
+ return session;
74
+ await new Promise(resolve => setTimeout(resolve, 100));
75
+ }
76
+ return undefined;
77
+ }
78
+ async function closeSession(all = false) {
79
+ if (all) {
80
+ const sessions = await (0, session_1.listAllSessions)();
81
+ if (sessions.length === 0)
82
+ throw new Error('No active sessions found.');
83
+ const results = [];
84
+ for (const session of sessions) {
85
+ try {
86
+ await session.stop();
87
+ results.push({ sessionId: session.name, closed: true });
88
+ }
89
+ catch (e) {
90
+ results.push({ sessionId: session.name, closed: false, error: e.message });
91
+ }
92
+ }
93
+ return { sessions: results };
94
+ }
95
+ const session = await (0, session_1.loadSession)();
96
+ if (!session)
97
+ throw new Error('No active session. Run: cssprobe-cli open <url>');
98
+ await session.stop();
99
+ return { sessions: [{ sessionId: 'default', closed: true }] };
100
+ }
101
+ async function getSessionStatus() {
102
+ const session = await (0, session_1.loadSession)();
103
+ if (!session)
104
+ return { sessionId: 'default', alive: false };
105
+ const canConnect = await session.canConnect();
106
+ return {
107
+ sessionId: 'default',
108
+ alive: canConnect,
109
+ config: session.config,
110
+ };
111
+ }
112
+ function buildDaemonArgs(commandName, command, parsed) {
113
+ const daemonArgs = [commandName];
114
+ if (command.args) {
115
+ for (const name of Object.keys(command.args.shape)) {
116
+ if (parsed[name] !== undefined)
117
+ daemonArgs.push(String(parsed[name]));
118
+ }
119
+ }
120
+ if (command.options) {
121
+ for (const name of Object.keys(command.options.shape)) {
122
+ if (parsed[name] === undefined)
123
+ continue;
124
+ if (typeof parsed[name] === 'boolean') {
125
+ if (parsed[name])
126
+ daemonArgs.push(`--${name}`);
127
+ }
128
+ else {
129
+ daemonArgs.push(`--${name}=${parsed[name]}`);
130
+ }
131
+ }
132
+ }
133
+ return daemonArgs;
134
+ }
135
+ async function runSessionCommand(commandName, command, parsed, json = false) {
136
+ const session = await (0, session_1.loadSession)();
137
+ if (!session)
138
+ throw new Error('No active session. Run: cssprobe-cli open <url>');
139
+ const daemonArgs = buildDaemonArgs(commandName, command, parsed);
140
+ const runArgs = { ...parsed, _: daemonArgs };
141
+ if (json)
142
+ runArgs.json = true;
143
+ return await session.run(runArgs);
144
+ }
145
+ function saveScreenshotFile(dataUrl, out) {
146
+ const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
147
+ const buffer = Buffer.from(base64, 'base64');
148
+ const resolvedOut = out
149
+ ? path.resolve(out)
150
+ : (() => {
151
+ const dir = path.join(os.homedir(), '.cssprobe-cli', 'screenshots');
152
+ if (!fs.existsSync(dir))
153
+ fs.mkdirSync(dir, { recursive: true });
154
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
155
+ return path.join(dir, `screenshot-${ts}.png`);
156
+ })();
157
+ const dir = path.dirname(resolvedOut);
158
+ if (!fs.existsSync(dir))
159
+ fs.mkdirSync(dir, { recursive: true });
160
+ fs.writeFileSync(resolvedOut, buffer);
161
+ return resolvedOut;
162
+ }
163
+ //# sourceMappingURL=session-ops.js.map
package/dist/help.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "global": "Usage: cssprobe-cli <command> [args] [options]\n\n\nSession Management:\n open [url] open browser in session mode (non-blocking)\n close close the browser session\n status show current session status\n resize <width> <height> resize the browser viewport\n eval <expression> evaluate javascript expression on the page (browser context)\n playwright <call> execute playwright api call (node.js context)\n state-import [file] import cookies from netscape format file\n state-save save current browser session state (cookies + localstorage)\n\nCSS Inspection:\n inspect <selector> inspect the runtime css of a page element\n tree <selector> show dom tree structure\n layout <selector> show ascii layout diagram\n findings <selector> show only issues/warnings/errors\n inject-css <css> inject css into the current page\n\nExport:\n screenshot take a screenshot of the current page\n\nConfiguration:\n config-show show current configuration\n config-set <key> <value> set a configuration value\n config-list list all profiles\n config-use <name> switch active profile\n config-new <name> create a new profile\n config-path show configuration file path\n\nAgent Skills:\n skill-install install agent skill for cssprobe-cli\n skill-uninstall remove agent skill for cssprobe-cli\n\nGlobal options:\n --help [command] print help\n --json output response as JSON\n --raw output only the result value\n --version print version\n -p, --profile <name> use named profile (or CSSPROBE_PROFILE env)",
2
+ "global": "Usage: cssprobe-cli <command> [args] [options]\n\n\nSession Management:\n open [url] open browser in session mode (non-blocking)\n close close the browser session\n status show current session status\n resize <width> <height> resize the browser viewport\n eval <expression> evaluate javascript expression on the page (browser context)\n playwright <call> execute playwright api call (node.js context)\n state-import [file] import cookies from netscape format file\n state-save save current browser session state (cookies + localstorage)\n mcp start mcp server (stdio transport) for ai clients\n mcp-install install mcp server config into ai client configs\n mcp-uninstall remove mcp server config from ai client configs\n\nCSS Inspection:\n inspect <selector> inspect the runtime css of a page element\n tree <selector> show dom tree structure\n layout <selector> show ascii layout diagram\n findings <selector> show only issues/warnings/errors\n inject-css <css> inject css into the current page\n\nExport:\n screenshot take a screenshot of the current page\n\nConfiguration:\n config-show show current configuration\n config-set <key> <value> set a configuration value\n config-list list all profiles\n config-use <name> switch active profile\n config-new <name> create a new profile\n config-path show configuration file path\n\nAgent Skills:\n skill-install install agent skill for cssprobe-cli\n skill-uninstall remove agent skill for cssprobe-cli\n\nGlobal options:\n --help [command] print help\n --json output response as JSON\n --raw output only the result value\n --version print version\n -p, --profile <name> use named profile (or CSSPROBE_PROFILE env)",
3
3
  "commands": {
4
4
  "open": {
5
5
  "help": "cssprobe-cli open [url]\n\nOpen browser in session mode (non-blocking)\n\nArguments:\n [url] url, file:// path, or local html file path\nOptions:\n --browser browser engine (default chromium)\n --headed show the browser window\n --viewport viewport size as wxh (e.g. 1280x720)\n --state path to saved state file (cookies + localstorage)\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
@@ -173,6 +173,27 @@
173
173
  "path": "string"
174
174
  },
175
175
  "args": []
176
+ },
177
+ "mcp": {
178
+ "help": "cssprobe-cli mcp \n\nStart MCP server (stdio transport) for AI clients\n\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
179
+ "flags": {},
180
+ "args": []
181
+ },
182
+ "mcp-install": {
183
+ "help": "cssprobe-cli mcp-install \n\nInstall MCP server config into AI client configs\n\nOptions:\n --target client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)\n --local write project-local config instead of global\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
184
+ "flags": {
185
+ "target": "string",
186
+ "local": "boolean"
187
+ },
188
+ "args": []
189
+ },
190
+ "mcp-uninstall": {
191
+ "help": "cssprobe-cli mcp-uninstall \n\nRemove MCP server config from AI client configs\n\nOptions:\n --target client target: auto, all, none, claude, cursor, codex, opencode, hermes, gemini (default: auto)\n --local remove project-local config instead of global\n\nGlobal options:\n --json output as JSON\n --profile, -p <name> use named profile",
192
+ "flags": {
193
+ "target": "string",
194
+ "local": "boolean"
195
+ },
196
+ "args": []
176
197
  }
177
198
  },
178
199
  "booleanOptions": [
@@ -1,3 +1,5 @@
1
1
  export { buildSkillMd, buildPitfallsMd } from './skill-template';
2
2
  export { ALL_TARGETS, getTarget, listTargetIds, resolveTargetFlag } from './targets/registry';
3
3
  export type { AgentTarget, Location, TargetId, WriteResult } from './targets/types';
4
+ export { installMcpConfig, uninstallMcpConfig, listMcpClientIds, MCP_SERVER_NAME } from './mcp';
5
+ export type { McpCommandResult, McpFileResult } from './mcp';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveTargetFlag = exports.listTargetIds = exports.getTarget = exports.ALL_TARGETS = exports.buildPitfallsMd = exports.buildSkillMd = void 0;
3
+ exports.MCP_SERVER_NAME = exports.listMcpClientIds = exports.uninstallMcpConfig = exports.installMcpConfig = exports.resolveTargetFlag = exports.listTargetIds = exports.getTarget = exports.ALL_TARGETS = exports.buildPitfallsMd = exports.buildSkillMd = void 0;
4
4
  var skill_template_1 = require("./skill-template");
5
5
  Object.defineProperty(exports, "buildSkillMd", { enumerable: true, get: function () { return skill_template_1.buildSkillMd; } });
6
6
  Object.defineProperty(exports, "buildPitfallsMd", { enumerable: true, get: function () { return skill_template_1.buildPitfallsMd; } });
@@ -9,4 +9,9 @@ Object.defineProperty(exports, "ALL_TARGETS", { enumerable: true, get: function
9
9
  Object.defineProperty(exports, "getTarget", { enumerable: true, get: function () { return registry_1.getTarget; } });
10
10
  Object.defineProperty(exports, "listTargetIds", { enumerable: true, get: function () { return registry_1.listTargetIds; } });
11
11
  Object.defineProperty(exports, "resolveTargetFlag", { enumerable: true, get: function () { return registry_1.resolveTargetFlag; } });
12
+ var mcp_1 = require("./mcp");
13
+ Object.defineProperty(exports, "installMcpConfig", { enumerable: true, get: function () { return mcp_1.installMcpConfig; } });
14
+ Object.defineProperty(exports, "uninstallMcpConfig", { enumerable: true, get: function () { return mcp_1.uninstallMcpConfig; } });
15
+ Object.defineProperty(exports, "listMcpClientIds", { enumerable: true, get: function () { return mcp_1.listMcpClientIds; } });
16
+ Object.defineProperty(exports, "MCP_SERVER_NAME", { enumerable: true, get: function () { return mcp_1.MCP_SERVER_NAME; } });
12
17
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,14 @@
1
+ import type { Location, TargetId } from './targets/types';
2
+ export declare const MCP_SERVER_NAME = "cssprobe-cli";
3
+ export interface McpFileResult {
4
+ path: string;
5
+ action: 'created' | 'updated' | 'unchanged' | 'removed' | 'not-found' | 'kept';
6
+ agent: string;
7
+ }
8
+ export interface McpCommandResult {
9
+ files: McpFileResult[];
10
+ notes: string[];
11
+ }
12
+ export declare function installMcpConfig(loc: Location, targetFlag?: string): McpCommandResult;
13
+ export declare function uninstallMcpConfig(loc: Location, targetFlag?: string): McpCommandResult;
14
+ export declare function listMcpClientIds(): TargetId[];