slicejs-cli 3.3.0 → 3.4.1

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.
Files changed (46) hide show
  1. package/AGENTS.md +247 -0
  2. package/LICENSE +21 -21
  3. package/client.js +663 -626
  4. package/commands/Print.js +163 -167
  5. package/commands/Validations.js +92 -103
  6. package/commands/build/build.js +40 -40
  7. package/commands/buildProduction/buildProduction.js +576 -579
  8. package/commands/bundle/bundle.js +234 -235
  9. package/commands/createComponent/VisualComponentTemplate.js +55 -55
  10. package/commands/createComponent/createComponent.js +124 -126
  11. package/commands/deleteComponent/deleteComponent.js +77 -77
  12. package/commands/doctor/doctor.js +366 -369
  13. package/commands/getComponent/getComponent.js +684 -747
  14. package/commands/init/init.js +269 -261
  15. package/commands/listComponents/listComponents.js +172 -175
  16. package/commands/startServer/startServer.js +261 -264
  17. package/commands/startServer/watchServer.js +79 -79
  18. package/commands/types/types.js +69 -27
  19. package/commands/utils/LocalCliDelegation.js +53 -53
  20. package/commands/utils/PathHelper.js +75 -68
  21. package/commands/utils/VersionChecker.js +167 -167
  22. package/commands/utils/bundling/BundleGenerator.js +2292 -2292
  23. package/commands/utils/bundling/DependencyAnalyzer.js +925 -933
  24. package/commands/utils/loadConfig.js +31 -0
  25. package/commands/utils/updateManager.js +452 -453
  26. package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +105 -105
  27. package/package.json +58 -46
  28. package/post.js +66 -65
  29. package/tests/bundle-generator.test.js +691 -708
  30. package/tests/bundle-v2-register-output.test.js +470 -470
  31. package/tests/client-launcher-contract.test.js +211 -211
  32. package/tests/client-update-flow-contract.test.js +272 -272
  33. package/tests/component-registry-parse.test.js +34 -0
  34. package/tests/dependency-analyzer.test.js +24 -24
  35. package/tests/fixtures/components.js +8 -0
  36. package/tests/fixtures/sliceConfig.json +74 -0
  37. package/tests/getcomponent.test.js +407 -0
  38. package/tests/helpers/setup.js +97 -0
  39. package/tests/init-command-contract.test.js +46 -0
  40. package/tests/local-cli-delegation.test.js +81 -79
  41. package/tests/path-helper.test.js +206 -0
  42. package/tests/types-breakage.test.js +491 -0
  43. package/tests/types-generator-errors.test.js +361 -0
  44. package/tests/types-generator.test.js +172 -184
  45. package/tests/update-manager-notifications.test.js +88 -88
  46. package/.github/workflows/docs-render-cicd.yml +0 -65
