grg-kit-cli 0.6.10 → 0.6.12

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
@@ -21,6 +21,10 @@ grg init
21
21
  # Or with a specific theme
22
22
  grg init --theme claude
23
23
 
24
+ # Add or switch theme
25
+ grg add theme claude
26
+ grg add theme modern-minimal
27
+
24
28
  # Add blocks (all files)
25
29
  grg add block auth
26
30
  grg add block shell
@@ -32,10 +36,14 @@ grg add block shell sidebar
32
36
  # Add all blocks
33
37
  grg add block --all
34
38
 
39
+ # Add components
40
+ grg add component file-upload
41
+
35
42
  # List available resources
36
43
  grg list
37
44
  grg list blocks
38
45
  grg list themes
46
+ grg list components
39
47
  ```
40
48
 
41
49
  ## Commands
@@ -70,12 +78,34 @@ Examples:
70
78
  - Updates `src/styles.css` with theme import
71
79
 
72
80
  **Available themes:**
73
- - `grg-theme` - Default theme with purple/orange accents
81
+ - `grg-theme` - Default theme with blue accents
74
82
  - `claude` - Claude-inspired warm tones
75
83
  - `clean-slate` - Minimal grayscale palette
76
84
  - `modern-minimal` - Contemporary minimal design
77
85
  - `amber-minimal` - Warm amber accents
78
- - `mocks` - Theme for mockups and prototypes
86
+ - `chroma-clinic` - Professional blue theme for healthcare
87
+ - `bio-lab` - Fresh green theme for life sciences
88
+ - `pharma-teal` - Calming teal for pharmaceutical apps
89
+ - `helix-purple` - DNA-inspired purple for genomics
90
+
91
+ ### `grg add theme`
92
+
93
+ Add or switch theme in an existing project.
94
+
95
+ ```bash
96
+ grg add theme <themeName>
97
+
98
+ Options:
99
+ -o, --output <path> Custom themes directory (default: "src/themes")
100
+
101
+ Examples:
102
+ grg add theme claude # Download and set claude theme
103
+ grg add theme modern-minimal # Download and set modern-minimal
104
+ ```
105
+
106
+ **What it does:**
107
+ - Downloads the theme CSS file to `src/themes/`
108
+ - Updates `src/styles.css` to import the new theme (replaces existing theme import)
79
109
 
80
110
  ### `grg add block`
81
111
 
@@ -97,6 +127,22 @@ Examples:
97
127
  grg add block --all # All blocks
98
128
  ```
99
129
 
130
+ ### `grg add component`
131
+
132
+ Add GRG Kit components to your project.
133
+
134
+ ```bash
135
+ grg add component <componentName>
136
+
137
+ Options:
138
+ --all Add all components
139
+ -o, --output <path> Custom output directory
140
+
141
+ Examples:
142
+ grg add component file-upload # Download file-upload component
143
+ grg add component --all # All components
144
+ ```
145
+
100
146
  **Available blocks and files:**
101
147
 
102
148
  | Block | Files |
@@ -110,17 +156,23 @@ Examples:
110
156
  List available blocks and themes.
111
157
 
112
158
  ```bash
113
- grg list [category]
159
+ grg list [category] [options]
160
+
161
+ Options:
162
+ --json Output as JSON (for MCP server integration)
114
163
 
115
164
  Examples:
116
165
  grg list # Show overview
117
166
  grg list blocks # List all blocks
118
167
  grg list themes # List all themes
168
+ grg list --json # Output all resources as JSON
119
169
  ```
120
170
 
171
+ The `--json` flag outputs structured data used by the MCP server to get resource information.
172
+
121
173
  ### `grg llm-setup`
122
174
 
123
- Generate LLM-specific prompts and rules for AI assistants (Windsurf, Cursor, etc.).
175
+ Generate design system rules for AI assistants (Windsurf, Cursor, Claude Code).
124
176
 
125
177
  ```bash
126
178
  grg llm-setup [options]
@@ -129,60 +181,17 @@ Options:
129
181
  -o, --output <path> Output directory for rules (default: ".windsurf/rules")
130
182
 
131
183
  Examples:
132
- grg llm-setup # Windsurf (multiple .md files)
184
+ grg llm-setup # Windsurf/Cursor (design-system.md)
133
185
  grg llm-setup --output .claude # Claude Code (single CLAUDE.md)
134
186
  ```
135
187
 
