@sebgroup/green-core 2.33.0 → 2.34.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/README.md CHANGED
@@ -125,3 +125,27 @@ Then you can just use `<Button />` like a regular React component.
125
125
  ## Documentation
126
126
 
127
127
  Check out the [Storybook (@sebgroup/core)](https://storybook.seb.io/latest/core/) for components and documentation.
128
+
129
+ ## Testing
130
+
131
+ The test suite is split by runtime so Node tooling tests do not run in browser mode.
132
+
133
+ - **Node tests** (`src/bin/**`) for developer tooling such as CLI and MCP server:
134
+
135
+ ```bash
136
+ nx run core:test:node
137
+ ```
138
+
139
+ - **Browser tests** for web components and UI behavior (Chromium + WebKit):
140
+
141
+ ```bash
142
+ nx run core:test:browser
143
+ ```
144
+
145
+ - **Full suite** (same split as CI/local default target):
146
+
147
+ ```bash
148
+ nx run core:test
149
+ ```
150
+
151
+ Use `core:test:node` when working on CLI/MCP code for faster feedback, and `core:test:browser` when working on components, interactions, or rendering behavior.
@@ -0,0 +1,3 @@
1
+ export declare const DOCS_FRAMEWORK_CANONICAL: readonly ["angular", "react", "web-component"];
2
+ export type DocsFramework = (typeof DOCS_FRAMEWORK_CANONICAL)[number];
3
+ export declare function normalizeDocsFramework(input: string): DocsFramework | null;
@@ -0,0 +1,23 @@
1
+ import "../../chunks/chunk.QU3DSPNU.js";
2
+ const DOCS_FRAMEWORK_CANONICAL = [
3
+ "angular",
4
+ "react",
5
+ "web-component"
6
+ ];
7
+ const DOCS_FRAMEWORK_ALIASES = {
8
+ angular: "angular",
9
+ react: "react",
10
+ "web-component": "web-component",
11
+ "web-components": "web-component",
12
+ webcomponent: "web-component",
13
+ webcomponents: "web-component",
14
+ web: "web-component"
15
+ };
16
+ function normalizeDocsFramework(input) {
17
+ const normalized = DOCS_FRAMEWORK_ALIASES[input.trim().toLowerCase()];
18
+ return normalized ?? null;
19
+ }
20
+ export {
21
+ DOCS_FRAMEWORK_CANONICAL,
22
+ normalizeDocsFramework
23
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { parseArgs } from './parse-args.js';
@@ -0,0 +1,363 @@
1
+ #!/usr/bin/env node
2
+ import "../../chunks/chunk.QU3DSPNU.js";
3
+ import { McpError } from "../mcp-server/errors.js";
4
+ import {
5
+ handleGetComponentDocs,
6
+ handleGetGuide,
7
+ handleGetInstructions,
8
+ handleListGuides,
9
+ handleResolveUri,
10
+ handleSearchComponents
11
+ } from "../mcp-server/handlers.js";
12
+ import { getPackageVersion } from "../mcp-server/utils.js";
13
+ import {
14
+ DOCS_FRAMEWORK_CANONICAL,
15
+ normalizeDocsFramework
16
+ } from "./framework.js";
17
+ import { parseArgs } from "./parse-args.js";
18
+ function getPrimaryTextContent(result) {
19
+ const textBlock = result.content.find(
20
+ (block) => block.type === "text"
21
+ );
22
+ if (!textBlock) {
23
+ throw new Error("Tool response did not include a text content block");
24
+ }
25
+ return textBlock.text;
26
+ }
27
+ import { parseArgs as parseArgs2 } from "./parse-args.js";
28
+ const PROGRAM_NAME = "green-core-context";
29
+ const HELP_TEXT = `
30
+ Green Design System \u2014 Context CLI
31
+
32
+ Provides design system documentation, component APIs, guides, and usage
33
+ instructions directly from the command line. Outputs to stdout for easy
34
+ piping to grep, jq, less, etc.
35
+
36
+ USAGE
37
+ ${PROGRAM_NAME} <command> [options]
38
+
39
+ COMMANDS
40
+ search <query> Search for components and icons
41
+ docs <component> <framework> Get component documentation
42
+ get <uri> Fetch raw content by green:// URI
43
+ guides List available guides
44
+ guide <name> Get a specific guide's content
45
+ instructions Get base usage instructions
46
+
47
+ GLOBAL OPTIONS
48
+ -h, --help Show this help message
49
+ -v, --version Show version number
50
+
51
+ Run '${PROGRAM_NAME} <command> --help' for command-specific options.
52
+ `.trim();
53
+ const SEARCH_HELP = `
54
+ Search for Green Design System components and icons by name, description,
55
+ or functionality.
56
+
57
+ USAGE
58
+ ${PROGRAM_NAME} search <query> [options]
59
+
60
+ ARGUMENTS
61
+ query Search term (required)
62
+
63
+ OPTIONS
64
+ --category <type> Filter by type: component, icon, all
65
+ (default: all)
66
+ --no-split-terms Don't split query on spaces/commas
67
+ --match-all Require ALL terms to match (AND logic)
68
+ --use-regex Treat query as a regular expression
69
+ --max-results <n> Maximum results to return, 1-100
70
+ (default: 20)
71
+ -h, --help Show this help message
72
+
73
+ EXAMPLES
74
+ ${PROGRAM_NAME} search button
75
+ ${PROGRAM_NAME} search "dropdown menu" --match-all
76
+ ${PROGRAM_NAME} search "^gds-card" --use-regex
77
+ ${PROGRAM_NAME} search arrow --category icon
78
+ ${PROGRAM_NAME} search button | jq '.results[0]'
79
+ `.trim();
80
+ const DOCS_HELP = `
81
+ Get complete documentation for a specific Green component, including
82
+ framework-specific import paths, API reference, and design guidelines.
83
+
84
+ USAGE
85
+ ${PROGRAM_NAME} docs <component> <framework> [options]
86
+
87
+ ARGUMENTS
88
+ component Component name, e.g. "button" or
89
+ "gds-button" (required)
90
+ framework Target framework (required)
91
+ Allowed: ${DOCS_FRAMEWORK_CANONICAL.join(", ")}
92
+ Aliases: web, webcomponent, web-components
93
+
94
+ OPTIONS
95
+ --no-guidelines Exclude UX/design guidelines
96
+ --no-instructions Exclude agent-specific instructions
97
+ -h, --help Show this help message
98
+
99
+ EXAMPLES
100
+ ${PROGRAM_NAME} docs button angular
101
+ ${PROGRAM_NAME} docs gds-dropdown react
102
+ ${PROGRAM_NAME} docs card web
103
+ ${PROGRAM_NAME} docs card web-component --no-guidelines
104
+ `.trim();
105
+ const GET_HELP = `
106
+ Fetch raw content for a green:// resource URI. Use 'search' to discover
107
+ URIs, then use 'get' to retrieve the raw document content.
108
+
109
+ USAGE
110
+ ${PROGRAM_NAME} get <uri>
111
+
112
+ ARGUMENTS
113
+ uri A green:// resource URI (required)
114
+
115
+ SUPPORTED URI FORMATS
116
+ green://components/{name}/{doc} Component docs (api, angular, react,
117
+ guidelines, instructions)
118
+ green://icons/{name}/{doc} Icon docs (api, angular, react)
119
+ green://guides/{name} Setup and framework guides
120
+ green://concepts/{name} Conceptual documentation
121
+ green://instructions Base instructions document
122
+
123
+ OPTIONS
124
+ -h, --help Show this help message
125
+
126
+ EXAMPLES
127
+ ${PROGRAM_NAME} get green://components/button/api
128
+ ${PROGRAM_NAME} get green://components/dropdown/angular
129
+ ${PROGRAM_NAME} get green://guides/react
130
+ ${PROGRAM_NAME} get green://concepts/tokens
131
+ ${PROGRAM_NAME} get green://instructions
132
+ `.trim();
133
+ const GUIDES_HELP = `
134
+ List available setup guides and conceptual documentation.
135
+
136
+ USAGE
137
+ ${PROGRAM_NAME} guides [options]
138
+
139
+ OPTIONS
140
+ --category <type> Filter by category: framework-setup,
141
+ getting-started, concepts,
142
+ troubleshooting, migration, all
143
+ (default: all)
144
+ --framework <name> Filter by framework: angular, react, all
145
+ -h, --help Show this help message
146
+
147
+ EXAMPLES
148
+ ${PROGRAM_NAME} guides
149
+ ${PROGRAM_NAME} guides --category concepts
150
+ ${PROGRAM_NAME} guides --framework angular
151
+ `.trim();
152
+ const GUIDE_HELP = `
153
+ Get the full content of a specific guide.
154
+
155
+ USAGE
156
+ ${PROGRAM_NAME} guide <name>
157
+
158
+ ARGUMENTS
159
+ name Guide name, e.g. "angular", "installing",
160
+ "troubleshooting" (required).
161
+ Run '${PROGRAM_NAME} guides' to see
162
+ available guide names.
163
+
164
+ OPTIONS
165
+ -h, --help Show this help message
166
+
167
+ EXAMPLES
168
+ ${PROGRAM_NAME} guide angular
169
+ ${PROGRAM_NAME} guide troubleshooting | less
170
+ `.trim();
171
+ const INSTRUCTIONS_HELP = `
172
+ Get the base instructions for using Green Design System. Contains critical
173
+ rules, typography guidelines, layout system requirements, and best practices.
174
+
175
+ USAGE
176
+ ${PROGRAM_NAME} instructions
177
+
178
+ OPTIONS
179
+ -h, --help Show this help message
180
+ `.trim();
181
+ async function runSearch(args) {
182
+ if (args.flags["h"] || args.flags["help"]) {
183
+ process.stdout.write(SEARCH_HELP + "\n");
184
+ return;
185
+ }
186
+ const query = args.positional[0];
187
+ if (!query) {
188
+ process.stderr.write("Error: search requires a <query> argument.\n\n");
189
+ process.stderr.write(SEARCH_HELP + "\n");
190
+ process.exitCode = 1;
191
+ return;
192
+ }
193
+ const input = { query };
194
+ if (args.flags["category"] !== void 0) {
195
+ input.category = args.flags["category"];
196
+ }
197
+ if (args.flags["split-terms"] === false) {
198
+ input.splitTerms = false;
199
+ }
200
+ if (args.flags["match-all"]) {
201
+ input.matchAll = true;
202
+ }
203
+ if (args.flags["use-regex"]) {
204
+ input.useRegex = true;
205
+ }
206
+ if (args.flags["max-results"] !== void 0) {
207
+ const n = Number(args.flags["max-results"]);
208
+ if (Number.isNaN(n)) {
209
+ process.stderr.write("Error: --max-results must be a number.\n");
210
+ process.exitCode = 1;
211
+ return;
212
+ }
213
+ input.maxResults = n;
214
+ }
215
+ const result = await handleSearchComponents(input);
216
+ process.stdout.write(getPrimaryTextContent(result) + "\n");
217
+ }
218
+ async function runDocs(args) {
219
+ if (args.flags["h"] || args.flags["help"]) {
220
+ process.stdout.write(DOCS_HELP + "\n");
221
+ return;
222
+ }
223
+ const componentName = args.positional[0];
224
+ const frameworkInput = args.positional[1];
225
+ if (!componentName || !frameworkInput) {
226
+ process.stderr.write(
227
+ "Error: docs requires <component> and <framework> arguments.\n\n"
228
+ );
229
+ process.stderr.write(DOCS_HELP + "\n");
230
+ process.exitCode = 1;
231
+ return;
232
+ }
233
+ const framework = normalizeDocsFramework(frameworkInput);
234
+ if (!framework) {
235
+ process.stderr.write(
236
+ `Error: Invalid framework '${frameworkInput}'. Allowed values: ${DOCS_FRAMEWORK_CANONICAL.join(", ")}.
237
+ `
238
+ );
239
+ process.stderr.write(
240
+ "Aliases accepted for web-component: web, webcomponent, web-components.\n\n"
241
+ );
242
+ process.stderr.write(DOCS_HELP + "\n");
243
+ process.exitCode = 1;
244
+ return;
245
+ }
246
+ const input = { componentName, framework };
247
+ if (args.flags["guidelines"] === false) {
248
+ input.includeGuidelines = false;
249
+ }
250
+ if (args.flags["instructions"] === false) {
251
+ input.includeInstructions = false;
252
+ }
253
+ const result = await handleGetComponentDocs(input);
254
+ process.stdout.write(getPrimaryTextContent(result) + "\n");
255
+ }
256
+ async function runGet(args) {
257
+ if (args.flags["h"] || args.flags["help"]) {
258
+ process.stdout.write(GET_HELP + "\n");
259
+ return;
260
+ }
261
+ const uri = args.positional[0];
262
+ if (!uri) {
263
+ process.stderr.write("Error: get requires a <uri> argument.\n\n");
264
+ process.stderr.write(GET_HELP + "\n");
265
+ process.exitCode = 1;
266
+ return;
267
+ }
268
+ const result = await handleResolveUri(uri);
269
+ process.stdout.write(getPrimaryTextContent(result) + "\n");
270
+ }
271
+ async function runGuides(args) {
272
+ if (args.flags["h"] || args.flags["help"]) {
273
+ process.stdout.write(GUIDES_HELP + "\n");
274
+ return;
275
+ }
276
+ const input = {};
277
+ if (args.flags["category"] !== void 0) {
278
+ input.category = args.flags["category"];
279
+ }
280
+ if (args.flags["framework"] !== void 0) {
281
+ input.framework = args.flags["framework"];
282
+ }
283
+ const result = await handleListGuides(input);
284
+ process.stdout.write(getPrimaryTextContent(result) + "\n");
285
+ }
286
+ async function runGuide(args) {
287
+ if (args.flags["h"] || args.flags["help"]) {
288
+ process.stdout.write(GUIDE_HELP + "\n");
289
+ return;
290
+ }
291
+ const name = args.positional[0];
292
+ if (!name) {
293
+ process.stderr.write("Error: guide requires a <name> argument.\n\n");
294
+ process.stderr.write(GUIDE_HELP + "\n");
295
+ process.exitCode = 1;
296
+ return;
297
+ }
298
+ const result = await handleGetGuide({ name });
299
+ process.stdout.write(getPrimaryTextContent(result) + "\n");
300
+ }
301
+ async function runInstructions(args) {
302
+ if (args.flags["h"] || args.flags["help"]) {
303
+ process.stdout.write(INSTRUCTIONS_HELP + "\n");
304
+ return;
305
+ }
306
+ const result = await handleGetInstructions();
307
+ const cliText = getPrimaryTextContent(result).replace(/\bMCP server\b/gi, "Context CLI").replace(/\bMCP Server\b/g, "Context CLI").replace(/\bMCP\b/g, "CLI");
308
+ process.stdout.write(cliText + "\n");
309
+ }
310
+ async function main() {
311
+ const args = parseArgs(process.argv.slice(2));
312
+ if (args.flags["v"] || args.flags["version"]) {
313
+ const version = await getPackageVersion();
314
+ process.stdout.write(version + "\n");
315
+ return;
316
+ }
317
+ if (!args.command) {
318
+ process.stdout.write(HELP_TEXT + "\n");
319
+ return;
320
+ }
321
+ switch (args.command) {
322
+ case "search":
323
+ await runSearch(args);
324
+ break;
325
+ case "docs":
326
+ await runDocs(args);
327
+ break;
328
+ case "get":
329
+ await runGet(args);
330
+ break;
331
+ case "guides":
332
+ await runGuides(args);
333
+ break;
334
+ case "guide":
335
+ await runGuide(args);
336
+ break;
337
+ case "instructions":
338
+ await runInstructions(args);
339
+ break;
340
+ default:
341
+ process.stderr.write(`Error: Unknown command '${args.command}'.
342
+
343
+ `);
344
+ process.stdout.write(HELP_TEXT + "\n");
345
+ process.exitCode = 1;
346
+ }
347
+ }
348
+ main().catch((error) => {
349
+ if (error instanceof McpError) {
350
+ process.stderr.write(`Error [${error.code}]: ${error.message}
351
+ `);
352
+ } else if (error instanceof Error) {
353
+ process.stderr.write(`Error: ${error.message}
354
+ `);
355
+ } else {
356
+ process.stderr.write(`Error: ${String(error)}
357
+ `);
358
+ }
359
+ process.exitCode = 1;
360
+ });
361
+ export {
362
+ parseArgs2 as parseArgs
363
+ };
@@ -0,0 +1,22 @@
1
+ /** Parsed result from the argument parser */
2
+ export interface ParsedArgs {
3
+ /** The subcommand (or null for top-level flags like --help / --version) */
4
+ command: string | null;
5
+ /** Positional arguments following the subcommand */
6
+ positional: string[];
7
+ /** Named flags (e.g. --category → category) mapped to their values */
8
+ flags: Record<string, string | boolean>;
9
+ }
10
+ /**
11
+ * Parse process.argv into a structured object.
12
+ *
13
+ * Supports:
14
+ * - Positional arguments
15
+ * - Long flags: --flag, --flag value, --flag=value
16
+ * - Short flags: -h, -v
17
+ * - Boolean negation: --no-<flag> → flag = false
18
+ *
19
+ * @param argv - Raw argument array (typically process.argv.slice(2))
20
+ * @returns Parsed command, positional args, and flags
21
+ */
22
+ export declare function parseArgs(argv: string[]): ParsedArgs;
@@ -0,0 +1,46 @@
1
+ import "../../chunks/chunk.QU3DSPNU.js";
2
+ function parseArgs(argv) {
3
+ const positional = [];
4
+ const flags = {};
5
+ let i = 0;
6
+ while (i < argv.length) {
7
+ const arg = argv[i];
8
+ if (arg === "--") {
9
+ positional.push(...argv.slice(i + 1));
10
+ break;
11
+ }
12
+ if (arg.startsWith("--")) {
13
+ const eqIndex = arg.indexOf("=");
14
+ if (eqIndex !== -1) {
15
+ const key = arg.slice(2, eqIndex);
16
+ flags[key] = arg.slice(eqIndex + 1);
17
+ } else {
18
+ const key = arg.slice(2);
19
+ if (key.startsWith("no-")) {
20
+ flags[key.slice(3)] = false;
21
+ } else {
22
+ const next = argv[i + 1];
23
+ if (next !== void 0 && !next.startsWith("-")) {
24
+ flags[key] = next;
25
+ i++;
26
+ } else {
27
+ flags[key] = true;
28
+ }
29
+ }
30
+ }
31
+ } else if (arg.startsWith("-") && arg.length === 2) {
32
+ flags[arg.slice(1)] = true;
33
+ } else {
34
+ positional.push(arg);
35
+ }
36
+ i++;
37
+ }
38
+ return {
39
+ command: positional.length > 0 ? positional[0] : null,
40
+ positional: positional.slice(1),
41
+ flags
42
+ };
43
+ }
44
+ export {
45
+ parseArgs
46
+ };
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Error handling for the Green Design System MCP Server
3
3
  */
4
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
4
5
  /**
5
6
  * Error codes for MCP operations
6
7
  */
@@ -75,13 +76,7 @@ export declare class RegexError extends McpError {
75
76
  * @param error - The error to convert
76
77
  * @returns Formatted error response
77
78
  */
78
- export declare function formatErrorResponse(error: unknown): {
79
- content: Array<{
80
- type: string;
81
- text: string;
82
- }>;
83
- isError: boolean;
84
- };
79
+ export declare function formatErrorResponse(error: unknown): CallToolResult;
85
80
  /**
86
81
  * Log error with structured information
87
82
  * @param error - The error to log
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Tool handler implementations for the Green Design System.
3
+ *
4
+ * This module contains the core business logic for each tool operation:
5
+ * searching components, fetching documentation, listing guides, and
6
+ * retrieving instructions. These handlers are shared between the MCP
7
+ * server (tools.ts) and the context CLI (context-cli/index.ts).
8
+ *
9
+ * Each handler:
10
+ * 1. Validates its input via the validation module
11
+ * 2. Loads pre-generated MCP data from disk
12
+ * 3. Applies business logic (search, filtering, assembly)
13
+ * 4. Returns a uniform `{ content: [{ type: 'text', text: string }] }` response
14
+ *
15
+ * @module handlers
16
+ */
17
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
18
+ /**
19
+ * Standard response shape returned by all handlers.
20
+ * Mirrors the MCP content response format so both the MCP server
21
+ * and the CLI can consume results uniformly.
22
+ */
23
+ export type HandlerResponse = CallToolResult;
24
+ /**
25
+ * Handle search_components — search for components and icons by name,
26
+ * description, or functionality.
27
+ *
28
+ * @param input - Raw input object (validated internally)
29
+ * @returns Search results as JSON text
30
+ * @throws {ValidationError} If input fails validation
31
+ */
32
+ export declare function handleSearchComponents(input: unknown): Promise<HandlerResponse>;
33
+ /**
34
+ * Handle get_component_docs — retrieve full documentation for a specific
35
+ * component, including framework-specific imports, API reference, design
36
+ * guidelines, and agent usage instructions.
37
+ *
38
+ * @param input - Raw input object (validated internally)
39
+ * @returns Assembled markdown documentation
40
+ * @throws {ValidationError} If input fails validation
41
+ * @throws {NotFoundError} If the component cannot be found
42
+ */
43
+ export declare function handleGetComponentDocs(input: unknown): Promise<HandlerResponse>;
44
+ /**
45
+ * Handle list_guides — list available setup guides and conceptual
46
+ * documentation, optionally filtered by category and framework.
47
+ *
48
+ * @param input - Raw input object (validated internally)
49
+ * @returns JSON list of guides with metadata and resource URIs
50
+ * @throws {ValidationError} If input fails validation
51
+ * @throws {NotFoundError} If the global index cannot be loaded
52
+ */
53
+ export declare function handleListGuides(input: unknown): Promise<HandlerResponse>;
54
+ /**
55
+ * Handle get_guide — retrieve the full content of a specific guide.
56
+ *
57
+ * @param input - Raw input object (validated internally)
58
+ * @returns Guide content as markdown
59
+ * @throws {ValidationError} If input fails validation
60
+ * @throws {NotFoundError} If the guide cannot be found
61
+ */
62
+ export declare function handleGetGuide(input: unknown): Promise<HandlerResponse>;
63
+ /**
64
+ * Handle get_instructions — retrieve the base instructions document
65
+ * containing critical rules, typography guidelines, layout system
66
+ * requirements, and general best practices.
67
+ *
68
+ * @returns Instructions content as markdown
69
+ * @throws {NotFoundError} If instructions are not available
70
+ */
71
+ export declare function handleGetInstructions(): Promise<HandlerResponse>;
72
+ /**
73
+ * Handle URI resolution — read the raw content of a green:// resource URI.
74
+ *
75
+ * Supports all URI types:
76
+ * - green://components/{name}/{docType} (e.g. green://components/button/api)
77
+ * - green://icons/{name}/{docType} (e.g. green://icons/arrow/api)
78
+ * - green://guides/{name} (e.g. green://guides/angular)
79
+ * - green://concepts/{name} (e.g. green://concepts/tokens)
80
+ * - green://instructions (the root instructions document)
81
+ *
82
+ * @param uri - A green:// resource URI string
83
+ * @returns Raw markdown content of the resource
84
+ * @throws {NotFoundError} If the URI is invalid or the resource doesn't exist
85
+ */
86
+ export declare function handleResolveUri(uri: string): Promise<HandlerResponse>;