lumina-code-agent 1.2.0 → 1.3.0
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 +259 -52
- package/package.json +19 -12
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @ts-nocheck
|
|
3
|
-
import {
|
|
3
|
+
import React, { useState, useCallback, useRef } from 'react';
|
|
4
|
+
import { Box, Text, useInput, useApp, Spacer } from 'ink';
|
|
5
|
+
import TextInput from 'ink-text-input';
|
|
4
6
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
5
7
|
import { homedir } from 'os';
|
|
6
8
|
import { join } from 'path';
|
|
7
9
|
const CONFIG_DIR = join(homedir(), '.lumina');
|
|
8
10
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
9
|
-
|
|
11
|
+
function loadConfig() {
|
|
10
12
|
try {
|
|
11
13
|
if (!existsSync(CONFIG_FILE))
|
|
12
14
|
return null;
|
|
@@ -16,57 +18,262 @@ async function loadConfig() {
|
|
|
16
18
|
return null;
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
|
-
|
|
21
|
+
function saveConfig(config) {
|
|
20
22
|
if (!existsSync(CONFIG_DIR))
|
|
21
23
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
25
|
+
}
|
|
26
|
+
// ── Onboarding Screen ───────────────────────────────────────────────
|
|
27
|
+
function Onboarding({ onComplete }) {
|
|
28
|
+
const [step, setStep] = useState(0);
|
|
29
|
+
const [apiKey, setApiKey] = useState('');
|
|
30
|
+
const [name, setName] = useState('');
|
|
31
|
+
useInput((input, key) => {
|
|
32
|
+
if (key.return) {
|
|
33
|
+
if (step === 0) {
|
|
34
|
+
setStep(1);
|
|
35
|
+
}
|
|
36
|
+
else if (step === 1 && apiKey.trim().length > 10) {
|
|
37
|
+
setStep(2);
|
|
38
|
+
}
|
|
39
|
+
else if (step === 2) {
|
|
40
|
+
saveConfig({ openrouterKey: apiKey.trim(), userName: name.trim() || 'User', defaultEffort: 'normal' });
|
|
41
|
+
onComplete();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return React.createElement(Box, { flexDirection: 'column', padding: 2 }, React.createElement(Box, { flexDirection: 'column', marginBottom: 2 }, React.createElement(Text, { bold: true, color: '#7C5CFC' }, ' ⚡ LUMINA CODE'), React.createElement(Text, { color: '#52525B' }, ' AI Coding Agent — Better than Claude Code')), step === 0 && React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { color: '#A1A1AA' }, ' Welcome! Let\'s get you set up.'), React.createElement(Text, { color: '#52525B' }, ' Press Enter to continue...')), step === 1 && React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { color: '#A1A1AA' }, ' Enter your OpenRouter API key:'), React.createElement(Text, { color: '#52525B' }, ' (Get one at https://openrouter.ai/keys)'), React.createElement(Box, { marginTop: 1 }, React.createElement(Text, { color: '#7C5CFC' }, ' > '), React.createElement(TextInput, { value: apiKey, onChange: setApiKey, placeholder: 'sk-or-...' })), React.createElement(Text, { color: '#3F3F46', marginTop: 1 }, ' Press Enter to continue')), step === 2 && React.createElement(Box, { flexDirection: 'column' }, React.createElement(Text, { color: '#A1A1AA' }, ' What should I call you? (optional)'), React.createElement(Box, { marginTop: 1 }, React.createElement(Text, { color: '#7C5CFC' }, ' > '), React.createElement(TextInput, { value: name, onChange: setName, placeholder: 'Your name' })), React.createElement(Text, { color: '#3F3F46', marginTop: 1 }, ' Press Enter to start coding!')));
|
|
46
|
+
}
|
|
47
|
+
// ── Chat Screen ─────────────────────────────────────────────────────
|
|
48
|
+
function ChatScreen({ config }) {
|
|
49
|
+
const { exit } = useApp();
|
|
50
|
+
const [messages, setMessages] = useState([
|
|
51
|
+
{ role: 'system', content: `You are LUMINA CODE — an elite AI coding agent running locally.
|
|
52
|
+
|
|
53
|
+
MODEL: openrouter/owl-alpha (1M+ context, best reasoning)
|
|
54
|
+
|
|
55
|
+
WORKFLOW:
|
|
56
|
+
1. PLAN: Analyze the task. Create a brief plan.
|
|
57
|
+
2. ACT: Execute tools step by step. Read before writing.
|
|
58
|
+
3. VERIFY: Run builds, check for errors.
|
|
59
|
+
4. FIX: If something fails, debug and fix.
|
|
60
|
+
5. DEPLOY: If requested, deploy automatically.
|
|
61
|
+
|
|
62
|
+
QUALITY: Production-grade code. No placeholders. No TODOs. No emoji in code.
|
|
63
|
+
Handle errors. Use TypeScript. Responsive design. Accessible.
|
|
64
|
+
|
|
65
|
+
TOOLS: run_command, read_file, write_file, edit_file, list_dir, search_files, grep, git, npm, deploy, detect_project, get_file_tree
|
|
66
|
+
|
|
67
|
+
When using a tool, output ONLY:
|
|
68
|
+
TOOL: <name>
|
|
69
|
+
PARAMS: <json>
|
|
70
|
+
|
|
71
|
+
Working directory: ${process.cwd()}` },
|
|
72
|
+
]);
|
|
73
|
+
const [input, setInput] = useState('');
|
|
74
|
+
const [status, setStatus] = useState('Ready');
|
|
75
|
+
const [thinking, setThinking] = useState(false);
|
|
76
|
+
const scrollRef = useRef(0);
|
|
77
|
+
const visibleMessages = messages.filter(m => m.role !== 'system').slice(-50);
|
|
78
|
+
const handleSubmit = useCallback(async () => {
|
|
79
|
+
const trimmed = input.trim();
|
|
80
|
+
if (!trimmed || thinking)
|
|
81
|
+
return;
|
|
82
|
+
setInput('');
|
|
83
|
+
setThinking(true);
|
|
84
|
+
setStatus('Thinking...');
|
|
85
|
+
const userMsg = { role: 'user', content: trimmed };
|
|
86
|
+
const newMessages = [...messages, userMsg];
|
|
87
|
+
setMessages(newMessages);
|
|
88
|
+
scrollRef.current++;
|
|
89
|
+
try {
|
|
90
|
+
const OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';
|
|
91
|
+
const tools = [
|
|
92
|
+
{ type: 'function', function: { name: 'run_command', description: 'Run any shell command', parameters: { type: 'object', properties: { command: { type: 'string' }, cwd: { type: 'string' }, timeout: { type: 'number' } }, required: ['command'] } } },
|
|
93
|
+
{ type: 'function', function: { name: 'read_file', description: 'Read file contents', parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] } } },
|
|
94
|
+
{ type: 'function', function: { name: 'write_file', description: 'Create or overwrite a file', parameters: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] } } },
|
|
95
|
+
{ type: 'function', function: { name: 'edit_file', description: 'Make precise edits to a file', parameters: { type: 'object', properties: { path: { type: 'string' }, search: { type: 'string' }, replace: { type: 'string' } }, required: ['path', 'search', 'replace'] } } },
|
|
96
|
+
{ type: 'function', function: { name: 'list_dir', description: 'List directory contents', parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] } } },
|
|
97
|
+
{ type: 'function', function: { name: 'search_files', description: 'Find files by pattern', parameters: { type: 'object', properties: { pattern: { type: 'string' }, cwd: { type: 'string' } }, required: ['pattern'] } } },
|
|
98
|
+
{ type: 'function', function: { name: 'grep', description: 'Search file contents', parameters: { type: 'object', properties: { pattern: { type: 'string' }, path: { type: 'string' } }, required: ['pattern', 'path'] } } },
|
|
99
|
+
{ type: 'function', function: { name: 'git', description: 'Run git commands', parameters: { type: 'object', properties: { args: { type: 'string' }, cwd: { type: 'string' } }, required: ['args'] } } },
|
|
100
|
+
{ type: 'function', function: { name: 'npm', description: 'Run npm/yarn/pnpm commands', parameters: { type: 'object', properties: { args: { type: 'string' }, cwd: { type: 'string' } }, required: ['args'] } } },
|
|
101
|
+
{ type: 'function', function: { name: 'deploy', description: 'Deploy to Vercel', parameters: { type: 'object', properties: { target: { type: 'string' }, cwd: { type: 'string' } } } } },
|
|
102
|
+
];
|
|
103
|
+
let assistantContent = '';
|
|
104
|
+
let iterations = 0;
|
|
105
|
+
const maxIterations = 30;
|
|
106
|
+
let currentMessages = [...newMessages];
|
|
107
|
+
while (iterations < maxIterations) {
|
|
108
|
+
iterations++;
|
|
109
|
+
setStatus(`Working (step ${iterations})...`);
|
|
110
|
+
const res = await fetch(OPENROUTER_URL, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${config.openrouterKey}`, 'HTTP-Referer': 'https://luminaai.co.in', 'X-Title': 'Lumina Code' },
|
|
113
|
+
body: JSON.stringify({ model: 'openrouter/owl-alpha', messages: currentMessages, tools, stream: false, max_tokens: 32000, temperature: 0.1 }),
|
|
114
|
+
});
|
|
115
|
+
if (!res.ok) {
|
|
116
|
+
const err = await res.text().catch(() => '');
|
|
117
|
+
throw new Error(`API error ${res.status}: ${err.slice(0, 200)}`);
|
|
118
|
+
}
|
|
119
|
+
const data = await res.json();
|
|
120
|
+
const choice = data.choices?.[0];
|
|
121
|
+
if (!choice)
|
|
122
|
+
throw new Error('No response from model');
|
|
123
|
+
const content = choice.message?.content || '';
|
|
124
|
+
const toolCalls = choice.message?.tool_calls || [];
|
|
125
|
+
assistantContent = content;
|
|
126
|
+
currentMessages.push({ role: 'assistant', content, ...(toolCalls.length ? { tool_calls: toolCalls } : {}) });
|
|
127
|
+
if (toolCalls.length === 0) {
|
|
128
|
+
setMessages([...currentMessages]);
|
|
129
|
+
setStatus('Done');
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
// Execute tools
|
|
133
|
+
for (const tc of toolCalls) {
|
|
134
|
+
const args = JSON.parse(tc.function.arguments || '{}');
|
|
135
|
+
const toolName = tc.function.name;
|
|
136
|
+
setStatus(`Running: ${toolName}...`);
|
|
137
|
+
scrollRef.current++;
|
|
138
|
+
let output = '';
|
|
139
|
+
try {
|
|
140
|
+
switch (toolName) {
|
|
141
|
+
case 'run_command': {
|
|
142
|
+
const { execSync } = await import('child_process');
|
|
143
|
+
const cwd = args.cwd || process.cwd();
|
|
144
|
+
output = execSync(args.command, { cwd, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: args.timeout || 120000, shell: true }) || '(no output)';
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case 'read_file': {
|
|
148
|
+
output = readFileSync(args.path, 'utf-8');
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case 'write_file': {
|
|
152
|
+
const dir = join(args.path, '..');
|
|
153
|
+
if (!existsSync(dir))
|
|
154
|
+
mkdirSync(dir, { recursive: true });
|
|
155
|
+
writeFileSync(args.path, args.content, 'utf-8');
|
|
156
|
+
output = `Wrote ${args.content.length} chars to ${args.path}`;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case 'edit_file': {
|
|
160
|
+
let fileContent = readFileSync(args.path, 'utf-8');
|
|
161
|
+
if (!fileContent.includes(args.search))
|
|
162
|
+
throw new Error(`String not found: "${args.search.slice(0, 50)}"`);
|
|
163
|
+
fileContent = fileContent.replace(args.search, args.replace);
|
|
164
|
+
writeFileSync(args.path, fileContent, 'utf-8');
|
|
165
|
+
output = `Edited ${args.path}`;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case 'list_dir': {
|
|
169
|
+
const entries = readdirSync(args.path, { withFileTypes: true });
|
|
170
|
+
output = entries.map(e => `${e.isDirectory() ? '📁' : '📄'} ${e.name}`).join('\n');
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'search_files': {
|
|
174
|
+
const { readdirSync } = await import('fs');
|
|
175
|
+
const { join: pathJoin, relative: pathRelative } = await import('path');
|
|
176
|
+
const results = [];
|
|
177
|
+
const search = (dir, depth) => {
|
|
178
|
+
if (depth > 5)
|
|
179
|
+
return;
|
|
180
|
+
try {
|
|
181
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
182
|
+
if (e.name.startsWith('.') || e.name === 'node_modules')
|
|
183
|
+
continue;
|
|
184
|
+
const fp = pathJoin(dir, e.name);
|
|
185
|
+
if (e.isDirectory())
|
|
186
|
+
search(fp, depth + 1);
|
|
187
|
+
else if (new RegExp(pattern.replace(/\*/g, '.*'), 'i').test(e.name))
|
|
188
|
+
results.push(pathRelative(cwd, fp));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch { }
|
|
192
|
+
};
|
|
193
|
+
search(args.cwd || process.cwd(), 0);
|
|
194
|
+
output = results.join('\n') || 'No files found';
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case 'grep': {
|
|
198
|
+
const content = readFileSync(args.path, 'utf-8');
|
|
199
|
+
const lines = content.split('\n');
|
|
200
|
+
const regex = new RegExp(args.pattern, 'gi');
|
|
201
|
+
output = lines.map((l, i) => regex.test(l) ? `${i + 1}: ${l}` : null).filter(Boolean).join('\n') || 'No matches';
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case 'git': {
|
|
205
|
+
const { execSync } = await import('child_process');
|
|
206
|
+
output = execSync(`git ${args.args}`, { cwd: args.cwd || process.cwd(), encoding: 'utf-8', maxBuffer: 1024 * 1024 }) || '(ok)';
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case 'npm': {
|
|
210
|
+
const { execSync } = await import('child_process');
|
|
211
|
+
const pm = existsSync(join(args.cwd || process.cwd(), 'bun.lockb')) ? 'bun' : existsSync(join(args.cwd || process.cwd(), 'yarn.lock')) ? 'yarn' : 'npm';
|
|
212
|
+
output = execSync(`${pm} ${args.args}`, { cwd: args.cwd || process.cwd(), encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: 120000 }) || '(ok)';
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 'deploy': {
|
|
216
|
+
const { execSync } = await import('child_process');
|
|
217
|
+
output = execSync('npx vercel deploy --prod --yes', { cwd: args.cwd || process.cwd(), encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: 300000 }) || '(deployed)';
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
default:
|
|
221
|
+
output = `Unknown tool: ${toolName}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
output = `Error: ${e.message}`;
|
|
226
|
+
}
|
|
227
|
+
currentMessages.push({ role: 'tool', content: output.slice(0, 2000), tool_call_id: tc.id });
|
|
228
|
+
setMessages([...currentMessages]);
|
|
229
|
+
scrollRef.current++;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (iterations >= maxIterations) {
|
|
233
|
+
setStatus('Reached max iterations');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (e) {
|
|
237
|
+
setMessages(prev => [...prev, { role: 'assistant', content: `⚠ Error: ${e.message}` }]);
|
|
238
|
+
setStatus('Error');
|
|
239
|
+
}
|
|
240
|
+
setThinking(false);
|
|
241
|
+
}, [input, thinking, messages, config]);
|
|
242
|
+
useInput((_input, key) => {
|
|
243
|
+
if (key.ctrl && _input === 'c')
|
|
244
|
+
exit();
|
|
245
|
+
if (key.ctrl && _input === 'd')
|
|
246
|
+
exit();
|
|
247
|
+
});
|
|
248
|
+
return React.createElement(Box, { flexDirection: 'column', height: '100%' },
|
|
249
|
+
// Header
|
|
250
|
+
React.createElement(Box, { borderStyle: 'round', borderColor: '#7C5CFC', paddingX: 2, paddingY: 1 }, React.createElement(Text, { bold: true, color: '#7C5CFC' }, ' ⚡ LUMINA CODE'), React.createElement(Spacer, null), React.createElement(Text, { color: '#52525B' }, thinking ? ' ⏳ ' + status : ' ● ' + status)),
|
|
251
|
+
// Messages
|
|
252
|
+
React.createElement(Box, { flexDirection: 'column', flexGrow: 1, paddingX: 2, paddingY: 1, overflowY: 'hidden' }, visibleMessages.map((m, i) => {
|
|
253
|
+
if (m.role === 'user') {
|
|
254
|
+
return React.createElement(Box, { key: i, marginTop: 1 }, React.createElement(Text, { color: '#7C5CFC', bold: true }, '> '), React.createElement(Text, { color: '#FAFAFA' }, m.content));
|
|
255
|
+
}
|
|
256
|
+
if (m.role === 'tool') {
|
|
257
|
+
return React.createElement(Box, { key: i, marginTop: 0, paddingLeft: 2 }, React.createElement(Text, { color: '#52525B' }, ' ' + m.content.slice(0, 200)));
|
|
258
|
+
}
|
|
259
|
+
// Assistant
|
|
260
|
+
return React.createElement(Box, { key: i, marginTop: 1, paddingLeft: 2 }, React.createElement(Text, { color: '#A1A1AA' }, m.content.slice(0, 500)));
|
|
261
|
+
}), thinking && React.createElement(Text, { color: '#F59E0B' }, ' ⏳ thinking...')),
|
|
262
|
+
// Input
|
|
263
|
+
React.createElement(Box, { borderStyle: 'single', borderColor: '#3F3F46', paddingX: 1 }, React.createElement(Text, { color: '#7C5CFC' }, ' > '), React.createElement(TextInput, {
|
|
264
|
+
value: input,
|
|
265
|
+
onChange: setInput,
|
|
266
|
+
onSubmit: handleSubmit,
|
|
267
|
+
placeholder: 'What do you want to build?',
|
|
268
|
+
})),
|
|
269
|
+
// Footer
|
|
270
|
+
React.createElement(Box, { paddingX: 2 }, React.createElement(Text, { color: '#3F3F46' }, ' Ctrl+C to exit | OWL-Alpha ')));
|
|
28
271
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.argument('[prompt]', 'What you want to build')
|
|
35
|
-
.option('-e, --effort <level>', 'Effort level: quick, normal, beast', 'normal')
|
|
36
|
-
.option('-y, --yes', 'Auto-approve all actions')
|
|
37
|
-
.option('--cwd <dir>', 'Working directory', process.cwd())
|
|
38
|
-
.action(async (prompt, opts) => {
|
|
39
|
-
const config = await ensureConfig();
|
|
40
|
-
if (!config.openrouterKey) {
|
|
41
|
-
console.error('\n ERROR: OpenRouter API key not set.\n');
|
|
42
|
-
console.error(' Set it with: lumina config set openrouter-key YOUR_KEY');
|
|
43
|
-
console.error(' Get a key at: https://openrouter.ai/keys\n');
|
|
44
|
-
process.exit(1);
|
|
272
|
+
// ── Main App ────────────────────────────────────────────────────────
|
|
273
|
+
export default function App() {
|
|
274
|
+
const config = loadConfig();
|
|
275
|
+
if (!config?.openrouterKey) {
|
|
276
|
+
return React.createElement(Onboarding, { onComplete: () => { } });
|
|
45
277
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const { render } = await import('ink');
|
|
49
|
-
const React = await import('react');
|
|
50
|
-
render(React.createElement(TUIApp, { prompt, config, effort, autoApprove: opts.yes || false, cwd: opts.cwd }));
|
|
51
|
-
});
|
|
52
|
-
// Config commands: lumina config, lumina config set <key> <value>
|
|
53
|
-
const configCmd = program.command('config').description('Manage configuration');
|
|
54
|
-
configCmd.action(async () => {
|
|
55
|
-
const config = await ensureConfig();
|
|
56
|
-
console.log('\n Lumina Code Configuration\n');
|
|
57
|
-
console.log(' Config:', CONFIG_FILE);
|
|
58
|
-
console.log(' API Key:', config.openrouterKey ? 'Set (' + config.openrouterKey.slice(0, 8) + '...)' : 'NOT SET');
|
|
59
|
-
console.log(' Default Effort:', config.defaultEffort || 'normal');
|
|
60
|
-
console.log('\n Commands:');
|
|
61
|
-
console.log(' lumina config set openrouter-key <key>');
|
|
62
|
-
console.log(' lumina config set default-effort <quick|normal|beast>\n');
|
|
63
|
-
});
|
|
64
|
-
configCmd.command('set <key> <value>')
|
|
65
|
-
.description('Set a config value')
|
|
66
|
-
.action(async (key, value) => {
|
|
67
|
-
const config = await ensureConfig();
|
|
68
|
-
config[key] = value;
|
|
69
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
70
|
-
console.log(' Set', key, '=', value);
|
|
71
|
-
});
|
|
72
|
-
program.parse();
|
|
278
|
+
return React.createElement(ChatScreen, { config });
|
|
279
|
+
}
|
package/package.json
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lumina-code-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Lumina Code - AI coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"bin": {
|
|
7
|
+
"bin": {
|
|
8
|
+
"lumina": "dist/index.js"
|
|
9
|
+
},
|
|
8
10
|
"main": "dist/index.js",
|
|
9
|
-
"files": [
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc --noEmit false",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
12
22
|
"dependencies": {
|
|
13
|
-
"commander": "^12.1.0",
|
|
14
|
-
"ink": "^5.1.0",
|
|
15
|
-
"react": "^18.3.1",
|
|
16
23
|
"chalk": "^5.3.0",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
24
|
+
"ink": "^5.1.0",
|
|
25
|
+
"ink-text-input": "^5.0.1",
|
|
26
|
+
"react": "^18.3.1"
|
|
19
27
|
},
|
|
20
28
|
"devDependencies": {
|
|
21
|
-
"@types/
|
|
22
|
-
"@types/cross-spawn": "^6.0.6",
|
|
29
|
+
"@types/react": "^18.3.12",
|
|
23
30
|
"typescript": "^5.7.2"
|
|
24
31
|
}
|
|
25
32
|
}
|