136
- ## MCP Server Integration
137
-
138
- For AI assistants to automatically discover and use GRG Kit resources:
139
-
140
- ### 1. Install the MCP Server
141
-
142
- ```bash
143
- npm install -g grg-kit-mcp-server
144
- ```
145
-
146
- ### 2. Configure Your AI Assistant
147
-
148
- **Windsurf** (`~/.codeium/windsurf/mcp_config.json`):
149
-
150
- ```json
151
- {
152
- "mcpServers": {
153
- "grg-kit": {
154
- "command": "grg-mcp-server"
155
- }
156
- }
157
- }
158
- ```
159
-
160
- **Claude Code** (`~/.claude/settings.json`):
161
-
162
- ```json
163
- {
164
- "mcpServers": {
165
- "grg-kit": {
166
- "command": "grg-mcp-server"
167
- }
168
- }
169
- }
170
- ```
171
-
172
- ### 3. Generate AI Rules
173
-
174
- ```bash
175
- cd your-angular-project
176
- grg llm-setup
177
- ```
178
-
179
- ### 4. Restart Your IDE
188
+ **What this creates:**
189
+ - `design-system.md` - Component usage patterns, styling rules, import patterns
180
190
 
181
191
  **What this enables:**
182
- - āœ… AI automatically searches GRG Kit before writing custom code
183
- - āœ… AI knows about themes, components, blocks, and examples
184
192
  - āœ… AI follows GRG Kit design system patterns
185
- - āœ… AI can install blocks directly via MCP tools
193
+ - āœ… AI uses Spartan-NG components correctly
194
+ - āœ… AI uses semantic colors instead of raw Tailwind colors
186
195
 
187
196
  ## Quick Reference
188
197
 
@@ -195,6 +204,10 @@ cd my-app
195
204
  grg init # Default theme
196
205
  grg init --theme claude # Custom theme
197
206
 
207
+ # Add/switch theme
208
+ grg add theme claude # Switch to claude theme
209
+ grg add theme modern-minimal # Switch to modern-minimal
210
+
198
211
  # Add blocks (all files)
199
212
  grg add block auth # All auth files
200
213
  grg add block shell # All shell layouts
@@ -205,10 +218,14 @@ grg add block auth login # Just login
205
218
  grg add block shell sidebar # Just sidebar shell
206
219
  grg add block --all # All blocks
207
220
 
221
+ # Add components
222
+ grg add component file-upload # File upload component
223
+
208
224
  # List resources
209
225
  grg list # Overview
210
226
  grg list blocks # Available blocks
211
227
  grg list themes # Available themes
228
+ grg list components # Available components
212
229
 
213
230
  # AI setup
214
231
  grg llm-setup # Generate AI rules
package/bin/grg.js CHANGED
@@ -41,10 +41,20 @@ addCommand
41
41
  await addComponent(componentName, options);
42
42
  });
43
43
 
44
+ addCommand
45
+ .command('theme [themeName]')
46
+ .description('Add or switch theme (e.g., grg add theme claude)')
47
+ .option('-o, --output <path>', 'Custom themes directory', 'src/themes')
48
+ .action(async (themeName, options) => {
49
+ const { addTheme } = require('../commands/add');
50
+ await addTheme(themeName, options);
51
+ });
52
+
44
53
  // List command
45
54
  program
46
55
  .command('list [category]')
47
56
  .description('List available blocks and components')
57
+ .option('--json', 'Output as JSON (for MCP server integration)')
48
58
  .action(list);
49
59
 
50
60
  // LLM Setup command
package/commands/add.js CHANGED
@@ -4,7 +4,7 @@ const ora = require('ora');
4
4
  const fs = require('fs').promises;
5
5
  const path = require('path');
6
6
  const https = require('https');
7
- const { fetchCatalog, REPO } = require('../config/catalog-fetcher');
7
+ const { RESOURCES, REPO } = require('../config/resources');
8
8
 
9
9
  /**
10
10
  * Add command - downloads blocks or specific block files
@@ -16,10 +16,6 @@ const { fetchCatalog, REPO } = require('../config/catalog-fetcher');
16
16
  * grg add block --all # All blocks with all files
17
17
  */
