zerostart-cli 0.0.42 → 0.0.44
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 +35 -5
- package/out/commands/ai.js +175 -0
- package/out/managers/ConfigManager.js +83 -0
- package/out/managers/TemplateManager.js +314 -1
- package/out/services/aiService.js +82 -0
- package/out/types.js +4 -0
- package/package.json +5 -2
package/out/cli.js
CHANGED
|
@@ -51,6 +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 open_1 = __importDefault(require("open"));
|
|
54
|
+
const ai_1 = require("./commands/ai");
|
|
54
55
|
const program = new commander_1.Command();
|
|
55
56
|
// Basic ASCII banner
|
|
56
57
|
function showBanner() {
|
|
@@ -179,7 +180,7 @@ async function initializeProject(name, language, type, options) {
|
|
|
179
180
|
program
|
|
180
181
|
.name('zerostart')
|
|
181
182
|
.description('Create and deploy a complete project with one command')
|
|
182
|
-
.version('0.0.
|
|
183
|
+
.version('0.0.44');
|
|
183
184
|
// zerostart init [project-name]
|
|
184
185
|
program
|
|
185
186
|
.command('init [project-name]')
|
|
@@ -188,6 +189,14 @@ program
|
|
|
188
189
|
showBanner();
|
|
189
190
|
await startWizard(projectName);
|
|
190
191
|
});
|
|
192
|
+
// zerostart ai
|
|
193
|
+
program
|
|
194
|
+
.command('ai [prompt]')
|
|
195
|
+
.description('Generate a project using AI based on your description')
|
|
196
|
+
.action(async (prompt) => {
|
|
197
|
+
showBanner();
|
|
198
|
+
await (0, ai_1.handleAICommand)(prompt);
|
|
199
|
+
});
|
|
191
200
|
// zerostart deploy
|
|
192
201
|
program
|
|
193
202
|
.command('deploy')
|
|
@@ -509,7 +518,7 @@ program
|
|
|
509
518
|
return;
|
|
510
519
|
}
|
|
511
520
|
const latestVersion = stdout.trim();
|
|
512
|
-
const currentVersion = '0.0.
|
|
521
|
+
const currentVersion = '0.0.44';
|
|
513
522
|
if (latestVersion === currentVersion) {
|
|
514
523
|
spinner.succeed(chalk_1.default.green('You are using the latest version!'));
|
|
515
524
|
}
|
|
@@ -568,6 +577,10 @@ const shortcuts = [
|
|
|
568
577
|
{ cmd: 'dsa-java', lang: types_1.ProjectLanguage.Java, type: types_1.ProjectType.DSAPractice },
|
|
569
578
|
{ cmd: 'dsa-cpp', lang: types_1.ProjectLanguage.CPP, type: types_1.ProjectType.DSAPractice },
|
|
570
579
|
{ cmd: 'web-react', lang: types_1.ProjectLanguage.React, type: types_1.ProjectType.WebApp },
|
|
580
|
+
{ cmd: 'web-next', lang: types_1.ProjectLanguage.Nextjs, type: types_1.ProjectType.WebApp },
|
|
581
|
+
{ cmd: 'web-vue', lang: types_1.ProjectLanguage.Vue, type: types_1.ProjectType.WebApp },
|
|
582
|
+
{ cmd: 'web-svelte', lang: types_1.ProjectLanguage.Svelte, type: types_1.ProjectType.WebApp },
|
|
583
|
+
{ cmd: 'web-express', lang: types_1.ProjectLanguage.Express, type: types_1.ProjectType.WebApp },
|
|
571
584
|
{ cmd: 'web-html', lang: types_1.ProjectLanguage.HTMLCSS, type: types_1.ProjectType.WebApp },
|
|
572
585
|
{ cmd: 'web-py', lang: types_1.ProjectLanguage.Python, type: types_1.ProjectType.WebApp },
|
|
573
586
|
{ cmd: 'web-java', lang: types_1.ProjectLanguage.Java, type: types_1.ProjectType.WebApp },
|
|
@@ -618,7 +631,17 @@ async function startWizard(initialName) {
|
|
|
618
631
|
}
|
|
619
632
|
else if (step === 2) {
|
|
620
633
|
const langChoices = category === CAT_WEB
|
|
621
|
-
? [
|
|
634
|
+
? [
|
|
635
|
+
types_1.ProjectLanguage.React,
|
|
636
|
+
types_1.ProjectLanguage.Nextjs,
|
|
637
|
+
types_1.ProjectLanguage.Vue,
|
|
638
|
+
types_1.ProjectLanguage.Svelte,
|
|
639
|
+
types_1.ProjectLanguage.Express,
|
|
640
|
+
types_1.ProjectLanguage.TypeScript,
|
|
641
|
+
types_1.ProjectLanguage.HTMLCSS,
|
|
642
|
+
new inquirer_1.default.Separator(),
|
|
643
|
+
BACK
|
|
644
|
+
]
|
|
622
645
|
: [types_1.ProjectLanguage.CPP, types_1.ProjectLanguage.Java, types_1.ProjectLanguage.Python, new inquirer_1.default.Separator(), BACK];
|
|
623
646
|
const langAns = await inquirer_1.default.prompt([{
|
|
624
647
|
type: 'list',
|
|
@@ -793,11 +816,18 @@ async function startWizard(initialName) {
|
|
|
793
816
|
else if (category === CAT_WEB && deployChoice === 'local') {
|
|
794
817
|
console.log();
|
|
795
818
|
console.log(chalk_1.default.bold.cyan(' 💻 Run your project locally:'));
|
|
796
|
-
if (
|
|
819
|
+
if ([types_1.ProjectLanguage.React, types_1.ProjectLanguage.Nextjs, types_1.ProjectLanguage.Vue, types_1.ProjectLanguage.Svelte, types_1.ProjectLanguage.TypeScript].includes(language)) {
|
|
820
|
+
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(`cd ${name}`));
|
|
821
|
+
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('npm install'));
|
|
822
|
+
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('npm run dev'));
|
|
823
|
+
const port = language === types_1.ProjectLanguage.Nextjs ? '3000' : '5173';
|
|
824
|
+
console.log(chalk_1.default.gray('\n Then open: ') + chalk_1.default.cyan(`http://localhost:${port}`));
|
|
825
|
+
}
|
|
826
|
+
else if (language === types_1.ProjectLanguage.Express) {
|
|
797
827
|
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(`cd ${name}`));
|
|
798
828
|
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('npm install'));
|
|
799
829
|
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan('npm run dev'));
|
|
800
|
-
console.log(chalk_1.default.gray('\n
|
|
830
|
+
console.log(chalk_1.default.gray('\n Server running on: ') + chalk_1.default.cyan('http://localhost:3000'));
|
|
801
831
|
}
|
|
802
832
|
else if (language === types_1.ProjectLanguage.HTMLCSS) {
|
|
803
833
|
console.log(chalk_1.default.gray(' - ') + chalk_1.default.cyan(`cd ${name}`));
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.handleAICommand = handleAICommand;
|
|
40
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
41
|
+
const ora_1 = __importDefault(require("ora"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const fs = __importStar(require("fs-extra"));
|
|
45
|
+
const aiService_1 = require("../services/aiService");
|
|
46
|
+
const ConfigManager_1 = require("../managers/ConfigManager");
|
|
47
|
+
const open_1 = __importDefault(require("open"));
|
|
48
|
+
async function ensureAuth() {
|
|
49
|
+
const configManager = new ConfigManager_1.ConfigManager();
|
|
50
|
+
const existingKey = await configManager.getOpenAIApiKey();
|
|
51
|
+
if (existingKey) {
|
|
52
|
+
return existingKey;
|
|
53
|
+
}
|
|
54
|
+
console.log(chalk_1.default.yellow(' OpenAI API Key not found.'));
|
|
55
|
+
console.log(chalk_1.default.gray(' To use the AI Architect, you need an API key from OpenAI.'));
|
|
56
|
+
console.log(chalk_1.default.cyan(' 1. Visit: ') + chalk_1.default.white('https://platform.openai.com/api-keys'));
|
|
57
|
+
console.log(chalk_1.default.cyan(' 2. Create a new secret key.'));
|
|
58
|
+
console.log(chalk_1.default.cyan(' 3. Paste it here to save it for future use.\n'));
|
|
59
|
+
const { openLink } = await inquirer_1.default.prompt([{
|
|
60
|
+
type: 'confirm',
|
|
61
|
+
name: 'openLink',
|
|
62
|
+
message: 'Would you like to open the OpenAI API key page in your browser?',
|
|
63
|
+
default: true
|
|
64
|
+
}]);
|
|
65
|
+
if (openLink) {
|
|
66
|
+
await (0, open_1.default)('https://platform.openai.com/api-keys');
|
|
67
|
+
}
|
|
68
|
+
const { apiKey } = await inquirer_1.default.prompt([{
|
|
69
|
+
type: 'password',
|
|
70
|
+
name: 'apiKey',
|
|
71
|
+
message: 'Paste your OpenAI API Key:',
|
|
72
|
+
validate: (input) => input.startsWith('sk-') || 'Invalid key format. Usually starts with "sk-"'
|
|
73
|
+
}]);
|
|
74
|
+
const { saveKey } = await inquirer_1.default.prompt([{
|
|
75
|
+
type: 'confirm',
|
|
76
|
+
name: 'saveKey',
|
|
77
|
+
message: 'Save this key locally for future use?',
|
|
78
|
+
default: true
|
|
79
|
+
}]);
|
|
80
|
+
if (saveKey) {
|
|
81
|
+
await configManager.setOpenAIApiKey(apiKey);
|
|
82
|
+
console.log(chalk_1.default.green(' ✔ Key saved successfully to ~/.zerostart/config.json\n'));
|
|
83
|
+
}
|
|
84
|
+
return apiKey;
|
|
85
|
+
}
|
|
86
|
+
async function handleAICommand(initialPrompt) {
|
|
87
|
+
console.log(chalk_1.default.bold.cyan('\n ✨ ZeroStart AI Architect'));
|
|
88
|
+
console.log(chalk_1.default.gray(' Describe your project and let AI build the foundation.\n'));
|
|
89
|
+
const apiKey = await ensureAuth();
|
|
90
|
+
if (!apiKey)
|
|
91
|
+
return;
|
|
92
|
+
let description = initialPrompt;
|
|
93
|
+
let stack = '';
|
|
94
|
+
if (!description) {
|
|
95
|
+
const answers = await inquirer_1.default.prompt([
|
|
96
|
+
{
|
|
97
|
+
type: 'input',
|
|
98
|
+
name: 'description',
|
|
99
|
+
message: 'Describe your project idea:',
|
|
100
|
+
validate: (input) => input.length > 5 || 'Please provide a more detailed description.'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'input',
|
|
104
|
+
name: 'stack',
|
|
105
|
+
message: 'Preferred stack (optional, e.g., React, Next.js, Express):',
|
|
106
|
+
default: ''
|
|
107
|
+
}
|
|
108
|
+
]);
|
|
109
|
+
description = answers.description;
|
|
110
|
+
stack = answers.stack;
|
|
111
|
+
}
|
|
112
|
+
const spinner = (0, ora_1.default)({
|
|
113
|
+
text: 'AI is architecting your project...',
|
|
114
|
+
color: 'cyan'
|
|
115
|
+
}).start();
|
|
116
|
+
try {
|
|
117
|
+
const aiService = new aiService_1.AIService(apiKey);
|
|
118
|
+
const projectTemplate = await aiService.generateProject(description, stack);
|
|
119
|
+
spinner.text = 'AI analysis complete. Generating files...';
|
|
120
|
+
const rootDir = process.cwd();
|
|
121
|
+
// Sanitize project name: lowercase, replace spaces/special chars with dashes, remove redundant dashes
|
|
122
|
+
const sanitizedName = projectTemplate.projectName
|
|
123
|
+
.toLowerCase()
|
|
124
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
125
|
+
.replace(/\s+/g, '-')
|
|
126
|
+
.replace(/-+/g, '-')
|
|
127
|
+
.trim();
|
|
128
|
+
// Final safety check: ensuring it stays within the current directory
|
|
129
|
+
const projectDir = path.join(rootDir, path.basename(sanitizedName));
|
|
130
|
+
// Check if directory exists
|
|
131
|
+
if (fs.existsSync(projectDir)) {
|
|
132
|
+
spinner.stop();
|
|
133
|
+
const { overwrite } = await inquirer_1.default.prompt([
|
|
134
|
+
{
|
|
135
|
+
type: 'confirm',
|
|
136
|
+
name: 'overwrite',
|
|
137
|
+
message: `Directory ${chalk_1.default.yellow(projectTemplate.projectName)} already exists. Overwrite?`,
|
|
138
|
+
default: false
|
|
139
|
+
}
|
|
140
|
+
]);
|
|
141
|
+
if (!overwrite) {
|
|
142
|
+
console.log(chalk_1.default.yellow('\n Aborted. No files were created.'));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
spinner.start('Removing existing directory...');
|
|
146
|
+
await fs.remove(projectDir);
|
|
147
|
+
spinner.text = 'Generating files...';
|
|
148
|
+
}
|
|
149
|
+
// Create folders
|
|
150
|
+
await fs.ensureDir(projectDir);
|
|
151
|
+
for (const folder of projectTemplate.folders) {
|
|
152
|
+
await fs.ensureDir(path.join(projectDir, folder));
|
|
153
|
+
}
|
|
154
|
+
// Create files
|
|
155
|
+
for (const file of projectTemplate.files) {
|
|
156
|
+
const filePath = path.join(projectDir, file.path);
|
|
157
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
158
|
+
await fs.writeFile(filePath, file.content);
|
|
159
|
+
}
|
|
160
|
+
spinner.succeed(chalk_1.default.green(` ✔ Project "${projectTemplate.projectName}" generated successfully!`));
|
|
161
|
+
console.log('\n' + chalk_1.default.bold(' Next Steps:'));
|
|
162
|
+
console.log(chalk_1.default.cyan(` cd ${path.basename(projectDir)}`));
|
|
163
|
+
if (projectTemplate.dependencies.length > 0 || projectTemplate.devDependencies.length > 0) {
|
|
164
|
+
console.log(chalk_1.default.cyan(' npm install'));
|
|
165
|
+
}
|
|
166
|
+
console.log(chalk_1.default.cyan(' npm start (or your project\'s start command)'));
|
|
167
|
+
console.log();
|
|
168
|
+
if (projectTemplate.dependencies.length > 0) {
|
|
169
|
+
console.log(chalk_1.default.gray(' Key dependencies suggested: ') + chalk_1.default.white(projectTemplate.dependencies.slice(0, 5).join(', ') + (projectTemplate.dependencies.length > 5 ? '...' : '')));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
spinner.fail(chalk_1.default.red(` Error: ${error.message}`));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ConfigManager = void 0;
|
|
37
|
+
const fs = __importStar(require("fs-extra"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
class ConfigManager {
|
|
41
|
+
constructor() {
|
|
42
|
+
// Store config in ~/.zerostart/config.json
|
|
43
|
+
const homeDir = os.homedir();
|
|
44
|
+
const configDir = path.join(homeDir, '.zerostart');
|
|
45
|
+
this.configPath = path.join(configDir, 'config.json');
|
|
46
|
+
}
|
|
47
|
+
async ensureConfigDir() {
|
|
48
|
+
const configDir = path.dirname(this.configPath);
|
|
49
|
+
if (!fs.existsSync(configDir)) {
|
|
50
|
+
await fs.ensureDir(configDir);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async getConfig() {
|
|
54
|
+
try {
|
|
55
|
+
if (fs.existsSync(this.configPath)) {
|
|
56
|
+
const content = await fs.readFile(this.configPath, 'utf8');
|
|
57
|
+
return JSON.parse(content);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error('Error reading config file:', error);
|
|
62
|
+
}
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
async setConfig(newConfig) {
|
|
66
|
+
await this.ensureConfigDir();
|
|
67
|
+
const currentConfig = await this.getConfig();
|
|
68
|
+
const updatedConfig = { ...currentConfig, ...newConfig };
|
|
69
|
+
await fs.writeFile(this.configPath, JSON.stringify(updatedConfig, null, 2));
|
|
70
|
+
}
|
|
71
|
+
async getOpenAIApiKey() {
|
|
72
|
+
// Environment variable takes precedence
|
|
73
|
+
if (process.env.OPENAI_API_KEY) {
|
|
74
|
+
return process.env.OPENAI_API_KEY;
|
|
75
|
+
}
|
|
76
|
+
const config = await this.getConfig();
|
|
77
|
+
return config.openaiApiKey;
|
|
78
|
+
}
|
|
79
|
+
async setOpenAIApiKey(key) {
|
|
80
|
+
await this.setConfig({ openaiApiKey: key });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.ConfigManager = ConfigManager;
|
|
@@ -63,6 +63,18 @@ class TemplateManager {
|
|
|
63
63
|
case types_1.ProjectLanguage.React:
|
|
64
64
|
await this.createReactStructure(config);
|
|
65
65
|
break;
|
|
66
|
+
case types_1.ProjectLanguage.Nextjs:
|
|
67
|
+
await this.createNextjsStructure(config);
|
|
68
|
+
break;
|
|
69
|
+
case types_1.ProjectLanguage.Vue:
|
|
70
|
+
await this.createVueStructure(config);
|
|
71
|
+
break;
|
|
72
|
+
case types_1.ProjectLanguage.Svelte:
|
|
73
|
+
await this.createSvelteStructure(config);
|
|
74
|
+
break;
|
|
75
|
+
case types_1.ProjectLanguage.Express:
|
|
76
|
+
await this.createExpressStructure(config);
|
|
77
|
+
break;
|
|
66
78
|
case types_1.ProjectLanguage.TypeScript:
|
|
67
79
|
await this.createTypeScriptStructure(config);
|
|
68
80
|
break;
|
|
@@ -181,8 +193,12 @@ npm start # or python main.py / java -jar target/app.jar
|
|
|
181
193
|
let content = '';
|
|
182
194
|
switch (config.language) {
|
|
183
195
|
case types_1.ProjectLanguage.React:
|
|
196
|
+
case types_1.ProjectLanguage.Nextjs:
|
|
197
|
+
case types_1.ProjectLanguage.Vue:
|
|
198
|
+
case types_1.ProjectLanguage.Svelte:
|
|
199
|
+
case types_1.ProjectLanguage.Express:
|
|
184
200
|
case types_1.ProjectLanguage.TypeScript:
|
|
185
|
-
content = 'node_modules/\n.env\ndist/\nbuild/';
|
|
201
|
+
content = 'node_modules/\n.env\ndist/\nbuild/\n.next/\n.svelte-kit/';
|
|
186
202
|
break;
|
|
187
203
|
case types_1.ProjectLanguage.Python:
|
|
188
204
|
content = '__pycache__/\n*.pyc\nvenv/\n.env';
|
|
@@ -400,6 +416,303 @@ console.log("${config.name} initialized!");
|
|
|
400
416
|
`;
|
|
401
417
|
fs.writeFileSync(path.join(config.path, 'netlify.toml'), netlifyContent);
|
|
402
418
|
}
|
|
419
|
+
async createNextjsStructure(config) {
|
|
420
|
+
const packageJson = {
|
|
421
|
+
name: config.name,
|
|
422
|
+
version: "0.1.0",
|
|
423
|
+
private: true,
|
|
424
|
+
scripts: {
|
|
425
|
+
dev: "next dev",
|
|
426
|
+
build: "next build",
|
|
427
|
+
start: "next start",
|
|
428
|
+
lint: "next lint"
|
|
429
|
+
},
|
|
430
|
+
dependencies: {
|
|
431
|
+
"next": "14.1.0",
|
|
432
|
+
"react": "^18",
|
|
433
|
+
"react-dom": "^18"
|
|
434
|
+
},
|
|
435
|
+
devDependencies: {
|
|
436
|
+
"typescript": "^5",
|
|
437
|
+
"@types/node": "^20",
|
|
438
|
+
"@types/react": "^18",
|
|
439
|
+
"@types/react-dom": "^18",
|
|
440
|
+
"autoprefixer": "^10.0.1",
|
|
441
|
+
"postcss": "^8",
|
|
442
|
+
"tailwindcss": "^3.3.0",
|
|
443
|
+
"eslint": "^8",
|
|
444
|
+
"eslint-config-next": "14.1.0"
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
fs.writeFileSync(path.join(config.path, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
448
|
+
const app = path.join(config.path, 'app');
|
|
449
|
+
if (!fs.existsSync(app))
|
|
450
|
+
fs.mkdirSync(app);
|
|
451
|
+
fs.writeFileSync(path.join(app, 'layout.tsx'), `
|
|
452
|
+
import type { Metadata } from "next";
|
|
453
|
+
import "./globals.css";
|
|
454
|
+
|
|
455
|
+
export const metadata: Metadata = {
|
|
456
|
+
title: "${config.name}",
|
|
457
|
+
description: "${config.description}",
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
export default function RootLayout({
|
|
461
|
+
children,
|
|
462
|
+
}: Readonly<{
|
|
463
|
+
children: React.ReactNode;
|
|
464
|
+
}>) {
|
|
465
|
+
return (
|
|
466
|
+
<html lang="en">
|
|
467
|
+
<body>{children}</body>
|
|
468
|
+
</html>
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
`);
|
|
472
|
+
fs.writeFileSync(path.join(app, 'page.tsx'), `
|
|
473
|
+
export default function Home() {
|
|
474
|
+
return (
|
|
475
|
+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
|
476
|
+
<h1 className="text-4xl font-bold">Welcome to ${config.name} (Next.js)</h1>
|
|
477
|
+
</main>
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
`);
|
|
481
|
+
fs.writeFileSync(path.join(app, 'globals.css'), `
|
|
482
|
+
@tailwind base;
|
|
483
|
+
@tailwind components;
|
|
484
|
+
@tailwind utilities;
|
|
485
|
+
`);
|
|
486
|
+
fs.writeFileSync(path.join(config.path, 'tailwind.config.ts'), `
|
|
487
|
+
import type { Config } from "tailwindcss";
|
|
488
|
+
|
|
489
|
+
const config: Config = {
|
|
490
|
+
content: [
|
|
491
|
+
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
|
492
|
+
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
493
|
+
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
|
494
|
+
],
|
|
495
|
+
theme: {
|
|
496
|
+
extend: {},
|
|
497
|
+
},
|
|
498
|
+
plugins: [],
|
|
499
|
+
};
|
|
500
|
+
export default config;
|
|
501
|
+
`);
|
|
502
|
+
fs.writeFileSync(path.join(config.path, 'next.config.mjs'), `
|
|
503
|
+
/** @type {import('next').NextConfig} */
|
|
504
|
+
const nextConfig = {};
|
|
505
|
+
|
|
506
|
+
export default nextConfig;
|
|
507
|
+
`);
|
|
508
|
+
fs.writeFileSync(path.join(config.path, 'tsconfig.json'), `
|
|
509
|
+
{
|
|
510
|
+
"compilerOptions": {
|
|
511
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
512
|
+
"allowJs": true,
|
|
513
|
+
"skipLibCheck": true,
|
|
514
|
+
"strict": true,
|
|
515
|
+
"noEmit": true,
|
|
516
|
+
"esModuleInterop": true,
|
|
517
|
+
"module": "esnext",
|
|
518
|
+
"moduleResolution": "bundler",
|
|
519
|
+
"resolveJsonModule": true,
|
|
520
|
+
"isolatedModules": true,
|
|
521
|
+
"jsx": "preserve",
|
|
522
|
+
"incremental": true,
|
|
523
|
+
"plugins": [
|
|
524
|
+
{
|
|
525
|
+
"name": "next"
|
|
526
|
+
}
|
|
527
|
+
],
|
|
528
|
+
"paths": {
|
|
529
|
+
"@/*": ["./*"]
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
533
|
+
"exclude": ["node_modules"]
|
|
534
|
+
}
|
|
535
|
+
`);
|
|
536
|
+
this.createNetlifyConfig(config);
|
|
537
|
+
}
|
|
538
|
+
async createVueStructure(config) {
|
|
539
|
+
const packageJson = {
|
|
540
|
+
name: config.name,
|
|
541
|
+
version: "0.0.0",
|
|
542
|
+
private: true,
|
|
543
|
+
type: "module",
|
|
544
|
+
scripts: {
|
|
545
|
+
dev: "vite",
|
|
546
|
+
build: "vue-tsc && vite build",
|
|
547
|
+
preview: "vite preview"
|
|
548
|
+
},
|
|
549
|
+
dependencies: {
|
|
550
|
+
"vue": "^3.4.15"
|
|
551
|
+
},
|
|
552
|
+
devDependencies: {
|
|
553
|
+
"@vitejs/plugin-vue": "^5.0.3",
|
|
554
|
+
"typescript": "^5.3.3",
|
|
555
|
+
"vite": "^5.0.12",
|
|
556
|
+
"vue-tsc": "^1.8.27"
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
fs.writeFileSync(path.join(config.path, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
560
|
+
const src = path.join(config.path, 'src');
|
|
561
|
+
if (!fs.existsSync(src))
|
|
562
|
+
fs.mkdirSync(src);
|
|
563
|
+
fs.writeFileSync(path.join(src, 'App.vue'), `
|
|
564
|
+
<template>
|
|
565
|
+
<div>
|
|
566
|
+
<h1>Hello Vue 3 + Vite!</h1>
|
|
567
|
+
</div>
|
|
568
|
+
</template>
|
|
569
|
+
`);
|
|
570
|
+
fs.writeFileSync(path.join(src, 'main.ts'), `
|
|
571
|
+
import { createApp } from 'vue'
|
|
572
|
+
import App from './App.vue'
|
|
573
|
+
|
|
574
|
+
createApp(App).mount('#app')
|
|
575
|
+
`);
|
|
576
|
+
fs.writeFileSync(path.join(config.path, 'index.html'), `
|
|
577
|
+
<!DOCTYPE html>
|
|
578
|
+
<html lang="en">
|
|
579
|
+
<head>
|
|
580
|
+
<meta charset="UTF-8" />
|
|
581
|
+
<title>${config.name}</title>
|
|
582
|
+
</head>
|
|
583
|
+
<body>
|
|
584
|
+
<div id="app"></div>
|
|
585
|
+
<script type="module" src="/src/main.ts"></script>
|
|
586
|
+
</body>
|
|
587
|
+
</html>
|
|
588
|
+
`);
|
|
589
|
+
fs.writeFileSync(path.join(config.path, 'vite.config.ts'), `
|
|
590
|
+
import { defineConfig } from 'vite'
|
|
591
|
+
import vue from '@vitejs/plugin-vue'
|
|
592
|
+
|
|
593
|
+
export default defineConfig({
|
|
594
|
+
plugins: [vue()],
|
|
595
|
+
})
|
|
596
|
+
`);
|
|
597
|
+
this.createNetlifyConfig(config);
|
|
598
|
+
}
|
|
599
|
+
async createSvelteStructure(config) {
|
|
600
|
+
const packageJson = {
|
|
601
|
+
name: config.name,
|
|
602
|
+
version: "0.0.1",
|
|
603
|
+
private: true,
|
|
604
|
+
type: "module",
|
|
605
|
+
scripts: {
|
|
606
|
+
dev: "vite",
|
|
607
|
+
build: "vite build",
|
|
608
|
+
preview: "vite preview",
|
|
609
|
+
check: "svelte-check --tsconfig ./tsconfig.json"
|
|
610
|
+
},
|
|
611
|
+
devDependencies: {
|
|
612
|
+
"@sveltejs/vite-plugin-svelte": "^3.0.1",
|
|
613
|
+
"@tsconfig/svelte": "^5.0.2",
|
|
614
|
+
"svelte": "^4.2.9",
|
|
615
|
+
"svelte-check": "^3.6.3",
|
|
616
|
+
"tslib": "^2.6.2",
|
|
617
|
+
"typescript": "^5.3.3",
|
|
618
|
+
"vite": "^5.0.12"
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
fs.writeFileSync(path.join(config.path, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
622
|
+
const src = path.join(config.path, 'src');
|
|
623
|
+
if (!fs.existsSync(src))
|
|
624
|
+
fs.mkdirSync(src);
|
|
625
|
+
fs.writeFileSync(path.join(src, 'App.svelte'), `
|
|
626
|
+
<script lang="ts">
|
|
627
|
+
let name = "${config.name}";
|
|
628
|
+
</script>
|
|
629
|
+
|
|
630
|
+
<main>
|
|
631
|
+
<h1>Hello {name} (Svelte)</h1>
|
|
632
|
+
</main>
|
|
633
|
+
`);
|
|
634
|
+
fs.writeFileSync(path.join(src, 'main.ts'), `
|
|
635
|
+
import './app.css'
|
|
636
|
+
import App from './App.svelte'
|
|
637
|
+
|
|
638
|
+
const app = new App({
|
|
639
|
+
target: document.getElementById('app')!,
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
export default app
|
|
643
|
+
`);
|
|
644
|
+
fs.writeFileSync(path.join(src, 'app.css'), `
|
|
645
|
+
:root {
|
|
646
|
+
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
647
|
+
}
|
|
648
|
+
`);
|
|
649
|
+
fs.writeFileSync(path.join(config.path, 'index.html'), `
|
|
650
|
+
<!doctype html>
|
|
651
|
+
<html lang="en">
|
|
652
|
+
<head>
|
|
653
|
+
<meta charset="UTF-8" />
|
|
654
|
+
<title>${config.name}</title>
|
|
655
|
+
</head>
|
|
656
|
+
<body>
|
|
657
|
+
<div id="app"></div>
|
|
658
|
+
<script type="module" src="/src/main.ts"></script>
|
|
659
|
+
</body>
|
|
660
|
+
</html>
|
|
661
|
+
`);
|
|
662
|
+
fs.writeFileSync(path.join(config.path, 'vite.config.ts'), `
|
|
663
|
+
import { defineConfig } from 'vite'
|
|
664
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
|
665
|
+
|
|
666
|
+
export default defineConfig({
|
|
667
|
+
plugins: [svelte()],
|
|
668
|
+
})
|
|
669
|
+
`);
|
|
670
|
+
this.createNetlifyConfig(config);
|
|
671
|
+
}
|
|
672
|
+
async createExpressStructure(config) {
|
|
673
|
+
const packageJson = {
|
|
674
|
+
name: config.name,
|
|
675
|
+
version: "1.0.0",
|
|
676
|
+
description: config.description,
|
|
677
|
+
main: "src/index.js",
|
|
678
|
+
scripts: {
|
|
679
|
+
start: "node src/index.js",
|
|
680
|
+
dev: "nodemon src/index.js"
|
|
681
|
+
},
|
|
682
|
+
dependencies: {
|
|
683
|
+
"express": "^4.18.2",
|
|
684
|
+
"cors": "^2.8.5",
|
|
685
|
+
"dotenv": "^16.3.1"
|
|
686
|
+
},
|
|
687
|
+
devDependencies: {
|
|
688
|
+
"nodemon": "^3.0.2"
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
fs.writeFileSync(path.join(config.path, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
692
|
+
const src = path.join(config.path, 'src');
|
|
693
|
+
if (!fs.existsSync(src))
|
|
694
|
+
fs.mkdirSync(src);
|
|
695
|
+
fs.writeFileSync(path.join(src, 'index.js'), `
|
|
696
|
+
const express = require('express');
|
|
697
|
+
const cors = require('cors');
|
|
698
|
+
require('dotenv').config();
|
|
699
|
+
|
|
700
|
+
const app = express();
|
|
701
|
+
const PORT = process.env.PORT || 3000;
|
|
702
|
+
|
|
703
|
+
app.use(cors());
|
|
704
|
+
app.use(express.json());
|
|
705
|
+
|
|
706
|
+
app.get('/', (req, res) => {
|
|
707
|
+
res.json({ message: 'Welcome to ${config.name} API' });
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
app.listen(PORT, () => {
|
|
711
|
+
console.log(\`Server is running on http://localhost:\${PORT}\`);
|
|
712
|
+
});
|
|
713
|
+
`);
|
|
714
|
+
fs.writeFileSync(path.join(config.path, '.env'), "PORT=3000\n");
|
|
715
|
+
}
|
|
403
716
|
createNetlifyConfig(config) {
|
|
404
717
|
const content = `[build]
|
|
405
718
|
command = "npm run build"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AIService = void 0;
|
|
7
|
+
const openai_1 = __importDefault(require("openai"));
|
|
8
|
+
const ConfigManager_1 = require("../managers/ConfigManager");
|
|
9
|
+
class AIService {
|
|
10
|
+
constructor(apiKey) {
|
|
11
|
+
this.openai = null;
|
|
12
|
+
if (apiKey) {
|
|
13
|
+
this.openai = new openai_1.default({ apiKey });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async ensureInitialized() {
|
|
17
|
+
if (this.openai)
|
|
18
|
+
return;
|
|
19
|
+
const configManager = new ConfigManager_1.ConfigManager();
|
|
20
|
+
const apiKey = await configManager.getOpenAIApiKey();
|
|
21
|
+
if (apiKey) {
|
|
22
|
+
this.openai = new openai_1.default({ apiKey });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async generateProject(prompt, stack) {
|
|
26
|
+
await this.ensureInitialized();
|
|
27
|
+
if (!this.openai) {
|
|
28
|
+
throw new Error('OpenAI API Key not found. Please set it using environment variables or user config.');
|
|
29
|
+
}
|
|
30
|
+
const systemPrompt = `
|
|
31
|
+
You are a world-class software architect and lead developer.
|
|
32
|
+
Generate a complete, professional project structure based on the user's requirements.
|
|
33
|
+
|
|
34
|
+
Strictly follow this JSON schema for your response:
|
|
35
|
+
{
|
|
36
|
+
"projectName": "Name of the project",
|
|
37
|
+
"folders": ["list/of/folders", "src", "src/components"],
|
|
38
|
+
"files": [
|
|
39
|
+
{
|
|
40
|
+
"path": "folder/filename.ext",
|
|
41
|
+
"content": "Full code content"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"dependencies": ["list", "of", "npm", "dependencies"],
|
|
45
|
+
"devDependencies": ["list", "of", "npm", "dev", "dependencies"]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Rules:
|
|
49
|
+
- Create a robust, production-ready starting point.
|
|
50
|
+
- Include all necessary configuration files:
|
|
51
|
+
- For Node.js: package.json (with scripts), tsconfig.json, .gitignore, README.md.
|
|
52
|
+
- For others: Relevant manifest/config files.
|
|
53
|
+
- Ensure the \`dependencies\` and \`devDependencies\` arrays match what is in the package.json/manifest.
|
|
54
|
+
- Ensure all paths are relative to the project root.
|
|
55
|
+
- Do not include placeholders; provide actual initial code.
|
|
56
|
+
- The output must be valid JSON only.
|
|
57
|
+
`;
|
|
58
|
+
const userMessage = `Project Description: ${prompt}${stack ? `\nPreferred Stack: ${stack}` : ''}`;
|
|
59
|
+
try {
|
|
60
|
+
const response = await this.openai.chat.completions.create({
|
|
61
|
+
model: 'gpt-4o',
|
|
62
|
+
messages: [
|
|
63
|
+
{ role: 'system', content: systemPrompt },
|
|
64
|
+
{ role: 'user', content: userMessage }
|
|
65
|
+
],
|
|
66
|
+
response_format: { type: 'json_object' }
|
|
67
|
+
});
|
|
68
|
+
const content = response.choices[0].message.content;
|
|
69
|
+
if (!content) {
|
|
70
|
+
throw new Error('AI returned an empty response.');
|
|
71
|
+
}
|
|
72
|
+
return JSON.parse(content);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error.status === 401) {
|
|
76
|
+
throw new Error('Invalid OpenAI API Key.');
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`AI Service Error: ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.AIService = AIService;
|
package/out/types.js
CHANGED
|
@@ -4,6 +4,10 @@ exports.ProjectType = exports.ProjectLanguage = void 0;
|
|
|
4
4
|
var ProjectLanguage;
|
|
5
5
|
(function (ProjectLanguage) {
|
|
6
6
|
ProjectLanguage["React"] = "React";
|
|
7
|
+
ProjectLanguage["Nextjs"] = "Next.js";
|
|
8
|
+
ProjectLanguage["Vue"] = "Vue";
|
|
9
|
+
ProjectLanguage["Svelte"] = "Svelte";
|
|
10
|
+
ProjectLanguage["Express"] = "Express";
|
|
7
11
|
ProjectLanguage["TypeScript"] = "TypeScript";
|
|
8
12
|
ProjectLanguage["HTMLCSS"] = "HTML/CSS";
|
|
9
13
|
ProjectLanguage["Python"] = "Python";
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"zerostart": "./out/cli.js"
|
|
6
6
|
},
|
|
7
7
|
"description": "Create and deploy a complete project with one command.",
|
|
8
|
-
"version": "0.0.
|
|
8
|
+
"version": "0.0.44",
|
|
9
9
|
"engines": {
|
|
10
10
|
"vscode": "^1.85.0"
|
|
11
11
|
},
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"lint": "eslint src --ext ts"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
+
"@types/fs-extra": "^11.0.4",
|
|
32
33
|
"@types/inquirer": "^9.0.9",
|
|
33
34
|
"@types/node": "18.x",
|
|
34
35
|
"@types/ora": "^3.1.0",
|
|
@@ -42,8 +43,10 @@
|
|
|
42
43
|
"@octokit/rest": "^19.0.13",
|
|
43
44
|
"chalk": "^4.1.2",
|
|
44
45
|
"commander": "^14.0.3",
|
|
46
|
+
"fs-extra": "^11.3.3",
|
|
45
47
|
"inquirer": "^9.3.8",
|
|
46
48
|
"open": "^8.4.2",
|
|
49
|
+
"openai": "^6.25.0",
|
|
47
50
|
"ora": "^5.4.1"
|
|
48
51
|
}
|
|
49
|
-
}
|
|
52
|
+
}
|