lumina-code-agent 1.6.6 → 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.
- package/dist/index.js +84 -140
- 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,6 +34,38 @@ async function onboarding() {
|
|
|
34
34
|
saveConfig({ openrouterKey: apiKey.trim(), userName: name.trim() || 'User' });
|
|
35
35
|
console.log(' ✓ Ready!\n');
|
|
36
36
|
}
|
|
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 ───────────────────────────────────────────────────────
|
|
37
69
|
async function chat(config) {
|
|
38
70
|
console.log(' Type what you want to build. I\'ll handle the rest.');
|
|
39
71
|
console.log(' Commands: /help /clear /files /exit\n');
|
|
@@ -65,10 +97,9 @@ async function chat(config) {
|
|
|
65
97
|
console.log(' /help /clear /files /exit\n');
|
|
66
98
|
continue;
|
|
67
99
|
}
|
|
68
|
-
|
|
69
|
-
const systemPrompt = `You are LUMINA CODE. You MUST output files using this EXACT format. Do NOT describe what you will do. DO IT.
|
|
100
|
+
const systemPrompt = `You are LUMINA CODE. You create COMPLETE production-grade websites.
|
|
70
101
|
|
|
71
|
-
OUTPUT
|
|
102
|
+
MANDATORY OUTPUT FORMAT — You MUST use this for EVERY file:
|
|
72
103
|
|
|
73
104
|
FILENAME: index.html
|
|
74
105
|
<!DOCTYPE html>
|
|
@@ -76,36 +107,34 @@ FILENAME: index.html
|
|
|
76
107
|
<head>
|
|
77
108
|
<meta charset="UTF-8">
|
|
78
109
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
79
|
-
<title>
|
|
80
|
-
<style>
|
|
81
|
-
/* ALL CSS here */
|
|
82
|
-
</style>
|
|
110
|
+
<title>Title</title>
|
|
111
|
+
<link rel="stylesheet" href="style.css">
|
|
83
112
|
</head>
|
|
84
113
|
<body>
|
|
85
|
-
<!--
|
|
86
|
-
<script>
|
|
87
|
-
// ALL JavaScript here
|
|
88
|
-
</script>
|
|
114
|
+
<!-- Complete HTML -->
|
|
115
|
+
<script src="script.js"></script>
|
|
89
116
|
</body>
|
|
90
117
|
</html>
|
|
91
118
|
END FILE
|
|
92
119
|
|
|
93
120
|
FILENAME: style.css
|
|
94
|
-
/*
|
|
121
|
+
/* Complete CSS */
|
|
95
122
|
END FILE
|
|
96
123
|
|
|
97
124
|
FILENAME: script.js
|
|
98
|
-
//
|
|
125
|
+
// Complete JavaScript
|
|
99
126
|
END FILE
|
|
100
127
|
|
|
101
|
-
RULES:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
109
138
|
|
|
110
139
|
Working directory: ${process.cwd()}`;
|
|
111
140
|
console.log(' ⏳ Generating...\n');
|
|
@@ -126,7 +155,7 @@ Working directory: ${process.cwd()}`;
|
|
|
126
155
|
{ role: 'system', content: systemPrompt },
|
|
127
156
|
{ role: 'user', content: trimmed },
|
|
128
157
|
],
|
|
129
|
-
stream:
|
|
158
|
+
stream: false,
|
|
130
159
|
max_tokens: 16000,
|
|
131
160
|
temperature: 0.1,
|
|
132
161
|
}),
|
|
@@ -137,126 +166,41 @@ Working directory: ${process.cwd()}`;
|
|
|
137
166
|
const err = await res.text().catch(() => '');
|
|
138
167
|
throw new Error(`API error ${res.status}: ${err.slice(0, 200)}`);
|
|
139
168
|
}
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
continue;
|
|
159
|
-
const data = line.slice(6).trim();
|
|
160
|
-
if (data === '[DONE]')
|
|
161
|
-
continue;
|
|
162
|
-
let delta = '';
|
|
163
|
-
try {
|
|
164
|
-
const parsed = JSON.parse(data);
|
|
165
|
-
delta = parsed.choices?.[0]?.delta?.content || '';
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (!delta)
|
|
171
|
-
continue;
|
|
172
|
-
// Process delta for file detection
|
|
173
|
-
for (let i = 0; i < delta.length; i++) {
|
|
174
|
-
const char = delta[i];
|
|
175
|
-
if (!inFile) {
|
|
176
|
-
// Look for FILENAME:
|
|
177
|
-
outputBuffer += char;
|
|
178
|
-
if (outputBuffer.endsWith('FILENAME:')) {
|
|
179
|
-
// Found it! Extract filename on next line
|
|
180
|
-
currentFile = '';
|
|
181
|
-
inFile = true;
|
|
182
|
-
currentContent = '';
|
|
183
|
-
outputBuffer = '';
|
|
184
|
-
process.stdout.write(' 📄 ');
|
|
185
|
-
}
|
|
186
|
-
else if (outputBuffer.length > 20) {
|
|
187
|
-
// Not matching, flush
|
|
188
|
-
process.stdout.write(outputBuffer);
|
|
189
|
-
outputBuffer = '';
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
// In filename or content
|
|
194
|
-
if (char === '\n' && !currentFile.includes('/')) {
|
|
195
|
-
// Still in filename line (no path separator yet)
|
|
196
|
-
if (currentFile.length > 0 && !currentFile.includes('.')) {
|
|
197
|
-
// This is a path separator line, keep accumulating
|
|
198
|
-
currentFile += char;
|
|
199
|
-
}
|
|
200
|
-
else {
|
|
201
|
-
// End of filename
|
|
202
|
-
currentFile = currentFile.trim();
|
|
203
|
-
console.log(currentFile);
|
|
204
|
-
fileCount++;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
else if (currentFile && !currentContent && char === '\n' && currentFile.includes('.')) {
|
|
208
|
-
// First newline after filename, start content
|
|
209
|
-
currentContent = '';
|
|
210
|
-
}
|
|
211
|
-
else if (currentContent !== null) {
|
|
212
|
-
currentContent += char;
|
|
213
|
-
// Check for END FILE
|
|
214
|
-
if (currentContent.endsWith('END FILE')) {
|
|
215
|
-
const fileContent = currentContent.slice(0, -8).trim();
|
|
216
|
-
try {
|
|
217
|
-
const filePath = join(process.cwd(), currentFile);
|
|
218
|
-
const dir = dirname(resolve(filePath));
|
|
219
|
-
if (!existsSync(dir))
|
|
220
|
-
mkdirSync(dir, { recursive: true });
|
|
221
|
-
writeFileSync(filePath, fileContent, 'utf-8');
|
|
222
|
-
createdFiles.add(currentFile);
|
|
223
|
-
console.log(` ✓ Saved: ${currentFile} (${fileContent.length} chars)`);
|
|
224
|
-
}
|
|
225
|
-
catch (e) {
|
|
226
|
-
console.log(` ✗ Error: ${currentFile}: ${e.message}`);
|
|
227
|
-
}
|
|
228
|
-
currentFile = null;
|
|
229
|
-
currentContent = null;
|
|
230
|
-
inFile = false;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
currentFile += char;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
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;
|
|
174
|
+
}
|
|
175
|
+
// Show the raw output for debugging
|
|
176
|
+
console.log(' 📝 Model output preview:');
|
|
177
|
+
console.log(' ' + content.slice(0, 200).replace(/\n/g, '\n '));
|
|
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;
|
|
239
187
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
console.log(` ✗ Error: ${currentFile}: ${e.message}`);
|
|
255
|
-
}
|
|
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}`);
|
|
256
201
|
}
|
|
257
202
|
}
|
|
258
|
-
console.log(`\n 📊
|
|
259
|
-
console.log('');
|
|
203
|
+
console.log(`\n 📊 Total files: ${createdFiles.size}\n`);
|
|
260
204
|
}
|
|
261
205
|
catch (e) {
|
|
262
206
|
console.log(` ⚠ Error: ${e.message}\n`);
|