zerostart-cli 0.0.43 → 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 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.41');
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.41';
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
  }
@@ -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;
@@ -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/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.43",
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
+ }