mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-27 15:16:53 +09:00
This PR expands Webstudio MCP and CLI into a more complete agent authoring interface. Agents can discover project capabilities, edit real Webstudio content, use data/resources, verify visual output, and report useful issues when workflows fail. ## MCP User Capabilities - Create, update, duplicate, reorder, and delete pages. - Create pages from templates and manage reusable page templates. - Inspect project pages, instances, props, styles, assets, variables, resources, and metadata. - Insert Webstudio components and authored JSX fragments into pages. - Edit text, props, styles, breakpoints, design tokens, and CSS variables. - Update page metadata, SEO fields, redirects, marketplace metadata, and project settings. - Create and use static data variables. - Create and use dynamic resources fetched over HTTP. - Use REST, GraphQL, and built-in system resources in authored pages. - Bind data variables and resources to visible page content. - Create resource-backed pages such as sitemap XML, structured SEO data, and API-driven content sections. - Preview current project state without requiring a publish step. - Capture screenshots for visual verification. - Verify responsive designs across desktop, tablet, and mobile breakpoints. - Use screenshot diff and OCR-style checks to inspect rendered output. - Reset and refresh local MCP project state when needed. - Discover available CLI/MCP commands from generated manuals. - Use clearer examples for resources, data variables, JSX authoring, previewing, and visual checks. - Read marketplace product metadata without requiring a full project snapshot. - Get safer guidance when using low-level patch tools. - Get a ready-to-copy Discord report template when MCP/CLI behavior is confusing or broken.
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import { afterEach, beforeEach, expect, test } from "vitest";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const scriptPath = fileURLToPath(
|
|
new URL("../../../scripts/check-package-boundaries.mjs", import.meta.url)
|
|
);
|
|
|
|
let tempDir: string;
|
|
|
|
const writeJson = async (path: string, value: unknown) => {
|
|
await mkdir(join(tempDir, path, ".."), { recursive: true });
|
|
await writeFile(join(tempDir, path), JSON.stringify(value, undefined, 2));
|
|
};
|
|
|
|
const runCheck = async () =>
|
|
await execFileAsync("node", [scriptPath], { cwd: tempDir });
|
|
|
|
beforeEach(async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), "webstudio-package-boundaries-"));
|
|
await mkdir(join(tempDir, "packages"), { recursive: true });
|
|
await mkdir(join(tempDir, "apps"), { recursive: true });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rm(tempDir, { recursive: true, force: true });
|
|
});
|
|
|
|
test("passes when public packages depend only on public packages at runtime", async () => {
|
|
await writeJson("packages/public-a/package.json", {
|
|
name: "@webstudio-is/public-a",
|
|
private: false,
|
|
dependencies: {
|
|
"@webstudio-is/public-b": "workspace:*",
|
|
},
|
|
});
|
|
await writeJson("packages/public-b/package.json", {
|
|
name: "@webstudio-is/public-b",
|
|
private: false,
|
|
});
|
|
|
|
await expect(runCheck()).resolves.toMatchObject({
|
|
stdout: "Package boundaries are valid.\n",
|
|
});
|
|
});
|
|
|
|
test("passes when public packages use private packages only for dev-time generation", async () => {
|
|
await writeJson("packages/public/package.json", {
|
|
name: "@webstudio-is/public",
|
|
private: false,
|
|
devDependencies: {
|
|
"@webstudio-is/private": "workspace:*",
|
|
},
|
|
});
|
|
await writeJson("packages/private/package.json", {
|
|
name: "@webstudio-is/private",
|
|
private: true,
|
|
});
|
|
|
|
await expect(runCheck()).resolves.toMatchObject({
|
|
stdout: "Package boundaries are valid.\n",
|
|
});
|
|
});
|
|
|
|
test("fails when public packages depend on private packages at runtime", async () => {
|
|
await writeJson("packages/public/package.json", {
|
|
name: "@webstudio-is/public",
|
|
private: false,
|
|
dependencies: {
|
|
"@webstudio-is/private": "workspace:*",
|
|
},
|
|
});
|
|
await writeJson("packages/private/package.json", {
|
|
name: "@webstudio-is/private",
|
|
private: true,
|
|
});
|
|
|
|
await expect(runCheck()).rejects.toMatchObject({
|
|
stderr: expect.stringContaining(
|
|
"@webstudio-is/public (packages/public/package.json) lists private workspace package @webstudio-is/private in dependencies"
|
|
),
|
|
});
|
|
});
|