grg-kit-cli 0.3.0 → 0.3.2

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
@@ -5,19 +5,20 @@ CLI tool for initializing Angular projects with GRG Kit and adding blocks.
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install -g grg-kit-cli
8
+ pnpm install -g grg-kit-cli
9
9
  ```
10
10
 
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- # Initialize GRG Kit (theme + all components + spartan-ng examples)
15
- grg init
14
+ # Initialize a new Angular project with GRG Kit
15
+ grg init my-app
16
16
 
17
17
  # Initialize with a specific theme
18
- grg init --theme claude
18
+ grg init my-app --theme claude
19
19
 
20
- # Add blocks
20
+ # Add blocks (inside project directory)
21
+ cd my-app
21
22
  grg add block --auth
22
23
  grg add block --shell
23
24
  grg add block --all
@@ -30,27 +31,30 @@ grg list themes
30
31
 
31
32
  ## Commands
32
33
 
33
- ### `grg init`
34
+ ### `grg init <project-name>`
34
35
 
35
- Initialize GRG Kit in your Angular project. Downloads theme, all components, and all spartan-ng examples in one shot.
36
+ Create a new Angular project with GRG Kit fully configured. One command to set up everything.
36
37
 
37
38
  ```bash
38
- grg init [options]
39
+ grg init <project-name> [options]
39
40
 
40
41
  Options:
41
42
  -t, --theme <name> Theme to install (default: "grg-theme")
42
43
 
43
44
  Examples:
44
- grg init
45
- grg init --theme claude
46
- grg init -t modern-minimal
45
+ grg init my-app
46
+ grg init my-app --theme claude
47
+ grg init dashboard -t modern-minimal
47
48
  ```
48
49
 
49
50
  **What it does:**
50
- - Creates `src/themes`, `src/app/components`, `src/app/spartan-examples` directories
51
+ - Creates Angular project with CSS styling
52
+ - Installs Tailwind CSS v4 (`tailwindcss @tailwindcss/postcss postcss`)
53
+ - Creates `.postcssrc.json` with PostCSS configuration
54
+ - Installs and configures Spartan-NG CLI
55
+ - Runs `ng g @spartan-ng/cli:ui all` to install all Spartan-NG UI components
56
+ - Downloads 56+ Spartan-NG examples to `libs/examples`
51
57
  - Downloads the selected theme
52
- - Downloads all GRG Kit components
53
- - Downloads all spartan-ng examples (56+)
54
58
  - Updates `src/styles.css` with theme import
55
59
 
56
60
  **Available themes:**
@@ -116,7 +120,7 @@ For AI assistants to automatically discover and use GRG Kit resources:
116
120
  ### 1. Install the MCP Server
117
121
 
118
122
  ```bash
119
- npm install -g @grg-kit/mcp-server
123
+ pnpm install -g @grg-kit/mcp-server
120
124
  ```
121
125
 
122
126
  ### 2. Configure Your AI Assistant
@@ -164,10 +168,11 @@ grg llm-prompts
164
168
 
165
169
  ```bash
166
170
  # Initialize project
167
- grg init # Default theme
168
- grg init --theme claude # Custom theme
171
+ grg init my-app # Default theme
172
+ grg init my-app --theme claude # Custom theme
169
173
 
170
- # Add blocks
174
+ # Add blocks (inside project directory)
175
+ cd my-app
171
176
  grg add block --auth # Auth pages
172
177
  grg add block --shell # App shell
173
178
  grg add block --settings # Settings page
package/bin/grg.js CHANGED
@@ -15,14 +15,16 @@ program
15
15
 
16
16
  // Init command - sets up everything in one shot
17
17
  program
18
- .command('init')
19
- .description('Initialize GRG Kit: sets up styles.css with theme, adds all components and spartan-ng examples')
18
+ .command('init <project-name>')
19
+ .description('Initialize GRG Kit: creates Angular project with zoneless, sets up styles.css with theme, adds all components')
20
20
  .option('-t, --theme <name>', 'Theme to install (grg-theme, claude, clean-slate, modern-minimal, amber-minimal, mocks)', 'grg-theme')
