@husar.ai/cli 0.4.3 → 0.4.4

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/dist/ui.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ import chalk from 'chalk';
2
+ import boxen from 'boxen';
3
+ import ora, { Ora } from 'ora';
4
+ import logSymbols from 'log-symbols';
5
+ import Enquirer from 'enquirer';
6
+ export declare const theme: {
7
+ primary: import("chalk").ChalkInstance;
8
+ secondary: import("chalk").ChalkInstance;
9
+ success: import("chalk").ChalkInstance;
10
+ warning: import("chalk").ChalkInstance;
11
+ error: import("chalk").ChalkInstance;
12
+ info: import("chalk").ChalkInstance;
13
+ muted: import("chalk").ChalkInstance;
14
+ bold: import("chalk").ChalkInstance;
15
+ dim: import("chalk").ChalkInstance;
16
+ link: import("chalk").ChalkInstance;
17
+ };
18
+ export declare function printBanner(subtitle?: string): void;
19
+ export declare function printCompactBanner(): void;
20
+ export declare function box(content: string, title?: string): void;
21
+ export declare function successBox(content: string, title?: string): void;
22
+ export declare function errorBox(content: string, title?: string): void;
23
+ export declare function infoBox(content: string, title?: string): void;
24
+ export declare function spinner(text: string): Ora;
25
+ export declare const log: {
26
+ success: (message: string) => void;
27
+ error: (message: string) => void;
28
+ warning: (message: string) => void;
29
+ info: (message: string) => void;
30
+ step: (message: string) => void;
31
+ substep: (message: string) => void;
32
+ done: (message: string) => void;
33
+ fail: (message: string) => void;
34
+ dim: (message: string) => void;
35
+ blank: () => void;
36
+ };
37
+ interface SelectChoice {
38
+ name: string;
39
+ message: string;
40
+ hint?: string;
41
+ }
42
+ export declare function select<T extends string>(message: string, choices: SelectChoice[]): Promise<T>;
43
+ export declare function input(message: string, defaultValue?: string): Promise<string>;
44
+ export declare function confirm(message: string, defaultValue?: boolean): Promise<boolean>;
45
+ export declare function password(message: string): Promise<string>;
46
+ export declare function list(items: string[], title?: string): void;
47
+ export declare function keyValue(pairs: Record<string, string | undefined>, title?: string): void;
48
+ export declare function divider(char?: string, length?: number): void;
49
+ export declare function header(text: string): void;
50
+ export declare function nextSteps(steps: string[], title?: string): void;
51
+ export { chalk, boxen, ora, logSymbols, Enquirer };
package/dist/ui.js ADDED
@@ -0,0 +1,181 @@
1
+ import chalk from 'chalk';
2
+ import boxen from 'boxen';
3
+ import ora from 'ora';
4
+ import logSymbols from 'log-symbols';
5
+ import Enquirer from 'enquirer';
6
+ export const theme = {
7
+ primary: chalk.hex('#6366f1'),
8
+ secondary: chalk.hex('#8b5cf6'),
9
+ success: chalk.hex('#22c55e'),
10
+ warning: chalk.hex('#f59e0b'),
11
+ error: chalk.hex('#ef4444'),
12
+ info: chalk.hex('#3b82f6'),
13
+ muted: chalk.gray,
14
+ bold: chalk.bold,
15
+ dim: chalk.dim,
16
+ link: chalk.cyan.underline,
17
+ };
18
+ const HUSAR_LOGO = `
19
+ ██╗ ██╗██╗ ██╗███████╗ █████╗ ██████╔╗
20
+ ██║ ██║██║ ██║██╔════╝██╔══██╗██╔══██╗
21
+ ███████║██║ ██║███████╗███████║██████╔╝
22
+ ██╔══██║██║ ██║╚════██║██╔══██║██╔══██╗
23
+ ██║ ██║╚██████╔╝███████║██║ ██║██║ ██║
24
+ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝`;
25
+ export function printBanner(subtitle) {
26
+ const gradient = HUSAR_LOGO.split('\n')
27
+ .map((line, i) => {
28
+ const colors = ['#818cf8', '#8b5cf6', '#a855f7', '#c084fc', '#d8b4fe', '#e9d5ff'];
29
+ return chalk.hex(colors[i % colors.length])(line);
30
+ })
31
+ .join('\n');
32
+ console.log(gradient);
33
+ console.log();
34
+ console.log(theme.muted(' Headless CMS for Modern Developers'));
35
+ if (subtitle) {
36
+ console.log(theme.primary(` ${subtitle}`));
37
+ }
38
+ console.log();
39
+ }
40
+ export function printCompactBanner() {
41
+ console.log();
42
+ console.log(chalk.bold(theme.primary(' HUSAR.AI')) + theme.muted(' • Headless CMS'));
43
+ console.log();
44
+ }
45
+ export function box(content, title) {
46
+ console.log(boxen(content, {
47
+ padding: 1,
48
+ margin: { top: 1, bottom: 1, left: 0, right: 0 },
49
+ borderStyle: 'round',
50
+ borderColor: '#6366f1',
51
+ title: title,
52
+ titleAlignment: 'left',
53
+ }));
54
+ }
55
+ export function successBox(content, title = 'Success') {
56
+ console.log(boxen(content, {
57
+ padding: 1,
58
+ margin: { top: 1, bottom: 1, left: 0, right: 0 },
59
+ borderStyle: 'round',
60
+ borderColor: '#22c55e',
61
+ title: `${logSymbols.success} ${title}`,
62
+ titleAlignment: 'left',
63
+ }));
64
+ }
65
+ export function errorBox(content, title = 'Error') {
66
+ console.log(boxen(content, {
67
+ padding: 1,
68
+ margin: { top: 1, bottom: 1, left: 0, right: 0 },
69
+ borderStyle: 'round',
70
+ borderColor: '#ef4444',
71
+ title: `${logSymbols.error} ${title}`,
72
+ titleAlignment: 'left',
73
+ }));
74
+ }
75
+ export function infoBox(content, title = 'Info') {
76
+ console.log(boxen(content, {
77
+ padding: 1,
78
+ margin: { top: 1, bottom: 1, left: 0, right: 0 },
79
+ borderStyle: 'round',
80
+ borderColor: '#3b82f6',
81
+ title: `${logSymbols.info} ${title}`,
82
+ titleAlignment: 'left',
83
+ }));
84
+ }
85
+ export function spinner(text) {
86
+ return ora({
87
+ text,
88
+ color: 'magenta',
89
+ spinner: 'dots',
90
+ }).start();
91
+ }
92
+ export const log = {
93
+ success: (message) => console.log(`${logSymbols.success} ${theme.success(message)}`),
94
+ error: (message) => console.log(`${logSymbols.error} ${theme.error(message)}`),
95
+ warning: (message) => console.log(`${logSymbols.warning} ${theme.warning(message)}`),
96
+ info: (message) => console.log(`${logSymbols.info} ${theme.info(message)}`),
97
+ step: (message) => console.log(` ${theme.primary('→')} ${message}`),
98
+ substep: (message) => console.log(` ${theme.muted('•')} ${message}`),
99
+ done: (message) => console.log(` ${theme.success('✓')} ${message}`),
100
+ fail: (message) => console.log(` ${theme.error('✗')} ${message}`),
101
+ dim: (message) => console.log(theme.dim(` ${message}`)),
102
+ blank: () => console.log(),
103
+ };
104
+ const enquirer = new Enquirer();
105
+ export async function select(message, choices) {
106
+ const response = (await enquirer.prompt({
107
+ type: 'select',
108
+ name: 'value',
109
+ message: theme.primary(message),
110
+ choices: choices.map((c) => ({
111
+ name: c.name,
112
+ message: c.message,
113
+ hint: c.hint ? theme.dim(c.hint) : undefined,
114
+ })),
115
+ }));
116
+ return response.value;
117
+ }
118
+ export async function input(message, defaultValue) {
119
+ const response = (await enquirer.prompt({
120
+ type: 'input',
121
+ name: 'value',
122
+ message: theme.primary(message),
123
+ initial: defaultValue,
124
+ }));
125
+ return response.value;
126
+ }
127
+ export async function confirm(message, defaultValue = true) {
128
+ const response = (await enquirer.prompt({
129
+ type: 'confirm',
130
+ name: 'value',
131
+ message: theme.primary(message),
132
+ initial: defaultValue,
133
+ }));
134
+ return response.value;
135
+ }
136
+ export async function password(message) {
137
+ const response = (await enquirer.prompt({
138
+ type: 'password',
139
+ name: 'value',
140
+ message: theme.primary(message),
141
+ }));
142
+ return response.value;
143
+ }
144
+ export function list(items, title) {
145
+ if (title) {
146
+ console.log(chalk.bold(title));
147
+ }
148
+ items.forEach((item) => {
149
+ console.log(` ${theme.primary('•')} ${item}`);
150
+ });
151
+ }
152
+ export function keyValue(pairs, title) {
153
+ if (title) {
154
+ console.log(chalk.bold(title));
155
+ }
156
+ const maxKeyLen = Math.max(...Object.keys(pairs).map((k) => k.length));
157
+ Object.entries(pairs).forEach(([key, value]) => {
158
+ if (value !== undefined) {
159
+ const paddedKey = key.padEnd(maxKeyLen);
160
+ console.log(` ${theme.muted(paddedKey)} ${value}`);
161
+ }
162
+ });
163
+ }
164
+ export function divider(char = '─', length = 50) {
165
+ console.log(theme.muted(char.repeat(length)));
166
+ }
167
+ export function header(text) {
168
+ console.log();
169
+ console.log(theme.primary(chalk.bold(text)));
170
+ divider();
171
+ }
172
+ export function nextSteps(steps, title = 'Next Steps') {
173
+ console.log();
174
+ console.log(chalk.bold(title));
175
+ steps.forEach((step, i) => {
176
+ console.log(` ${theme.primary(`${i + 1}.`)} ${step}`);
177
+ });
178
+ console.log();
179
+ }
180
+ export { chalk, boxen, ora, logSymbols, Enquirer };
181
+ //# sourceMappingURL=ui.js.map
package/dist/ui.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@husar.ai/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -16,13 +16,17 @@
16
16
  "watch": "tspc --watch"
