feat: release design system engine, studio and component contracts
This commit is contained in:
1 parent
ff1065fb27
commit
3943b946d6
693 files changed
+175546
-3894
No files matched your search
@@ -0,0 +1,138 @@
|
||||
import type { McpTool } from "./index.ts";
|
||||
const string = { type: "string" };
|
||||
const fields: Record<string, Record<string, unknown>> = {
|
||||
package: { notes: string },
|
||||
upgrade_plan: { source: string },
|
||||
upgrade: { source: string },
|
||||
inspect: {},
|
||||
capabilities: {},
|
||||
context: { components: { type: "array", items: string, maxItems: 12 } },
|
||||
create: { source: string },
|
||||
propose: {
|
||||
source: string,
|
||||
patches: {
|
||||
type: "array",
|
||||
maxItems: 48,
|
||||
items: {
|
||||
type: "object",
|
||||
properties: { path: string, value: {} },
|
||||
required: ["path", "value"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
locks: { locks: { type: "array", items: string, maxItems: 128 } },
|
||||
page: {
|
||||
page: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: string,
|
||||
title: string,
|
||||
components: {
|
||||
type: "array",
|
||||
minItems: 1,
|
||||
maxItems: 12,
|
||||
items: {
|
||||
type: "object",
|
||||
properties: { component: string, props: { type: "object" } },
|
||||
required: ["component"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["id", "title", "components"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
validate: {},
|
||||
preview: { pageId: string },
|
||||
diff: {},
|
||||
apply: { baseApplicationRevision: string },
|
||||
revert: {
|
||||
target: { type: "string", enum: ["draft", "checkpoint", "application"] },
|
||||
baseApplicationRevision: string,
|
||||
},
|
||||
export: {},
|
||||
conformance: {
|
||||
source: string,
|
||||
exceptions: {
|
||||
type: "array",
|
||||
maxItems: 64,
|
||||
items: {
|
||||
type: "object",
|
||||
properties: { code: string, path: string, reason: string },
|
||||
required: ["code", "path", "reason"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const descriptions: Record<string, string> = {
|
||||
package:
|
||||
"Export a verified self-contained design package with a content digest. No filesystem or draft mutation.",
|
||||
upgrade_plan:
|
||||
"Review a shared package upgrade, including integrity, identity, locks and migration diagnostics, without changing the draft.",
|
||||
upgrade:
|
||||
"Adopt a verified shared design package as a recoverable history entry; draft revision and decision locks are rechecked.",
|
||||
inspect: "Inspect recoverable source, valid preview, locks, pages and revisions.",
|
||||
capabilities: "List contracted components, operations and explicit integration limits.",
|
||||
context: "Get resolved tokens, selected component APIs, parts, constraints and generation rules.",
|
||||
create:
|
||||
"Create a recoverable design draft from optional canonical source; invalid source remains recoverable.",
|
||||
propose:
|
||||
"Propose canonical source or token/recipe JSON-pointer patches respecting decision locks.",
|
||||
locks: "Set explicit decision locks for future edits.",
|
||||
page: "Save a bounded literal page specification composed of contracted components; returns generated .wrn source.",
|
||||
validate: "Validate draft and saved page specifications.",
|
||||
preview:
|
||||
"Render a saved page through trusted shipped components. Returns CSP-protected static HTML.",
|
||||
diff: "Review design changes against the applied baseline and affected page ids.",
|
||||
apply:
|
||||
"Save an application design bundle after checking draft and application revisions. Explicit app integration still required.",
|
||||
revert:
|
||||
"Undo a draft edit, swap the previous draft checkpoint, or revert the application bundle pointer.",
|
||||
export:
|
||||
"Export raw draft recovery data, valid design artifacts and generated page sources without writing routes.",
|
||||
conformance:
|
||||
"Statically check .wrn source for unknown APIs, invented variants and token bypasses. Warnings need exact reviewed exceptions.",
|
||||
};
|
||||
export const DESIGN_WRITES = new Set([
|
||||
"create",
|
||||
"propose",
|
||||
"locks",
|
||||
"page",
|
||||
"apply",
|
||||
"revert",
|
||||
"upgrade",
|
||||
]);
|
||||
export function designTools(writable: boolean): McpTool[] {
|
||||
return Object.entries(fields)
|
||||
.filter(([name]) => writable || !DESIGN_WRITES.has(name))
|
||||
.map(([name, properties]) => ({
|
||||
name: "wrnexus_design_" + name,
|
||||
description: descriptions[name]!,
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
...properties,
|
||||
...(["context", "preview", "export", "apply"].includes(name)
|
||||
? { mode: string, density: string }
|
||||
: {}),
|
||||
...(DESIGN_WRITES.has(name) ? { baseRevision: string } : {}),
|
||||
},
|
||||
required: [
|
||||
...(DESIGN_WRITES.has(name) ? ["baseRevision"] : []),
|
||||
...({
|
||||
upgrade_plan: ["source"],
|
||||
upgrade: ["source"],
|
||||
page: ["page"],
|
||||
preview: ["pageId"],
|
||||
locks: ["locks"],
|
||||
apply: ["baseApplicationRevision"],
|
||||
conformance: ["source"],
|
||||
}[name] ?? []),
|
||||
],
|
||||
additionalProperties: false,
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { designTools } from "./design-tools.ts";
|
||||
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { extname, join, relative, resolve } from "node:path";
|
||||
|
||||
@@ -8,9 +9,13 @@ export interface McpTool {
|
||||
type: "object";
|
||||
properties?: Record<string, unknown>;
|
||||
additionalProperties?: boolean;
|
||||
required?: string[];
|
||||
};
|
||||
}
|
||||
export interface McpServerOptions {
|
||||
/** Opt-in local workspace design operations. */
|
||||
design?: boolean;
|
||||
designWrite?: boolean;
|
||||
maxFiles?: number;
|
||||
maxFileBytes?: number;
|
||||
fetch?: typeof fetch;
|
||||
@@ -138,6 +143,8 @@ export function createFrameworkMcpServer(
|
||||
options: McpServerOptions = {},
|
||||
): McpServer {
|
||||
const root = resolve(appRoot);
|
||||
const toolSet = [...TOOLS, ...(options.design ? designTools(options.designWrite === true) : [])];
|
||||
let design: import("@wrnexus/design-studio/agents").DesignAgentService | undefined;
|
||||
const limits = {
|
||||
maxFiles: options.maxFiles ?? 200,
|
||||
maxFileBytes: options.maxFileBytes ?? 128 * 1024,
|
||||
@@ -145,6 +152,26 @@ export function createFrameworkMcpServer(
|
||||
const files = (directories: string[], extensions?: string[]) =>
|
||||
walk(root, directories, limits, extensions);
|
||||
const call = async (name: string, args: Record<string, unknown> = {}): Promise<unknown> => {
|
||||
if (name.startsWith("wrnexus_design_") && toolSet.some((tool) => tool.name === name)) {
|
||||
if (!design) {
|
||||
const { DesignAgentService } = await import("@wrnexus/design-studio/agents").catch(
|
||||
(error: unknown) => {
|
||||
if (
|
||||
error &&
|
||||
typeof error === "object" &&
|
||||
"code" in error &&
|
||||
["MODULE_NOT_FOUND", "ERR_MODULE_NOT_FOUND"].includes(String(error.code))
|
||||
)
|
||||
throw Error(
|
||||
"WRN-MCP-DESIGN-INSTALL: install a compatible @wrnexus/design-studio package to enable design tools.",
|
||||
);
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
design = new DesignAgentService(root, options.designWrite === true);
|
||||
}
|
||||
return design.call(name.slice("wrnexus_design_".length), args);
|
||||
}
|
||||
if (name === "wrnexus_routes")
|
||||
return files(["app/pages", "app/api"], [".wrn", ".ts", ".tsx", ".js", ".jsx"])
|
||||
.map(({ file }) => ({
|
||||
@@ -219,7 +246,7 @@ export function createFrameworkMcpServer(
|
||||
throw new Error(`WRN-MCP-TOOL: unknown tool '${name}'.`);
|
||||
};
|
||||
return {
|
||||
tools: () => TOOLS.map((tool) => ({ ...tool })),
|
||||
tools: () => toolSet.map((tool) => ({ ...tool })),
|
||||
call,
|
||||
async handle(message) {
|
||||
if (!message || typeof message !== "object")
|
||||
@@ -237,7 +264,7 @@ export function createFrameworkMcpServer(
|
||||
},
|
||||
};
|
||||
if (request.method === "tools/list")
|
||||
return { jsonrpc: "2.0", id: request.id ?? null, result: { tools: TOOLS } };
|
||||
return { jsonrpc: "2.0", id: request.id ?? null, result: { tools: toolSet } };
|
||||
if (request.method === "tools/call") {
|
||||
try {
|
||||
const value = await call(
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env bun
|
||||
import { createInterface } from "node:readline";
|
||||
import { createFrameworkMcpServer } from "./index.ts";
|
||||
import { createFrameworkMcpServer, type McpServerOptions } from "./index.ts";
|
||||
|
||||
export async function runMcpStdio(root = process.cwd()): Promise<void> {
|
||||
const server = createFrameworkMcpServer(root);
|
||||
export async function runMcpStdio(
|
||||
root = process.cwd(),
|
||||
options: McpServerOptions = {},
|
||||
): Promise<void> {
|
||||
const server = createFrameworkMcpServer(root, options);
|
||||
const lines = createInterface({ input: process.stdin, terminal: false });
|
||||
for await (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
@@ -23,5 +26,8 @@ export async function runMcpStdio(root = process.cwd()): Promise<void> {
|
||||
|
||||
if (import.meta.main) {
|
||||
const root = process.argv.find((value) => value.startsWith("--root="))?.slice(7) ?? process.cwd();
|
||||
await runMcpStdio(root);
|
||||
await runMcpStdio(root, {
|
||||
design: process.argv.includes("--design") || process.argv.includes("--design-write"),
|
||||
designWrite: process.argv.includes("--design-write"),
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user