@@ -1,261 +1,269 @@
1
- import fs from 'fs-extra';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import ora from 'ora';
5
- import Print from '../Print.js';
6
- import { getProjectRoot, getApiPath, getSrcPath } from '../utils/PathHelper.js';
7
- import { execSync } from 'child_process';
8
-
9
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
-
11
- // Importar la clase ComponentRegistry del getComponent
12
- import { ComponentRegistry } from '../getComponent/getComponent.js';
13
-
14
- export default async function initializeProject(projectType) {
15
- try {
16
- const projectRoot = getProjectRoot(import.meta.url);
17
- const destinationApi = getApiPath(import.meta.url);
18
- const destinationSrc = getSrcPath(import.meta.url);
19
-
20
- const fwSpinner = ora('Ensuring latest Slice framework...').start();
21
- let latestVersion = null;
22
- let sliceBaseDir;
23
- try {
24
- const latest = execSync('npm view slicejs-web-framework version', { cwd: projectRoot }).toString().trim();
25
- latestVersion = latest;
26
- const installedPkgPath = path.join(projectRoot, 'node_modules', 'slicejs-web-framework', 'package.json');
27
- let installed = null;
28
- if (await fs.pathExists(installedPkgPath)) {
29
- const pkg = await fs.readJson(installedPkgPath);
30
- installed = pkg.version;
31
- }
32
- if (installed !== latest) {
33
- execSync(`npm install slicejs-web-framework@${latest} --save`, { cwd: projectRoot, stdio: 'inherit' });
34
- }
35
- sliceBaseDir = path.join(projectRoot, 'node_modules', 'slicejs-web-framework');
36
- fwSpinner.succeed(`slicejs-web-framework@${latest} ready`);
37
- } catch (err) {
38
- const fallback = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../slicejs-web-framework');
39
- if (await fs.pathExists(fallback)) {
40
- sliceBaseDir = fallback;
41
- fwSpinner.warn('Using local slicejs-web-framework fallback');
42
- } else {
43
- fwSpinner.fail('Failed to ensure latest slicejs-web-framework');
44
- Print.error(err.message);
45
- return;
46
- }
47
- }
48
-
49
- const apiDir = path.join(sliceBaseDir, 'api');
50
- const srcDir = path.join(sliceBaseDir, 'src');
51
-
52
- try {
53
- if (fs.existsSync(destinationApi)) throw new Error(`The "api" directory already exists: ${destinationApi}`);
54
- if (fs.existsSync(destinationSrc)) throw new Error(`The "src" directory already exists: ${destinationSrc}`);
55
- } catch (error) {
56
- Print.error('Validating destination directories:', error.message);
57
- return;
58
- }
59
-
60
- // 1. COPIAR LA CARPETA API (mantener lógica original)
61
- const apiSpinner = ora('Copying API structure...').start();
62
- try {
63
- if (!fs.existsSync(apiDir)) throw new Error(`API folder not found: ${apiDir}`);
64
- await fs.copy(apiDir, destinationApi, { recursive: true });
65
- apiSpinner.succeed('API structure created successfully');
66
- } catch (error) {
67
- apiSpinner.fail('Error copying API structure');
68
- Print.error(error.message);
69
- return;
70
- }
71
-
72
- // 2. CREAR ESTRUCTURA SRC BÁSICA (sin copiar componentes Visual)
73
- const srcSpinner = ora('Creating src structure...').start();
74
- try {
75
- if (!fs.existsSync(srcDir)) throw new Error(`src folder not found: ${srcDir}`);
76
-
77
- // Copiar solo los archivos base de src, excluyendo Components/Visual
78
- await fs.ensureDir(destinationSrc);
79
-
80
- // Copiar archivos y carpetas de src excepto Components/Visual
81
- const srcItems = await fs.readdir(srcDir);
82
-
83
- for (const item of srcItems) {
84
- const srcItemPath = path.join(srcDir, item);
85
- const destItemPath = path.join(destinationSrc, item);
86
- const stat = await fs.stat(srcItemPath);
87
-
88
- if (stat.isDirectory()) {
89
- if (item === 'Components') {
90
- // Crear estructura de Components pero sin copiar Visual
91
- await fs.ensureDir(destItemPath);
92
-
93
- const componentItems = await fs.readdir(srcItemPath);
94
- for (const componentItem of componentItems) {
95
- const componentItemPath = path.join(srcItemPath, componentItem);
96
- const destComponentItemPath = path.join(destItemPath, componentItem);
97
-
98
- if (componentItem !== 'Visual') {
99
- // Copy Service and other component types
100
- await fs.copy(componentItemPath, destComponentItemPath, { recursive: true });
101
- } else {
102
- // Only create empty Visual directory
103
- await fs.ensureDir(destComponentItemPath);
104
- }
105
- }
106
- } else {
107
- // Copy other folders normally
108
- await fs.copy(srcItemPath, destItemPath, { recursive: true });
109
- }
110
- } else {
111
- // Copy files normally
112
- await fs.copy(srcItemPath, destItemPath);
113
- }
114
- }
115
-
116
- srcSpinner.succeed('Source structure created successfully');
117
- } catch (error) {
118
- srcSpinner.fail('Error creating source structure');
119
- Print.error(error.message);
120
- return;
121
- }
122
-
123
- // 3. DESCARGAR TODOS LOS COMPONENTES VISUAL DESDE EL REPOSITORIO OFICIAL
124
- const componentsSpinner = ora('Loading component registry...').start();
125
- try {
126
- const registry = new ComponentRegistry();
127
- await registry.loadRegistry();
128
-
129
- // Obtener TODOS los componentes Visual disponibles
130
- const allVisualComponents = await getAllVisualComponents(registry);
131
-
132
- if (allVisualComponents.length > 0) {
133
- componentsSpinner.text = `Installing ${allVisualComponents.length} Visual components...`;
134
-
135
- const results = await registry.installMultipleComponents(
136
- allVisualComponents,
137
- 'Visual',
138
- true // force = true para instalación inicial
139
- );
140
-
141
- const successful = results.filter(r => r.success).length;
142
- const failed = results.filter(r => !r.success).length;
143
-
144
- if (successful > 0 && failed === 0) {
145
- componentsSpinner.succeed(`All ${successful} Visual components installed successfully`);
146
- } else if (successful > 0) {
147
- componentsSpinner.warn(`${successful} components installed, ${failed} failed`);
148
- Print.info('You can install failed components later using "slice get <component-name>"');
149
- } else {
150
- componentsSpinner.fail('Failed to install Visual components');
151
- }
152
- } else {
153
- componentsSpinner.warn('No Visual components found in registry');
154
- Print.info('You can add components later using "slice get <component-name>"');
155
- }
156
-
157
- } catch (error) {
158
- componentsSpinner.fail('Could not download Visual components from official repository');
159
- Print.error(`Repository error: ${error.message}`);
160
- Print.info('Project initialized without Visual components');
161
- Print.info('You can add them later using "slice get <component-name>"');
162
- }
163
-
164
- // 4. CONFIGURAR SCRIPTS EN package.json DEL PROYECTO
165
- const pkgSpinner = ora('Configuring npm scripts...').start();
166
- try {
167
- const projectRoot = getProjectRoot(import.meta.url);
168
- const pkgPath = path.join(projectRoot, 'package.json');
169
-
170
- let pkg;
171
- if (await fs.pathExists(pkgPath)) {
172
- pkg = await fs.readJson(pkgPath);
173
- } else {
174
- pkg = {
175
- name: path.basename(projectRoot),
176
- version: '1.0.0',
177
- description: 'Slice.js project',
178
- main: 'api/index.js',
179
- scripts: {}
180
- };
181
- }
182
-
183
- pkg.scripts = pkg.scripts || {};
184
- pkg.dependencies = pkg.dependencies || {};
185
-
186
- // Comandos principales
187
- pkg.scripts['dev'] = 'slice dev';
188
- pkg.scripts['start'] = 'slice start';
189
-
190
- // Gestión de componentes
191
- pkg.scripts['component:create'] = 'slice component create';
192
- pkg.scripts['component:list'] = 'slice component list';
193
- pkg.scripts['component:delete'] = 'slice component delete';
194
-
195
- // Atajos de repositorio
196
- pkg.scripts['get'] = 'slice get';
197
- pkg.scripts['browse'] = 'slice browse';
198
- pkg.scripts['sync'] = 'slice sync';
199
-
200
- // Utilidades
201
- pkg.scripts['slice:version'] = 'slice version';
202
- pkg.scripts['slice:update'] = 'slice update';
203
-
204
- // Legacy (compatibilidad)
205
- pkg.scripts['slice:init'] = 'slice init';
206
- pkg.scripts['slice:start'] = 'slice start';
207
- pkg.scripts['slice:dev'] = 'slice dev';
208
- pkg.scripts['slice:create'] = 'slice component create';
209
- pkg.scripts['slice:list'] = 'slice component list';
210
- pkg.scripts['slice:delete'] = 'slice component delete';
211
- pkg.scripts['slice:get'] = 'slice get';
212
- pkg.scripts['slice:browse'] = 'slice browse';
213
- pkg.scripts['slice:sync'] = 'slice sync';
214
- pkg.scripts['run'] = 'slice dev';
215
-
216
- // Configuración de módulo
217
- pkg.type = 'module';
218
- pkg.engines = pkg.engines || { node: '>=20.0.0' };
219
-
220
- // Ensure framework dependency is present
221
- if (!pkg.dependencies['slicejs-web-framework']) {
222
- pkg.dependencies['slicejs-web-framework'] = latestVersion ? latestVersion : 'latest';
223
- }
224
-
225
- await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
226
- pkgSpinner.succeed('npm scripts configured successfully');
227
-
228
- console.log('\n🎯 New recommended commands:');
229
- console.log(' npm run dev - Start development server');
230
- console.log(' npm run get - Install components');
231
- console.log(' npm run browse - Browse components');
232
- } catch (error) {
233
- pkgSpinner.fail('Failed to configure npm scripts');
234
- Print.error(error.message);
235
- }
236
-
237
- Print.success('Project initialized successfully.');
238
- Print.newLine();
239
- Print.info('Next steps:');
240
- console.log(' slice browse - View available components');
241
- console.log(' slice get Button - Install specific components');
242
- console.log(' slice sync - Update all components to latest versions');
243
-
244
- } catch (error) {
245
- Print.error('Unexpected error initializing project:', error.message);
246
- }
247
- }
248
-
249
- /**
250
- * Obtiene TODOS los componentes Visual disponibles en el registry
251
- * @param {ComponentRegistry} registry - Instancia del registry cargado
252
- * @returns {Array} - Array con todos los nombres de componentes Visual
253
- */
254
- async function getAllVisualComponents(registry) {
255
- const availableComponents = registry.getAvailableComponents('Visual');
256
- const allVisualComponents = Object.keys(availableComponents);
257
-
258
- Print.info(`Found ${allVisualComponents.length} Visual components in official repository`);
259
-
260
- return allVisualComponents;
261
- }
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import ora from 'ora';
5
+ import Print from '../Print.js';
6
+ import { getProjectRoot, getApiPath, getSrcPath, getPath } from '../utils/PathHelper.js';
7
+ import { execSync } from 'child_process';
8
+
9
+ // Import ComponentRegistry class from getComponent
10
+ import { ComponentRegistry } from '../getComponent/getComponent.js';
11
+
12
+ // Visual components used by the App Shell + MultiRoute starter project.
13
+ // We install only these on init; newcomers add more on demand with `slice get <Name>`.
14
+ const STARTER_VISUAL_COMPONENTS = [
15
+ 'Button',
16
+ 'Link',
17
+ 'Loading',
18
+ 'MultiRoute',
19
+ 'Navbar',
20
+ 'NotFound',
21
+ 'Route'
22
+ ];
23
+
24
+ export default async function initializeProject(projectType) {
25
+ try {
26
+ const projectRoot = getProjectRoot(import.meta.url);
27
+ const destinationApi = getApiPath(import.meta.url);
28
+ const destinationSrc = getSrcPath(import.meta.url);
29
+
30
+ const fwSpinner = ora('Ensuring latest Slice framework...').start();
31
+ let latestVersion = null;
32
+ let sliceBaseDir;
33
+ try {
34
+ const latest = execSync('npm view slicejs-web-framework version', { cwd: projectRoot }).toString().trim();
35
+ latestVersion = latest;
36
+ const installedPkgPath = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework', 'package.json');
37
+ let installed = null;
38
+ if (await fs.pathExists(installedPkgPath)) {
39
+ const pkg = await fs.readJson(installedPkgPath);
40
+ installed = pkg.version;
41
+ }
42
+ if (installed !== latest) {
43
+ execSync(`npm install slicejs-web-framework@${latest} --save`, { cwd: projectRoot, stdio: 'inherit' });
44
+ }
45
+ sliceBaseDir = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework');
46
+ fwSpinner.succeed(`slicejs-web-framework@${latest} ready`);
47
+ } catch (err) {
48
+ // Fallback uses __dirname-style path because it looks for a local development copy,
49
+ // not a project-relative path — npm install failed, so we fall back to monorepo sibling.
50
+ const fallback = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../slicejs-web-framework');
51
+ if (await fs.pathExists(fallback)) {
52
+ sliceBaseDir = fallback;
53
+ fwSpinner.warn('Using local slicejs-web-framework fallback');
54
+ } else {
55
+ fwSpinner.fail('Failed to ensure latest slicejs-web-framework');
56
+ Print.error(err.message);
57
+ return;
58
+ }
59
+ }
60
+
61
+ // These derive from sliceBaseDir (which comes from npm install or fallback),
62
+ // so they're already dynamic — no PathHelper needed.
63
+ const apiDir = path.join(sliceBaseDir, 'api');
64
+ const srcDir = path.join(sliceBaseDir, 'src');
65
+
66
+ try {
67
+ if (fs.existsSync(destinationApi)) throw new Error(`The "api" directory already exists: ${destinationApi}`);
68
+ if (fs.existsSync(destinationSrc)) throw new Error(`The "src" directory already exists: ${destinationSrc}`);
69
+ } catch (error) {
70
+ Print.error('Validating destination directories:', error.message);
71
+ return;
72
+ }
73
+
74
+ // 1. COPY API FOLDER (keep original logic)
75
+ const apiSpinner = ora('Copying API structure...').start();
76
+ try {
77
+ if (!fs.existsSync(apiDir)) throw new Error(`API folder not found: ${apiDir}`);
78
+ await fs.copy(apiDir, destinationApi, { recursive: true });
79
+ apiSpinner.succeed('API structure created successfully');
80
+ } catch (error) {
81
+ apiSpinner.fail('Error copying API structure');
82
+ Print.error(error.message);
83
+ return;
84
+ }
85
+
86
+ // 2. CREATE BASIC SRC STRUCTURE (without copying Visual components)
87
+ const srcSpinner = ora('Creating src structure...').start();
88
+ try {
89
+ if (!fs.existsSync(srcDir)) throw new Error(`src folder not found: ${srcDir}`);
90
+
91
+ // Copy only base src files, excluding Components/Visual
92
+ await fs.ensureDir(destinationSrc);
93
+
94
+ // Copy src files and folders except Components/Visual
95
+ const srcItems = await fs.readdir(srcDir);
96
+
97
+ for (const item of srcItems) {
98
+ const srcItemPath = path.join(srcDir, item);
99
+ const destItemPath = path.join(destinationSrc, item);
100
+ const stat = await fs.stat(srcItemPath);
101
+
102
+ if (stat.isDirectory()) {
103
+ if (item === 'Components') {
104
+ // Create Components structure but without copying Visual
105
+ await fs.ensureDir(destItemPath);
106
+
107
+ const componentItems = await fs.readdir(srcItemPath);
108
+ for (const componentItem of componentItems) {
109
+ const componentItemPath = path.join(srcItemPath, componentItem);
110
+ const destComponentItemPath = path.join(destItemPath, componentItem);
111
+
112
+ if (componentItem !== 'Visual') {
113
+ // Copy Service and other component types
114
+ await fs.copy(componentItemPath, destComponentItemPath, { recursive: true });
115
+ } else {
116
+ // Only create empty Visual directory
117
+ await fs.ensureDir(destComponentItemPath);
118
+ }
119
+ }
120
+ } else {
121
+ // Copy other folders normally
122
+ await fs.copy(srcItemPath, destItemPath, { recursive: true });
123
+ }
124
+ } else {
125
+ // Copy files normally
126
+ await fs.copy(srcItemPath, destItemPath);
127
+ }
128
+ }
129
+
130
+ srcSpinner.succeed('Source structure created successfully');
131
+ } catch (error) {
132
+ srcSpinner.fail('Error creating source structure');
133
+ Print.error(error.message);
134
+ return;
135
+ }
136
+
137
+ // 3. DOWNLOAD ALL VISUAL COMPONENTS FROM OFFICIAL REPOSITORY
138
+ const componentsSpinner = ora('Loading component registry...').start();
139
+ try {
140
+ const registry = new ComponentRegistry();
141
+ await registry.loadRegistry();
142
+
143
+ // Install only the Visual components the starter project uses.
144
+ const allVisualComponents = STARTER_VISUAL_COMPONENTS;
145
+ Print.info(`Installing ${allVisualComponents.length} starter Visual components: ${allVisualComponents.join(', ')}`);
146
+
147
+ if (allVisualComponents.length > 0) {
148
+ componentsSpinner.text = `Installing ${allVisualComponents.length} starter Visual components...`;
149
+
150
+ const results = await registry.installMultipleComponents(
151
+ allVisualComponents,
152
+ 'Visual',
153
+ true // force = true for initial installation
154
+ );
155
+
156
+ const successful = results.filter(r => r.success).length;
157
+ const failed = results.filter(r => !r.success).length;
158
+
159
+ if (successful > 0 && failed === 0) {
160
+ componentsSpinner.succeed(`All ${successful} Visual components installed successfully`);
161
+ } else if (successful > 0) {
162
+ componentsSpinner.warn(`${successful} components installed, ${failed} failed`);
163
+ Print.info('You can install failed components later using "slice get <component-name>"');
164
+ } else {
165
+ componentsSpinner.fail('Failed to install Visual components');
166
+ }
167
+ } else {
168
+ componentsSpinner.warn('No Visual components found in registry');
169
+ Print.info('You can add components later using "slice get <component-name>"');
170
+ }
171
+
172
+ } catch (error) {
173
+ componentsSpinner.fail('Could not download Visual components from official repository');
174
+ Print.error(`Repository error: ${error.message}`);
175
+ Print.info('Project initialized without Visual components');
176
+ Print.info('You can add them later using "slice get <component-name>"');
177
+ }
178
+
179
+ // 4. CONFIGURE SCRIPTS IN PROJECT package.json
180
+ const pkgSpinner = ora('Configuring npm scripts...').start();
181
+ try {
182
+ const projectRoot = getProjectRoot(import.meta.url);
183
+ const pkgPath = getPath(import.meta.url, 'package.json');
184
+
185
+ let pkg;
186
+ if (await fs.pathExists(pkgPath)) {
187
+ pkg = await fs.readJson(pkgPath);
188
+ } else {
189
+ pkg = {
190
+ name: path.basename(projectRoot),
191
+ version: '1.0.0',
192
+ description: 'Slice.js project',
193
+ main: 'api/index.js',
194
+ scripts: {}
195
+ };
196
+ }
197
+
198
+ pkg.scripts = pkg.scripts || {};
199
+ pkg.dependencies = pkg.dependencies || {};
200
+
201
+ // Comandos principales
202
+ pkg.scripts['dev'] = 'slice dev';
203
+ pkg.scripts['start'] = 'slice start';
204
+
205
+ // Component management
206
+ pkg.scripts['component:create'] = 'slice component create';
207
+ pkg.scripts['component:list'] = 'slice component list';
208
+ pkg.scripts['component:delete'] = 'slice component delete';
209
+
210
+ // Atajos de repositorio
211
+ pkg.scripts['get'] = 'slice get';
212
+ pkg.scripts['browse'] = 'slice browse';
213
+ pkg.scripts['sync'] = 'slice sync';
214
+
215
+ // Utilidades
216
+ pkg.scripts['slice:version'] = 'slice version';
217
+ pkg.scripts['slice:update'] = 'slice update';
218
+ pkg.scripts['slice:types'] = 'slice types generate';
219
+
220
+ // Legacy (compatibility)
221
+ pkg.scripts['slice:init'] = 'slice init';
222
+ pkg.scripts['slice:start'] = 'slice start';
223
+ pkg.scripts['slice:dev'] = 'slice dev';
224
+ pkg.scripts['slice:create'] = 'slice component create';
225
+ pkg.scripts['slice:list'] = 'slice component list';
226
+ pkg.scripts['slice:delete'] = 'slice component delete';
227
+ pkg.scripts['slice:get'] = 'slice get';
228
+ pkg.scripts['slice:browse'] = 'slice browse';
229
+ pkg.scripts['slice:sync'] = 'slice sync';
230
+ pkg.scripts['run'] = 'slice dev';
231
+
232
+ // Module configuration
233
+ pkg.type = 'module';
234
+ pkg.engines = pkg.engines || { node: '>=20.0.0' };
235
+
236
+ // Ensure framework dependency is present
237
+ if (!pkg.dependencies['slicejs-web-framework']) {
238
+ pkg.dependencies['slicejs-web-framework'] = latestVersion ? latestVersion : 'latest';
239
+ }
240
+
241
+ await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
242
+ pkgSpinner.succeed('npm scripts configured successfully');
243
+
244
+ Print.title('New recommended commands:');
245
+ console.log(' npm run dev - Start development server');
246
+ console.log(' npm run get - Install components');
247
+ console.log(' npm run browse - Browse components');
248
+ } catch (error) {
249
+ pkgSpinner.fail('Failed to configure npm scripts');
250
+ Print.error(error.message);
251
+ }
252
+
253
+ const projectName = path.basename(process.cwd());
254
+ Print.success(`Project initialized successfully in "${projectName}/"`);
255
+ Print.newLine();
256
+ Print.title('Next steps:');
257
+ console.log(` cd ${projectName}`);
258
+ console.log(' slice browse - View available components');
259
+ console.log(' slice get Button - Install specific components');
260
+ console.log(' slice sync - Update all components to latest versions');
261
+
262
+ } catch (error) {
263
+ Print.error('Unexpected error initializing project:', error.message);
264
+ }
265
+ }
266
+
267
+ // NOTE: `slice init` now installs only STARTER_VISUAL_COMPONENTS (see top of file).
268
+ // To install every registry component instead, iterate
269
+ // `Object.keys(registry.getAvailableComponents('Visual'))`.