17
17
  },
18
18
  "dependencies": {
19
- "graphql": "^16.9.0",
20
- "@husar.ai/ssr": "^0.4.2",
21
19
  "@husar.ai/genai": "^0.4.2",
20
+ "@husar.ai/ssr": "^0.4.2",
22
21
  "@modelcontextprotocol/sdk": "^1.17.4",
22
+ "boxen": "^8.0.1",
23
+ "chalk": "^5.6.2",
23
24
  "commander": "^11.0.0",
24
25
  "config-maker": "^0.0.6",
26
+ "enquirer": "^2.4.1",
27
+ "graphql": "^16.9.0",
28
+ "log-symbols": "^7.0.1",
29
+ "ora": "^9.3.0",
25
30
  "zod": "^3.23.8"
26
- },
27
- "devDependencies": {}
28
- }
31
+ }
32
+ }
package/src/cli.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  /* eslint-disable no-useless-escape */
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
+
5
+ // Suppress punycode deprecation warning
6
+ process.removeAllListeners('warning');
7
+ process.on('warning', (warning) => {
8
+ if (warning.name === 'DeprecationWarning' && warning.message.includes('punycode')) {
9
+ return;
10
+ }
11
+ console.warn(warning.name, warning.message);
12
+ });
13
+
4
14
  import { Command } from 'commander';
