zobro-cli 1.1.4 → 1.1.6

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.
Files changed (3) hide show
  1. package/index.js +79 -42
  2. package/package.json +1 -1
  3. package/.env +0 -1
package/index.js CHANGED
@@ -2,77 +2,116 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const os = require('os');
6
+ const readline = require('readline');
7
+
8
+ // ---------------- CONFIG ----------------
9
+ const CONFIG_DIR = path.join(os.homedir(), '.projectgen');
10
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
11
+
12
+ // ---------------- HELPERS ----------------
13
+ function askQuestion(question) {
14
+ const rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout
17
+ });
18
+ return new Promise(resolve =>
19
+ rl.question(question, answer => {
20
+ rl.close();
21
+ resolve(answer.trim());
22
+ })
23
+ );
24
+ }
25
+
26
+ async function getApiKey() {
27
+ // 1️⃣ ENV variable (preferred)
28
+ if (process.env.GEMINI_API_KEY) {
29
+ return process.env.GEMINI_API_KEY;
30
+ }
5
31
 
6
- // 🛡️ SECURE: This pulls the key from your computer's hidden settings
32
+ // 2️⃣ Local config file
33
+ if (fs.existsSync(CONFIG_FILE)) {
34
+ const saved = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
35
+ if (saved.apiKey) return saved.apiKey;
36
+ }
37
+
38
+ // 3️⃣ Ask user once
39
+ console.log('🔐 Gemini API key not found.');
40
+ const key = await askQuestion('👉 Please enter your Gemini API Key: ');
41
+
42
+ if (!key) {
43
+ console.error('❌ API key is required.');
44
+ process.exit(1);
45
+ }
7
46
 
8
- const API_KEY = process.env.GEMINI_API_KEY;
47
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
48
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify({ apiKey: key }, null, 2));
9
49
 
10
- if (!API_KEY) {
11
- console.error('❌ ERROR: API Key not found on this system.');
12
- // ... rest of your error message
50
+ console.log('✅ API key saved securely for future use.');
51
+ return key;
13
52
  }
53
+
54
+ // ---------------- CORE ----------------
14
55
  async function generateProject(prompt, directoryName) {
15
- if (!API_KEY) {
16
- console.error('❌ ERROR: API Key not found on this system.');
17
- console.log('👉 To fix this, run: setx GEMINI_API_KEY "your_real_key_here"');
18
- console.log('Then restart your terminal.');
19
- return;
20
- }
56
+ const API_KEY = await getApiKey();
21
57
 
22
- const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`;
58
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent`;
23
59
 
24
60
  const newPrompt = `
25
61
  You are an expert software developer and project scaffolder.
26
62
  Based on the user's prompt, generate a complete file structure as a JSON array.
27
- Each object in the array must have two keys:
28
- 1. "filename": (string) The full relative path for the file.
29
- 2. "code": (string) The complete code for that file.
63
+ Each object in the array must have:
64
+ - filename
65
+ - code
30
66
 
31
- Only return the raw JSON array, with no other text, explanations, or markdown fences.
32
- User Prompt: "${prompt}"`;
67
+ Return ONLY raw JSON.
68
+ User Prompt: "${prompt}"
69
+ `;
33
70
 
34
- const body = { contents: [{ parts: [{ text: newPrompt }] }] };
71
+ const body = {
72
+ contents: [{ parts: [{ text: newPrompt }] }]
73
+ };
35
74
 
36
75
  try {
37
76
  console.log(`🤖 Building project in "${directoryName}"...`);
38
77
 
39
78
  const response = await fetch(url, {
40
79
  method: 'POST',
41
- headers: { 'Content-Type': 'application/json' },
42
- body: JSON.stringify(body),
80
+ headers: {
81
+ 'Content-Type': 'application/json',
82
+ 'x-goog-api-key': API_KEY // 🔐 SECURE
83
+ },
84
+ body: JSON.stringify(body)
43
85
  });
44
86
 
45
87
  if (!response.ok) {
46
- const errorData = await response.json();
47
- console.error('❌ API Error Details:', JSON.stringify(errorData.error, null, 2));
48
- return;
88
+ const err = await response.text();
89
+ throw new Error(err);
49
90
  }
50
91
 
51
92
  const data = await response.json();
52
93
  let responseText = data.candidates[0].content.parts[0].text;
53
-
54
- // Clean up markdown if AI adds it
55
- responseText = responseText.replace(/```json|```/g, "").trim();
94
+ responseText = responseText.replace(/```json|```/g, '').trim();
56
95
 
57
96
  const files = JSON.parse(responseText);
97
+
58
98
  fs.mkdirSync(directoryName, { recursive: true });
59
99
 
60
100
  for (const file of files) {
61
101
  const filePath = path.join(directoryName, file.filename);
62
- const fileDir = path.dirname(filePath);
63
- if (!fs.existsSync(fileDir)) fs.mkdirSync(fileDir, { recursive: true });
102
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
64
103
  fs.writeFileSync(filePath, file.code);
65
104
  console.log(`✅ Created: ${file.filename}`);
66
105
  }
67
106
 
68
- console.log(`\n🎉 Done! Your project is ready in the folder: ${directoryName}`);
107
+ console.log(`🎉 Project ready in "${directoryName}"`);
69
108
 
70
- } catch (error) {
71
- console.error('❌ An error occurred:', error.message);
109
+ } catch (err) {
110
+ console.error('❌ Error:', err.message);
72
111
  }
73
112
  }
74
113
 
75
- // Fixed Yargs setup to prevent ESM errors
114
+ // ---------------- CLI ----------------
76
115
  async function startCLI() {
77
116
  const yargs = (await import('yargs/yargs')).default;
78
117
  const { hideBin } = await import('yargs/helpers');
@@ -80,17 +119,15 @@ async function startCLI() {
80
119
  yargs(hideBin(process.argv))
81
120
  .command(
82
121
  '$0 <prompt>',
83
- 'Generates a full project structure.',
84
- (yargs) => {
85
- return yargs
86
- .positional('prompt', { type: 'string' })
87
- .option('directory', { alias: 'd', type: 'string', demandOption: true });
88
- },
89
- (argv) => {
90
- generateProject(argv.prompt, argv.directory);
91
- }
122
+ 'Generate a full project',
123
+ y => y.option('directory', {
124
+ alias: 'd',
125
+ type: 'string',
126
+ demandOption: true
127
+ }),
128
+ argv => generateProject(argv.prompt, argv.directory)
92
129
  )
93
130
  .parse();
94
131
  }
95
132
 
96
- startCLI();
133
+ startCLI();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zobro-cli",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "main": "index.js",
5
5
  "bin": {
6
6
  "zobro-cli": "./index.js"
package/.env DELETED
@@ -1 +0,0 @@
1
- GEMINI_API_KEY=AIzaSyBY0LrIq4HTmCfgN-1XuuiKpYUpTAeSiKU