zerostart-cli 0.0.32 → 0.0.36

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/out/cli.js CHANGED
@@ -51,7 +51,7 @@ const VercelManager_1 = require("./managers/VercelManager");
51
51
  const NetlifyManager_1 = require("./managers/NetlifyManager");
52
52
  const child_process_1 = require("child_process");
53
53
  const program = new commander_1.Command();
54
- // Basic ASCII banner for maximum compatibility
54
+ // Basic ASCII banner
55
55
  function showBanner() {
56
56
  console.log();
57
57
  console.log(chalk_1.default.cyan('------------------------------------------------------------'));
@@ -59,13 +59,10 @@ function showBanner() {
59
59
  console.log(chalk_1.default.gray(' Create and deploy projects in seconds '));
60
60
  console.log(chalk_1.default.cyan('------------------------------------------------------------'));
61
61
  }
62
- // Show GitHub token help (ASCII only)
63
- // Open URL in default browser
64
62
  function openUrl(url) {
65
63
  const start = (process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open');
66
64
  (0, child_process_1.exec)(`${start} "${url}"`);
67
65
  }
68
- // Show GitHub token help (ASCII only)
69
66
  function showGitHubTokenHelp() {
70
67
  const tokenUrl = 'https://github.com/settings/tokens/new?scopes=repo&description=ZeroStart%20CLI%20Token';
71
68
  console.log();
@@ -73,642 +70,530 @@ function showGitHubTokenHelp() {
73
70
  console.log(chalk_1.default.gray(' +----------------------------------------------------------+'));
74
71
  console.log(chalk_1.default.gray(' | ') + chalk_1.default.white('Opening GitHub in your browser... ') + chalk_1.default.gray(' |'));
75
72
  console.log(chalk_1.default.gray(' | ') + chalk_1.default.cyan(tokenUrl.substring(0, 50) + '...') + chalk_1.default.gray(' |'));
76
- console.log(chalk_1.default.gray(' | |'));
77
- console.log(chalk_1.default.gray(' | ') + chalk_1.default.white('1. Scroll down and click "Generate token" ') + chalk_1.default.gray(' |'));
78
- console.log(chalk_1.default.gray(' | ') + chalk_1.default.white('2. Copy the token and paste it below ') + chalk_1.default.gray(' |'));
79
73
  console.log(chalk_1.default.gray(' +----------------------------------------------------------+'));
80
74
  console.log();
81
75
  openUrl(tokenUrl);
82
76
  }
83
- program
84
- .name('zerostart')
85
- .description('Create and deploy a complete project with one command')
86
- .version('0.0.32');
87
- program
88
- .command('deploy-vercel')
89
- .description('Deploy the current project to Vercel')
90
- .action(async () => {
91
- showBanner();
92
- const vercelManager = new VercelManager_1.VercelManager();
77
+ async function initializeProject(name, language, type, options) {
93
78
  const cwd = process.cwd();
94
- // 1. Check Install
95
- let vercelInstalled = await vercelManager.checkVercelInstalled();
96
- if (!vercelInstalled) {
97
- const { installVercel } = await inquirer_1.default.prompt([{
98
- type: 'confirm',
99
- name: 'installVercel',
100
- message: 'Vercel CLI not found. Install it globally?',
101
- default: true
102
- }]);
103
- if (installVercel) {
104
- const iSpinner = (0, ora_1.default)('Installing Vercel CLI...').start();
105
- if (await vercelManager.installGlobal()) {
106
- iSpinner.succeed(chalk_1.default.green('Vercel CLI installed!'));
107
- vercelInstalled = true;
79
+ const projectPath = path.join(cwd, name);
80
+ if (fs.existsSync(projectPath)) {
81
+ console.log(chalk_1.default.red('\n Error: Directory "' + name + '" already exists!'));
82
+ process.exit(1);
83
+ }
84
+ const config = {
85
+ name,
86
+ language,
87
+ type,
88
+ isTheRepoPublic: options.isPublic,
89
+ description: `A ${type} project in ${language}`,
90
+ path: projectPath
91
+ };
92
+ const spinner = (0, ora_1.default)({ text: 'Initializing...', color: 'cyan' }).start();
93
+ try {
94
+ const templateManager = new TemplateManager_1.TemplateManager();
95
+ const gitManager = new GitManager_1.GitManager();
96
+ const gitHubService = options.githubToken ? new GitHubServiceCLI_1.GitHubServiceCLI(options.githubToken) : null;
97
+ if (!await gitManager.checkGitInstalled()) {
98
+ spinner.fail(chalk_1.default.red('Git is not installed!'));
99
+ return;
100
+ }
101
+ spinner.text = chalk_1.default.cyan('Generating project structure...');
102
+ await templateManager.createProjectStructure(config);
103
+ spinner.succeed(chalk_1.default.green('Project structure created'));
104
+ if (type !== types_1.ProjectType.DSAPractice) {
105
+ spinner.start(chalk_1.default.cyan('Initializing Git repository...'));
106
+ await gitManager.init(projectPath);
107
+ await gitManager.commit(projectPath, "Initial commit");
108
+ spinner.succeed(chalk_1.default.green('Git repository initialized'));
109
+ }
110
+ if (options.createRemote) {
111
+ spinner.start(chalk_1.default.cyan('Creating GitHub repository...'));
112
+ let repoUrl;
113
+ if (options.authMethod === 'GitHub CLI') {
114
+ repoUrl = await gitManager.createRepoWithGh(projectPath, name, options.isPublic);
115
+ }
116
+ else if (gitHubService) {
117
+ repoUrl = await gitHubService.createRepo(config);
118
+ }
119
+ if (repoUrl) {
120
+ spinner.succeed(chalk_1.default.green('GitHub repository created'));
121
+ spinner.start(chalk_1.default.cyan('Pushing to GitHub...'));
122
+ if (options.authMethod !== 'GitHub CLI')
123
+ await gitManager.addRemote(projectPath, repoUrl);
124
+ await gitManager.push(projectPath);
125
+ spinner.succeed(chalk_1.default.green('Pushed to GitHub'));
108
126
  }
109
127
  else {
110
- iSpinner.fail(chalk_1.default.red('Failed to install Vercel CLI.'));
111
- return;
128
+ spinner.warn(chalk_1.default.yellow('GitHub repository creation failed'));
112
129
  }
113
130
  }
114
- else {
131
+ console.log();
132
+ console.log(chalk_1.default.bold.green(' Success! Your project is ready!'));
133
+ console.log(chalk_1.default.gray(' Location: ') + chalk_1.default.cyan(projectPath));
134
+ console.log();
135
+ const isWeb = [types_1.ProjectLanguage.React, types_1.ProjectLanguage.HTMLCSS].includes(language);
136
+ const isPractice = type === types_1.ProjectType.DSAPractice;
137
+ if (!isWeb || isPractice) {
138
+ const gdbLinks = {
139
+ [types_1.ProjectLanguage.Python]: 'https://www.onlinegdb.com/online_python_compiler',
140
+ [types_1.ProjectLanguage.Java]: 'https://www.onlinegdb.com/online_java_compiler',
141
+ [types_1.ProjectLanguage.CPP]: 'https://www.onlinegdb.com/online_c++_compiler',
142
+ [types_1.ProjectLanguage.NodeJS]: 'https://www.onlinegdb.com/online_node.js_compiler',
143
+ [types_1.ProjectLanguage.React]: 'https://www.onlinegdb.com/'
144
+ };
145
+ const link = gdbLinks[language] || 'https://www.onlinegdb.com/';
146
+ console.log(chalk_1.default.bold.yellow(' Practice Online:'));
147
+ console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(link));
148
+ console.log(chalk_1.default.gray(' (Opening in your browser...)'));
149
+ openUrl(link);
115
150
  return;
116
151
  }
152
+ console.log(chalk_1.default.bold(' Get started:'));
153
+ console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(`cd ${name}`));
154
+ console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('code .') + chalk_1.default.gray(' (or your favorite editor)'));
155
+ console.log();
117
156
  }
118
- // 2. Auth & Deploy
119
- if (vercelInstalled) {
120
- const vSpinner = (0, ora_1.default)('Checking Vercel authentication...').start();
121
- const loggedIn = await vercelManager.checkAuth();
122
- if (!loggedIn) {
123
- vSpinner.stop();
124
- console.log(chalk_1.default.yellow('\n Authentication required.'));
125
- const { doLogin } = await inquirer_1.default.prompt([{
126
- type: 'confirm',
127
- name: 'doLogin',
128
- message: 'Log in to Vercel now?',
129
- default: true
130
- }]);
131
- if (doLogin) {
132
- const loginSuccess = await vercelManager.login();
133
- if (!loginSuccess) {
134
- console.log(chalk_1.default.red('\n Login failed.'));
135
- return;
136
- }
137
- }
138
- else {
139
- return;
140
- }
157
+ catch (error) {
158
+ spinner.fail(chalk_1.default.red('Error: ' + error.message));
159
+ }
160
+ }
161
+ program
162
+ .name('zerostart')
163
+ .description('Create and deploy a complete project with one command')
164
+ .version('0.0.36');
165
+ // zerostart init [project-name]
166
+ program
167
+ .command('init [project-name]')
168
+ .description('Initialize a new project with interactive prompts')
169
+ .action(async (projectName) => {
170
+ showBanner();
171
+ const answers = await inquirer_1.default.prompt([
172
+ { type: 'input', name: 'name', message: 'Project Name:', when: !projectName, default: 'my-project' },
173
+ { type: 'list', name: 'language', message: 'Language:', choices: Object.values(types_1.ProjectLanguage) },
174
+ { type: 'list', name: 'type', message: 'Type:', choices: Object.values(types_1.ProjectType) },
175
+ { type: 'list', name: 'createRemote', message: 'Push to GitHub?', choices: ['Yes', 'No'], default: 'No', when: (ans) => ans.type !== types_1.ProjectType.DSAPractice }
176
+ ]);
177
+ await initializeProject(projectName || answers.name, answers.language, answers.type, {
178
+ isPublic: false,
179
+ createRemote: answers.createRemote === 'Yes',
180
+ githubToken: null,
181
+ authMethod: 'none'
182
+ });
183
+ });
184
+ // zerostart deploy
185
+ program
186
+ .command('deploy')
187
+ .description('Deploy your project to a hosting provider')
188
+ .action(async () => {
189
+ showBanner();
190
+ const { provider } = await inquirer_1.default.prompt([{
191
+ type: 'list',
192
+ name: 'provider',
193
+ message: 'Choose deployment provider:',
194
+ choices: ['Vercel', 'Netlify', 'GitHub Pages']
195
+ }]);
196
+ const cwd = process.cwd();
197
+ const projectName = path.basename(cwd);
198
+ if (provider === 'Vercel') {
199
+ const vercelManager = new VercelManager_1.VercelManager();
200
+ if (await vercelManager.checkAuth()) {
201
+ const { vName } = await inquirer_1.default.prompt([{ type: 'input', name: 'vName', message: 'Vercel Project Name:', default: projectName }]);
202
+ const url = await vercelManager.deploy(cwd, vName);
203
+ if (url)
204
+ console.log(chalk_1.default.green(' ✔ Deployed! URL: ') + chalk_1.default.cyan(url));
141
205
  }
142
- else {
143
- vSpinner.succeed(chalk_1.default.green('Authenticated'));
206
+ }
207
+ else if (provider === 'Netlify') {
208
+ const netlifyManager = new NetlifyManager_1.NetlifyManager();
209
+ if (await netlifyManager.checkAuth()) {
210
+ const { nName } = await inquirer_1.default.prompt([{ type: 'input', name: 'nName', message: 'Netlify Project Name:', default: projectName }]);
211
+ const res = await netlifyManager.createSite(nName, cwd);
212
+ const success = await netlifyManager.deploy(cwd, res.siteId);
213
+ if (success)
214
+ console.log(chalk_1.default.green(' ✔ Deployed to Netlify!'));
144
215
  }
145
- // Prompt for project name for Vercel
146
- const defaultName = path.basename(cwd);
147
- const { vName } = await inquirer_1.default.prompt([{
148
- type: 'input',
149
- name: 'vName',
150
- message: 'Vercel Project Name:',
151
- default: defaultName,
152
- validate: (input) => {
153
- if (input.trim() === '')
154
- return 'Name is required';
155
- return true;
156
- }
216
+ }
217
+ else if (provider === 'GitHub Pages') {
218
+ console.log(chalk_1.default.yellow(' GitHub Pages deployment coming soon!'));
219
+ }
220
+ });
221
+ // zerostart git
222
+ program
223
+ .command('git')
224
+ .description('Initialize Git repository and optionally create GitHub repo')
225
+ .action(async () => {
226
+ showBanner();
227
+ const cwd = process.cwd();
228
+ const gitManager = new GitManager_1.GitManager();
229
+ const spinner = (0, ora_1.default)({ text: 'Checking Git...', color: 'cyan' }).start();
230
+ if (!await gitManager.checkGitInstalled()) {
231
+ spinner.fail(chalk_1.default.red('Git is not installed!'));
232
+ return;
233
+ }
234
+ spinner.text = 'Initializing Git repository...';
235
+ await gitManager.init(cwd);
236
+ spinner.succeed(chalk_1.default.green('Git repository initialized'));
237
+ const { createGitHub } = await inquirer_1.default.prompt([{
238
+ type: 'list',
239
+ name: 'createGitHub',
240
+ message: 'Create GitHub repository?',
241
+ choices: ['Yes', 'No'],
242
+ default: 'No'
243
+ }]);
244
+ if (createGitHub === 'Yes') {
245
+ const { isPublic } = await inquirer_1.default.prompt([{
246
+ type: 'list',
247
+ name: 'isPublic',
248
+ message: 'Repository visibility:',
249
+ choices: ['Public', 'Private'],
250
+ default: 'Private'
157
251
  }]);
158
- const dSpinner = (0, ora_1.default)('Deploying to Vercel...').start();
159
- const url = await vercelManager.deploy(cwd, vName);
160
- if (url) {
161
- dSpinner.succeed(chalk_1.default.green('Deployed to Vercel!'));
162
- console.log(chalk_1.default.gray(' URL: ') + chalk_1.default.cyan(url));
252
+ spinner.start('Creating GitHub repository...');
253
+ const repoName = path.basename(cwd);
254
+ const repoUrl = await gitManager.createRepoWithGh(cwd, repoName, isPublic === 'Public');
255
+ if (repoUrl) {
256
+ spinner.succeed(chalk_1.default.green('GitHub repository created'));
257
+ spinner.start('Pushing to GitHub...');
258
+ await gitManager.commit(cwd, 'Initial commit');
259
+ await gitManager.push(cwd);
260
+ spinner.succeed(chalk_1.default.green('Pushed to GitHub'));
261
+ console.log(chalk_1.default.cyan(` Repository: ${repoUrl}`));
163
262
  }
164
263
  else {
165
- dSpinner.fail(chalk_1.default.red('Deployment failed'));
166
- console.log(chalk_1.default.gray(' Try running ') + chalk_1.default.cyan('vercel deploy') + chalk_1.default.gray(' manually.'));
264
+ spinner.fail(chalk_1.default.red('Failed to create GitHub repository'));
167
265
  }
168
266
  }
267
+ else {
268
+ await gitManager.commit(cwd, 'Initial commit');
269
+ console.log(chalk_1.default.green(' ✔ Git initialized with initial commit'));
270
+ }
169
271
  });
272
+ // zerostart add [feature]
170
273
  program
171
- .command('deploy-netlify')
172
- .description('Deploy the current project to Netlify')
274
+ .command('add [feature]')
275
+ .description('Add features to your existing project')
276
+ .action(async (feature) => {
277
+ showBanner();
278
+ const features = ['auth', 'database', 'api', 'ui-components', 'testing', 'docker'];
279
+ let selectedFeature = feature;
280
+ if (!feature) {
281
+ const { feat } = await inquirer_1.default.prompt([{
282
+ type: 'list',
283
+ name: 'feat',
284
+ message: 'Select feature to add:',
285
+ choices: features
286
+ }]);
287
+ selectedFeature = feat;
288
+ }
289
+ const spinner = (0, ora_1.default)({ text: `Adding ${selectedFeature}...`, color: 'cyan' }).start();
290
+ // Placeholder implementation - would need actual feature templates
291
+ setTimeout(() => {
292
+ spinner.succeed(chalk_1.default.green(`${selectedFeature} feature added successfully!`));
293
+ console.log(chalk_1.default.gray(` Note: Feature templates are being developed`));
294
+ }, 1000);
295
+ });
296
+ // zerostart env
297
+ program
298
+ .command('env')
299
+ .description('Manage environment variables interactively')
173
300
  .action(async () => {
174
301
  showBanner();
175
- const netlifyManager = new NetlifyManager_1.NetlifyManager();
176
302
  const cwd = process.cwd();
177
- // 1. Check Install
178
- let netlifyInstalled = await netlifyManager.checkNetlifyInstalled();
179
- if (!netlifyInstalled) {
180
- const { installNetlify } = await inquirer_1.default.prompt([{
181
- type: 'confirm',
182
- name: 'installNetlify',
183
- message: 'Netlify CLI not found. Install it globally?',
184
- default: true
185
- }]);
186
- if (installNetlify) {
187
- const iSpinner = (0, ora_1.default)('Installing Netlify CLI...').start();
188
- if (await netlifyManager.installGlobal()) {
189
- iSpinner.succeed(chalk_1.default.green('Netlify CLI installed!'));
190
- netlifyInstalled = true;
191
- }
192
- else {
193
- iSpinner.fail(chalk_1.default.red('Failed to install Netlify CLI.'));
194
- return;
195
- }
303
+ const envPath = path.join(cwd, '.env');
304
+ const { action } = await inquirer_1.default.prompt([{
305
+ type: 'list',
306
+ name: 'action',
307
+ message: 'Environment variable action:',
308
+ choices: ['Add/Edit', 'View', 'Remove']
309
+ }]);
310
+ if (action === 'Add/Edit') {
311
+ const { key, value } = await inquirer_1.default.prompt([
312
+ { type: 'input', name: 'key', message: 'Variable name:' },
313
+ { type: 'input', name: 'value', message: 'Variable value:' }
314
+ ]);
315
+ let envContent = '';
316
+ if (fs.existsSync(envPath)) {
317
+ envContent = fs.readFileSync(envPath, 'utf-8');
318
+ }
319
+ const lines = envContent.split('\n').filter(line => !line.startsWith(`${key}=`));
320
+ lines.push(`${key}=${value}`);
321
+ fs.writeFileSync(envPath, lines.join('\n'));
322
+ console.log(chalk_1.default.green(` ✔ ${key} added to .env`));
323
+ }
324
+ else if (action === 'View') {
325
+ if (fs.existsSync(envPath)) {
326
+ const content = fs.readFileSync(envPath, 'utf-8');
327
+ console.log(chalk_1.default.cyan('\n Environment variables:'));
328
+ console.log(chalk_1.default.gray(content));
196
329
  }
197
330
  else {
198
- return;
331
+ console.log(chalk_1.default.yellow(' No .env file found'));
199
332
  }
200
333
  }
201
- // 2. Auth & Deploy
202
- if (netlifyInstalled) {
203
- const nSpinner = (0, ora_1.default)('Checking Netlify authentication...').start();
204
- const loggedIn = await netlifyManager.checkAuth();
205
- if (!loggedIn) {
206
- nSpinner.stop();
207
- console.log(chalk_1.default.yellow('\n Authentication required.'));
208
- const { doLogin } = await inquirer_1.default.prompt([{
209
- type: 'confirm',
210
- name: 'doLogin',
211
- message: 'Log in to Netlify now?',
212
- default: true
334
+ else if (action === 'Remove') {
335
+ if (fs.existsSync(envPath)) {
336
+ const content = fs.readFileSync(envPath, 'utf-8');
337
+ const vars = content.split('\n').filter(l => l.includes('=')).map(l => l.split('=')[0]);
338
+ const { varToRemove } = await inquirer_1.default.prompt([{
339
+ type: 'list',
340
+ name: 'varToRemove',
341
+ message: 'Select variable to remove:',
342
+ choices: vars
213
343
  }]);
214
- if (doLogin) {
215
- const loginSuccess = await netlifyManager.login();
216
- if (!loginSuccess) {
217
- console.log(chalk_1.default.red('\n Login failed.'));
218
- return;
219
- }
220
- }
221
- else {
222
- return;
223
- }
224
- }
225
- else {
226
- nSpinner.succeed(chalk_1.default.green('Authenticated'));
344
+ const newContent = content.split('\n').filter(l => !l.startsWith(`${varToRemove}=`)).join('\n');
345
+ fs.writeFileSync(envPath, newContent);
346
+ console.log(chalk_1.default.green(` ✔ ${varToRemove} removed`));
227
347
  }
228
- // Enforce name for standalone deploy
229
- const defaultName = path.basename(cwd);
230
- let siteName = defaultName;
231
- let siteCreated = false;
232
- let activeSiteId = undefined;
233
- while (!siteCreated) {
234
- const result = await netlifyManager.createSite(siteName, cwd);
235
- if (result.success) {
236
- siteCreated = true;
237
- activeSiteId = result.siteId;
238
- }
239
- else if (result.reason === 'taken') {
240
- console.log(chalk_1.default.yellow(`\n The site name "${siteName}" is already taken.`));
241
- const { newName } = await inquirer_1.default.prompt([{
242
- type: 'input',
243
- name: 'newName',
244
- message: 'Enter a unique name for your Netlify site:',
245
- validate: (input) => {
246
- if (input.trim() === '')
247
- return 'Name is required';
248
- if (!/^[a-zA-Z0-9-_]+$/.test(input))
249
- return 'Use only letters, numbers, hyphens, and underscores';
250
- return true;
251
- }
252
- }]);
253
- siteName = newName;
348
+ }
349
+ });
350
+ // zerostart test
351
+ program
352
+ .command('test')
353
+ .description('Set up testing framework with sample tests')
354
+ .action(async () => {
355
+ showBanner();
356
+ const { framework } = await inquirer_1.default.prompt([{
357
+ type: 'list',
358
+ name: 'framework',
359
+ message: 'Choose testing framework:',
360
+ choices: ['Jest', 'Vitest', 'Cypress', 'Mocha']
361
+ }]);
362
+ const spinner = (0, ora_1.default)({ text: `Setting up ${framework}...`, color: 'cyan' }).start();
363
+ // Placeholder - would install actual dependencies
364
+ setTimeout(() => {
365
+ spinner.succeed(chalk_1.default.green(`${framework} configured successfully!`));
366
+ console.log(chalk_1.default.gray(` Run tests with: npm test`));
367
+ }, 1500);
368
+ });
369
+ // zerostart build
370
+ program
371
+ .command('build')
372
+ .description('Build your project for production')
373
+ .action(async () => {
374
+ showBanner();
375
+ const cwd = process.cwd();
376
+ const spinner = (0, ora_1.default)({ text: 'Building for production...', color: 'cyan' }).start();
377
+ try {
378
+ const packageJsonPath = path.join(cwd, 'package.json');
379
+ if (fs.existsSync(packageJsonPath)) {
380
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
381
+ if (packageJson.scripts && packageJson.scripts.build) {
382
+ (0, child_process_1.exec)('npm run build', { cwd }, (error, stdout, stderr) => {
383
+ if (error) {
384
+ spinner.fail(chalk_1.default.red('Build failed!'));
385
+ console.error(stderr);
386
+ }
387
+ else {
388
+ spinner.succeed(chalk_1.default.green('Build completed successfully!'));
389
+ console.log(chalk_1.default.gray(stdout));
390
+ }
391
+ });
254
392
  }
255
393
  else {
256
- console.log(chalk_1.default.red('\n Failed to configure Netlify site.'));
257
- const { action } = await inquirer_1.default.prompt([{
258
- type: 'list',
259
- name: 'action',
260
- message: 'What would you like to do?',
261
- choices: ['Try again with a different name', 'Continue deployment anyway', 'Abort']
262
- }]);
263
- if (action === 'Abort')
264
- return;
265
- if (action === 'Continue deployment anyway')
266
- break;
267
- const { retryName } = await inquirer_1.default.prompt([{
268
- type: 'input',
269
- name: 'retryName',
270
- message: 'Enter a unique name:',
271
- validate: (input) => {
272
- if (input.trim() === '')
273
- return 'Name is required';
274
- return true;
275
- }
276
- }]);
277
- siteName = retryName;
394
+ spinner.warn(chalk_1.default.yellow('No build script found in package.json'));
278
395
  }
279
396
  }
280
- const dSpinner = (0, ora_1.default)('Deploying to Netlify...').start();
281
- dSpinner.stop();
282
- const success = await netlifyManager.deploy(cwd, activeSiteId);
283
- if (success) {
284
- console.log();
285
- console.log(chalk_1.default.green(' ✔ Deployed to Netlify!'));
286
- console.log(chalk_1.default.gray(' Run ') + chalk_1.default.cyan('netlify open') + chalk_1.default.gray(' to view your site.'));
287
- }
288
397
  else {
289
- console.log();
290
- console.log(chalk_1.default.red(' ✖ Deployment failed'));
291
- console.log(chalk_1.default.gray(' Try running ') + chalk_1.default.cyan('netlify login') + chalk_1.default.gray(' and ') + chalk_1.default.cyan('netlify deploy') + chalk_1.default.gray(' manually.'));
398
+ spinner.warn(chalk_1.default.yellow('No package.json found'));
292
399
  }
293
400
  }
401
+ catch (error) {
402
+ spinner.fail(chalk_1.default.red('Build failed: ' + error.message));
403
+ }
294
404
  });
405
+ // zerostart dev
295
406
  program
296
- .argument('[projectName]', 'Name of the project')
297
- .action(async (projectName) => {
407
+ .command('dev')
408
+ .description('Start development server with hot reload')
409
+ .action(async () => {
410
+ showBanner();
411
+ const cwd = process.cwd();
412
+ console.log(chalk_1.default.cyan(' Starting development server...\n'));
298
413
  try {
299
- showBanner();
300
- // 1. Get Project Name
301
- let name = projectName;
302
- // 2. State-based Prompt Wizard
303
- const wizardSteps = [
304
- {
305
- id: 'name',
306
- type: 'input',
307
- message: 'Project Name:',
308
- validate: (input) => {
309
- if (input.trim() === '')
310
- return chalk_1.default.red('Project name is required!');
311
- if (!/^[a-zA-Z0-9-_]+$/.test(input))
312
- return chalk_1.default.red('Use only letters, numbers, hyphens, and underscores');
313
- return true;
314
- },
315
- skip: !!projectName
316
- },
317
- {
318
- id: 'language',
319
- type: 'list',
320
- message: 'Select Programming Language:',
321
- choices: (ans) => {
322
- const base = Object.values(types_1.ProjectLanguage);
323
- return ans.name ? ['<- Back', ...base] : base;
324
- }
325
- },
326
- {
327
- id: 'type',
328
- type: 'list',
329
- message: 'Select Project Type:',
330
- choices: ['<- Back', ...Object.values(types_1.ProjectType)]
331
- },
332
- {
333
- id: 'visibility',
334
- type: 'list',
335
- message: 'Select Repository Visibility:',
336
- choices: ['<- Back', 'Public', 'Private'],
337
- default: 'Private',
338
- when: (ans) => ans.type !== types_1.ProjectType.DSAPractice
339
- },
340
- {
341
- id: 'createRemote',
342
- type: 'list',
343
- message: 'Push to GitHub?',
344
- choices: ['<- Back', 'Yes', 'No'],
345
- default: 'No',
346
- when: (ans) => ans.type !== types_1.ProjectType.DSAPractice
347
- }
348
- ];
349
- const answers = { name: projectName };
350
- let stepIndex = projectName ? 1 : 0;
351
- while (stepIndex < wizardSteps.length) {
352
- const step = wizardSteps[stepIndex];
353
- // Skip if needed
354
- if (step.skip) {
355
- stepIndex++;
356
- continue;
357
- }
358
- // Handle 'when' logic manually
359
- if (typeof step.when === 'function' && !step.when(answers)) {
360
- stepIndex++;
361
- continue;
362
- }
363
- // Prepare choices (handle dynamic choices)
364
- const promptConfig = { ...step, name: 'value' };
365
- if (typeof step.choices === 'function') {
366
- promptConfig.choices = step.choices(answers);
367
- }
368
- const result = await inquirer_1.default.prompt([promptConfig]);
369
- const value = result.value;
370
- if (value === '<- Back') {
371
- // Go back
372
- stepIndex--;
373
- // Skip back further if the previous step was skipped or not shown
374
- while (stepIndex >= 0) {
375
- const prevStep = wizardSteps[stepIndex];
376
- const isSkipped = !!prevStep.skip;
377
- const whenCondition = prevStep.when;
378
- const isWhenFalse = typeof whenCondition === 'function' && !whenCondition(answers);
379
- if (isSkipped || isWhenFalse) {
380
- stepIndex--;
381
- }
382
- else {
383
- break;
414
+ const packageJsonPath = path.join(cwd, 'package.json');
415
+ if (fs.existsSync(packageJsonPath)) {
416
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
417
+ if (packageJson.scripts && packageJson.scripts.dev) {
418
+ (0, child_process_1.exec)('npm run dev', { cwd }, (error, stdout, stderr) => {
419
+ if (error) {
420
+ console.error(chalk_1.default.red('Failed to start dev server:'), stderr);
384
421
  }
385
- }
386
- if (stepIndex < 0)
387
- stepIndex = 0;
388
- continue;
422
+ });
389
423
  }
390
- // Save answer and move forward
391
- answers[step.id] = value;
392
- stepIndex++;
393
- }
394
- // Sync the name back in case it was set in the wizard
395
- name = answers.name;
396
- // Normalizing answers for later logic
397
- const normalizedAnswers = {
398
- ...answers,
399
- createRemote: answers.createRemote === 'Yes',
400
- visibility: answers.visibility
401
- };
402
- let githubToken = null;
403
- let authMethod = 'Personal Access Token';
404
- const gitManager = new GitManager_1.GitManager();
405
- // 3. GitHub Auth Flow (also support Back?)
406
- if (normalizedAnswers.createRemote) {
407
- const ghInstalled = await gitManager.checkGhInstalled();
408
- const ghAuth = await gitManager.checkGhAuth();
409
- if (ghInstalled && ghAuth) {
410
- const authAnswer = await inquirer_1.default.prompt([
411
- {
412
- type: 'list',
413
- name: 'method',
414
- message: 'How would you like to authenticate?',
415
- choices: ['<- Back', 'GitHub CLI (Recommended)', 'Personal Access Token']
424
+ else if (packageJson.scripts && packageJson.scripts.start) {
425
+ (0, child_process_1.exec)('npm start', { cwd }, (error, stdout, stderr) => {
426
+ if (error) {
427
+ console.error(chalk_1.default.red('Failed to start dev server:'), stderr);
416
428
  }
417
- ]);
418
- if (authAnswer.method === '<- Back') {
419
- // This is a special case, we'd need to loop the whole thing
420
- // For now, let's just allow returning to the 'Push to GitHub' question
421
- stepIndex--;
422
- // Re-run the main loop
423
- // Wait, to make this work we should probably put the auth into the wizard steps
424
- // But for simplicity, let's just move forward or let user re-run the whole tool
425
- }
426
- authMethod = authAnswer.method;
429
+ });
427
430
  }
428
- if (authMethod === 'Personal Access Token') {
429
- showGitHubTokenHelp();
430
- const tokenAnswer = await inquirer_1.default.prompt([
431
- {
432
- type: 'password',
433
- name: 'githubToken',
434
- message: 'Enter GitHub Token (or press Enter to skip):',
435
- mask: '*'
436
- }
437
- ]);
438
- githubToken = tokenAnswer.githubToken?.trim() || null;
439
- if (!githubToken) {
440
- console.log(chalk_1.default.yellow('\n Skipping GitHub integration (no token provided)'));
441
- console.log(chalk_1.default.gray(' You can manually push later with: ') + chalk_1.default.cyan('git remote add origin <url>'));
442
- normalizedAnswers.createRemote = false;
443
- }
431
+ else {
432
+ console.log(chalk_1.default.yellow(' No dev or start script found in package.json'));
444
433
  }
445
434
  }
446
- const cwd = process.cwd();
447
- const projectPath = path.join(cwd, name);
448
- if (fs.existsSync(projectPath)) {
449
- console.log();
450
- console.log(chalk_1.default.red(' Error: Directory "' + name + '" already exists!'));
451
- console.log(chalk_1.default.gray(' Try a different name or remove the existing directory'));
452
- console.log();
453
- process.exit(1);
435
+ else {
436
+ console.log(chalk_1.default.yellow(' No package.json found'));
454
437
  }
455
- const config = {
456
- name: name,
457
- language: answers.language,
458
- type: answers.type,
459
- isTheRepoPublic: answers.visibility === 'Public',
460
- description: `A ${answers.type} project in ${answers.language}`,
461
- path: projectPath
462
- };
463
- console.log();
464
- const spinner = (0, ora_1.default)({
465
- text: 'Initializing...',
466
- color: 'cyan',
467
- spinner: 'dots'
468
- }).start();
469
- try {
470
- // Managers
471
- const templateManager = new TemplateManager_1.TemplateManager();
472
- // gitManager already instantiated
473
- const gitHubService = githubToken ? new GitHubServiceCLI_1.GitHubServiceCLI(githubToken) : null;
474
- // 0. Check Git
475
- if (!await gitManager.checkGitInstalled()) {
476
- spinner.fail(chalk_1.default.red('Git is not installed!'));
477
- console.log(chalk_1.default.yellow('\n Install Git from: ') + chalk_1.default.cyan('https://git-scm.com/'));
478
- console.log();
479
- return;
480
- }
481
- // 1. Structure
482
- spinner.text = chalk_1.default.cyan('Generating project structure...');
483
- await templateManager.createProjectStructure(config);
484
- spinner.succeed(chalk_1.default.green('Project structure created'));
485
- // 2. Git Init & Initial Commits
486
- if (config.type !== types_1.ProjectType.DSAPractice) {
487
- spinner.start(chalk_1.default.cyan('Initializing Git repository...'));
488
- await gitManager.init(config.path);
489
- await gitManager.commit(config.path, "Initial commit");
490
- // Update README and create second commit
491
- try {
492
- const readmePath = path.join(config.path, 'README.md');
493
- if (fs.existsSync(readmePath)) {
494
- fs.appendFileSync(readmePath, '\n\nProject initialized by ZeroStart CLI');
495
- await gitManager.commit(config.path, "Update README.md");
496
- }
497
- }
498
- catch (error) {
499
- console.warn(`Failed to update README: ${error}`);
500
- }
501
- spinner.succeed(chalk_1.default.green('Git repository initialized'));
502
- }
503
- // 3. GitHub
504
- if (normalizedAnswers.createRemote) {
505
- spinner.start(chalk_1.default.cyan('Creating GitHub repository...'));
506
- let repoUrl;
507
- if (authMethod === 'GitHub CLI') {
508
- try {
509
- repoUrl = await gitManager.createRepoWithGh(config.path, config.name, config.isTheRepoPublic);
510
- }
511
- catch (error) {
512
- repoUrl = undefined;
513
- spinner.fail(chalk_1.default.red(`GH CLI Error: ${error.message}`));
514
- }
515
- }
516
- else if (gitHubService) {
517
- repoUrl = await gitHubService.createRepo(config);
518
- }
519
- if (repoUrl) {
520
- spinner.succeed(chalk_1.default.green('GitHub repository created'));
521
- spinner.start(chalk_1.default.cyan('Pushing to GitHub...'));
522
- // If GH CLI used, remote is already set to 'origin'
523
- if (authMethod !== 'GitHub CLI') {
524
- await gitManager.addRemote(config.path, repoUrl);
525
- }
526
- await gitManager.push(config.path);
527
- // Cleanup token if used in URL (only for non-CLI)
528
- if (authMethod !== 'GitHub CLI' && repoUrl.includes('@github.com')) {
529
- const cleanUrl = repoUrl.replace(/\/\/[^@]+@/, '//');
530
- await gitManager.setRemoteUrl(config.path, cleanUrl);
531
- }
532
- spinner.succeed(chalk_1.default.green('Pushed to GitHub'));
533
- }
534
- else {
535
- // Only warn if we actually tried to create a repo (repoUrl is undefined but we entered the block)
536
- if (!spinner.isSpinning) {
537
- // It might have failed in GH CLI block with spinner.fail
538
- }
539
- else {
540
- spinner.warn(chalk_1.default.yellow('GitHub repository creation failed'));
541
- console.log(chalk_1.default.gray(' Check your token permissions and try again'));
542
- }
543
- }
544
- }
545
- // Success message
546
- console.log();
547
- console.log(chalk_1.default.bold.green(' Success! Your project is ready!'));
548
- console.log();
549
- console.log(chalk_1.default.gray(' Location: ') + chalk_1.default.cyan(config.path));
550
- console.log();
551
- console.log(chalk_1.default.bold(' Get started:'));
552
- console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(`cd ${name}`));
553
- console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('code .') + chalk_1.default.gray(' (or your favorite editor)'));
554
- console.log();
555
- // Conditional logic based on language OR project type
556
- const isWebLanguage = [types_1.ProjectLanguage.React, types_1.ProjectLanguage.HTMLCSS].includes(config.language);
557
- const isPractice = config.type === types_1.ProjectType.DSAPractice;
558
- if (!isWebLanguage || isPractice) {
559
- const gdbLinks = {
560
- [types_1.ProjectLanguage.Python]: 'https://www.onlinegdb.com/online_python_compiler',
561
- [types_1.ProjectLanguage.Java]: 'https://www.onlinegdb.com/online_java_compiler',
562
- [types_1.ProjectLanguage.CPP]: 'https://www.onlinegdb.com/online_c++_compiler',
563
- [types_1.ProjectLanguage.NodeJS]: 'https://www.onlinegdb.com/online_node.js_compiler',
564
- [types_1.ProjectLanguage.React]: 'https://www.onlinegdb.com/' // Fallback
565
- };
566
- const link = gdbLinks[config.language] || 'https://www.onlinegdb.com/';
567
- if (link) {
568
- console.log(chalk_1.default.bold.yellow(' Practice Online:'));
569
- console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(link));
570
- console.log(chalk_1.default.gray(' (Opening in your browser...)'));
571
- console.log();
572
- openUrl(link);
573
- }
574
- return; // Skip deployment
575
- }
576
- // Deployment Integration (Only for Web)
577
- const { deploymentTarget } = await inquirer_1.default.prompt([
578
- {
579
- type: 'list',
580
- name: 'deploymentTarget',
581
- message: 'Select Deployment Provider:',
582
- choices: ['Vercel', 'None'],
583
- default: 'None'
584
- }
585
- ]);
586
- // 4. Install Dependencies & Build (Required for Deployment)
587
- if (deploymentTarget !== 'None' && config.language !== types_1.ProjectLanguage.HTMLCSS) {
588
- console.log();
589
- const iSpinner = (0, ora_1.default)('Installing dependencies...').start();
590
- try {
591
- await new Promise((resolve, reject) => {
592
- (0, child_process_1.exec)('npm install', { cwd: config.path }, (error, stdout, stderr) => {
593
- if (error)
594
- reject(error);
595
- else
596
- resolve(stdout);
597
- });
598
- });
599
- iSpinner.succeed(chalk_1.default.green('Dependencies installed'));
600
- if (config.language === types_1.ProjectLanguage.React || config.type === types_1.ProjectType.WebApp) {
601
- const bSpinner = (0, ora_1.default)('Building project...').start();
602
- await new Promise((resolve, reject) => {
603
- (0, child_process_1.exec)('npm run build', { cwd: config.path }, (error, stdout, stderr) => {
604
- if (error)
605
- reject(error);
606
- else
607
- resolve(stdout);
608
- });
609
- });
610
- bSpinner.succeed(chalk_1.default.green('Project built successfully'));
611
- }
612
- }
613
- catch (error) {
614
- iSpinner.fail(chalk_1.default.red(`Build failed: ${error.message}`));
615
- console.log(chalk_1.default.yellow(' Deployment might fail. Continuing...'));
616
- }
617
- }
618
- if (deploymentTarget === 'Vercel') {
619
- const vercelManager = new VercelManager_1.VercelManager();
620
- let vercelInstalled = await vercelManager.checkVercelInstalled();
621
- if (!vercelInstalled) {
622
- console.log();
623
- const { installVercel } = await inquirer_1.default.prompt([
624
- {
625
- type: 'confirm',
626
- name: 'installVercel',
627
- message: 'Vercel CLI not found. Install it globally?',
628
- default: false
629
- }
630
- ]);
631
- if (installVercel) {
632
- const iSpinner = (0, ora_1.default)('Installing Vercel CLI...').start();
633
- const installed = await vercelManager.installGlobal();
634
- if (installed) {
635
- iSpinner.succeed(chalk_1.default.green('Vercel CLI installed!'));
636
- vercelInstalled = true;
637
- }
638
- else {
639
- iSpinner.fail(chalk_1.default.red('Failed to install Vercel CLI.'));
640
- }
641
- }
642
- }
643
- if (vercelInstalled) {
644
- // Check Auth
645
- const vSpinner = (0, ora_1.default)('Checking Vercel authentication...').start();
646
- const loggedIn = await vercelManager.checkAuth();
647
- if (!loggedIn) {
648
- vSpinner.stop();
649
- console.log(chalk_1.default.yellow('\n Authentication required to deploy.'));
650
- const { doLogin } = await inquirer_1.default.prompt([
651
- {
652
- type: 'confirm',
653
- name: 'doLogin',
654
- message: 'Log in to Vercel now?',
655
- default: true
656
- }
657
- ]);
658
- if (doLogin) {
659
- console.log(chalk_1.default.gray(' Opening Vercel login...'));
660
- const loginSuccess = await vercelManager.login();
661
- if (!loginSuccess) {
662
- console.log(chalk_1.default.red('\n Login failed. Skipping deployment.'));
663
- return;
664
- }
665
- console.log(chalk_1.default.green('\n Successfully logged in!'));
666
- }
667
- else {
668
- console.log(chalk_1.default.yellow(' Skipping deployment (not logged in).'));
669
- return;
670
- }
671
- }
672
- else {
673
- vSpinner.succeed(chalk_1.default.green('Authenticated with Vercel'));
674
- }
675
- const dSpinner = (0, ora_1.default)('Deploying to Vercel...').start();
676
- const deploymentUrl = await vercelManager.deploy(config.path, config.name);
677
- if (deploymentUrl) {
678
- dSpinner.succeed(chalk_1.default.green('Deployed to Vercel!'));
679
- console.log(chalk_1.default.gray(' URL: ') + chalk_1.default.cyan(deploymentUrl));
680
- }
681
- else {
682
- dSpinner.fail(chalk_1.default.red('Vercel deployment failed'));
683
- console.log(chalk_1.default.gray(' Try running ') + chalk_1.default.cyan('vercel deploy') + chalk_1.default.gray(' manually inside the project folder.'));
684
- }
685
- }
438
+ }
439
+ catch (error) {
440
+ console.error(chalk_1.default.red('Error: ' + error.message));
441
+ }
442
+ });
443
+ // zerostart clean
444
+ program
445
+ .command('clean')
446
+ .description('Clean build artifacts and cache files')
447
+ .action(async () => {
448
+ showBanner();
449
+ const cwd = process.cwd();
450
+ const { confirm, reinstall } = await inquirer_1.default.prompt([
451
+ {
452
+ type: 'confirm',
453
+ name: 'confirm',
454
+ message: 'This will delete node_modules, dist, build, and cache files. Continue?',
455
+ default: false
456
+ },
457
+ {
458
+ type: 'confirm',
459
+ name: 'reinstall',
460
+ message: 'Reinstall dependencies after cleaning?',
461
+ default: true,
462
+ when: (ans) => ans.confirm
463
+ }
464
+ ]);
465
+ if (!confirm) {
466
+ console.log(chalk_1.default.yellow(' Cancelled'));
467
+ return;
468
+ }
469
+ const spinner = (0, ora_1.default)({ text: 'Cleaning...', color: 'cyan' }).start();
470
+ const dirsToClean = ['node_modules', 'dist', 'build', 'out', '.next', '.cache'];
471
+ for (const dir of dirsToClean) {
472
+ const dirPath = path.join(cwd, dir);
473
+ if (fs.existsSync(dirPath)) {
474
+ fs.rmSync(dirPath, { recursive: true, force: true });
475
+ spinner.text = `Removed ${dir}`;
476
+ }
477
+ }
478
+ spinner.succeed(chalk_1.default.green('Cleaned successfully!'));
479
+ if (reinstall) {
480
+ spinner.start('Reinstalling dependencies...');
481
+ (0, child_process_1.exec)('npm install', { cwd }, (error, stdout, stderr) => {
482
+ if (error) {
483
+ spinner.fail(chalk_1.default.red('Failed to reinstall dependencies'));
484
+ console.error(stderr);
686
485
  }
687
- if (!answers.createRemote || !githubToken && authMethod !== 'GitHub CLI') {
688
- console.log(chalk_1.default.bold.yellow(' To push to GitHub later:'));
689
- console.log(chalk_1.default.gray(' - Create a repository on GitHub'));
690
- console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('git remote add origin <your-repo-url>'));
691
- console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('git push -u origin main'));
692
- console.log();
486
+ else {
487
+ spinner.succeed(chalk_1.default.green('Dependencies reinstalled!'));
693
488
  }
489
+ });
490
+ }
491
+ });
492
+ // zerostart update
493
+ program
494
+ .command('update')
495
+ .description('Update ZeroStart CLI to the latest version')
496
+ .action(async () => {
497
+ showBanner();
498
+ const spinner = (0, ora_1.default)({ text: 'Checking for updates...', color: 'cyan' }).start();
499
+ (0, child_process_1.exec)('npm view zerostart-cli version', (error, stdout, stderr) => {
500
+ if (error) {
501
+ spinner.fail(chalk_1.default.red('Failed to check for updates'));
502
+ return;
503
+ }
504
+ const latestVersion = stdout.trim();
505
+ const currentVersion = '0.0.36';
506
+ if (latestVersion === currentVersion) {
507
+ spinner.succeed(chalk_1.default.green('You are using the latest version!'));
694
508
  }
695
- catch (error) {
696
- spinner.fail(chalk_1.default.red('Error: ' + error.message));
697
- console.log();
698
- console.log(chalk_1.default.gray(' Need help? Check the documentation or open an issue'));
699
- console.log();
509
+ else {
510
+ spinner.info(chalk_1.default.yellow(`Update available: ${currentVersion} ${latestVersion}`));
511
+ console.log(chalk_1.default.cyan(' Run: npm install -g zerostart-cli@latest'));
700
512
  }
513
+ });
514
+ });
515
+ // zerostart docs
516
+ program
517
+ .command('docs')
518
+ .description('Open comprehensive documentation in your browser')
519
+ .action(() => {
520
+ showBanner();
521
+ const docsUrl = 'https://github.com/yourusername/zerostart-cli#readme';
522
+ console.log(chalk_1.default.cyan(' Opening documentation...'));
523
+ openUrl(docsUrl);
524
+ });
525
+ // Standalone deployment commands
526
+ program
527
+ .command('deploy-vercel')
528
+ .description('Deploy the current project to Vercel')
529
+ .action(async () => {
530
+ showBanner();
531
+ const vercelManager = new VercelManager_1.VercelManager();
532
+ const cwd = process.cwd();
533
+ if (await vercelManager.checkAuth()) {
534
+ const { vName } = await inquirer_1.default.prompt([{ type: 'input', name: 'vName', message: 'Vercel Project Name:', default: path.basename(cwd) }]);
535
+ const url = await vercelManager.deploy(cwd, vName);
536
+ if (url)
537
+ console.log(chalk_1.default.green(' ✔ Deployed! URL: ') + chalk_1.default.cyan(url));
701
538
  }
702
- catch (error) {
703
- console.error(chalk_1.default.red('\n Unexpected error:'), error);
704
- console.log();
539
+ });
540
+ program
541
+ .command('deploy-netlify')
542
+ .description('Deploy the current project to Netlify')
543
+ .action(async () => {
544
+ showBanner();
545
+ const netlifyManager = new NetlifyManager_1.NetlifyManager();
546
+ const cwd = process.cwd();
547
+ if (await netlifyManager.checkAuth()) {
548
+ const { nName } = await inquirer_1.default.prompt([{ type: 'input', name: 'nName', message: 'Netlify Project Name:', default: path.basename(cwd) }]);
549
+ const res = await netlifyManager.createSite(nName, cwd);
550
+ const success = await netlifyManager.deploy(cwd, res.siteId);
551
+ if (success)
552
+ console.log(chalk_1.default.green(' ✔ Deployed to Netlify!'));
705
553
  }
706
554
  });
707
- // Helper functions for emojis (Now text based)
708
- function getLanguageEmoji(lang) {
709
- return '[LANG]';
710
- }
711
- function getTypeEmoji(type) {
712
- return '[TYPE]';
713
- }
555
+ // Shortcut commands (18 total)
556
+ const shortcuts = [
557
+ { cmd: 'dsa-py', lang: types_1.ProjectLanguage.Python, type: types_1.ProjectType.DSAPractice },
558
+ { cmd: 'dsa-java', lang: types_1.ProjectLanguage.Java, type: types_1.ProjectType.DSAPractice },
559
+ { cmd: 'dsa-cpp', lang: types_1.ProjectLanguage.CPP, type: types_1.ProjectType.DSAPractice },
560
+ { cmd: 'dsa-node', lang: types_1.ProjectLanguage.NodeJS, type: types_1.ProjectType.DSAPractice },
561
+ { cmd: 'web-react', lang: types_1.ProjectLanguage.React, type: types_1.ProjectType.WebApp },
562
+ { cmd: 'web-html', lang: types_1.ProjectLanguage.HTMLCSS, type: types_1.ProjectType.WebApp },
563
+ { cmd: 'web-node', lang: types_1.ProjectLanguage.NodeJS, type: types_1.ProjectType.WebApp },
564
+ { cmd: 'web-py', lang: types_1.ProjectLanguage.Python, type: types_1.ProjectType.WebApp },
565
+ { cmd: 'web-java', lang: types_1.ProjectLanguage.Java, type: types_1.ProjectType.WebApp },
566
+ { cmd: 'web-cpp', lang: types_1.ProjectLanguage.CPP, type: types_1.ProjectType.WebApp },
567
+ { cmd: 'cli-py', lang: types_1.ProjectLanguage.Python, type: types_1.ProjectType.CLITool },
568
+ { cmd: 'cli-node', lang: types_1.ProjectLanguage.NodeJS, type: types_1.ProjectType.CLITool },
569
+ { cmd: 'cli-java', lang: types_1.ProjectLanguage.Java, type: types_1.ProjectType.CLITool },
570
+ { cmd: 'cli-cpp', lang: types_1.ProjectLanguage.CPP, type: types_1.ProjectType.CLITool },
571
+ { cmd: 'ml-py', lang: types_1.ProjectLanguage.Python, type: types_1.ProjectType.MLProject },
572
+ { cmd: 'ml-node', lang: types_1.ProjectLanguage.NodeJS, type: types_1.ProjectType.MLProject },
573
+ { cmd: 'ml-java', lang: types_1.ProjectLanguage.Java, type: types_1.ProjectType.MLProject },
574
+ { cmd: 'ml-cpp', lang: types_1.ProjectLanguage.CPP, type: types_1.ProjectType.MLProject },
575
+ ];
576
+ shortcuts.forEach(s => {
577
+ program.command(s.cmd).argument('[name]', 'Project name', `my-${s.cmd}`).action(n => {
578
+ showBanner();
579
+ initializeProject(n, s.lang, s.type, { isPublic: false, createRemote: false, githubToken: null, authMethod: 'none' });
580
+ });
581
+ });
582
+ // Main wizard
583
+ program.argument('[projectName]').action(async (projectName) => {
584
+ showBanner();
585
+ let name = projectName;
586
+ const answers = await inquirer_1.default.prompt([
587
+ { type: 'input', name: 'name', message: 'Project Name:', skip: !!projectName, when: !projectName },
588
+ { type: 'list', name: 'language', message: 'Language:', choices: Object.values(types_1.ProjectLanguage) },
589
+ { type: 'list', name: 'type', message: 'Type:', choices: Object.values(types_1.ProjectType) },
590
+ { type: 'list', name: 'createRemote', message: 'Push to GitHub?', choices: ['Yes', 'No'], default: 'No', when: (ans) => ans.type !== types_1.ProjectType.DSAPractice }
591
+ ]);
592
+ await initializeProject(projectName || answers.name, answers.language, answers.type, {
593
+ isPublic: false,
594
+ createRemote: answers.createRemote === 'Yes',
595
+ githubToken: null,
596
+ authMethod: 'none'
597
+ });
598
+ });
714
599
  program.parse(process.argv);