@nitrostack/cli 1.0.6 → 1.0.7

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.
@@ -1,24 +1,21 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import { dirname } from 'path';
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
3
+ import { createHeader, createSuccessBox, NitroSpinner, spacer } from '../ui/branding.js';
7
4
  /**
8
5
  * Generates TypeScript types from tool definitions
9
- * Scans all *.tools.ts files and extracts types from Zod schemas and examples
10
6
  */
11
7
  export async function generateTypes(options = {}) {
12
8
  const projectRoot = process.cwd();
13
9
  const outputPath = options.output || path.join(projectRoot, 'src', 'types', 'generated-tools.ts');
14
- console.log('🔍 Scanning for tool definitions...');
10
+ console.log(createHeader('Types', 'Generate TypeScript definitions'));
11
+ const spinner = new NitroSpinner('Scanning for tool definitions...').start();
15
12
  // Find all *.tools.ts files
16
13
  const toolFiles = await findToolFiles(path.join(projectRoot, 'src'));
17
14
  if (toolFiles.length === 0) {
18
- console.log('⚠️ No tool files found');
15
+ spinner.fail('No tool files found');
19
16
  return;
20
17
  }
21
- console.log(`📝 Found ${toolFiles.length} tool file(s)`);
18
+ spinner.update(`Found ${toolFiles.length} tool file(s)`);
22
19
  // Parse tools and generate types
23
20
  const types = await generateTypesFromFiles(toolFiles);
24
21
  // Write output file
@@ -27,7 +24,12 @@ export async function generateTypes(options = {}) {
27
24
  fs.mkdirSync(outputDir, { recursive: true });
28
25
  }
29
26
  fs.writeFileSync(outputPath, types);
30
- console.log(`✅ Generated types at: ${outputPath}`);
27
+ spinner.succeed('Type generation complete');
28
+ spacer();
29
+ console.log(createSuccessBox('Types Generated', [
30
+ `Location: ${path.relative(projectRoot, outputPath)}`,
31
+ `Count: ${toolFiles.length} tools processed`
32
+ ]));
31
33
  }