5
- // nofs
6
15
  import { ConfigMaker } from 'config-maker';
7
16
  import { parser } from './functions/parser.js';
8
17
  import { generateCms } from './functions/generate.js';
@@ -11,85 +20,170 @@ import { HusarConfigType, getAdminToken } from '@/types/config.js';
11
20
  import { startLoginFlow } from './auth/login.js';
12
21
  import { clearAuth, getCurrentUser, getConfigPath, isLoggedIn, readAuthConfig } from './auth/config.js';
13
22
  import { runCreateCommand } from './functions/create.js';
23
+ import { printBanner, printCompactBanner, log, successBox, errorBox, infoBox, spinner, theme, chalk } from './ui.js';
14
24
 
15
25
  const config = new ConfigMaker<HusarConfigType>('husar', { decoders: {} });
16
26
 
17
27
  const program = new Command();
18
28
 
19
- program.name('husar').description('HUSAR CLI for complete generation').version('0.0.5');
29
+ program
30
+ .name('husar.ai')
31
+ .description(theme.muted('Headless CMS for Modern Developers'))
32
+ .version('0.4.3')
33
+ .hook('preAction', (thisCommand) => {
34
+ // Show compact banner for all commands except 'create' and 'mcp'
35
+ const cmdName = thisCommand.args[0];
36
+ if (cmdName !== 'mcp') {
37
+ printCompactBanner();
38
+ }
39
+ });
40
+
41
+ // ═══════════════════════════════════════════════════════════════════════════
42
+ // GENERATE COMMAND
43
+ // ═══════════════════════════════════════════════════════════════════════════
20
44
 
