lumina-code-agent 1.5.0 → 1.5.1

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 +22 -30
  2. package/package.json +15 -5
package/dist/index.js CHANGED
@@ -1,26 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  // @ts-nocheck
3
- // Check if we have a proper TTY for the TUI
4
3
  const hasTTY = process.stdin.isTTY && process.stdout.isTTY;
5
4
  if (!hasTTY) {
6
- // Non-TUI mode: just show help
7
5
  console.log('');
8
6
  console.log(' ⚡ LUMINA CODE — AI Coding Agent');
9
7
  console.log('');
10
8
  console.log(' Lumina Code requires a proper terminal (TTY) to run.');
11
- console.log(' Please open one of these and type: lumina');
12
- console.log('');
13
- console.log(' • Windows Terminal (recommended)');
14
- console.log(' • PowerShell');
15
- console.log(' • Command Prompt (cmd.exe)');
16
- console.log(' • iTerm2 / Terminal.app (macOS)');
17
- console.log(' • Any Linux terminal');
18
- console.log('');
19
- console.log(' If you\'re in Git Bash, try running from Windows Terminal instead.');
9
+ console.log(' Please open Windows Terminal, PowerShell, or Command Prompt.');
20
10
  console.log('');
21
11
  process.exit(0);
22
12
  }
23
- import React, { useState, useCallback, useRef } from 'react';
13
+ import React, { useState, useCallback } from 'react';
24
14
  import { Box, Text, useInput, useApp, Spacer } from 'ink';
25
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';
26
16
  import { homedir } from 'os';
@@ -42,8 +32,22 @@ function saveConfig(config) {
42
32
  mkdirSync(CONFIG_DIR, { recursive: true });
43
33
  writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
44
34
  }
35
+ // ── Main App (handles onboarding vs chat) ───────────────────────────
36
+ function App() {
37
+ const [config, setConfig] = useState(loadConfig());
38
+ const [screen, setScreen] = useState(config?.openrouterKey ? 'chat' : 'onboarding');
39
+ const handleOnboardingComplete = useCallback(() => {
40
+ const newConfig = loadConfig();
41
+ setConfig(newConfig);
42
+ setScreen('chat');
43
+ }, []);
44
+ if (screen === 'onboarding') {
45
+ return React.createElement(OnboardingScreen, { onComplete: handleOnboardingComplete });
46
+ }
47
+ return React.createElement(ChatScreen, { config });
48
+ }
45
49
  // ── Onboarding Screen ───────────────────────────────────────────────