32
34
  async function findToolFiles(dir) {
33
35
  const files = [];
@@ -135,9 +137,7 @@ function toPascalCase(str) {
135
137
  }
136
138
  function generateTypeFromZodSchema(zodContent) {
137
139
  // Simple parser for basic Zod schemas
138
- // This is a simplified version - in production, you might want to use actual Zod parsing
139
140
  const fields = [];
140
- // Match field definitions like: field_name: z.string().describe('...')
141
141
  const fieldRegex = /(\w+):\s*z\.(\w+)\(\)(?:\.optional\(\))?(?:\.describe\([^)]*\))?/g;
142
142
  let match;
143
143
  while ((match = fieldRegex.exec(zodContent)) !== null) {
@@ -156,14 +156,7 @@ function generateTypeFromZodSchema(zodContent) {
156
156
  tsType = 'boolean';
157
157
  break;
158
158
  case 'enum':
159
- // Extract enum values if possible
160
- const enumMatch = zodContent.match(new RegExp(`${fieldName}:\\s*z\\.enum\\(\\[([^\\]]+)\\]\\)`));
161
- if (enumMatch) {
162
- tsType = enumMatch[1].replace(/'/g, '"');
163
- }
164
- else {
165
- tsType = 'string';
166
- }
159
+ tsType = 'string';
167
160
  break;
168
161
  case 'array':
169
162
  tsType = 'unknown[]';
@@ -178,11 +171,7 @@ function generateTypeFromZodSchema(zodContent) {
178
171
  }
179
172
  function generateTypeFromExample(exampleJson) {
180
173
  try {
181
- // Parse the example JSON to infer types
182
- // This is a simplified version
183
- const cleaned = exampleJson
184
- .replace(/\/\/.*/g, '') // Remove comments
185
- .trim();
174
+ const cleaned = exampleJson.replace(/\/\/.*/g, '').trim();
186
175
  const obj = eval('(' + cleaned + ')');
187
176
  return generateTypeFromObject(obj);
188
177
  }
@@ -191,29 +180,19 @@ function generateTypeFromExample(exampleJson) {
191
180
  }
192
181
  }
193
182
  function generateTypeFromObject(obj, indent = ' ') {
194
- if (obj === null || obj === undefined) {
183
+ if (obj === null || obj === undefined)
195
184
  return 'unknown';
196
- }
197
185
  if (Array.isArray(obj)) {
198
- if (obj.length === 0) {
186
+ if (obj.length === 0)
199
187
  return 'unknown[]';
200
- }
201
- const itemType = generateTypeFromObject(obj[0], indent + ' ');
202
- return `Array<${itemType}>`;
188
+ return `Array<${generateTypeFromObject(obj[0], indent + ' ')}>`;
203
189
  }
204
190
  if (typeof obj === 'object') {
205
191
  const fields = [];
206
192
  for (const [key, value] of Object.entries(obj)) {
207
- const type = generateTypeFromObject(value, indent + ' ');
208
- fields.push(`${key}: ${type}`);
193
+ fields.push(`${key}: ${generateTypeFromObject(value, indent + ' ')}`);
209
194
  }
210
195
  return `{\n${indent}${fields.join(`;\n${indent}`)};\n${indent.slice(2)}}`;
211
196
  }
212
- if (typeof obj === 'string')
213
- return 'string';
214
- if (typeof obj === 'number')
215
- return 'number';
216
- if (typeof obj === 'boolean')
217
- return 'boolean';
218
- return 'unknown';
197
+ return typeof obj;
219
198
  }
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AA8TA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6FvG"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAgUA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsGvG"}
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
- import { createHeader, createSuccessBox, createErrorBox, log, spacer, nextSteps } from '../ui/branding.js';
4
+ import { createHeader, createSuccessBox, createErrorBox, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
5
5
  /**
6
6
  * Generator templates
7
7
  */
@@ -295,6 +295,7 @@ function getFilePath(type, name, module) {
295
295
  * Generate command
296
296
  */
297
297
  export async function generate(type, name, options = {}) {
298
+ console.log(NITRO_BANNER_FULL);
298
299
  // Special case for types generation
299
300
  if (type === 'types') {
300
301
  const { generateTypes } = await import('./generate-types.js');
@@ -305,6 +306,7 @@ export async function generate(type, name, options = {}) {
305
306
  // Validate type
306
307
  const validTypes = Object.keys(TEMPLATES);
307
308
  if (!validTypes.includes(type)) {
309
+ spacer();
308
310
  console.log(createErrorBox('Invalid Type', `Valid types: ${validTypes.join(', ')}, types`));
309
311
  process.exit(1);
310
312
  }
@@ -332,9 +334,9 @@ export async function generate(type, name, options = {}) {
332
334
  }
333
335
  // Check if file already exists
334
336
  if (fs.existsSync(filePath) && !options.force) {
335
- console.log(createErrorBox('File Exists', 'Use --force to overwrite'));
336
337
  spacer();
337
- log(`Path: ${filePath}`, 'dim');
338
+ console.log(createErrorBox('File Exists', 'Use --force to overwrite if you want to replace the existing file.'));
339
+ log(`Path: ${path.relative(process.cwd(), filePath)}`, 'dim');
338
340
  process.exit(1);
339
341
  }
340
342
  // Write file
@@ -366,10 +368,16 @@ export async function generate(type, name, options = {}) {
366
368
  }
367
369
  // Summary
368
370
  spacer();
369
- console.log(createSuccessBox('Generation Complete', generatedFiles));
371
+ console.log(createSuccessBox('Generation Complete', [
372
+ ...generatedFiles,
373
+ '',
374
+ chalk.dim(`Type: ${type}`),
375
+ chalk.dim(`Name: ${name}`)
376
+ ]));
370
377
  nextSteps([
371
378
  'Open the generated file and implement the TODOs',
372
379
  'Register the component in your module',
373
380
  'Build and test your changes',
374
381
  ]);
382
+ showFooter();
375
383
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAiDA,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA+MtF"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoDA,UAAU,WAAW;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBAgNtF"}
@@ -5,7 +5,7 @@ import { dirname } from 'path';
5
5
  import chalk from 'chalk';
6
6
  import inquirer from 'inquirer';
7
7
  import { execSync } from 'child_process';
8
- import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand } from '../ui/branding.js';
8
+ import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand, showFooter } from '../ui/branding.js';
9
9
  // ES module equivalent of __dirname
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = dirname(__filename);
@@ -40,7 +40,7 @@ export async function initCommand(projectName, options) {
40
40
  try {
41
41
  // Show banner
42
42
  console.log(NITRO_BANNER_FULL);
43
- console.log(chalk.dim(' Create a new MCP server project\n'));
43
+ spacer();
44
44
  // Prompt for project name if not provided
45
45
  if (!projectName) {
46
46
  const answers = await inquirer.prompt([
@@ -85,15 +85,15 @@ export async function initCommand(projectName, options) {
85
85
  message: chalk.white('Choose a template:'),
86
86
  choices: [
87
87
  {
88
- name: `${brand.accent('Starter')} ${chalk.dim('Simple calculator for learning basics')}`,
88
+ name: `${brand.signal('Starter')} ${chalk.dim('Simple calculator for learning basics')}`,
89
89
  value: 'typescript-starter',
90
90
  },
91
91
  {
92
- name: `${brand.accent('Advanced')} ${chalk.dim('Pizza shop finder with maps & widgets')}`,
92
+ name: `${brand.signal('Advanced')} ${chalk.dim('Pizza shop finder with maps & widgets')}`,
93
93
  value: 'typescript-pizzaz',
94
94
  },
95
95
  {
96
- name: `${brand.accent('OAuth')} ${chalk.dim('Flight booking with OAuth 2.1 auth')}`,
96
+ name: `${brand.signal('OAuth')} ${chalk.dim('Flight booking with OAuth 2.1 auth')}`,
97
97
  value: 'typescript-oauth',
98
98
  },
99
99
  ],
@@ -215,6 +215,7 @@ export async function initCommand(projectName, options) {
215
215
  console.log(chalk.dim(' Mapbox (optional): Get free key from mapbox.com\n'));
216
216
  }
217
217
  console.log(chalk.dim(' Happy coding! 🎉\n'));
218
+ showFooter();
218
219
  }
219
220
  catch (error) {
220
221
  if (spinner) {
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAcA,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA+D3E"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAiBA,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAkE3E"}
@@ -1,7 +1,7 @@
1
1
  import { execSync } from 'child_process';
2
2
  import path from 'path';
3
3
  import fs from 'fs-extra';
4
- import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps } from '../ui/branding.js';
4
+ import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
5
5
  /**
6
6
  * Run npm install silently
7
7
  */
@@ -22,6 +22,7 @@ function hasPackageJson(directory) {
22
22
  * Main install command handler
23
23
  */
24
24
  export async function installCommand(options) {
25
+ console.log(NITRO_BANNER_FULL);
25
26
  console.log(createHeader('Install', 'Install project dependencies'));
26
27
  const projectRoot = process.cwd();
27
28
  const rootPackageJsonPath = path.join(projectRoot, 'package.json');
@@ -77,4 +78,5 @@ export async function installCommand(options) {
77
78
  'npm run build - Build for production',
78
79
  'npm run upgrade - Upgrade nitrostack',
79
80
  ]);
81
+ showFooter();
80
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAaA,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBA4EvD"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAgBA,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBA6EvD"}
@@ -2,8 +2,9 @@ import chalk from 'chalk';
2
2
  import { spawn } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
5
- import { createHeader, createInfoBox, createErrorBox, log, spacer, brand } from '../ui/branding.js';
5
+ import { createHeader, createBox, createInfoBox, createErrorBox, log, spacer, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
6
  export async function startCommand(options) {
7
+ console.log(NITRO_BANNER_FULL);
7
8
  console.log(createHeader('Start', 'Production mode'));
8
9
  const port = options.port || '3000';
9
10
  // Check if dist/index.js exists
@@ -53,9 +54,7 @@ export async function startCommand(options) {
53
54
  // Handle graceful shutdown
54
55
  const shutdown = () => {
55
56
  spacer();
56
- console.log(brand.accentBold('┌──────────────────────────────────────────────────────────────────┐'));
57
- console.log(brand.accentBold('│') + chalk.yellow.bold(' Shutting down... ') + brand.accentBold('│'));
58
- console.log(brand.accentBold('└──────────────────────────────────────────────────────────────────┘'));
57
+ console.log(createBox([chalk.yellow.bold('Shutting down...')]));
59
58
  serverProcess.kill('SIGTERM');
60
59
  setTimeout(() => {
61
60
  serverProcess.kill('SIGKILL');
@@ -65,6 +64,7 @@ export async function startCommand(options) {
65
64
  };
66
65
  process.on('SIGINT', shutdown);
67
66
  process.on('SIGTERM', shutdown);
67
+ showFooter();
68
68
  // Keep process alive
69
69
  await new Promise(() => { });
70
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAsBA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA2GD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8I3E"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAyBA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAwHD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqI3E"}
@@ -2,33 +2,33 @@ import chalk from 'chalk';
2
2
  import { execSync } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs-extra';
5
- import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, brand } from '../ui/branding.js';
5
+ import { createHeader, createBox, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
6
  /**
7
- * Get the latest version of nitrostack from npm registry
7
+ * Get the latest version of a package from npm registry
8
8
  */
9
- function getLatestVersion() {
9
+ function getLatestVersion(packageName = '@nitrostack/core') {
10
10
  try {
11
- const result = execSync('npm view nitrostack version', {
11
+ const result = execSync(`npm view ${packageName} version`, {
12
12
  encoding: 'utf-8',
13
13
  stdio: ['pipe', 'pipe', 'pipe'],
14
14
  }).trim();
15
15
  return result;
16
16
  }
17
17
  catch {
18
- throw new Error('Failed to fetch latest version from npm');
18
+ throw new Error(`Failed to fetch latest version for ${packageName} from npm`);
19
19
  }
20
20
  }
21
21
  /**
22
- * Get the current installed version of nitrostack from package.json
22
+ * Get the current installed version of @nitrostack/core from package.json
23
23
  */
24
- function getCurrentVersion(packageJsonPath) {
24
+ function getCoreVersion(packageJsonPath) {
25
25
  if (!fs.existsSync(packageJsonPath)) {
26
26
  return null;
27
27
  }
28
28
  const packageJson = fs.readJSONSync(packageJsonPath);
29
- const version = packageJson.dependencies?.nitrostack ||
30
- packageJson.devDependencies?.nitrostack;
31
- return version || null;
29
+ return packageJson.dependencies?.['@nitrostack/core'] ||
30
+ packageJson.devDependencies?.['@nitrostack/core'] ||
31
+ null;
32
32
  }
33
33
  /**
34
34
  * Parse version string to extract the actual version number
@@ -53,26 +53,41 @@ function compareVersions(v1, v2) {
53
53
  return 0;
54
54
  }
55
55
  /**
56
- * Update nitrostack version in package.json
56
+ * Update all @nitrostack/* versions in package.json
57
57
  */
58
58
  function updatePackageJson(packageJsonPath, newVersion, dryRun) {
59
59
  if (!fs.existsSync(packageJsonPath)) {
60
- return false;
60
+ return [];
61
61
  }
62
62
  const packageJson = fs.readJSONSync(packageJsonPath);
63
- let updated = false;
64
- if (packageJson.dependencies?.nitrostack) {
65
- packageJson.dependencies.nitrostack = `^${newVersion}`;
66
- updated = true;
67
- }
68
- if (packageJson.devDependencies?.nitrostack) {
69
- packageJson.devDependencies.nitrostack = `^${newVersion}`;
70
- updated = true;
71
- }
72
- if (updated && !dryRun) {
63
+ const results = [];
64
+ let hasChanges = false;
65
+ const updateDeps = (deps) => {
66
+ if (!deps)
67
+ return;
68
+ for (const pkg of Object.keys(deps)) {
69
+ if (pkg.startsWith('@nitrostack/')) {
70
+ const currentVersion = deps[pkg];
71
+ if (compareVersions(currentVersion, newVersion) < 0) {
72
+ results.push({
73
+ location: path.basename(path.dirname(packageJsonPath)),
74
+ packageName: pkg,
75
+ previousVersion: currentVersion,
76
+ newVersion: `^${newVersion}`,
77
+ upgraded: true,
78
+ });
79
+ deps[pkg] = `^${newVersion}`;
80
+ hasChanges = true;
81
+ }
82
+ }
83
+ }
84
+ };
85
+ updateDeps(packageJson.dependencies);
86
+ updateDeps(packageJson.devDependencies);
87
+ if (hasChanges && !dryRun) {
73
88
  fs.writeJSONSync(packageJsonPath, packageJson, { spaces: 2 });
74
89
  }
75
- return updated;
90
+ return results;
76
91
  }
77
92
  /**
78
93
  * Run npm install silently
@@ -87,7 +102,8 @@ function runNpmInstall(directory) {
87
102
  * Main upgrade command handler
88
103
  */
89
104
  export async function upgradeCommand(options) {
90
- console.log(createHeader('Upgrade', 'Update nitrostack to the latest version'));
105
+ console.log(NITRO_BANNER_FULL);
106
+ console.log(createHeader('Upgrade', 'Update @nitrostack packages to latest'));
91
107
  const projectRoot = process.cwd();
92
108
  const rootPackageJsonPath = path.join(projectRoot, 'package.json');
93
109
  const widgetsPath = path.join(projectRoot, 'src', 'widgets');
@@ -97,16 +113,16 @@ export async function upgradeCommand(options) {
97
113
  console.log(createErrorBox('Not a NitroStack Project', 'package.json not found'));
98
114
  process.exit(1);
99
115
  }
100
- const rootCurrentVersion = getCurrentVersion(rootPackageJsonPath);
101
- if (!rootCurrentVersion) {
102
- console.log(createErrorBox('Not a NitroStack Project', 'nitrostack is not a dependency'));
116
+ const coreVersion = getCoreVersion(rootPackageJsonPath);
117
+ if (!coreVersion) {
118
+ console.log(createErrorBox('Not a NitroStack Project', '@nitrostack/core is not a dependency'));
103
119
  process.exit(1);
104
120
  }
105
121
  // Fetch latest version
106
122
  const spinner = new NitroSpinner('Checking for updates...').start();
107
123
  let latestVersion;
108
124
  try {
109
- latestVersion = getLatestVersion();
125
+ latestVersion = getLatestVersion('@nitrostack/core');
110
126
  spinner.succeed(`Latest version: ${chalk.cyan(latestVersion)}`);
111
127
  }
112
128
  catch (error) {
@@ -114,16 +130,16 @@ export async function upgradeCommand(options) {
114
130
  process.exit(1);
115
131
  }
116
132
  // Check if already on latest
117
- const rootParsedVersion = parseVersion(rootCurrentVersion);
118
- if (compareVersions(rootParsedVersion, latestVersion) >= 0) {
133
+ const currentParsedVersion = parseVersion(coreVersion);
134
+ if (compareVersions(currentParsedVersion, latestVersion) >= 0) {
119
135
  spacer();
120
136
  console.log(createSuccessBox('Already Up to Date', [
121
- `Current version: ${rootParsedVersion}`,
137
+ `Current version: ${currentParsedVersion}`,
122
138
  `Latest version: ${latestVersion}`,
123
139
  ]));
124
140
  return;
125
141
  }
126
- const results = [];
142
+ const allResults = [];
127
143
  const dryRun = options.dryRun ?? false;
128
144
  if (dryRun) {
129
145
  spacer();
@@ -135,15 +151,10 @@ export async function upgradeCommand(options) {
135
151
  // Upgrade root
136
152
  const rootSpinner = new NitroSpinner('Updating root package.json...').start();
137
153
  try {
138
- const rootUpdated = updatePackageJson(rootPackageJsonPath, latestVersion, dryRun);
139
- if (rootUpdated) {
140
- results.push({
141
- location: 'root',
142
- previousVersion: rootCurrentVersion,
143
- newVersion: `^${latestVersion}`,
144
- upgraded: true,
145
- });
146
- rootSpinner.succeed(`Root: ${chalk.dim(rootCurrentVersion)} → ${chalk.cyan(`^${latestVersion}`)}`);
154
+ const results = updatePackageJson(rootPackageJsonPath, latestVersion, dryRun);
155
+ if (results.length > 0) {
156
+ allResults.push(...results);
157
+ rootSpinner.succeed(`Root: Updated ${results.length} @nitrostack packages`);
147
158
  if (!dryRun) {
148
159
  const installSpinner = new NitroSpinner('Installing dependencies...').start();
149
160
  runNpmInstall(projectRoot);
@@ -151,64 +162,68 @@ export async function upgradeCommand(options) {
151
162
  }
152
163
  }
153
164
  else {
154
- rootSpinner.info('Root: No changes needed');
165
+ rootSpinner.info('Root: All @nitrostack packages are up to date');
155
166
  }
156
167
  }
157
168
  catch (error) {
158
169
  rootSpinner.fail('Failed to upgrade root');
159
- process.exit(1);
170
+ console.error(error);
160
171
  }
161
172
  // Upgrade widgets if they exist
162
173
  if (fs.existsSync(widgetsPackageJsonPath)) {
163
- const widgetsCurrentVersion = getCurrentVersion(widgetsPackageJsonPath);
164
- if (widgetsCurrentVersion) {
165
- const widgetsSpinner = new NitroSpinner('Updating widgets package.json...').start();
166
- try {
167
- const widgetsUpdated = updatePackageJson(widgetsPackageJsonPath, latestVersion, dryRun);
168
- if (widgetsUpdated) {
169
- results.push({
170
- location: 'src/widgets',
171
- previousVersion: widgetsCurrentVersion,
172
- newVersion: `^${latestVersion}`,
173
- upgraded: true,
174
- });
175
- widgetsSpinner.succeed(`Widgets: ${chalk.dim(widgetsCurrentVersion)} → ${chalk.cyan(`^${latestVersion}`)}`);
176
- if (!dryRun) {
177
- const installSpinner = new NitroSpinner('Installing widget dependencies...').start();
178
- runNpmInstall(widgetsPath);
179
- installSpinner.succeed('Widget dependencies installed');
180
- }
181
- }
182
- else {
183
- widgetsSpinner.info('Widgets: No changes needed');
174
+ const widgetsSpinner = new NitroSpinner('Updating widgets package.json...').start();
175
+ try {
176
+ const results = updatePackageJson(widgetsPackageJsonPath, latestVersion, dryRun);
177
+ if (results.length > 0) {
178
+ allResults.push(...results);
179
+ widgetsSpinner.succeed(`Widgets: Updated ${results.length} @nitrostack packages`);
180
+ if (!dryRun) {
181
+ const installSpinner = new NitroSpinner('Installing widget dependencies...').start();
182
+ runNpmInstall(widgetsPath);
183
+ installSpinner.succeed('Widget dependencies installed');
184
184
  }
185
185
  }
186
- catch (error) {
187
- widgetsSpinner.fail('Failed to upgrade widgets');
186
+ else {
187
+ widgetsSpinner.info('Widgets: All @nitrostack packages are up to date');
188
188
  }
189
189
  }
190
- }
191
- else {
192
- log('No widgets directory found', 'dim');
190
+ catch (error) {
191
+ widgetsSpinner.fail('Failed to upgrade widgets');
192
+ }
193
193
  }
194
194
  // Summary
195
195
  spacer();
196
- if (results.length === 0) {
196
+ if (allResults.length === 0) {
197
197
  log('No packages were upgraded', 'warning');
198
198
  }
199
199
  else {
200
- const summaryItems = results.map(r => `${r.location}: ${parseVersion(r.previousVersion)} → ${parseVersion(r.newVersion)}`);
200
+ // Unique packages upgraded
201
+ const uniquePackages = Array.from(new Set(allResults.map(r => r.packageName)));
202
+ const summaryItems = uniquePackages.map(pkg => {
203
+ const result = allResults.find(r => r.packageName === pkg);
204
+ return `${pkg}: ${parseVersion(result.previousVersion)} → ${parseVersion(result.newVersion)}`;
205
+ });
201
206
  if (dryRun) {
202
- console.log(brand.accentBold('\n Dry run complete - no changes made\n'));
203
- log('Run without --dry-run to apply upgrade', 'dim');
207
+ spacer();
208
+ console.log(createBox([
209
+ chalk.yellow.bold('Dry run complete'),
210
+ '',
211
+ chalk.dim('No changes were made to your project.'),
212
+ chalk.dim('Run without --dry-run to apply the upgrade.'),
213
+ ], 'warning'));
204
214
  }
205
215
  else {
206
- console.log(createSuccessBox('Upgrade Complete', summaryItems));
216
+ console.log(createSuccessBox('Upgrade Complete', [
217
+ ...summaryItems,
218
+ '',
219
+ chalk.dim(`Total updates across all packages: ${allResults.length}`)
220
+ ]));
207
221
  nextSteps([
208
- 'Review the changes',
209
- 'Test your application',
210
- 'Check the changelog for breaking changes',
222
+ 'Review the changes in package.json',
223
+ 'Restart your development server',
224
+ 'Check docs.nitrostack.ai for migration guides',
211
225
  ]);
212
226
  }
227
+ showFooter();
213
228
  }
214
229
  }
@@ -1,9 +1,25 @@
1
+ export declare const LINKS: {
2
+ website: string;
3
+ docs: string;
4
+ studio: string;
5
+ };
1
6
  export declare const brand: {
2
- accent: import("chalk").ChalkInstance;
3
- accentBold: import("chalk").ChalkInstance;
4
- accentLight: import("chalk").ChalkInstance;
5
- accentLighter: import("chalk").ChalkInstance;
7
+ signal: import("chalk").ChalkInstance;
8
+ signalBold: import("chalk").ChalkInstance;
9
+ sky: import("chalk").ChalkInstance;
10
+ skyBold: import("chalk").ChalkInstance;
11
+ mint: import("chalk").ChalkInstance;
12
+ mintBold: import("chalk").ChalkInstance;
13
+ error: import("chalk").ChalkInstance;
14
+ warning: import("chalk").ChalkInstance;
6
15
  };
16
+ /**
17
+ * Creates a clickable terminal link using OSC 8 escape sequence
18
+ */
19
+ export declare function terminalLink(text: string, url: string): string;
20
+ /**
21
+ * Redesigned Banner with Restored ASCII NITRO
22
+ */
7
23
  export declare const NITRO_BANNER_FULL: string;
8
24
  export declare function createHeader(title: string, subtitle?: string): string;
9
25
  export declare function createBox(lines: string[], type?: 'success' | 'error' | 'info' | 'warning'): string;
@@ -28,4 +44,5 @@ export declare function log(message: string, type?: 'success' | 'error' | 'info'
28
44
  export declare function divider(): void;
29
45
  export declare function spacer(): void;
30
46
  export declare function nextSteps(steps: string[]): void;
47
+ export declare function showFooter(): void;
31
48
  //# sourceMappingURL=branding.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/ui/branding.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,KAAK;;;;;CAKjB,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAa7B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CASrE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAkB,GAAG,MAAM,CAoB1G;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAQvE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,CAK/E;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAM;gBAET,IAAI,EAAE,MAAM;IAQxB,KAAK,IAAI,IAAI;IAKb,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK1B,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAK5B,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,IAAI,IAAI;CAIb;AAED,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,KAAc,GAAG,IAAI,CAU1G;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,wBAAgB,MAAM,IAAI,IAAI,CAE7B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAM/C"}
1
+ {"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/ui/branding.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,KAAK;;;;CAIjB,CAAC;AAEF,eAAO,MAAM,KAAK;;;;;;;;;CASjB,CAAC;AASF;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9D;AAaD;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAa7B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAqBrE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAkB,GAAG,MAAM,CA4B1G;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAQvE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,CAK/E;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAM;gBAET,IAAI,EAAE,MAAM;IAQxB,KAAK,IAAI,IAAI;IAKb,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK1B,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAK5B,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,IAAI,IAAI;CAIb;AAED,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,KAAc,GAAG,IAAI,CAU1G;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,wBAAgB,MAAM,IAAI,IAAI,CAE7B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAc/C;AAED,wBAAgB,UAAU,IAAI,IAAI,CAYjC"}