21
21
  .action(init);
22
22
 
23
- // Add block command
24
- program
25
- .command('add block')
23
+ // Add command with block subcommand
24
+ const addCommand = program.command('add').description('Add resources to your project');
25
+
26
+ addCommand
27
+ .command('block')
26
28
  .description('Add blocks to your project')
27
29
  .option('--all', 'Add all blocks')
28
30
  .option('--auth', 'Add authentication block (login, signup, forgot password)')
package/commands/init.js CHANGED
@@ -1,15 +1,19 @@
1
1
  const fs = require('fs').promises;
2
2
  const path = require('path');
3
+ const { exec } = require('child_process');
4
+ const { promisify } = require('util');
3
5
  const degit = require('degit');
4
6
  const chalk = require('chalk');
5
7
  const ora = require('ora');
6
8
  const { RESOURCES, REPO } = require('../config/resources');
7
9
 
10
+ const execAsync = promisify(exec);
11
+
8
12
  /**
9
13
  * Init command - initializes GRG Kit in the project
10
- * Downloads theme, all components, and all spartan-ng examples in one shot
14
+ * Creates Angular project, installs Tailwind CSS v4, runs spartan-ng ui, downloads theme
11
15
  */
12
- async function init(options) {
16
+ async function init(projectName, options) {
13
17
  const themeName = options.theme || 'grg-theme';
14
18
  const theme = RESOURCES.themes.find(t => t.name === themeName);
15
19
 
@@ -21,74 +25,160 @@ async function init(options) {
21
25
  }
22
26
 
23
27
  console.log(chalk.bold.cyan('\n🚀 Initializing GRG Kit\n'));
28
+ console.log(chalk.gray(` Project: ${projectName}`));
29
+ console.log(chalk.gray(` Theme: ${theme.title}\n`));
24
30
 
25
31
  const spinner = ora();
26
32
 
27
- // Step 1: Create directories
28
- spinner.start('Creating directories...');
33
+ // Step 1: Create Angular project
34
+ spinner.start(`Creating Angular project "${projectName}"...`);
29
35
  try {
30
- await fs.mkdir('src/themes', { recursive: true });
31
- await fs.mkdir('src/app/components', { recursive: true });
32
- await fs.mkdir('src/app/spartan-examples', { recursive: true });
33
- spinner.succeed(chalk.green('✓ Created directories'));
36
+ await execAsync(`ng new ${projectName} --style=css`, { stdio: 'pipe' });
37
+ spinner.succeed(chalk.green(`✓ Created Angular project "${projectName}"`));
34
38
  } catch (error) {
35
- spinner.fail(chalk.red('Failed to create directories'));
39
+ spinner.fail(chalk.red('Failed to create Angular project'));
36
40
  console.error(chalk.red(error.message));
37
41
  process.exit(1);
38
42
  }
39
43
 
40
- // Step 2: Download theme
41
- spinner.start(`Downloading ${theme.title} theme...`);
44
+ // Change to project directory for remaining steps
45
+ process.chdir(projectName);
46
+
47
+ // Step 2: Install Tailwind CSS v4
48
+ spinner.start('Installing Tailwind CSS v4...');
42
49
  try {
43
- const emitter = degit(`${REPO}/${theme.path}`, {
50
+ await execAsync('pnpm install tailwindcss @tailwindcss/postcss postcss', { stdio: 'pipe' });
51
+ spinner.succeed(chalk.green('✓ Tailwind CSS v4 installed'));
52
+ } catch (error) {
53
+ spinner.warn(chalk.yellow('Tailwind CSS installation skipped (may already be installed)'));
54
+ }
55
+
56
+ // Step 3: Create .postcssrc.json
57
+ spinner.start('Creating PostCSS configuration...');
58
+ try {
59
+ const postcssConfig = {
60
+ plugins: {
61
+ '@tailwindcss/postcss': {}
62
+ }
63
+ };
64
+ await fs.writeFile('.postcssrc.json', JSON.stringify(postcssConfig, null, 2) + '\n');
65
+ spinner.succeed(chalk.green('✓ Created .postcssrc.json'));
66
+ } catch (error) {
67
+ spinner.warn(chalk.yellow('Could not create .postcssrc.json (may already exist)'));
68
+ }
69
+
70
+ // Step 4: Install Spartan-NG CLI
71
+ spinner.start('Installing Spartan-NG CLI...');
72
+ try {
73
+ await execAsync('pnpm install -D @spartan-ng/cli', { stdio: 'pipe' });
74
+ spinner.succeed(chalk.green('✓ Spartan-NG CLI installed'));
75
+ } catch (error) {
76
+ spinner.warn(chalk.yellow('Spartan-NG CLI installation skipped'));
77
+ }
78
+
79
+ // Step 5: Create components.json config file
80
+ spinner.start('Creating Spartan-NG configuration...');
81
+ try {
82
+ const componentsConfig = {
83
+ componentsPath: 'libs/spartan-ui',
84
+ importAlias: '@spartan-ng/helm'
85
+ };
86
+ await fs.writeFile('components.json', JSON.stringify(componentsConfig, null, 2) + '\n');
87
+ spinner.succeed(chalk.green('✓ Created components.json'));
88
+ } catch (error) {
89
+ spinner.warn(chalk.yellow('Could not create components.json'));
90
+ }
91
+
92
+ // Step 6: Update tsconfig.json with paths for Spartan-NG
93
+ spinner.start('Configuring TypeScript paths...');
94
+ try {
95
+ const tsconfigPath = 'tsconfig.json';
96
+ let tsconfigContent = await fs.readFile(tsconfigPath, 'utf-8');
97
+
98
+ // Strip comments from tsconfig (Angular generates tsconfig with comments)
99
+ tsconfigContent = tsconfigContent
100
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove block comments
101
+ .replace(/\/\/.*/g, ''); // Remove line comments
102
+
103
+ const tsconfig = JSON.parse(tsconfigContent);
104
+
105
+ // Add baseUrl and paths for @spartan-ng/helm
106
+ tsconfig.compilerOptions = tsconfig.compilerOptions || {};
107
+ tsconfig.compilerOptions.baseUrl = '.';
108
+ tsconfig.compilerOptions.paths = {
109
+ '@spartan-ng/helm/*': ['./libs/spartan-ui/*/src/index.ts']
110
+ };
111
+
112
+ await fs.writeFile(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n');
113
+ spinner.succeed(chalk.green('✓ Configured TypeScript paths'));
114
+ } catch (error) {
115
+ spinner.warn(chalk.yellow('Could not update tsconfig.json'));
116
+ console.error(chalk.gray(error.message));
117
+ }
118
+
119
+ // Step 7: Run Spartan-NG UI generator (install all components)
120
+ spinner.start('Installing all Spartan-NG UI components...');
121
+ try {
122
+ const { spawn } = require('child_process');
123
+ await new Promise((resolve, reject) => {
124
+ const child = spawn('pnpm', ['ng', 'g', '@spartan-ng/cli:ui', 'all', '--defaults'], {
125
+ stdio: 'inherit',
126
+ shell: true
127
+ });
128
+ child.on('close', (code) => {
129
+ if (code === 0) resolve();
130
+ else reject(new Error(`Process exited with code ${code}`));
131
+ });
132
+ child.on('error', reject);
133
+ });
134
+ spinner.succeed(chalk.green('✓ All Spartan-NG UI components installed'));
135
+ } catch (error) {
136
+ spinner.fail(chalk.red('Failed to run Spartan-NG UI generator'));
137
+ console.error(chalk.red(error.message));
138
+ process.exit(1);
139
+ }
140
+
141
+ // Step 8: Download Spartan-NG examples
142
+ spinner.start('Downloading Spartan-NG examples...');
143
+ try {
144
+ const examplesEmitter = degit(`${REPO}/templates/spartan-examples`, {
44
145
  cache: false,
45
146
  force: true,
46
147
  verbose: false,
47
148
  });
48
- await emitter.clone(theme.defaultOutput);
49
- spinner.succeed(chalk.green(`✓ Downloaded ${theme.title} theme`));
149
+ await examplesEmitter.clone('libs/examples');
150
+ spinner.succeed(chalk.green('✓ Downloaded Spartan-NG examples to libs/examples'));
50
151
  } catch (error) {
51
- spinner.fail(chalk.red('Failed to download theme'));
52
- console.error(chalk.red(error.message));
53
- process.exit(1);
152
+ spinner.warn(chalk.yellow('Could not download Spartan-NG examples'));
153
+ console.error(chalk.gray(error.message));
54
154
  }
55
155
 
56
- // Step 3: Download all components
57
- spinner.start(`Downloading ${RESOURCES.components.length} components...`);
156
+ // Step 9: Create themes directory
157
+ spinner.start('Creating themes directory...');
58
158
  try {
59
- for (const component of RESOURCES.components) {
60
- const emitter = degit(`${REPO}/${component.path}`, {
61
- cache: false,
62
- force: true,
63
- verbose: false,
64
- });
65
- await emitter.clone(component.defaultOutput);
66
- }
67
- spinner.succeed(chalk.green(`✓ Downloaded ${RESOURCES.components.length} components`));
159
+ await fs.mkdir('src/themes', { recursive: true });
160
+ spinner.succeed(chalk.green('✓ Created themes directory'));
68
161
  } catch (error) {
69
- spinner.fail(chalk.red('Failed to download components'));
70
- console.error(chalk.red(error.message));
71
- process.exit(1);
162
+ spinner.warn(chalk.yellow('Themes directory may already exist'));
72
163
  }
73
164
 
74
- // Step 4: Download all spartan-ng examples
75
- const examplesAll = RESOURCES.examples.all;
76
- spinner.start(`Downloading spartan-ng examples (${examplesAll.count})...`);
165
+ // Step 10: Download theme
166
+ spinner.start(`Downloading ${theme.title} theme...`);
77
167
  try {
78
- const emitter = degit(`${REPO}/${examplesAll.path}`, {
168
+ const emitter = degit(`${REPO}/${theme.path}`, {
79
169
  cache: false,
80
170
  force: true,
81
171
  verbose: false,
82
172
  });
83
- await emitter.clone(examplesAll.defaultOutput);
84
- spinner.succeed(chalk.green(`✓ Downloaded spartan-ng examples (${examplesAll.count})`));
173
+ await emitter.clone(theme.defaultOutput);
174
+ spinner.succeed(chalk.green(`✓ Downloaded ${theme.title} theme`));
85
175
  } catch (error) {
86
- spinner.fail(chalk.red('Failed to download spartan-ng examples'));
176
+ spinner.fail(chalk.red('Failed to download theme'));
87
177
  console.error(chalk.red(error.message));
88
178
  process.exit(1);
89
179
  }
90
180
 
91
- // Step 5: Update styles.css
181
+ // Step 11: Update styles.css
92
182
  spinner.start('Updating src/styles.css...');
93
183
  try {
94
184
  const stylesPath = 'src/styles.css';
@@ -128,13 +218,15 @@ async function init(options) {
128
218
  console.log(chalk.bold.green('\n✨ GRG Kit initialized successfully!\n'));
129
219
 
130
220
  console.log(chalk.bold('Installed:'));
221
+ console.log(chalk.gray(' Tailwind CSS:'), chalk.cyan('v4 with PostCSS'));
222
+ console.log(chalk.gray(' Spartan-NG:'), chalk.cyan('All UI components in libs/ui'));
223
+ console.log(chalk.gray(' Examples:'), chalk.cyan('56+ component examples in libs/examples'));
131
224
  console.log(chalk.gray(' Theme:'), chalk.cyan(theme.title));
132
- console.log(chalk.gray(' Components:'), chalk.cyan(`${RESOURCES.components.length} components`));
133
- console.log(chalk.gray(' Examples:'), chalk.cyan(`${examplesAll.count} spartan-ng examples`));
134
225
 
135
226
  console.log(chalk.yellow('\nNext steps:'));
136
- console.log(chalk.gray(' 1. Run'), chalk.cyan('grg list'), chalk.gray('to see available blocks'));
137
- console.log(chalk.gray(' 2. Add blocks with'), chalk.cyan('grg add <block-name>'));
227
+ console.log(chalk.gray(' 1.'), chalk.cyan(`cd ${projectName}`));
228
+ console.log(chalk.gray(' 2. Run'), chalk.cyan('grg list blocks'), chalk.gray('to see available blocks'));
229
+ console.log(chalk.gray(' 3. Add blocks with'), chalk.cyan('grg add block --auth'));
138
230
  console.log();
139
231
  }
140
232
 
@@ -156,13 +156,13 @@ const RESOURCES = {
156
156
  "prefix": "grg"
157
157
  }
158
158
  ],
159
- "layouts": [
159
+ "blocks": [
160
160
  {
161
161
  "name": "auth",
162
- "title": "Auth Layout",
162
+ "title": "Auth Block",
163
163
  "description": "Authentication pages layout (login, signup, forgot password)",
164
164
  "path": "templates/ui/layouts/auth",
165
- "defaultOutput": "src/app/layouts/auth",
165
+ "defaultOutput": "src/app/blocks/auth",
166
166
  "tags": [
167
167
  "auth",
168
168
  "login",
@@ -177,20 +177,24 @@ const RESOURCES = {
177
177
  },
178
178
  {
179
179
  "name": "settings",
180
- "title": "Settings Layout",
181
- "description": "settings layout",
180
+ "title": "Settings Block",
181
+ "description": "settings block",
182
182
  "path": "templates/ui/layouts/settings",
183
- "defaultOutput": "src/app/layouts/settings",
184
- "tags": [],
183
+ "defaultOutput": "src/app/blocks/settings",
184
+ "tags": [
185
+ "settings"
186
+ ],
185
187
  "dependencies": []
186
188
  },
187
189
  {
188
190
  "name": "shell",
189
- "title": "Shell Layout",
190
- "description": "shell layout",
191
+ "title": "Shell Block",
192
+ "description": "shell block",
191
193
  "path": "templates/ui/layouts/shell",
192
- "defaultOutput": "src/app/layouts/shell",
193
- "tags": [],
194
+ "defaultOutput": "src/app/blocks/shell",
195
+ "tags": [
196
+ "shell"
197
+ ],
194
198
  "dependencies": []
195
199
  }
196
200
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grg-kit-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "CLI tool for pulling GRG Kit resources into your Angular project",
5
5
  "main": "index.js",
6
6
  "bin": {
package/scripts/README.md CHANGED
@@ -16,7 +16,7 @@ Ensures the CLI always has up-to-date resource definitions based on what's actua
16
16
 
17
17
  ```bash
18
18
  # Run manually
19
- npm run generate
19
+ pnpm run generate
20
20
 
21
21
  # Or directly
22
22
  node scripts/generate-resources.js
@@ -26,7 +26,7 @@ node scripts/generate-resources.js
26
26
 
27
27
  The script runs automatically before publishing:
28
28
  ```bash
29
- npm publish # Runs prepublishOnly hook → npm run generate
29
+ pnpm publish # Runs prepublishOnly hook → pnpm run generate
30
30
  ```
31
31
 
32
32
  ### What It Does
@@ -96,6 +96,6 @@ const COMPONENT_METADATA = {
96
96
 
97
97
  1. Add new resource to `templates/` directory
98
98
  2. (Optional) Add metadata to script constants
99
- 3. Run `npm run generate`
99
+ 3. Run `pnpm run generate`
100
100
  4. Test with `grg list` or `grg add`
101
- 5. Publish with `npm publish` (auto-generates)
101
+ 5. Publish with `pnpm publish` (auto-generates)
@@ -50,8 +50,8 @@ const COMPONENT_METADATA = {
50
50
  }
51
51
  };
52
52
 
53
- // Layout metadata
54
- const LAYOUT_METADATA = {
53
+ // Block metadata (formerly layouts)
54
+ const BLOCK_METADATA = {
55
55
  'dashboard': {
56
56
  description: 'Full dashboard layout with sidebar and header',
57
57
  tags: ['dashboard', 'admin', 'sidebar', 'navigation'],
@@ -187,25 +187,25 @@ function generateComponents() {
187
187
  });
188
188
  }
189
189
 
190
- function generateLayouts() {
191
- const layoutsDir = path.join(TEMPLATES_DIR, 'ui/layouts');
192
- const dirs = scanDirectory(layoutsDir);
190
+ function generateBlocks() {
191
+ const blocksDir = path.join(TEMPLATES_DIR, 'ui/layouts');
192
+ const dirs = scanDirectory(blocksDir);
193
193
 
194
194
  return dirs
195
195
  .filter(dir => dir.isDirectory())
196
196
  .map(dir => {
197
- const metadata = LAYOUT_METADATA[dir.name] || {
198
- description: `${dir.name} layout`,
199
- tags: [],
197
+ const metadata = BLOCK_METADATA[dir.name] || {
198
+ description: `${dir.name} block`,
199
+ tags: [dir.name],
200
200
  dependencies: []
201
201
  };
202
202
 
203
203
  return {
204
204
  name: dir.name,
205
- title: dir.name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ') + ' Layout',
205
+ title: dir.name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ') + ' Block',
206
206
  description: metadata.description,
207
207
  path: `templates/ui/layouts/${dir.name}`,
208
- defaultOutput: `src/app/layouts/${dir.name}`,
208
+ defaultOutput: `src/app/blocks/${dir.name}`,
209
209
  tags: metadata.tags,
210
210
  dependencies: metadata.dependencies
211
211
  };
@@ -253,12 +253,12 @@ function generateResourcesFile() {
253
253
 
254
254
  const themes = generateThemes();
255
255
  const components = generateComponents();
256
- const layouts = generateLayouts();
256
+ const blocks = generateBlocks();
257
257
  const examples = generateExamples();
258
258
 
259
259
  console.log(`✓ Found ${themes.length} themes`);
260
260
  console.log(`✓ Found ${components.length} components`);
261
- console.log(`✓ Found ${layouts.length} layouts`);
261
+ console.log(`✓ Found ${blocks.length} blocks`);
262
262
  console.log(`✓ Found ${examples.components.length} example components`);
263
263
 
264
264
  const output = `/**
@@ -267,7 +267,7 @@ function generateResourcesFile() {
267
267
  * Run: node scripts/generate-resources.js to update
268
268
  */
269
269
 
270
- const RESOURCES = ${JSON.stringify({ themes, components, layouts, examples }, null, 2)};
270
+ const RESOURCES = ${JSON.stringify({ themes, components, blocks, examples }, null, 2)};
271
271
 
272
272
  const REPO = 'Genesis-Research/grg-kit';
273
273
 
@@ -279,7 +279,7 @@ module.exports = { RESOURCES, REPO };
279
279
  console.log('\n📦 Resource Summary:');
280
280
  console.log(` Themes: ${themes.length}`);
281
281
  console.log(` Components: ${components.length}`);
282
- console.log(` Layouts: ${layouts.length}`);
282
+ console.log(` Blocks: ${blocks.length}`);
283
283
  console.log(` Examples: ${examples.components.length}`);
284
284
  }
285
285