46
- function Onboarding({ onComplete }) {
50
+ function OnboardingScreen({ onComplete }) {
47
51
  const [step, setStep] = useState(0);
48
52
  const [apiKey, setApiKey] = useState('');
49
53
  const [name, setName] = useState('');
@@ -82,7 +86,6 @@ function ChatScreen({ config }) {
82
86
  const [input, setInput] = useState('');
83
87
  const [status, setStatus] = useState('Ready');
84
88
  const [thinking, setThinking] = useState(false);
85
- const scrollRef = useRef(0);
86
89
  const visibleMessages = messages.slice(-50);
87
90
  const handleSubmit = useCallback(async (text) => {
88
91
  const trimmed = text.trim();
@@ -91,7 +94,6 @@ function ChatScreen({ config }) {
91
94
  setInput('');
92
95
  setThinking(true);
93
96
  setStatus('Thinking...');
94
- scrollRef.current++;
95
97
  const userMsg = { role: 'user', content: trimmed };
96
98
  const newMessages = [...messages, userMsg];
97
99
  setMessages(newMessages);
@@ -99,11 +101,11 @@ function ChatScreen({ config }) {
99
101
  const OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';
100
102
  const tools = [
101
103
  { 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'] } } },
102
- { type: 'function', function: { name: 'read_file', description: 'Read file contents. ALWAYS read before editing.', parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] } } },
103
- { type: 'function', function: { name: 'write_file', description: 'Create or overwrite a file. Creates directories automatically.', parameters: { type: 'object', properties: { path: { type: 'string' }, content: { type: 'string' } }, required: ['path', 'content'] } } },
104
- { type: 'function', function: { name: 'edit_file', description: 'Make precise edits to a file. search/replace.', parameters: { type: 'object', properties: { path: { type: 'string' }, search: { type: 'string' }, replace: { type: 'string' } }, required: ['path', 'search', 'replace'] } } },
104
+ { type: 'function', function: { name: 'read_file', description: 'Read file contents', parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] } } },
105
+ { 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'] } } },
106
+ { 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'] } } },
105
107
  { type: 'function', function: { name: 'list_dir', description: 'List directory contents', parameters: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] } } },
106
- { type: 'function', function: { name: 'search_files', description: 'Find files by glob pattern', parameters: { type: 'object', properties: { pattern: { type: 'string' }, cwd: { type: 'string' } }, required: ['pattern'] } } },
108
+ { type: 'function', function: { name: 'search_files', description: 'Find files by pattern', parameters: { type: 'object', properties: { pattern: { type: 'string' }, cwd: { type: 'string' } }, required: ['pattern'] } } },
107
109
  { type: 'function', function: { name: 'grep', description: 'Search file contents', parameters: { type: 'object', properties: { pattern: { type: 'string' }, path: { type: 'string' } }, required: ['pattern', 'path'] } } },
108
110
  { type: 'function', function: { name: 'git', description: 'Run git commands', parameters: { type: 'object', properties: { args: { type: 'string' }, cwd: { type: 'string' } }, required: ['args'] } } },
109
111
  { type: 'function', function: { name: 'npm', description: 'Run npm/yarn/pnpm/bun commands', parameters: { type: 'object', properties: { args: { type: 'string' }, cwd: { type: 'string' } }, required: ['args'] } } },
@@ -172,7 +174,6 @@ Working directory: ${process.cwd()}` }, ...currentMessages],
172
174
  const toolCalls = choice.message?.tool_calls || [];
173
175
  currentMessages.push({ role: 'assistant', content });
174
176
  setMessages([...currentMessages]);
175
- scrollRef.current++;
176
177
  if (toolCalls.length === 0) {
177
178
  setStatus('Done');
178
179
  break;
@@ -181,7 +182,6 @@ Working directory: ${process.cwd()}` }, ...currentMessages],
181
182
  const args = JSON.parse(tc.function.arguments || '{}');
182
183
  const toolName = tc.function.name;
183
184
  setStatus(`Running: ${toolName}...`);
184
- scrollRef.current++;
185
185
  let output = '';
186
186
  try {
187
187
  const { execSync } = await import('child_process');
@@ -268,7 +268,6 @@ Working directory: ${process.cwd()}` }, ...currentMessages],
268
268
  }
269
269
  currentMessages.push({ role: 'tool', content: output.slice(0, 2000), tool_call_id: tc.id });
270
270
  setMessages([...currentMessages]);
271
- scrollRef.current++;
272
271
  }
273
272
  }
274
273
  if (iterations >= maxIterations)
@@ -305,11 +304,4 @@ Working directory: ${process.cwd()}` }, ...currentMessages],
305
304
  return React.createElement(Box, { key: i, marginTop: 1, paddingLeft: 2 }, React.createElement(Text, { color: '#A1A1AA' }, m.content.slice(0, 500)));
306
305
  }), thinking && React.createElement(Text, { color: '#F59E0B' }, ' ⏳ thinking...')), React.createElement(Box, { borderStyle: 'single', borderColor: '#3F3F46', paddingX: 1 }, React.createElement(Text, { color: '#7C5CFC' }, ' > '), React.createElement(Text, { color: '#FAFAFA' }, input || 'What do you want to build?'), React.createElement(Text, { color: '#52525B' }, '▌')), React.createElement(Box, { paddingX: 2 }, React.createElement(Text, { color: '#3F3F46' }, ' Ctrl+C to exit | OWL-Alpha ')));
307
306
  }
308
- // ── Main App ────────────────────────────────────────────────────────
309
- export default function App() {
310
- const config = loadConfig();
311
- if (!config?.openrouterKey) {
312
- return React.createElement(Onboarding, { onComplete: () => { } });
313
- }
314
- return React.createElement(ChatScreen, { config });
315
- }
307
+ export default App;
package/package.json CHANGED
@@ -1,14 +1,24 @@
1
1
  {
2
2
  "name": "lumina-code-agent",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Lumina Code - AI coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "bin": { "lumina": "dist/index.js" },
7
+ "bin": {
8
+ "lumina": "dist/index.js"
9
+ },
8
10
  "main": "dist/index.js",
9
- "files": ["dist/", "README.md"],
10
- "engines": { "node": ">=18.0.0" },
11
- "scripts": { "build": "tsc --noEmit false", "prepublishOnly": "npm run build" },
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
23
  "ink": "^5.1.0",
14
24
  "react": "^18.3.1",