lumina-code-agent 1.6.5 → 1.6.7

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 (2) hide show
  1. package/dist/index.js +101 -128
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @ts-nocheck
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
4
4
  import { homedir } from 'os';
5
- import { join, dirname, resolve } from 'path';
5
+ import { join, dirname, resolve, extname } from 'path';
6
6
  import { createInterface } from 'readline';
7
7
  const CONFIG_DIR = join(homedir(), '.lumina');
8
8
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
@@ -34,7 +34,38 @@ async function onboarding() {
34
34
  saveConfig({ openrouterKey: apiKey.trim(), userName: name.trim() || 'User' });
35
35
  console.log(' ✓ Ready!\n');
36
36
  }
37
- // ── Streaming chat with real-time file creation ─────────────────────
37
+ // ── Extract files from complete model output ────────────────────────
38
+ function extractFiles(text) {
39
+ const files = [];
40
+ // Match FILENAME: path\n...content...END FILE
41
+ const fileRegex = /FILENAME:\s*([^\n]+)\n([\s\S]*?)END FILE/g;
42
+ let match;
43
+ while ((match = fileRegex.exec(text)) !== null) {
44
+ const rawPath = match[1].trim();
45
+ const content = match[2].trim();
46
+ // Skip if it's a directory path (no extension or ends with /)
47
+ if (!rawPath || rawPath.endsWith('/') || !extname(rawPath))
48
+ continue;
49
+ // Skip if path contains invalid chars
50
+ if (rawPath.includes('<') || rawPath.includes('>') || rawPath.includes('"'))
51
+ continue;
52
+ files.push({ path: rawPath, content });
53
+ }
54
+ // Also match ```filename ... ``` code blocks
55
+ const blockRegex = /```([\w./\-_]+)\n([\s\S]*?)```/g;
56
+ while ((match = blockRegex.exec(text)) !== null) {
57
+ const rawPath = match[1].trim();
58
+ const content = match[2].trim();
59
+ if (!rawPath || rawPath.endsWith('/') || !extname(rawPath))
60
+ continue;
61
+ // Skip common non-file patterns
62
+ if (rawPath.startsWith('http') || rawPath.includes('node_modules'))
63
+ continue;
64
+ files.push({ path: rawPath, content });
65
+ }
66
+ return files;
67
+ }
68
+ // ── Chat Loop ───────────────────────────────────────────────────────
38
69
  async function chat(config) {
39
70
  console.log(' Type what you want to build. I\'ll handle the rest.');
40
71
  console.log(' Commands: /help /clear /files /exit\n');
@@ -66,23 +97,44 @@ async function chat(config) {
66
97
  console.log(' /help /clear /files /exit\n');
67
98
  continue;
68
99
  }
69
- const systemPrompt = `You are LUMINA CODE. Create COMPLETE production-grade websites.
100
+ const systemPrompt = `You are LUMINA CODE. You create COMPLETE production-grade websites.
101
+
102
+ MANDATORY OUTPUT FORMAT — You MUST use this for EVERY file:
70
103
 
71
- OUTPUT FORMAT — Use this exact format for every file:
104
+ FILENAME: index.html
105
+ <!DOCTYPE html>
106
+ <html lang="en">
107
+ <head>
108
+ <meta charset="UTF-8">
109
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
110
+ <title>Title</title>
111
+ <link rel="stylesheet" href="style.css">
112
+ </head>
113
+ <body>
114
+ <!-- Complete HTML -->
115
+ <script src="script.js"></script>
116
+ </body>
117
+ </html>
118
+ END FILE
72
119
 
73
- FILENAME: path/to/file.ext
74
- [COMPLETE file content — every line, no truncation]
120
+ FILENAME: style.css
121
+ /* Complete CSS */
75
122
  END FILE
76
123
 
77
- For commands:
78
- COMMAND: npm install something
124
+ FILENAME: script.js
125
+ // Complete JavaScript
126
+ END FILE
79
127
 
80
- RULES:
81
- - Output COMPLETE files every single line
82
- - Use Three.js for 3D, CSS animations for motion
83
- - No placeholders, no TODOs, no lorem ipsum
84
- - Beautiful, cinematic, production-quality
85
- - Create ALL files needed for a working project
128
+ CRITICAL RULES:
129
+ 1. ALWAYS start with FILENAME: index.html
130
+ 2. Output COMPLETE files every line, no truncation
131
+ 3. Do NOT describe what you will do — JUST DO IT
132
+ 4. Do NOT output any text before FILENAME:
133
+ 5. Do NOT use markdown code blocks ONLY FILENAME: ... END FILE
134
+ 6. Create ALL files needed for a complete working project
135
+ 7. Use Three.js from CDN: https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
136
+ 8. Use modern CSS (grid, flexbox, custom properties)
137
+ 9. No placeholders, no TODOs, no lorem ipsum
86
138
 
87
139
  Working directory: ${process.cwd()}`;
88
140
  console.log(' ⏳ Generating...\n');
@@ -103,9 +155,9 @@ Working directory: ${process.cwd()}`;
103
155
  { role: 'system', content: systemPrompt },
104
156
  { role: 'user', content: trimmed },
105
157
  ],