21
45
  program
22
46
  .command('generate [folderPath]')
23
- .description('Generate cms structure in the given path')
47
+ .description('Generate cms/ structure with GraphQL client')
24
48
  .action(async (folderPath: string = '.') => {
25
- const host = await config.getValueOrThrow('host', { saveOnInput: true });
26
- const conf = config.get();
27
- const hostEnv = conf.hostEnvironmentVariable;
28
- const authenticationEnv = conf.authenticationEnvironmentVariable;
29
- await generateCms({
30
- baseFolder: folderPath,
31
- host,
32
- hostEnvironmentVariable: hostEnv,
33
- authenticationEnvironmentVariable: authenticationEnv,
34
- });
49
+ const spin = spinner('Loading configuration...');
50
+
51
+ try {
52
+ const host = await config.getValueOrThrow('host', { saveOnInput: true });
53
+ const conf = config.get();
54
+ const hostEnv = conf.hostEnvironmentVariable;
55
+ const authenticationEnv = conf.authenticationEnvironmentVariable;
56
+
57
+ spin.text = 'Generating CMS client...';
58
+
59
+ await generateCms({
60
+ baseFolder: folderPath,
61
+ host,
62
+ hostEnvironmentVariable: hostEnv,
63
+ authenticationEnvironmentVariable: authenticationEnv,
64
+ });
65
+
66
+ spin.succeed('CMS client generated successfully');
67
+
68
+ log.blank();
69
+ log.done(`Generated in ${theme.info(folderPath + '/cms/')}`);
70
+ log.substep('zeus/ - GraphQL Zeus client');
71
+ log.substep('ssr.ts - Server-side client');
72
+ log.substep('react.ts - React components');
73
+ log.substep('host.ts - Host configuration');
74
+ log.blank();
75
+ } catch (err: any) {
76
+ spin.fail('Generation failed');
77
+ log.error(err.message);
78
+ process.exit(1);
79
+ }
35
80
  });
36
81
 
82
+ // ═══════════════════════════════════════════════════════════════════════════
83
+ // COPY COMMAND
84
+ // ═══════════════════════════════════════════════════════════════════════════
85
+
37
86
  program
38
87
  .command('copy <inputFile> <name>')
39
- .description('AI-parse HTML/JSX and upsert into CMS (model/shape) using gpt-5')
88
+ .description('AI-parse HTML/JSX and upsert into CMS')
40
89
  .option('-t, --type <type>', 'target type: model|shape', 'shape')
