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/COMMAND_EXAMPLES.md +262 -0
- package/COMMAND_VERIFICATION.md +275 -0
- package/QUICK_TEST_GUIDE.md +140 -0
- package/README.md +27 -0
- package/VERIFICATION_SUMMARY.md +213 -0
- package/out/cli.js +473 -588
- package/package.json +1 -1
- package/test-commands.sh +190 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
return;
|
|
128
|
+
spinner.warn(chalk_1.default.yellow('GitHub repository creation failed'));
|
|
112
129
|
}
|
|
113
130
|
}
|
|
114
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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('
|
|
172
|
-
.description('
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
331
|
+
console.log(chalk_1.default.yellow(' No .env file found'));
|
|
199
332
|
}
|
|
200
333
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
297
|
-
.
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
429
|
-
|
|
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
|
-
|
|
447
|
-
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
688
|
-
|
|
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
|
-
|
|
696
|
-
spinner.
|
|
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
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
//
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
|
|
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);
|