106
- stream: true,
158
+ stream: false,
107
159
  max_tokens: 16000,
108
- temperature: 0.2,
160
+ temperature: 0.1,
109
161
  }),
110
162
  signal: controller.signal,
111
163
  });
@@ -114,119 +166,41 @@ Working directory: ${process.cwd()}`;
114
166
  const err = await res.text().catch(() => '');
115
167
  throw new Error(`API error ${res.status}: ${err.slice(0, 200)}`);
116
168
  }
117
- // Stream the response and create files in real-time
118
- const reader = res.body.getReader();
119
- const decoder = new TextDecoder();
120
- let buffer = '';
121
- let currentFile = null;
122
- let currentContent = '';
123
- let inFile = false;
124
- let fileCount = 0;
125
- while (true) {
126
- const { done, value } = await reader.read();
127
- if (done)
128
- break;
129
- buffer += decoder.decode(value, { stream: true });
130
- const lines = buffer.split('\n');
131
- buffer = lines.pop(); // Keep incomplete line
132
- for (const line of lines) {
133
- if (!line.startsWith('data: '))
134
- continue;
135
- const data = line.slice(6).trim();
136
- if (data === '[DONE]')
137
- continue;
138
- try {
139
- const parsed = JSON.parse(data);
140
- const delta = parsed.choices?.[0]?.delta?.content;
141
- if (!delta)
142
- continue;
143
- // Process the delta character by character for file detection
144
- for (const char of delta) {
145
- if (!inFile) {
146
- // Check for FILENAME: pattern
147
- if (currentFile === null) {
148
- // Accumulate to check for FILENAME:
149
- if (!currentContent) {
150
- if (char === 'F')
151
- currentContent = 'F';
152
- else if (currentContent === 'F' && char === 'I')
153
- currentContent = 'FI';
154
- else if (currentContent === 'FI' && char === 'L')
155
- currentContent = 'FIL';
156
- else if (currentContent === 'FIL' && char === 'E')
157
- currentContent = 'FILE';
158
- else if (currentContent === 'FILE' && char === 'N')
159
- currentContent = 'FILEN';
160
- else if (currentContent === 'FILEN' && char === 'A')
161
- currentContent = 'FILENA';
162
- else if (currentContent === 'FILENA' && char === 'M')
163
- currentContent = 'FILENAM';
164
- else if (currentContent === 'FILENAM' && char === 'E')
165
- currentContent = 'FILENAME';
166
- else if (currentContent === 'FILENAME' && char === ':') {
167
- currentFile = '';
168
- currentContent = '';
169
- process.stdout.write(' 📄 Creating: ');
170
- }
171
- else {
172
- // Not a FILENAME: pattern, just output
173
- if (currentContent) {
174
- process.stdout.write(currentContent + char);
175
- currentContent = '';
176
- }
177
- else {
178
- process.stdout.write(char);
179
- }
180
- }
181
- }
182
- }
183
- else {
184
- // We're inside a filename or file content
185
- if (char === '\n' && !inFile) {
186
- // End of filename line
187
- const filename = currentFile.trim();
188
- currentFile = filename;
189
- inFile = true;
190
- currentContent = '';
191
- console.log(` ✓ ${filename}`);
192
- fileCount++;
193
- }
194
- else if (inFile) {
195
- currentContent += char;
196
- // Check for END FILE
197
- if (currentContent.endsWith('END FILE')) {
198
- const fileContent = currentContent.slice(0, -8).trim();
199
- try {
200
- const filePath = join(process.cwd(), currentFile);
201
- const dir = dirname(resolve(filePath));
202
- if (!existsSync(dir))
203
- mkdirSync(dir, { recursive: true });
204
- writeFileSync(filePath, fileContent, 'utf-8');
205
- createdFiles.add(currentFile);
206
- console.log(` ✓ Saved: ${currentFile} (${fileContent.length} chars)`);
207
- }
208
- catch (e) {
209
- console.log(` ✗ Error saving ${currentFile}: ${e.message}`);
210
- }
211
- currentFile = null;
212
- currentContent = '';
213
- inFile = false;
214
- }
215
- }
216
- else {
217
- currentFile += char;
218
- }
219
- }
220
- }
221
- }
222
- }
223
- catch (e) {
224
- // Skip malformed JSON chunks
225
- }
226
- }
169
+ const data = await res.json();
170
+ const content = data.choices?.[0]?.message?.content || '';
171
+ if (!content) {
172
+ console.log(' ⚠ No response from model.\n');
173
+ continue;
227
174
  }
228
- console.log(`\n 📊 Created ${fileCount} file(s) total: ${createdFiles.size}`);
175
+ // Show the raw output for debugging
176
+ console.log(' 📝 Model output preview:');
177
+ console.log(' ' + content.slice(0, 200).replace(/\n/g, '\n '));
229
178
  console.log('');
179
+ // Extract and create files
180
+ const files = extractFiles(content);
181
+ if (files.length === 0) {
182
+ console.log(' ⚠ No files detected in output. The model may not have used FILENAME: format.');
183
+ console.log(' Here is the full output:\n');
184
+ console.log(content);
185
+ console.log('');
186
+ continue;
187
+ }
188
+ console.log(` 📁 Creating ${files.length} file(s)...\n`);
189
+ for (const file of files) {
190
+ try {
191
+ const filePath = join(process.cwd(), file.path);
192
+ const dir = dirname(resolve(filePath));
193
+ if (!existsSync(dir))
194
+ mkdirSync(dir, { recursive: true });
195
+ writeFileSync(filePath, file.content, 'utf-8');
196
+ createdFiles.add(file.path);
197
+ console.log(` ✓ ${file.path} (${file.content.length} chars)`);
198
+ }
199
+ catch (e) {
200
+ console.log(` ✗ ${file.path}: ${e.message}`);
201
+ }
202
+ }
203
+ console.log(`\n 📊 Total files: ${createdFiles.size}\n`);
230
204
  }
231
205
  catch (e) {
232
206
  console.log(` ⚠ Error: ${e.message}\n`);
@@ -234,7 +208,6 @@ Working directory: ${process.cwd()}`;
234
208
  }
235
209
  rl.close();
236
210
  }
237
- // ── Main ────────────────────────────────────────────────────────────
238
211
  const config = loadConfig();
239
212
  if (!config?.openrouterKey) {
240
213
  onboarding().then(() => chat(loadConfig()).then(() => process.exit(0)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumina-code-agent",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "Lumina Code - AI coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",