18
18
  async function add(blockName, fileIds, options) {
19
- // Fetch catalog dynamically (with caching)
20
- const spinner = ora('Fetching catalog...').start();
21
- const RESOURCES = await fetchCatalog({ silent: true });
22
- spinner.stop();
23
19
 
24
20
  // Handle --all flag (download everything)
25
21
  if (options.all) {
@@ -292,4 +288,157 @@ function showComponentUsage(RESOURCES) {
292
288
  console.log(chalk.gray('\nRun'), chalk.cyan('grg list components'), chalk.gray('for more details'));
293
289
  }
294
290
 
295
- module.exports = { add, addComponent };
291
+ /**
292
+ * Add theme command - downloads and sets up a theme
293
+ * Format: grg add theme <themeName>
294
+ * Examples:
295
+ * grg add theme claude # Download and set claude theme
296
+ * grg add theme modern-minimal # Download and set modern-minimal theme
297
+ */
298
+ async function addTheme(themeName, options) {
299
+ // Fetch catalog dynamically (with caching)
300
+ const spinner = ora('Fetching catalog...').start();
301
+ const RESOURCES = await fetchCatalog({ silent: true });
302
+ spinner.stop();
303
+
304
+ // Validate theme name
305
+ if (!themeName) {
306
+ showThemeUsage(RESOURCES);
307
+ process.exit(1);
308
+ }
309
+
310
+ const theme = RESOURCES.themes.find(t => t.name === themeName);
311
+ if (!theme) {
312
+ console.error(chalk.red(`\nError: Theme "${themeName}" not found`));
313
+ console.log(chalk.yellow('\nAvailable themes:'));
314
+ RESOURCES.themes.forEach(t => {
315
+ console.log(chalk.cyan(` ${t.name}`), chalk.gray(`- ${t.description}`));
316
+ });
317
+ process.exit(1);
318
+ }
319
+
320
+ await downloadTheme(theme, options.output);
321
+ console.log(chalk.bold.green('✨ Done!'));
322
+ }
323
+
324
+ /**
325
+ * Download a theme file
326
+ */
327
+ async function downloadTheme(theme, customOutput) {
328
+ const downloadSpinner = ora();
329
+ const themesDir = customOutput || 'src/themes';
330
+ const outputPath = path.join(themesDir, theme.file);
331
+
332
+ // Ensure themes directory exists
333
+ await fs.mkdir(themesDir, { recursive: true });
334
+
335
+ downloadSpinner.start(`Downloading ${theme.title} theme...`);
336
+
337
+ try {
338
+ const themeUrl = `https://raw.githubusercontent.com/${REPO}/main/${theme.path}`;
339
+ await downloadFileToPath(themeUrl, outputPath);
340
+
341
+ downloadSpinner.succeed(chalk.green(`āœ“ ${theme.title} theme downloaded`));
342
+ console.log(chalk.gray(` Location: ${outputPath}`));
343
+
344
+ } catch (error) {
345
+ downloadSpinner.fail(chalk.red(`Failed to download ${theme.title}`));
346
+ console.error(chalk.red(error.message));
347
+ process.exit(1);
348
+ }
349
+
350
+ // Update styles.css
351
+ downloadSpinner.start('Updating src/styles.css...');
352
+ try {
353
+ const stylesPath = 'src/styles.css';
354
+ let stylesContent = '';
355
+
356
+ try {
357
+ stylesContent = await fs.readFile(stylesPath, 'utf-8');
358
+ } catch (error) {
359
+ // File doesn't exist, will create it
360
+ stylesContent = '';
361
+ }
362
+
363
+ const themeImport = `@import './themes/${theme.file}';`;
364
+
365
+ // Check if any theme is already imported
366
+ const themeImportRegex = /@import\s+['"]\.\/themes\/[^'"]+['"];?\n?/g;
367
+ const existingThemeImports = stylesContent.match(themeImportRegex);
368
+
369
+ if (existingThemeImports && existingThemeImports.length > 0) {
370
+ // Remove ALL existing theme imports first
371
+ stylesContent = stylesContent.replace(themeImportRegex, '');
372
+ // Clean up any resulting double blank lines
373
+ stylesContent = stylesContent.replace(/\n{3,}/g, '\n\n');
374
+ // Add the new theme import after the spartan preset import
375
+ const spartanImport = '@import "@spartan-ng/brain/hlm-tailwind-preset.css";';
376
+ if (stylesContent.includes(spartanImport)) {
377
+ stylesContent = stylesContent.replace(spartanImport, `${spartanImport}\n\n${themeImport}`);
378
+ } else {
379
+ // Fallback: add at the beginning
380
+ stylesContent = themeImport + '\n' + stylesContent;
381
+ }
382
+ await fs.writeFile(stylesPath, stylesContent);
383
+ downloadSpinner.succeed(chalk.green(`āœ“ Updated theme import in src/styles.css`));
384
+ } else if (!stylesContent.includes(themeImport)) {
385
+ // No theme import exists, add required imports
386
+ const requiredImports = [
387
+ '@import "@angular/cdk/overlay-prebuilt.css";',
388
+ '@import "tailwindcss";',
389
+ '@import "@spartan-ng/brain/hlm-tailwind-preset.css";',
390
+ '',
391
+ themeImport
392
+ ];
393
+
394
+ const newContent = requiredImports.join('\n') + '\n';
395
+ await fs.writeFile(stylesPath, newContent);
396
+ downloadSpinner.succeed(chalk.green('āœ“ Created src/styles.css with theme import'));
397
+ } else {
398
+ downloadSpinner.succeed(chalk.green('āœ“ Theme already imported in src/styles.css'));
399
+ }
400
+ } catch (error) {
401
+ downloadSpinner.warn(chalk.yellow('Could not update src/styles.css automatically'));
402
+ console.log(chalk.gray('\nPlease add the following to your src/styles.css:'));
403
+ console.log(chalk.cyan(` @import './themes/${theme.file}';`));
404
+ }
405
+
406
+ console.log();
407
+ }
408
+
409
+ /**
410
+ * Download file to a specific path
411
+ */
412
+ function downloadFileToPath(url, outputPath) {
413
+ return new Promise((resolve, reject) => {
414
+ https.get(url, (res) => {
415
+ if (res.statusCode === 301 || res.statusCode === 302) {
416
+ // Follow redirect
417
+ https.get(res.headers.location, (res2) => {
418
+ handleResponse(res2, outputPath, resolve, reject);
419
+ }).on('error', reject);
420
+ } else {
421
+ handleResponse(res, outputPath, resolve, reject);
422
+ }
423
+ }).on('error', reject);
424
+ });
425
+ }
426
+
427
+ /**
428
+ * Show theme usage help
429
+ */
430
+ function showThemeUsage(RESOURCES) {
431
+ console.log(chalk.yellow('\nUsage: grg add theme <themeName>\n'));
432
+ console.log(chalk.bold('Examples:'));
433
+ console.log(chalk.cyan(' grg add theme claude'), chalk.gray(' # Download and set claude theme'));
434
+ console.log(chalk.cyan(' grg add theme modern-minimal'), chalk.gray(' # Download and set modern-minimal'));
435
+
436
+ console.log(chalk.bold('\nAvailable themes:'));
437
+ RESOURCES.themes.forEach(t => {
438
+ console.log(chalk.cyan(` ${t.name}`), chalk.gray(`- ${t.description}`));
439
+ });
440
+
441
+ console.log(chalk.gray('\nRun'), chalk.cyan('grg list themes'), chalk.gray('for more details'));
442
+ }
443
+
444
+ module.exports = { add, addComponent, addTheme };
package/commands/init.js CHANGED
@@ -2,6 +2,7 @@ const fs = require('fs').promises;
2
2
  const path = require('path');
3
3
  const { exec } = require('child_process');
4
4
  const { promisify } = require('util');
5
+ const https = require('https');
5
6
  const degit = require('degit');
6
7
  const chalk = require('chalk');
7
8
  const ora = require('ora');
@@ -9,6 +10,28 @@ const { RESOURCES, REPO } = require('../config/resources');
9
10
 
10
11
  const execAsync = promisify(exec);
11
12
 
13
+ /**
14
+ * Download a file from a URL
15
+ */
16
+ function downloadFile(url) {
17
+ return new Promise((resolve, reject) => {
18
+ https.get(url, (res) => {
19
+ if (res.statusCode === 301 || res.statusCode === 302) {
20
+ // Follow redirect
21
+ return downloadFile(res.headers.location).then(resolve).catch(reject);
22
+ }
23
+ if (res.statusCode !== 200) {
24
+ reject(new Error(`HTTP ${res.statusCode}`));
25
+ return;
26
+ }
27
+
28
+ let data = '';
29
+ res.on('data', chunk => data += chunk);
30
+ res.on('end', () => resolve(data));
31
+ }).on('error', reject);
32
+ });
33
+ }
34
+
12
35
  /**
13
36
  * Init command - initializes GRG Kit in an existing Angular project
14
37
  * Installs Tailwind CSS v4, runs spartan-ng ui, downloads theme
@@ -45,7 +68,18 @@ async function init(options) {
45
68
  console.log(chalk.bold.cyan('\nšŸš€ Initializing GRG Kit\n'));
46
69
  console.log(chalk.gray(` Theme: ${theme.title}\n`));
47
70
 
48
- // Step 1: Install Tailwind CSS v4
71
+ // Step 1: Fresh install of packages
72
+ spinner.start('Cleaning node_modules and reinstalling packages...');
73
+ try {
74
+ await execAsync('rm -rf node_modules && npm install');
75
+ spinner.succeed(chalk.green('āœ“ Fresh package install complete'));
76
+ } catch (error) {
77
+ spinner.fail(chalk.red('Failed to reinstall packages'));
78
+ console.error(chalk.gray(error.message));
79
+ process.exit(1);
80
+ }
81
+
82
+ // Step 2: Install Tailwind CSS v4
49
83
  spinner.start('Installing Tailwind CSS v4...');
50
84
  try {
51
85
  await execAsync('npm install tailwindcss @tailwindcss/postcss postcss --force');
@@ -227,15 +261,12 @@ async function init(options) {
227
261
  spinner.warn(chalk.yellow('Themes directory may already exist'));
228
262
  }
229
263
 
230
- // Step 10: Download theme
264
+ // Step 10: Download theme file
231
265
  spinner.start(`Downloading ${theme.title} theme...`);
232
266
  try {
233
- const emitter = degit(`${REPO}/${theme.path}`, {
234
- cache: false,
235
- force: true,
236
- verbose: false,
237
- });
238
- await emitter.clone(theme.defaultOutput);
267
+ const themeUrl = `https://raw.githubusercontent.com/${REPO}/main/${theme.path}`;
268
+ const themeContent = await downloadFile(themeUrl);
269
+ await fs.writeFile(theme.defaultOutput, themeContent);
239
270
  spinner.succeed(chalk.green(`āœ“ Downloaded ${theme.title} theme`));
240
271
  } catch (error) {
241
272
  spinner.fail(chalk.red('Failed to download theme'));
package/commands/list.js CHANGED
@@ -1,16 +1,34 @@
1
1
  const chalk = require('chalk');
2
- const ora = require('ora');
3
- const { fetchCatalog } = require('../config/catalog-fetcher');
2
+ const { RESOURCES } = require('../config/resources');
3
+ const { version } = require('../package.json');
4
4
 
5
5
  /**
6
6
  * List command - displays available blocks, components, and themes
7
- * Usage: grg list [category]
7
+ * Usage: grg list [category] [--json]
8
8
  */
9
- async function list(category) {
10
- // Fetch catalog dynamically
11
- const spinner = ora('Fetching catalog...').start();
12
- const RESOURCES = await fetchCatalog({ silent: true });
13
- spinner.stop();
9
+ async function list(category, options = {}) {
10
+
11
+ // JSON output for MCP server integration
12
+ if (options.json) {
13
+ const output = {
14
+ cli: {
15
+ name: 'grg',
16
+ version,
17
+ commands: {
18
+ init: { usage: 'grg init [--theme <name>]', themeFlag: '--theme' },
19
+ addBlock: { usage: 'grg add block <blockName> [fileIds...]', validBlocks: RESOURCES.blocks.map(b => b.name) },
20
+ addComponent: { usage: 'grg add component <componentName>', validComponents: RESOURCES.components.map(c => c.name) },
21
+ addTheme: { usage: 'grg add theme <themeName>' },
22
+ list: { usage: 'grg list [category] [--json]' }
23
+ }
24
+ },
25
+ themes: RESOURCES.themes,
26
+ components: RESOURCES.components,
27
+ blocks: RESOURCES.blocks
28
+ };
29
+ console.log(JSON.stringify(output, null, 2));
30
+ return;
31
+ }
14
32
 
15
33
  if (!category) {
16
34
  // Show overview
@@ -25,7 +43,7 @@ async function list(category) {
25
43
  console.log(chalk.gray(' Run: grg list components\n'));
26
44
 
27
45
  console.log(chalk.bold('Themes') + chalk.gray(` (${RESOURCES.themes.length} available)`));
28
- console.log(chalk.gray(' Set with: grg init --theme <name>'));
46
+ console.log(chalk.gray(' Add with: grg add theme <name>'));
29
47
  console.log(chalk.gray(' Run: grg list themes\n'));
30
48
 
31
49
  return;
@@ -67,11 +85,11 @@ async function list(category) {
67
85
 
68
86
  case 'themes':
69
87
  console.log(chalk.bold.cyan('\nšŸŽØ Available Themes\n'));
70
- console.log(chalk.gray(' Use with: grg init --theme <name>\n'));
88
+ console.log(chalk.gray(' Use with: grg add theme <name>\n'));
71
89
  RESOURCES.themes.forEach(theme => {
72
90
  console.log(chalk.bold(` ${theme.name}`));
73
91
  console.log(chalk.gray(` ${theme.description}`));
74
- console.log(chalk.yellow(` grg init --theme ${theme.name}`));
92
+ console.log(chalk.yellow(` grg add theme ${theme.name}`));
75
93
  if (theme.tags && theme.tags.length > 0) {
76
94
  console.log(chalk.gray(` Tags: ${theme.tags.join(', ')}`));
77
95
  }