lumina-code-agent 1.6.9 → 1.6.10

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 +133 -127
  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, extname } from 'path';
5
+ import { join, dirname, resolve } 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');
@@ -24,107 +24,62 @@ function saveConfig(config) {
24
24
  const rl = createInterface({ input: process.stdin, output: process.stdout });
25
25
  function ask(q) { return new Promise(r => rl.question(q, r)); }
26
26
  async function onboarding() {
27
- console.log('\n ⚡ LUMINA CODE — AI Coding Agent\n');
28
- const apiKey = await ask(' Enter your OpenRouter API key: ');
27
+ console.log('\n╔══════════════════════════════════════╗');
28
+ console.log('║ ⚡ LUMINA CODE AI Coding Agent ║');
29
+ console.log('╚══════════════════════════════════════╝\n');
30
+ const apiKey = await ask(' 🔑 OpenRouter API key: ');
29
31
  if (!apiKey.trim() || apiKey.trim().length < 10) {
30
- console.log(' ❌ Invalid key.');
32
+ console.log(' ❌ Invalid.');
31
33
  process.exit(1);
32
34
  }
33
- const name = await ask(' Your name (optional): ');
34
- saveConfig({ openrouterKey: apiKey.trim(), userName: name.trim() || 'User' });
35
- console.log(' ✓ Ready!\n');
36
- }
37
- function extractFiles(text) {
38
- const files = [];
39
- const fileRegex = /FILENAME:\s*([^\n]+)\n([\s\S]*?)END FILE/g;
40
- let match;
41
- while ((match = fileRegex.exec(text)) !== null) {
42
- const rawPath = match[1].trim();
43
- const content = match[2].trim();
44
- if (!rawPath || rawPath.endsWith('/') || !extname(rawPath))
45
- continue;
46
- if (rawPath.includes('<') || rawPath.includes('>') || rawPath.includes('"'))
47
- continue;
48
- files.push({ path: rawPath, content });
49
- }
50
- return files;
35
+ saveConfig({ openrouterKey: apiKey.trim(), userName: 'User' });
36
+ console.log(' ✅ Ready!\n');
51
37
  }
52
38
  async function chat(config) {
53
- console.log(' Type what you want to build. I\'ll handle the rest.');
54
- console.log(' Commands: /help /clear /files /exit\n');
39
+ console.log(' 💬 Type what you want to build. /exit to quit.\n');
55
40
  const createdFiles = new Set();
56
- while (true) {
57
- const input = await ask(' > ');
58
- const trimmed = input.trim();
59
- if (!trimmed)
60
- continue;
61
- if (trimmed === '/exit' || trimmed === '/quit')
62
- break;
63
- if (trimmed === '/clear') {
64
- console.log(' ✓ Cleared.\n');
65
- continue;
66
- }
67
- if (trimmed === '/files') {
68
- if (createdFiles.size === 0) {
69
- console.log(' No files yet.\n');
70
- }
71
- else {
72
- console.log(' Files:');
73
- for (const f of createdFiles)
74
- console.log(` ${f}`);
75
- console.log('');
76
- }
77
- continue;
78
- }
79
- if (trimmed === '/help') {
80
- console.log(' /help /clear /files /exit\n');
81
- continue;
82
- }
83
- const systemPrompt = `You are LUMINA CODE. Create COMPLETE production-grade websites.
84
-
85
- OUTPUT FORMAT — Use this EXACT format for EVERY file:
41
+ const SYSTEM = `You are LUMINA CODE — the world's best AI coding agent.
86
42
 
43
+ OUTPUT FORMAT — MANDATORY:
87
44
  FILENAME: index.html
88
45
  <!DOCTYPE html>
89
46
  <html lang="en">
90
47
  <head>
91
48
  <meta charset="UTF-8">
92
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
49
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
93
50
  <title>Title</title>
94
51
  <link rel="stylesheet" href="style.css">
95
52
  </head>
96
53
  <body>
97
- <!-- Complete HTML here -->
54
+ <!-- COMPLETE HTML -->
98
55
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
56
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
57
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
99
58
  <script src="script.js"></script>
100
59
  </body>
101
60
  </html>
102
61
  END FILE
103
62
 
104
63
  FILENAME: style.css
105
- /* Complete CSS here */
64
+ /* COMPLETE CSS no truncation */
106
65
  END FILE
107
66
 
108
67
  FILENAME: script.js
109
- // Complete JavaScript here
68
+ // COMPLETE JS — Three.js robot, animations, scroll triggers
110
69
  END FILE
111
70
 
112
- RULES:
113
- - Start IMMEDIATELY with FILENAME: index.html
114
- - Output COMPLETE files — every line, no truncation
115
- - Do NOT describe what you will do — JUST DO IT
116
- - Do NOT output any text before FILENAME:
117
- - Do NOT use markdown code blocks
118
- - Create ALL files needed for a complete working project
119
- - Use Three.js from CDN for 3D
120
- - No placeholders, no TODOs, no lorem ipsum
71
+ RULES: Start IMMEDIATELY with FILENAME: index.html. Do NOT describe. JUST OUTPUT COMPLETE FILES. No placeholders. No TODOs. Robot: Three.js primitives, PBR materials, wave/blink animations, scroll-driven sections (Hero=wave, Features=point, About=think, Contact=wave goodbye), particles, dark gradient bg.
121
72
 
122
73
  Working directory: ${process.cwd()}`;
123
- console.log('');
124
- console.log(' Generating...');
74
+ while (true) {
75
+ const input = await ask(' > ');
76
+ const trimmed = input.trim();
77
+ if (!trimmed)
78
+ continue;
79
+ if (trimmed === '/exit')
80
+ break;
81
+ console.log('\n ⏳ Generating...\n');
125
82
  try {
126
- const controller = new AbortController();
127
- const timeout = setTimeout(() => controller.abort(), 600000); // 10 min timeout
128
83
  const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
129
84
  method: 'POST',
130
85
  headers: {
@@ -136,79 +91,130 @@ Working directory: ${process.cwd()}`;
136
91
  body: JSON.stringify({
137
92
  model: 'openrouter/owl-alpha',
138
93
  messages: [
139
- { role: 'system', content: systemPrompt },
94
+ { role: 'system', content: SYSTEM },
140
95
  { role: 'user', content: trimmed },
141
96
  ],
142
- stream: false,
143
- max_tokens: 65000,
97
+ stream: true,
98
+ max_tokens: 56000,
144
99
  temperature: 0.1,
145
100
  }),
146
- signal: controller.signal,
147
101
  });
148
- clearTimeout(timeout);
149
102
  if (!res.ok) {
150
103
  const err = await res.text().catch(() => '');
151
- throw new Error(`API error ${res.status}: ${err.slice(0, 200)}`);
152
- }
153
- // Read response as text first, then parse
154
- const rawText = await res.text();
155
- let data;
156
- try {
157
- data = JSON.parse(rawText);
104
+ throw new Error(`API ${res.status}: ${err.slice(0, 200)}`);
158
105
  }
159
- catch (e) {
160
- // If JSON is truncated, try to fix it
161
- console.log(' ⚠ Response was truncated. Attempting to parse...');
162
- // Try to find the last complete JSON object
163
- const lastBrace = rawText.lastIndexOf('}');
164
- if (lastBrace > 0) {
106
+ // Stream and parse files in real-time
107
+ const reader = res.body.getReader();
108
+ const decoder = new TextDecoder();
109
+ let buf = '';
110
+ let fullText = '';
111
+ let fileBuf = '';
112
+ let inFile = false;
113
+ let fileName = '';
114
+ let fileContent = '';
115
+ let filesCreated = 0;
116
+ while (true) {
117
+ const { done, value } = await reader.read();
118
+ if (done)
119
+ break;
120
+ buf += decoder.decode(value, { stream: true });
121
+ const lines = buf.split('\n');
122
+ buf = lines.pop();
123
+ for (const line of lines) {
124
+ if (!line.startsWith('data: '))
125
+ continue;
126
+ const d = line.slice(6).trim();
127
+ if (d === '[DONE]')
128
+ continue;
129
+ let delta = '';
165
130
  try {
166
- data = JSON.parse(rawText.slice(0, lastBrace + 1));
131
+ const p = JSON.parse(d);
132
+ delta = p.choices?.[0]?.delta?.content || '';
167
133
  }
168
- catch (e2) {
169
- throw new Error('Response was truncated and could not be parsed. Try a shorter prompt.');
134
+ catch {
135
+ continue;
136
+ }
137
+ if (!delta)
138
+ continue;
139
+ fullText += delta;
140
+ // Real-time file detection
141
+ for (const ch of delta) {
142
+ if (!inFile) {
143
+ fileBuf += ch;
144
+ if (fileBuf.endsWith('FILENAME:')) {
145
+ inFile = true;
146
+ fileName = '';
147
+ fileContent = '';
148
+ fileBuf = '';
149
+ process.stdout.write(' 📄 ');
150
+ }
151
+ else if (fileBuf.length > 50) {
152
+ // Not matching, flush to output
153
+ process.stdout.write(fileBuf);
154
+ fileBuf = '';
155
+ }
156
+ }
157
+ else {
158
+ if (ch === '\n' && !fileName.includes('.')) {
159
+ fileName = fileName.trim();
160
+ process.stdout.write(fileName + '\n');
161
+ }
162
+ else if (ch === '\n' && fileName && !fileContent) {
163
+ // First newline after filename, start content
164
+ fileContent = '';
165
+ }
166
+ else if (fileContent !== null) {
167
+ fileContent += ch;
168
+ if (fileContent.endsWith('END FILE')) {
169
+ const content = fileContent.slice(0, -8).trim();
170
+ try {
171
+ const fp = join(process.cwd(), fileName);
172
+ const dir = dirname(resolve(fp));
173
+ if (!existsSync(dir))
174
+ mkdirSync(dir, { recursive: true });
175
+ writeFileSync(fp, content, 'utf-8');
176
+ createdFiles.add(fileName);
177
+ filesCreated++;
178
+ console.log(` ✅ Saved: ${fileName} (${content.length} chars)`);
179
+ }
180
+ catch (e) {
181
+ console.log(` ❌ ${fileName}: ${e.message}`);
182
+ }
183
+ inFile = false;
184
+ fileName = '';
185
+ fileContent = null;
186
+ }
187
+ }
188
+ else {
189
+ fileName += ch;
190
+ }
191
+ }
170
192
  }
171
193
  }
172
- else {
173
- throw new Error('Response was truncated. Try a shorter prompt.');
174
- }
175
- }
176
- const content = data.choices?.[0]?.message?.content || '';
177
- if (!content) {
178
- console.log(' ⚠ No response from model.\n');
179
- continue;
180
- }
181
- console.log(` ✓ AI responded (${content.length} chars)`);
182
- const files = extractFiles(content);
183
- if (files.length === 0) {
184
- console.log(' ⚠ No files detected in output.\n');
185
- // Show first 50 lines of output
186
- const lines = content.split('\n').slice(0, 50);
187
- console.log(' ── Output preview ──');
188
- for (const line of lines)
189
- console.log(' ' + line);
190
- console.log(' ── End ──\n');
191
- continue;
192
194
  }
193
- console.log(` 📁 Creating ${files.length} file(s):\n`);
194
- for (const file of files) {
195
- try {
196
- const filePath = join(process.cwd(), file.path);
197
- const dir = dirname(resolve(filePath));
198
- if (!existsSync(dir))
199
- mkdirSync(dir, { recursive: true });
200
- writeFileSync(filePath, file.content, 'utf-8');
201
- createdFiles.add(file.path);
202
- console.log(` ✅ ${file.path} (${file.content.length} chars)`);
203
- }
204
- catch (e) {
205
- console.log(` ${file.path}: ${e.message}`);
195
+ // Handle any remaining file
196
+ if (inFile && fileName && fileContent) {
197
+ const content = fileContent.replace(/END FILE$/, '').trim();
198
+ if (content) {
199
+ try {
200
+ const fp = join(process.cwd(), fileName);
201
+ const dir = dirname(resolve(fp));
202
+ if (!existsSync(dir))
203
+ mkdirSync(dir, { recursive: true });
204
+ writeFileSync(fp, content, 'utf-8');
205
+ createdFiles.add(fileName);
206
+ filesCreated++;
207
+ console.log(` Saved: ${fileName} (${content.length} chars)`);
208
+ }
209
+ catch (e) {
210
+ console.log(` ❌ ${fileName}: ${e.message}`);
211
+ }
206
212
  }
207
213
  }
208
- console.log(`\n 📊 Total files: ${createdFiles.size}\n`);
214
+ console.log(`\n 📊 Created ${filesCreated} file(s) | Total: ${createdFiles.size}\n`);
209
215
  }
210
216
  catch (e) {
211
- console.log(` Error: ${e.message}\n`);
217
+ console.log(` ${e.message}\n`);
212
218
  }
213
219
  }
214
220
  rl.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lumina-code-agent",
3
- "version": "1.6.9",
3
+ "version": "1.6.10",
4
4
  "description": "Lumina Code - AI coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",