@agimon-ai/po-style-system-tooling 0.0.0-stage → 0.2.14-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vuong Ngo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,149 @@
1
- # Temporary Holding Version
1
+ # @agimon-ai/po-style-system-tooling
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ MCP server and CLI for exploring themes, Tailwind CSS classes, and UI components from Storybook, with
4
+ standalone component rendering.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pnpm install
10
+ pnpm exec nx run @agimon-ai/po-style-system-tooling:build
11
+ ```
12
+
13
+ ## CLI
14
+
15
+ ```bash
16
+ style-system --help
17
+ ```
18
+
19
+ | Command | Purpose |
20
+ | ------------------------ | ----------------------------------------------------------------------- |
21
+ | `get-css-classes` | Extract the valid CSS classes from a theme CSS file |
22
+ | `get-ui-component` | Render a component to a screenshot using the app's design-system config |
23
+ | `list-app-components` | List app-specific components and package components |
24
+ | `polish-component` | Design-system-aware component polish |
25
+ | `generate-oxlint-config` | Generate an oxlint `no-restricted-classes` config from theme tokens |
26
+ | `mcp-serve` | Run as an MCP server over stdio |
27
+
28
+ ## MCP server
29
+
30
+ ```json
31
+ {
32
+ "mcpServers": {
33
+ "style-system": {
34
+ "command": "pnpm",
35
+ "args": ["exec", "style-system", "mcp-serve"]
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Configuration
42
+
43
+ Configuration lives in files named `style-system.config.yaml`, at two levels:
44
+
45
+ ```
46
+ <workspace-root>/style-system.config.yaml # defines defaults and presets
47
+ <project>/style-system.config.yaml # selects a preset, applies overrides
48
+ ```
49
+
50
+ The workspace root is found by walking up from the current working directory until a `.git`
51
+ directory is found, falling back to the working directory itself. It is your repository root, not
52
+ the installed package location.
53
+
54
+ ### Resolution order
55
+
56
+ ```
57
+ built-in defaults -> root `defaults` -> preset chain (parents before child) -> project config
58
+ ```
59
+
60
+ Last wins, and **fields replace wholesale**. Lists and objects are never merged element-wise, so a
61
+ project that sets `cssFiles` replaces the inherited list entirely. Use `cssFiles: []` to clear one.
62
+
63
+ Every field is optional in any individual file. Required-ness is enforced against the merged result,
64
+ so a project config can be valid while inheriting nearly everything.
65
+
66
+ Both schemas are strict: an unknown or misspelled key is a hard error rather than a silent no-op. The
67
+ project schema additionally rejects `root`, `defaults` and `presets`, so the two file types can never
68
+ be confused for one another.
69
+
70
+ ### Workspace root file
71
+
72
+ ```yaml
73
+ # <workspace-root>/style-system.config.yaml
74
+ root: true # required literal; marks this as the workspace file
75
+
76
+ # Story tags that mark a component as shared/design-system.
77
+ sharedComponentTags:
78
+ - style-system
79
+
80
+ # Optional service overrides. Paths are workspace-root relative.
81
+ # getCssClasses:
82
+ # customService: packages/.../CustomCSSClassesService.ts
83
+ # bundler:
84
+ # customService: packages/.../CustomBundlerService.ts
85
+
86
+ # Applied to every project, beneath its preset chain.
87
+ defaults:
88
+ type: tailwind
89
+ colorScheme: automatic
90
+
91
+ presets:
92
+ web-app:
93
+ viewport: { width: 1280, height: 800 }
94
+ cssFiles: ['src/assets/main.css']
95
+ componentLibrary: '@acme/web-ui'
96
+ mcp-app:
97
+ extends: web-app # presets may extend other presets
98
+ rootComponent: 'src/components/Root/index.tsx'
99
+ native-app:
100
+ bundler: vite-react-native
101
+ unistylesConfig: unistyles.ts
102
+ viewport: { width: 393, height: 852 }
103
+ ```
104
+
105
+ ### Project file
106
+
107
+ ```yaml
108
+ # <project>/style-system.config.yaml
109
+ extends: web-app # a preset name, or a list: [base, overrides]
110
+ viewport: { width: 1440, height: 900 }
111
+ ```
112
+
113
+ ### Fields
114
+
115
+ | Field | Type | Purpose |
116
+ | --------------------- | -------------------------------- | -------------------------------------------------- |
117
+ | `type` | `tailwind` \| `shadcn` | Design system flavour |
118
+ | `bundler` | string | Bundler strategy, e.g. `vite-react-native` |
119
+ | `brand` | string | Brand identifier |
120
+ | `unistylesConfig` | string | Path to the Unistyles config, for native rendering |
121
+ | `colorScheme` | `automatic` \| `light` \| `dark` | Rendering colour scheme |
122
+ | `viewport` | `{ width, height }` | Screenshot viewport, positive integers |
123
+ | `tailwindConfig` | string | Path to a Tailwind config |
124
+ | `themeProvider` | string | Module providing the theme provider component |
125
+ | `rootComponent` | string | Root component wrapped around rendered stories |
126
+ | `cssFiles` | string[] | Stylesheets loaded before rendering |
127
+ | `componentLibrary` | string | Package holding shared components |
128
+ | `themePath` | string | Path to the theme CSS |
129
+ | `sharedComponentTags` | string[] | Story tags marking a component as shared |
130
+
131
+ `bundler` is shape-dependent on level: in `defaults`, a preset, or a project file it is a **string**
132
+ naming the strategy; at the workspace root, alongside `getCssClasses`, it is a **service override
133
+ object** taking `customService`.
134
+
135
+ ## Native rendering
136
+
137
+ Rendering React Native stories additionally requires the application being rendered to provide
138
+ `react-native`, `react-native-unistyles`, `react-native-reanimated`, `@babel/core`,
139
+ `expo-modules-core` and `@expo/vector-icons`. These are resolved from the target application's
140
+ directory, not from this package, so a React Native app already satisfies them.
141
+
142
+ ## Agent skill
143
+
144
+ The published package ships a `skills/style-system` skill describing when and how an agent should use
145
+ these commands.
146
+
147
+ ## License
148
+
149
+ MIT
package/dist/cli.cjs ADDED
@@ -0,0 +1,495 @@
1
+ #!/usr/bin/env node
2
+ const require_stdio = require("./stdio-DOYXz-Fj.cjs");
3
+ let node_fs = require("node:fs");
4
+ let node_path = require("node:path");
5
+ node_path = require_stdio.__toESM(node_path, 1);
6
+ let zod = require("zod");
7
+ let _agimon_ai_foundation_exec = require("@agimon-ai/foundation-exec");
8
+ let _modelcontextprotocol_server = require("@modelcontextprotocol/server");
9
+ require("reflect-metadata");
10
+ let commander = require("commander");
11
+ let chalk = require("chalk");
12
+ chalk = require_stdio.__toESM(chalk, 1);
13
+ let node_http = require("node:http");
14
+ let _modelcontextprotocol_node = require("@modelcontextprotocol/node");
15
+ //#region src/utils/print.ts
16
+ function resolveChalk(value, depth = 0) {
17
+ if (value && (typeof value === "object" || typeof value === "function") && "red" in value && typeof value.red === "function") return value;
18
+ if (depth < 3 && value && (typeof value === "object" || typeof value === "function") && "default" in value) return resolveChalk(value.default, depth + 1);
19
+ throw new Error("Unable to resolve chalk instance");
20
+ }
21
+ const chalk$1 = resolveChalk(chalk);
22
+ const print = {
23
+ info: (message) => {
24
+ console.log(chalk$1.cyan(message));
25
+ },
26
+ success: (message) => {
27
+ console.log(chalk$1.green(message));
28
+ },
29
+ warning: (message) => {
30
+ console.log(chalk$1.yellow(message));
31
+ },
32
+ error: (message, error) => {
33
+ if (error) {
34
+ const errorMsg = error instanceof Error ? error.message : error;
35
+ console.error(chalk$1.red(message), errorMsg);
36
+ } else console.error(chalk$1.red(message));
37
+ },
38
+ debug: (message) => {
39
+ console.log(chalk$1.gray(message));
40
+ },
41
+ header: (message) => {
42
+ console.log(chalk$1.bold.cyan(message));
43
+ },
44
+ item: (message) => {
45
+ console.log(chalk$1.white(` - ${message}`));
46
+ },
47
+ indent: (message) => {
48
+ console.log(chalk$1.white(` ${message}`));
49
+ },
50
+ highlight: (message) => {
51
+ console.log(chalk$1.bold.green(message));
52
+ },
53
+ newline: () => {
54
+ console.log();
55
+ },
56
+ divider: () => {
57
+ console.log(chalk$1.gray(".".repeat(60)));
58
+ }
59
+ };
60
+ //#endregion
61
+ //#region src/commands/generate-oxlint-config.ts
62
+ /**
63
+ * Generate Oxlint Config Command
64
+ *
65
+ * DESIGN PATTERNS:
66
+ * - Command pattern with Commander for CLI argument parsing
67
+ * - Async/await pattern for asynchronous operations
68
+ * - Error handling pattern with try-catch and proper exit codes
69
+ *
70
+ * CODING STANDARDS:
71
+ * - Use async action handlers for asynchronous operations
72
+ * - Provide clear option descriptions and default values
73
+ * - Handle errors gracefully with process.exit()
74
+ * - Log progress and errors to console
75
+ * - Use Commander's .option() and .argument() for inputs
76
+ *
77
+ * AVOID:
78
+ * - Synchronous blocking operations in action handlers
79
+ * - Missing error handling (always use try-catch)
80
+ * - Hardcoded values (use options or environment variables)
81
+ * - Not exiting with appropriate exit codes on errors
82
+ */
83
+ /**
84
+ * Generate oxlint no-restricted-classes config from theme CSS design tokens
85
+ */
86
+ const generateOxlintConfigCommand = new commander.Command("generate-oxlint-config").description("Generate oxlint no-restricted-classes config from theme CSS design tokens").option("-a, --app-path <path>", "App path to read theme path from its style-system.config.yaml").option("-t, --theme-path <path>", "Theme CSS file path relative to workspace root").option("-o, --output <path>", "Output file path (defaults to stdout)").action(async (options) => {
87
+ try {
88
+ const themePath = (options.appPath ? (await require_stdio.getAppDesignSystemConfig(options.appPath)).themePath : void 0) ?? options.themePath;
89
+ if (!themePath) throw new Error("No theme CSS path resolved. Pass --theme-path, or pass --app-path for an app that configures themePath.");
90
+ const resolvedThemePath = await require_stdio.resolveStyleSystemThemePath({
91
+ appPath: options.appPath ?? null,
92
+ themePath
93
+ });
94
+ const cssResult = await new require_stdio.TailwindCSSClassesService(require_stdio.DEFAULT_STYLE_SYSTEM_CONFIG).extractClasses("colors", resolvedThemePath);
95
+ const config = require_stdio.generateRestrictedClassesConfig(cssResult);
96
+ const jsonOutput = JSON.stringify(config, null, 2);
97
+ if (options.output) {
98
+ const outputPath = node_path.default.resolve(options.output);
99
+ await node_fs.promises.writeFile(outputPath, `${jsonOutput}\n`, "utf-8");
100
+ print.success(`Config written to ${outputPath}`);
101
+ } else print.info(jsonOutput);
102
+ } catch (error) {
103
+ print.error("Error generating oxlint config:", error instanceof Error ? error.message : String(error));
104
+ process.exit(1);
105
+ }
106
+ });
107
+ //#endregion
108
+ //#region src/commands/get-css-classes.ts
109
+ /**
110
+ * Get CSS Classes CLI Command
111
+ */
112
+ const getCSSClassesCommand = new commander.Command("get-css-classes").description("Extract and return all valid CSS classes from a theme CSS file").option("-c, --category <category>", "Category filter: 'colors', 'typography', 'spacing', 'effects', 'all'", "all").option("-a, --app-path <path>", "App path to read theme path from style-system.config.yaml (e.g., \"apps/agiflow-app\")").option("-t, --theme-path <path>", "Theme CSS file path relative to workspace root (required unless appPath resolves themePath from style-system.config.yaml)").action(async (options) => {
113
+ try {
114
+ const result = await require_stdio.createContainer({ defaultThemePath: options.themePath }).get(require_stdio.STYLE_SYSTEM_TYPES.GetCSSClassesTool).execute({
115
+ category: options.category,
116
+ appPath: options.appPath
117
+ });
118
+ if (result.isError) {
119
+ const firstContent = result.content[0];
120
+ const errorText = firstContent?.type === "text" ? firstContent.text : "Unknown error";
121
+ throw new Error(errorText);
122
+ }
123
+ const firstContent = result.content[0];
124
+ print.info(firstContent?.type === "text" ? firstContent.text : "");
125
+ } catch (error) {
126
+ print.error("Error getting CSS classes:", error instanceof Error ? error.message : String(error));
127
+ process.exit(1);
128
+ }
129
+ });
130
+ //#endregion
131
+ //#region src/commands/get-ui-component.ts
132
+ /**
133
+ * Get UI Component CLI Command
134
+ */
135
+ const LIGHT_COLOR_SCHEME = "light";
136
+ const DARK_COLOR_SCHEME = "dark";
137
+ const REQUESTABLE_COLOR_SCHEMES = [LIGHT_COLOR_SCHEME, DARK_COLOR_SCHEME];
138
+ const getUiComponentCommand = new commander.Command("get-ui-component").description("Get a screenshot of a UI component rendered with app-specific design system configuration. Returns screenshot path and story file content.").requiredOption("-c, --component-name <name>", "The name of the component to capture (e.g., \"Button\", \"Card\")").requiredOption("-a, --app-path <path>", "The app path (relative or absolute) to load design system config from (e.g., \"apps/agiflow-app\")").option("-s, --story-name <name>", "The story name to render (e.g., \"Playground\", \"Default\")", "Playground").option("-d, --dark-mode", "Request dark mode for automatic-theme apps").option("--color-scheme <scheme>", "Request light or dark mode for automatic-theme apps").option("--width <pixels>", "Override the configured viewport width", Number).option("--height <pixels>", "Override the configured viewport height", Number).action(async (options) => {
139
+ try {
140
+ if (options.colorScheme !== void 0 && !REQUESTABLE_COLOR_SCHEMES.includes(options.colorScheme)) throw new Error("--color-scheme must be light or dark");
141
+ const tool = require_stdio.createContainer().get(require_stdio.STYLE_SYSTEM_TYPES.GetComponentVisualTool);
142
+ const darkMode = options.colorScheme === DARK_COLOR_SCHEME ? true : options.colorScheme === LIGHT_COLOR_SCHEME ? false : options.darkMode || void 0;
143
+ const result = await tool.execute({
144
+ componentName: options.componentName,
145
+ appPath: options.appPath,
146
+ storyName: options.storyName,
147
+ darkMode,
148
+ width: options.width,
149
+ height: options.height
150
+ });
151
+ if (result.isError) {
152
+ const firstContent = result.content[0];
153
+ const errorText = firstContent?.type === "text" ? firstContent.text : "Unknown error";
154
+ throw new Error(errorText);
155
+ }
156
+ const firstContent = result.content[0];
157
+ process.stdout.write(`${firstContent?.type === "text" ? firstContent.text : ""}\n`);
158
+ } catch (error) {
159
+ const errorMessage = error instanceof Error ? error.message : String(error);
160
+ require_stdio.log.error("Error:", errorMessage);
161
+ process.stderr.write(`${errorMessage.startsWith("Error:") ? errorMessage : `Error: ${errorMessage}`}\n`);
162
+ process.exitCode = 1;
163
+ }
164
+ });
165
+ //#endregion
166
+ //#region src/commands/list-app-components.ts
167
+ /**
168
+ * List App Components CLI Command
169
+ */
170
+ const listAppComponentsCommand = new commander.Command("list-app-components").description("List app-specific components and package components. Reads the app's package.json to find workspace dependencies.").requiredOption("-a, --app-path <path>", "The app path (relative or absolute) to list components for (e.g., \"apps/agiflow-app\")").option("-c, --cursor <cursor>", "Pagination cursor to fetch the next page").action(async (options) => {
171
+ try {
172
+ const result = await require_stdio.createContainer().get(require_stdio.STYLE_SYSTEM_TYPES.ListAppComponentsTool).execute({
173
+ appPath: options.appPath,
174
+ cursor: options.cursor
175
+ });
176
+ if (result.isError) {
177
+ const firstContent = result.content[0];
178
+ const errorText = firstContent?.type === "text" ? firstContent.text : "Unknown error";
179
+ throw new Error(errorText);
180
+ }
181
+ const firstContent = result.content[0];
182
+ print.info(firstContent?.type === "text" ? firstContent.text : "");
183
+ } catch (error) {
184
+ print.error("Error:", error instanceof Error ? error.message : String(error));
185
+ process.exit(1);
186
+ }
187
+ });
188
+ //#endregion
189
+ //#region src/commands/list-themes.ts
190
+ /**
191
+ * List Themes Command
192
+ *
193
+ * DESIGN PATTERNS:
194
+ * - Command pattern with Commander for CLI argument parsing
195
+ * - Async/await pattern for asynchronous operations
196
+ * - Error handling pattern with try-catch and proper exit codes
197
+ *
198
+ * CODING STANDARDS:
199
+ * - Use async action handlers for asynchronous operations
200
+ * - Provide clear option descriptions and default values
201
+ * - Handle errors gracefully with process.exit()
202
+ * - Log progress and errors to console
203
+ * - Use Commander's .option() and .argument() for inputs
204
+ *
205
+ * AVOID:
206
+ * - Synchronous blocking operations in action handlers
207
+ * - Missing error handling (always use try-catch)
208
+ * - Hardcoded values (use options or environment variables)
209
+ * - Not exiting with appropriate exit codes on errors
210
+ */
211
+ /**
212
+ * List all available theme configurations from CSS files
213
+ */
214
+ const listThemesCommand = new commander.Command("list-themes").description("List all available theme configurations from CSS files configured in style-system.config.yaml").option("-a, --app-path <path>", "App path to read theme config from style-system.config.yaml (e.g., \"apps/my-app\")").action(async (options) => {
215
+ try {
216
+ const result = await require_stdio.createContainer().get(require_stdio.STYLE_SYSTEM_TYPES.ListThemesTool).execute({ appPath: options.appPath });
217
+ if (result.isError) {
218
+ const firstContent = result.content[0];
219
+ const errorText = firstContent?.type === "text" ? firstContent.text : "Unknown error";
220
+ throw new Error(errorText);
221
+ }
222
+ const firstContent = result.content[0];
223
+ print.info(firstContent?.type === "text" ? firstContent.text : "");
224
+ } catch (error) {
225
+ print.error("Error listing themes:", error instanceof Error ? error.message : String(error));
226
+ process.exit(1);
227
+ }
228
+ });
229
+ //#endregion
230
+ //#region src/commands/list-shared-components.ts
231
+ /**
232
+ * List Shared Components CLI Command
233
+ */
234
+ const listSharedComponentsCommand = new commander.Command("list-shared-components").description("List all shared UI components available in the design system").option("-c, --cursor <cursor>", "Pagination cursor to fetch the next page").action(async (options) => {
235
+ try {
236
+ const result = await require_stdio.createContainer().get(require_stdio.STYLE_SYSTEM_TYPES.ListSharedComponentsTool).execute({ cursor: options.cursor });
237
+ if (result.isError) {
238
+ const firstContent = result.content[0];
239
+ const errorText = firstContent?.type === "text" ? firstContent.text : "Unknown error";
240
+ throw new Error(errorText);
241
+ }
242
+ const firstContent = result.content[0];
243
+ print.info(firstContent?.type === "text" ? firstContent.text : "");
244
+ } catch (error) {
245
+ print.error("Error listing shared components:", error instanceof Error ? error.message : String(error));
246
+ process.exit(1);
247
+ }
248
+ });
249
+ //#endregion
250
+ //#region src/transports/http.ts
251
+ /** Request-scoped modern Streamable HTTP. No legacy HTTP/SSE endpoint is registered. */
252
+ var HttpTransportHandler = class {
253
+ serverFactory;
254
+ host;
255
+ port;
256
+ server = null;
257
+ handler = null;
258
+ constructor(serverFactory, port, host = "localhost") {
259
+ this.serverFactory = serverFactory;
260
+ if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error("Invalid HTTP port");
261
+ this.port = port;
262
+ this.host = host;
263
+ }
264
+ async start() {
265
+ if (this.server) throw new Error("MCP HTTP transport already running");
266
+ const handler = (0, _modelcontextprotocol_server.createMcpHandler)(this.serverFactory, { legacy: "reject" });
267
+ const serve = (0, _modelcontextprotocol_node.toNodeHandler)(handler);
268
+ const validateHost = (0, _modelcontextprotocol_node.localhostHostValidation)();
269
+ const validateOrigin = (0, _modelcontextprotocol_node.localhostOriginValidation)();
270
+ const loopback = [
271
+ "localhost",
272
+ "127.0.0.1",
273
+ "::1",
274
+ "[::1]"
275
+ ].includes(this.host);
276
+ const server = (0, node_http.createServer)((req, res) => {
277
+ if (req.url?.split("?")[0] !== "/mcp" || ![
278
+ "POST",
279
+ "GET",
280
+ "DELETE"
281
+ ].includes(req.method ?? "")) {
282
+ res.writeHead(404).end();
283
+ return;
284
+ }
285
+ if (loopback && (!validateHost(req, res) || !validateOrigin(req, res))) return;
286
+ serve(req, res).catch((error) => {
287
+ process.stderr.write(`MCP HTTP request failed: ${error instanceof Error ? error.message : String(error)}\n`);
288
+ if (!res.headersSent) res.writeHead(500).end();
289
+ });
290
+ });
291
+ try {
292
+ await new Promise((resolve, reject) => {
293
+ server.once("error", reject);
294
+ server.listen(this.port, this.host, () => resolve());
295
+ });
296
+ this.server = server;
297
+ this.handler = handler;
298
+ } catch (error) {
299
+ await handler.close();
300
+ throw error;
301
+ }
302
+ }
303
+ async stop() {
304
+ const server = this.server;
305
+ this.server = null;
306
+ const handler = this.handler;
307
+ this.handler = null;
308
+ await handler?.close();
309
+ if (server) await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
310
+ }
311
+ getPort() {
312
+ const address = this.server?.address();
313
+ return address && typeof address !== "string" ? address.port : this.port;
314
+ }
315
+ };
316
+ //#endregion
317
+ //#region src/commands/mcp-serve.ts
318
+ /**
319
+ * MCP Serve Command
320
+ *
321
+ * DESIGN PATTERNS:
322
+ * - Command pattern with Commander for CLI argument parsing
323
+ * - Transport abstraction pattern for modern stdio and HTTP
324
+ * - Factory pattern for creating transport handlers
325
+ * - Graceful shutdown pattern with signal handling
326
+ *
327
+ * CODING STANDARDS:
328
+ * - Use async/await for asynchronous operations
329
+ * - Implement proper error handling with try-catch blocks
330
+ * - Handle process signals for graceful shutdown
331
+ * - Provide clear CLI options and help messages
332
+ *
333
+ * AVOID:
334
+ * - Hardcoded configuration values (use CLI options or environment variables)
335
+ * - Missing error handling for transport startup
336
+ * - Not cleaning up resources on shutdown
337
+ */
338
+ /**
339
+ * Start MCP server with given transport handler
340
+ */
341
+ async function startServer(handler, bundlerService, devServerPort) {
342
+ await handler.start();
343
+ let releaseProcess = async () => {};
344
+ try {
345
+ releaseProcess = await (0, _agimon_ai_foundation_exec.registerManagedProcess)({
346
+ repositoryPath: process.cwd(),
347
+ serviceName: "style-system-mcp",
348
+ serviceType: "tool",
349
+ environment: process.env["NODE_ENV"] ?? "development",
350
+ pid: process.pid,
351
+ port: devServerPort,
352
+ command: "style-system mcp-serve",
353
+ killOnRelease: true,
354
+ releasePortOnRelease: false,
355
+ metadata: devServerPort ? { devServerPort } : {}
356
+ });
357
+ } catch (error) {
358
+ console.error("Process registry unavailable:", error);
359
+ }
360
+ const shutdown = async (signal) => {
361
+ console.error(`\nReceived ${signal}, shutting down gracefully...`);
362
+ try {
363
+ if (bundlerService.isServerRunning()) {
364
+ console.error("Stopping bundler dev server...");
365
+ await bundlerService.cleanup();
366
+ }
367
+ await releaseProcess();
368
+ await handler.stop();
369
+ process.exit(0);
370
+ } catch (error) {
371
+ console.error("Error during shutdown:", error);
372
+ process.exit(1);
373
+ }
374
+ };
375
+ process.on("SIGINT", () => shutdown("SIGINT"));
376
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
377
+ }
378
+ /**
379
+ * MCP Serve command
380
+ *
381
+ * Note: Design system configuration is now app-specific and read from each app's project.json.
382
+ * No global theme configuration is needed at the server level.
383
+ */
384
+ const mcpServeCommand = new commander.Command("mcp-serve").description("Start MCP server with specified transport").option("-t, --type <type>", "Transport type: stdio or http", "stdio").option("-p, --port <port>", "HTTP port (required for HTTP)").option("--host <host>", "HTTP bind host", "localhost").option("--theme-path <path>", "Theme CSS file path relative to workspace root for get-css-classes when app config does not define style-system.themePath").option("--dev", "Start Vite dev server for component hot reload and caching").option("--app-path <path>", "App path for dev server (e.g., apps/agiflow-app)").action(async (options) => {
385
+ try {
386
+ const parsed = zod.z.object({
387
+ type: zod.z.enum(["stdio", "http"]),
388
+ port: zod.z.coerce.number().int().min(0).max(65535).optional(),
389
+ host: zod.z.string().min(1),
390
+ themePath: zod.z.string().optional(),
391
+ dev: zod.z.boolean().optional(),
392
+ appPath: zod.z.string().optional()
393
+ }).parse(options);
394
+ const transportType = parsed.type;
395
+ let devServerPort;
396
+ const bundlerService = await require_stdio.getBundlerServiceFromConfig();
397
+ if (parsed.dev) {
398
+ if (!parsed.appPath) {
399
+ console.error("Error: --app-path is required when using --dev flag");
400
+ console.error(`Example: ${require_stdio.STYLE_SYSTEM_CLI_NAME} mcp-serve --dev --app-path apps/agiflow-app`);
401
+ process.exit(1);
402
+ }
403
+ const { url, port } = await bundlerService.startDevServer(parsed.appPath);
404
+ devServerPort = port;
405
+ console.error(`Dev server started at ${url} (port: ${port})`);
406
+ }
407
+ if (transportType === "stdio") {
408
+ const container = require_stdio.createContainer({ defaultThemePath: parsed.themePath });
409
+ await startServer(new require_stdio.StdioTransportHandler(() => require_stdio.createServer(container)), bundlerService, devServerPort);
410
+ } else if (transportType === "http" && parsed.port !== void 0) {
411
+ const container = require_stdio.createContainer({ defaultThemePath: parsed.themePath });
412
+ await startServer(new HttpTransportHandler(() => require_stdio.createServer(container), parsed.port, parsed.host), bundlerService, devServerPort);
413
+ } else {
414
+ console.error("HTTP requires --port");
415
+ process.exit(1);
416
+ }
417
+ } catch (error) {
418
+ console.error("Failed to start MCP server:", error);
419
+ process.exit(1);
420
+ }
421
+ });
422
+ //#endregion
423
+ //#region src/commands/polish-component.ts
424
+ /**
425
+ * Polish Component Command
426
+ *
427
+ * DESIGN PATTERNS:
428
+ * - Command pattern with Commander for CLI argument parsing
429
+ * - Async/await pattern for asynchronous operations
430
+ * - Error handling pattern with try-catch and proper exit codes
431
+ *
432
+ * CODING STANDARDS:
433
+ * - Use async action handlers for asynchronous operations
434
+ * - Handle errors gracefully with process.exit()
435
+ *
436
+ * AVOID:
437
+ * - Synchronous blocking operations in action handlers
438
+ * - Missing error handling (always use try-catch)
439
+ * - Hardcoded values (use options or environment variables)
440
+ */
441
+ const polishComponentCommand = new commander.Command("polish-component").description("Polish a UI component using Antigravity with design system context").argument("<file>", "Path to the component .tsx file to polish").option("-i, --instructions <text>", "Specific polishing instructions (e.g., \"improve spacing\")").action(async (file, options) => {
442
+ try {
443
+ const result = await require_stdio.createContainer().get(require_stdio.STYLE_SYSTEM_TYPES.PolishComponentTool).execute({
444
+ filePath: file,
445
+ instructions: options.instructions
446
+ });
447
+ if (result.isError) {
448
+ const firstContent = result.content[0];
449
+ const errorText = firstContent?.type === "text" ? firstContent.text : "Unknown error";
450
+ throw new Error(errorText);
451
+ }
452
+ for (const content of result.content) if (content.type === "text") console.error(content.text);
453
+ } catch (error) {
454
+ console.error("Error:", error instanceof Error ? error.message : String(error));
455
+ process.exit(1);
456
+ }
457
+ });
458
+ //#endregion
459
+ //#region src/cli.ts
460
+ /**
461
+ * MCP Server CLI Entry Point
462
+ *
463
+ * DESIGN PATTERNS:
464
+ * - CLI pattern with Commander for argument parsing
465
+ * - Command pattern for organizing CLI commands
466
+ * - Transport abstraction for multiple communication methods
467
+ *
468
+ * CODING STANDARDS:
469
+ * - Use async/await for asynchronous operations
470
+ * - Handle errors gracefully with try-catch
471
+ * - Log important events for debugging
472
+ * - Register all commands in main entry point
473
+ *
474
+ * AVOID:
475
+ * - Hardcoding command logic in index.ts (use separate command files)
476
+ * - Missing error handling for command execution
477
+ */
478
+ /**
479
+ * Main entry point
480
+ */
481
+ async function main() {
482
+ const program = new commander.Command();
483
+ program.name(require_stdio.STYLE_SYSTEM_CLI_NAME).description("MCP server for design system tools").version(require_stdio.STYLE_SYSTEM_VERSION);
484
+ program.addCommand(generateOxlintConfigCommand);
485
+ program.addCommand(getCSSClassesCommand);
486
+ program.addCommand(getUiComponentCommand);
487
+ program.addCommand(listAppComponentsCommand);
488
+ program.addCommand(listThemesCommand);
489
+ program.addCommand(listSharedComponentsCommand);
490
+ program.addCommand(mcpServeCommand);
491
+ program.addCommand(polishComponentCommand);
492
+ await program.parseAsync(process.argv);
493
+ }
494
+ main();
495
+ //#endregion
package/dist/cli.d.cts ADDED
@@ -0,0 +1 @@
1
+ import "reflect-metadata";
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ import "reflect-metadata";