41
90
  .action(async (inputFile, name, opts) => {
42
- const conf = config.get();
43
- if (!conf.host) {
44
- throw new Error('Host is not configured. Please run "husar generate" first.');
45
- }
46
- const adminToken = getAdminToken(conf);
47
- if (!adminToken) {
48
- throw new Error('Admin token not configured. Set HUSAR_ADMIN_TOKEN env var, or adminTokenEnv in husar.json');
91
+ const spin = spinner(`Parsing ${inputFile}...`);
92
+
93
+ try {
94
+ const conf = config.get();
95
+ if (!conf.host) {
96
+ spin.fail('Host not configured');
97
+ errorBox('Please run "husar.ai generate" first to configure your CMS host.');
98
+ process.exit(1);
99
+ }
100
+
101
+ const adminToken = getAdminToken(conf);
102
+ if (!adminToken) {
103
+ spin.fail('Admin token not configured');
104
+ errorBox(
105
+ 'Set HUSAR_ADMIN_TOKEN environment variable,\nor add adminTokenEnv to husar.json',
106
+ 'Authentication Required',
107
+ );
108
+ process.exit(1);
109
+ }
110
+
111
+ spin.text = `AI parsing ${inputFile}...`;
112
+
113
+ const result = await parser({
114
+ inputFile,
115
+ opts: { name, type: opts?.type },
116
+ authentication: { HUSAR_MCP_HOST: conf.host, HUSAR_MCP_ADMIN_TOKEN: adminToken },
117
+ });
118
+
119
+ if (result) {
120
+ spin.succeed(`Parsed and upserted as ${opts?.type || 'shape'}: ${name}`);
121
+ } else {
122
+ spin.fail('Failed to parse file');
123
+ process.exit(1);
124
+ }
125
+ } catch (err: any) {
126
+ spin.fail('Parse failed');
127
+ log.error(err.message);
128
+ process.exit(1);
49
129
  }
50
- const result = await parser({
51
- inputFile,
52
- opts: { name, type: opts?.type },
53
- authentication: { HUSAR_MCP_HOST: conf.host, HUSAR_MCP_ADMIN_TOKEN: adminToken },
54
- });
55
- console.log(result ? 'File parsed and upserted successfully.' : 'Failed to parse and upsert file.');
56
130
  });
57
131
 
132
+ // ═══════════════════════════════════════════════════════════════════════════
133
+ // MCP COMMAND
134
+ // ═══════════════════════════════════════════════════════════════════════════
135
+
58
136
  program
59
137
  .command('mcp')
60
- .description('Run the Husar MCP server over stdio')
138
+ .description('Start MCP server for AI IDE integration')
61
139
  .action(async () => {
140
+ // MCP runs in silent mode - no banner, no output except JSON-RPC
62
141
  await startMcpServer();
63
- // keep process alive
64
- // eslint-disable-next-line @typescript-eslint/no-empty-function
65
142
  await new Promise<void>(() => {});
66
143
  });
67
144
 
68
- // ============ Auth Commands ============
145
+ // ═══════════════════════════════════════════════════════════════════════════
146
+ // AUTH COMMANDS
147
+ // ═══════════════════════════════════════════════════════════════════════════
69
148
 
70
149
  program
71
150
  .command('login')
72
- .description('Log in to Husar.ai via browser authentication')
151
+ .description('Log in to Husar.ai via browser')
73
152
  .action(async () => {
74
153
  const loggedIn = await isLoggedIn();
75
154
  if (loggedIn) {
76
155
  const user = await getCurrentUser();
77
- console.log(`Already logged in as ${user?.email}`);
78
- console.log('Run "husar logout" to switch accounts.');
156
+ infoBox(
157
+ `Logged in as ${theme.info(user?.email ?? 'unknown')}\n\n` +
158
+ `Run ${theme.muted('husar.ai logout')} to switch accounts.`,
159
+ 'Already Authenticated',
160
+ );
79
161
  return;
80
162
  }
81
163
 
82
- console.log('Starting Husar.ai authentication...');
83
- const result = await startLoginFlow();
164
+ const spin = spinner('Opening browser for authentication...');
84
165
 
85
- if (result.success) {
86
- console.log(`\n✅ Successfully logged in as ${result.email}`);
87
- if (result.project) {
88
- console.log(` Connected to project: ${result.project.projectName}`);
89
- console.log(` Host: ${result.project.host}`);
166
+ try {
167
+ const result = await startLoginFlow();
168
+
169
+ if (result.success) {
170
+ spin.succeed('Authentication successful');
171
+
172
+ const details = [`Email: ${theme.info(result.email ?? 'unknown')}`];
173
+ if (result.project) {
174
+ details.push(`Project: ${theme.info(result.project.projectName)}`);
175
+ details.push(`Host: ${theme.muted(result.project.host)}`);
176
+ }
177
+
178
+ successBox(details.join('\n'), 'Logged In');
179
+ } else {
180
+ spin.fail('Authentication failed');
181
+ errorBox(result.error ?? 'Unknown error');
182
+ process.exit(1);
90
183
  }
91
- } else {
92
- console.error(`\n❌ Login failed: ${result.error}`);
184
+ } catch (err: any) {
185
+ spin.fail('Login failed');
186
+ log.error(err.message);
93
187
  process.exit(1);
94
188
  }
95
189
  });
@@ -100,57 +194,85 @@ program
100
194
  .action(async () => {
101
195
  const loggedIn = await isLoggedIn();
102
196
  if (!loggedIn) {
103
- console.log('Not logged in.');
197
+ log.info('Not logged in.');
104
198
  return;
105
199
  }
106
200
 
107
201
  const user = await getCurrentUser();
202
+ const spin = spinner('Clearing credentials...');
203
+
108
204
  await clearAuth();
109
- console.log(`✅ Logged out${user?.email ? ` (was: ${user.email})` : ''}`);
110
- console.log(` Cleared credentials from ${getConfigPath()}`);
205
+
206
+ spin.succeed('Logged out successfully');
207
+ log.dim(`Was: ${user?.email ?? 'unknown'}`);
208
+ log.dim(`Cleared: ${getConfigPath()}`);
111
209
  });
112
210
 
113
211
  program
114
212
  .command('whoami')
115
- .description('Display current logged-in user and projects')
213
+ .description('Display current user and connected projects')
116
214
  .action(async () => {
117
215
  const loggedIn = await isLoggedIn();
118
216
  if (!loggedIn) {
119
- console.log('Not logged in. Run "husar login" to authenticate.');
217
+ infoBox(`Not logged in.\n\nRun ${theme.info('husar.ai login')} to authenticate.`, 'Authentication Status');
120
218
  return;
121
219
  }
122
220
 
123
221
  const user = await getCurrentUser();
124
222
  const authConfig = await readAuthConfig();
125
223
 
126
- console.log(`\n👤 Logged in as: ${user?.email}`);
224
+ console.log();
225
+ console.log(chalk.bold(' Account'));
226
+ console.log(` ${theme.muted('Email')} ${theme.info(user?.email ?? 'unknown')}`);
227
+ console.log(` ${theme.muted('Config')} ${getConfigPath()}`);
127
228
 
128
229
  if (authConfig.projects && Object.keys(authConfig.projects).length > 0) {
129
- console.log('\n📦 Connected projects:');
230
+ console.log();
231
+ console.log(chalk.bold(' Connected Projects'));
130
232
  for (const [name, project] of Object.entries(authConfig.projects)) {
131
- console.log(` • ${name}`);
132
- console.log(` Host: ${project.host}`);
233
+ console.log(` ${theme.primary('')} ${name}`);
234
+ console.log(` ${theme.muted(project.host)}`);
133
235
  }
134
- } else {
135
- console.log('\nNo projects connected. Run "husar create" to set up a new project.');
136
236
  }
137
237
 
138
- console.log(`\n📁 Config: ${getConfigPath()}`);
238
+ console.log();
139
239
  });
140
240
 
141
- // ============ Create Command ============
241
+ // ═══════════════════════════════════════════════════════════════════════════
242
+ // CREATE COMMAND
243
+ // ═══════════════════════════════════════════════════════════════════════════
142
244
 
143
245
  program
144
246
  .command('create [projectName]')
145
- .description('Create a new project with Husar CMS integration')
146
- .option('-f, --framework <framework>', 'Framework to use: nextjs or vite', 'nextjs')
147
- .option('--skip-install', 'Skip package installation after scaffolding')
247
+ .description('Create a new project with Husar CMS')
248
+ .option('-f, --framework <framework>', 'nextjs, vite, or clean', 'nextjs')
249
+ .option('--skip-install', 'Skip npm install after scaffolding')
148
250
  .action(async (projectName: string | undefined, opts: { framework: string; skipInstall?: boolean }) => {
251
+ // Create command has its own banner
252
+ printBanner('Project Creator');
253
+
149
254
  await runCreateCommand({
150
255
  projectName,
151
- framework: opts.framework as 'nextjs' | 'vite',
256
+ framework: opts.framework as 'nextjs' | 'vite' | 'clean',
152
257
  skipInstall: opts.skipInstall,
153
258
  });
154
259
  });
155
260
 
261
+ // ═══════════════════════════════════════════════════════════════════════════
262
+ // HELP CUSTOMIZATION
263
+ // ═══════════════════════════════════════════════════════════════════════════
264
+
265
+ program.addHelpText('before', () => {
266
+ printBanner();
267
+ return '';
268
+ });
269
+
270
+ program.addHelpText('after', () => {
271
+ console.log();
272
+ console.log(theme.muted(' Documentation:'), theme.link('https://docs.husar.ai'));
273
+ console.log(theme.muted(' Discord:'), theme.link('https://discord.gg/husar'));
274
+ console.log();
275
+ return '';
276
+ });
277
+
156
278
  program.parse(process.argv);