closed-loop-cli 1.0.1 → 1.0.2

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.

Potentially problematic release.


This version of closed-loop-cli might be problematic. Click here for more details.

Files changed (3) hide show
  1. package/dist/index.js +183 -58
  2. package/package.json +1 -1
  3. package/src/index.ts +209 -61
package/dist/index.js CHANGED
@@ -42,78 +42,203 @@ const autogenesis_1 = require("./orchestrator/autogenesis");
42
42
  const server_1 = require("./dashboard/server");
43
43
  const telegram_bot_1 = require("./orchestrator/telegram-bot");
44
44
  const task_agent_1 = require("./orchestrator/task-agent");
45
- const tui_tools_1 = require("./tools/tui-tools");
46
45
  // Phase 3 is initialized
47
46
  // Load environment variables
48
47
  dotenv.config();
49
48
  function printHeader() {
49
+ const banner = `
50
+ \x1b[38;5;99m ___ _ _ _
51
+ / __\\ | ___ ___ ___ __| | | | ___ ___ _ __
52
+ / / | |/ _ \\/ __|/ _ \\/ _\` | | |/ _ \\ / _ \\| '_ \\
53
+ / /___| | (_) \\__ \\ __/ (_| | | | (_) | (_) | |_) |
54
+ \\____/|_|\\___/|___/\\___|\\__,_| |_|\\___/ \\___/| .__/
55
+ |_| \x1b[0m`;
56
+ console.log(banner);
57
+ console.log('\x1b[37mTips for getting started:\x1b[0m');
58
+ console.log('\x1b[90m1. Ask questions, edit files, or run commands.\x1b[0m');
59
+ console.log('\x1b[90m2. Be specific for the best results.\x1b[0m');
60
+ console.log('\x1b[90m3. Type "exit" or "quit" to close the session.\x1b[0m\n');
50
61
  const endpoint = process.env.ANTHROPIC_BASE_URL || 'default';
51
62
  const model = process.env.ANTHROPIC_MODEL || 'mimo-v2.5-pro[1m]';
52
63
  const subagent = process.env.CLAUDE_CODE_SUBAGENT_MODEL || 'mimo-v2.5-pro';
53
- const padRaw = (label, value) => {
54
- let valStr = value;
55
- if (valStr.length > 48) {
56
- valStr = valStr.substring(0, 45) + '...';
57
- }
58
- const visibleText = ` ${label} ❯ ${valStr}`;
59
- const padding = ' '.repeat(Math.max(0, 65 - visibleText.length));
60
- return ` \x1b[38;5;86m${label}\x1b[0m \x1b[90m❯\x1b[0m \x1b[38;5;153m${valStr}\x1b[0m${padding}`;
61
- };
62
- console.log('\x1b[38;5;99m╭──────────────────────────────────────────────────────────────────╮\x1b[0m');
63
- console.log('\x1b[38;5;99m│\x1b[0m \x1b[1;38;5;159m🤖 Closed-Loop Conversational Agent CLI\x1b[0m \x1b[38;5;99m│\x1b[0m');
64
- console.log('\x1b[38;5;99m├──────────────────────────────────────────────────────────────────┤\x1b[0m');
65
- console.log('\x1b[38;5;99m│\x1b[0m' + padRaw('Endpoint', endpoint) + '\x1b[38;5;99m│\x1b[0m');
66
- console.log('\x1b[38;5;99m│\x1b[0m' + padRaw('Model ', model) + '\x1b[38;5;99m│\x1b[0m');
67
- console.log('\x1b[38;5;99m│\x1b[0m' + padRaw('Subagent', subagent) + '\x1b[38;5;99m│\x1b[0m');
68
- console.log('\x1b[38;5;99m╰──────────────────────────────────────────────────────────────────╯\x1b[0m\n');
69
- console.log('\x1b[90m💡 Type your task to begin, or type "exit" to quit.\x1b[0m\n');
64
+ console.log(`\x1b[90mUsing: Endpoint: \x1b[38;5;86m${endpoint}\x1b[90m | Model: \x1b[38;5;153m${model}\x1b[90m | Subagent: \x1b[38;5;208m${subagent}\x1b[0m\n`);
70
65
  }
71
- async function startInteractiveCLI(effort = 'standard', codeactMode = false) {
72
- printHeader();
73
- const rl = readline.createInterface({
74
- input: process.stdin,
75
- output: process.stdout,
76
- prompt: '\x1b[38;5;99mclosed-loop\x1b[0m \x1b[38;5;86m❯\x1b[0m '
77
- });
78
- const history = [];
79
- rl.prompt();
80
- rl.on('line', async (line) => {
81
- const input = line.trim();
82
- if (!input) {
83
- rl.prompt();
66
+ class ClosedLoopTUI {
67
+ history = [];
68
+ input = '';
69
+ cursorIdx = 0;
70
+ isThinking = false;
71
+ effort;
72
+ constructor(effort = 'standard') {
73
+ this.effort = effort;
74
+ }
75
+ start() {
76
+ process.stdout.write('\x1b[?1049h'); // Enter alternate screen
77
+ process.stdout.write('\x1b[?25h'); // Show cursor
78
+ this.render();
79
+ readline.emitKeypressEvents(process.stdin);
80
+ if (process.stdin.isTTY) {
81
+ process.stdin.setRawMode(true);
82
+ }
83
+ process.stdin.on('keypress', this.handleKeypress.bind(this));
84
+ process.stdout.on('resize', () => {
85
+ this.render();
86
+ });
87
+ }
88
+ render() {
89
+ const rows = process.stdout.rows || 24;
90
+ const cols = process.stdout.columns || 80;
91
+ // Clear screen and move cursor to 1,1
92
+ process.stdout.write('\x1b[2J\x1b[H');
93
+ // 1. Draw Banner & Info
94
+ const banner = `
95
+ \x1b[38;5;99m ___ _ _ _
96
+ / __\\ | ___ ___ ___ __| | | | ___ ___ _ __
97
+ / / | |/ _ \\/ __|/ _ \\/ _\` | | |/ _ \\ / _ \\| '_ \\
98
+ / /___| | (_) \\__ \\ __/ (_| | | | (_) | (_) | |_) |
99
+ \\____/|_|\\___/|___/\\___|\\__,_| |_|\\___/ \\___/| .__/
100
+ |_| \x1b[0m`;
101
+ console.log(banner);
102
+ console.log('\x1b[37mTips for getting started:\x1b[0m');
103
+ console.log('\x1b[90m1. Ask questions, edit files, or run commands. 2. Be specific. 3. Type "exit" to quit.\x1b[0m');
104
+ const endpoint = process.env.ANTHROPIC_BASE_URL || 'default';
105
+ const model = process.env.ANTHROPIC_MODEL || 'mimo-v2.5-pro';
106
+ console.log(`\x1b[90mUsing: Endpoint: \x1b[38;5;86m${endpoint}\x1b[90m | Model: \x1b[38;5;153m${model}\x1b[0m\n`);
107
+ // 2. Draw History in the middle
108
+ const historyHeight = rows - 14;
109
+ const historyLines = [];
110
+ // Format history messages into lines
111
+ for (const msg of this.history) {
112
+ if (msg.role === 'user') {
113
+ historyLines.push(`\x1b[38;5;99mUser ❯\x1b[0m ${msg.content}`);
114
+ }
115
+ else {
116
+ const lines = msg.content.split('\n');
117
+ for (const line of lines) {
118
+ historyLines.push(`\x1b[38;5;86mAgent ❯\x1b[0m ${line}`);
119
+ }
120
+ }
121
+ }
122
+ // Slice history to fit historyHeight
123
+ const startIdx = Math.max(0, historyLines.length - historyHeight);
124
+ const visibleHistory = historyLines.slice(startIdx, startIdx + historyHeight);
125
+ for (let i = 0; i < historyHeight; i++) {
126
+ if (i < visibleHistory.length) {
127
+ console.log(visibleHistory[i]);
128
+ }
129
+ else {
130
+ console.log(''); // empty line
131
+ }
132
+ }
133
+ // 3. Draw Bottom Input Box (3 lines)
134
+ const border = '─'.repeat(cols - 2);
135
+ console.log(`\x1b[38;5;99m╭${border}╮\x1b[0m`);
136
+ const promptStr = this.isThinking ? ' \x1b[36m⠋ Thinking...\x1b[0m' : ` › ${this.input}`;
137
+ const visiblePromptLen = this.isThinking ? 14 : 3 + this.input.length;
138
+ const padding = ' '.repeat(Math.max(0, cols - 4 - visiblePromptLen));
139
+ console.log(`\x1b[38;5;99m│\x1b[0m${promptStr}${padding}\x1b[38;5;99m│\x1b[0m`);
140
+ console.log(`\x1b[38;5;99m╰${border}╯\x1b[0m`);
141
+ // Position cursor
142
+ if (!this.isThinking) {
143
+ const cursorCol = 4 + this.cursorIdx;
144
+ const cursorRow = rows - 1;
145
+ process.stdout.write(`\x1b[${cursorRow};${cursorCol}H`);
146
+ }
147
+ else {
148
+ process.stdout.write('\x1b[?25l');
149
+ }
150
+ }
151
+ async handleKeypress(str, key) {
152
+ if (this.isThinking)
84
153
  return;
154
+ if (key.ctrl && key.name === 'c') {
155
+ this.exit();
85
156
  }
86
- if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
87
- console.log('\x1b[90mGoodbye!\x1b[0m');
88
- process.exit(0);
157
+ if (key.name === 'return') {
158
+ const cmd = this.input.trim();
159
+ if (!cmd)
160
+ return;
161
+ if (cmd.toLowerCase() === 'exit' || cmd.toLowerCase() === 'quit') {
162
+ this.exit();
163
+ }
164
+ this.history.push({ role: 'user', content: cmd });
165
+ this.input = '';
166
+ this.cursorIdx = 0;
167
+ this.isThinking = true;
168
+ this.render();
169
+ try {
170
+ // Temporarily leave alternate screen buffer for execution
171
+ process.stdout.write('\x1b[?1049l');
172
+ process.stdout.write('\x1b[?25h');
173
+ if (process.stdin.isTTY) {
174
+ process.stdin.setRawMode(false);
175
+ }
176
+ console.log(`\n\x1b[35;1m=================== EXECUTION MODE ===================\x1b[0m`);
177
+ console.log(`\x1b[90mRunning Task:\x1b[0m ${cmd}\n`);
178
+ const response = await (0, task_agent_1.runTaskAgent)(cmd, {
179
+ role: 'coder_codeact',
180
+ effort: this.effort,
181
+ history: this.history.slice(0, -1)
182
+ });
183
+ // Re-enter TUI and raw mode
184
+ process.stdout.write('\x1b[?1049h');
185
+ if (process.stdin.isTTY) {
186
+ process.stdin.setRawMode(true);
187
+ }
188
+ this.history.push({ role: 'assistant', content: response.result });
189
+ }
190
+ catch (err) {
191
+ // Re-enter TUI and raw mode
192
+ process.stdout.write('\x1b[?1049h');
193
+ if (process.stdin.isTTY) {
194
+ process.stdin.setRawMode(true);
195
+ }
196
+ this.history.push({ role: 'assistant', content: `Error: ${err.message}` });
197
+ }
198
+ this.isThinking = false;
199
+ process.stdout.write('\x1b[?25h');
200
+ this.render();
201
+ return;
89
202
  }
90
- rl.pause(); // Pause standard input while agent runs
91
- const spinner = new tui_tools_1.Spinner('Thinking...');
92
- spinner.start();
93
- try {
94
- const response = await (0, task_agent_1.runTaskAgent)(input, {
95
- role: 'coder_codeact', // Coder with command execution tools
96
- effort,
97
- history: history
98
- });
99
- spinner.stop(true, 'Done!');
100
- // Update history
101
- history.push({ role: 'user', content: input });
102
- history.push({ role: 'assistant', content: response.result });
103
- console.log(`\n\x1b[38;5;86m╭── Agent Response ─────────────────────────────────────────────────────────────╮\x1b[0m`);
104
- console.log(response.result);
105
- console.log(`\x1b[38;5;86m╰───────────────────────────────────────────────────────────────────────────────╯\x1b[0m\n`);
203
+ if (key.name === 'backspace') {
204
+ if (this.cursorIdx > 0) {
205
+ this.input = this.input.slice(0, this.cursorIdx - 1) + this.input.slice(this.cursorIdx);
206
+ this.cursorIdx--;
207
+ this.render();
208
+ }
209
+ return;
106
210
  }
107
- catch (err) {
108
- spinner.stop(false, 'Failed');
109
- console.error('\n\x1b[31m[Agent Error]:\x1b[0m', err.message);
211
+ if (key.name === 'left') {
212
+ if (this.cursorIdx > 0) {
213
+ this.cursorIdx--;
214
+ this.render();
215
+ }
216
+ return;
217
+ }
218
+ if (key.name === 'right') {
219
+ if (this.cursorIdx < this.input.length) {
220
+ this.cursorIdx++;
221
+ this.render();
222
+ }
223
+ return;
110
224
  }
111
- rl.resume();
112
- rl.prompt();
113
- }).on('close', () => {
114
- console.log('\n\x1b[90mGoodbye!\x1b[0m');
225
+ // Handle normal character inputs
226
+ if (str && str.length === 1 && !key.ctrl && !key.meta) {
227
+ this.input = this.input.slice(0, this.cursorIdx) + str + this.input.slice(this.cursorIdx);
228
+ this.cursorIdx++;
229
+ this.render();
230
+ }
231
+ }
232
+ exit() {
233
+ process.stdout.write('\x1b[?1049l');
234
+ process.stdout.write('\x1b[?25h');
235
+ console.log('Goodbye!');
115
236
  process.exit(0);
116
- });
237
+ }
238
+ }
239
+ async function startInteractiveCLI(effort = 'standard', codeactMode = false) {
240
+ const tui = new ClosedLoopTUI(effort);
241
+ tui.start();
117
242
  }
118
243
  function setupLogRedirection() {
119
244
  const logFile = fs.createWriteStream(path.join(process.cwd(), 'evolution.log'), { flags: 'a' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "closed-loop-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Self-Developing Multi-Agent CLI Coding Assistant",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -15,83 +15,231 @@ import { Spinner } from './tools/tui-tools';
15
15
  dotenv.config();
16
16
 
17
17
  function printHeader() {
18
+ const banner = `
19
+ \x1b[38;5;99m ___ _ _ _
20
+ / __\\ | ___ ___ ___ __| | | | ___ ___ _ __
21
+ / / | |/ _ \\/ __|/ _ \\/ _\` | | |/ _ \\ / _ \\| '_ \\
22
+ / /___| | (_) \\__ \\ __/ (_| | | | (_) | (_) | |_) |
23
+ \\____/|_|\\___/|___/\\___|\\__,_| |_|\\___/ \\___/| .__/
24
+ |_| \x1b[0m`;
25
+
26
+ console.log(banner);
27
+ console.log('\x1b[37mTips for getting started:\x1b[0m');
28
+ console.log('\x1b[90m1. Ask questions, edit files, or run commands.\x1b[0m');
29
+ console.log('\x1b[90m2. Be specific for the best results.\x1b[0m');
30
+ console.log('\x1b[90m3. Type "exit" or "quit" to close the session.\x1b[0m\n');
31
+
18
32
  const endpoint = process.env.ANTHROPIC_BASE_URL || 'default';
19
33
  const model = process.env.ANTHROPIC_MODEL || 'mimo-v2.5-pro[1m]';
20
34
  const subagent = process.env.CLAUDE_CODE_SUBAGENT_MODEL || 'mimo-v2.5-pro';
21
-
22
- const padRaw = (label: string, value: string) => {
23
- let valStr = value;
24
- if (valStr.length > 48) {
25
- valStr = valStr.substring(0, 45) + '...';
26
- }
27
- const visibleText = ` ${label} ❯ ${valStr}`;
28
- const padding = ' '.repeat(Math.max(0, 65 - visibleText.length));
29
- return ` \x1b[38;5;86m${label}\x1b[0m \x1b[90m❯\x1b[0m \x1b[38;5;153m${valStr}\x1b[0m${padding}`;
30
- };
31
35
 
32
- console.log('\x1b[38;5;99m╭──────────────────────────────────────────────────────────────────╮\x1b[0m');
33
- console.log('\x1b[38;5;99m│\x1b[0m \x1b[1;38;5;159m🤖 Closed-Loop Conversational Agent CLI\x1b[0m \x1b[38;5;99m│\x1b[0m');
34
- console.log('\x1b[38;5;99m├──────────────────────────────────────────────────────────────────┤\x1b[0m');
35
- console.log('\x1b[38;5;99m│\x1b[0m' + padRaw('Endpoint', endpoint) + '\x1b[38;5;99m│\x1b[0m');
36
- console.log('\x1b[38;5;99m│\x1b[0m' + padRaw('Model ', model) + '\x1b[38;5;99m│\x1b[0m');
37
- console.log('\x1b[38;5;99m│\x1b[0m' + padRaw('Subagent', subagent) + '\x1b[38;5;99m│\x1b[0m');
38
- console.log('\x1b[38;5;99m╰──────────────────────────────────────────────────────────────────╯\x1b[0m\n');
39
- console.log('\x1b[90m💡 Type your task to begin, or type "exit" to quit.\x1b[0m\n');
36
+ console.log(`\x1b[90mUsing: Endpoint: \x1b[38;5;86m${endpoint}\x1b[90m | Model: \x1b[38;5;153m${model}\x1b[90m | Subagent: \x1b[38;5;208m${subagent}\x1b[0m\n`);
40
37
  }
41
38
 
42
- async function startInteractiveCLI(effort: 'standard' | 'ultracode' = 'standard', codeactMode = false) {
43
- printHeader();
39
+ class ClosedLoopTUI {
40
+ private history: { role: 'user' | 'assistant'; content: string }[] = [];
41
+ private input = '';
42
+ private cursorIdx = 0;
43
+ private isThinking = false;
44
+ private effort: 'standard' | 'ultracode';
45
+
46
+ constructor(effort: 'standard' | 'ultracode' = 'standard') {
47
+ this.effort = effort;
48
+ }
49
+
50
+ start() {
51
+ process.stdout.write('\x1b[?1049h'); // Enter alternate screen
52
+ process.stdout.write('\x1b[?25h'); // Show cursor
53
+ this.render();
54
+
55
+ readline.emitKeypressEvents(process.stdin);
56
+ if (process.stdin.isTTY) {
57
+ process.stdin.setRawMode(true);
58
+ }
59
+
60
+ process.stdin.on('keypress', this.handleKeypress.bind(this));
61
+ process.stdout.on('resize', () => {
62
+ this.render();
63
+ });
64
+ }
65
+
66
+ render() {
67
+ const rows = process.stdout.rows || 24;
68
+ const cols = process.stdout.columns || 80;
69
+
70
+ // Clear screen and move cursor to 1,1
71
+ process.stdout.write('\x1b[2J\x1b[H');
72
+
73
+ // 1. Draw Banner & Info
74
+ const banner = `
75
+ \x1b[38;5;99m ___ _ _ _
76
+ / __\\ | ___ ___ ___ __| | | | ___ ___ _ __
77
+ / / | |/ _ \\/ __|/ _ \\/ _\` | | |/ _ \\ / _ \\| '_ \\
78
+ / /___| | (_) \\__ \\ __/ (_| | | | (_) | (_) | |_) |
79
+ \\____/|_|\\___/|___/\\___|\\__,_| |_|\\___/ \\___/| .__/
80
+ |_| \x1b[0m`;
81
+ console.log(banner);
82
+
83
+ console.log('\x1b[37mTips for getting started:\x1b[0m');
84
+ console.log('\x1b[90m1. Ask questions, edit files, or run commands. 2. Be specific. 3. Type "exit" to quit.\x1b[0m');
85
+
86
+ const endpoint = process.env.ANTHROPIC_BASE_URL || 'default';
87
+ const model = process.env.ANTHROPIC_MODEL || 'mimo-v2.5-pro';
88
+ console.log(`\x1b[90mUsing: Endpoint: \x1b[38;5;86m${endpoint}\x1b[90m | Model: \x1b[38;5;153m${model}\x1b[0m\n`);
89
+
90
+ // 2. Draw History in the middle
91
+ const historyHeight = rows - 14;
92
+ const historyLines: string[] = [];
93
+
94
+ // Format history messages into lines
95
+ for (const msg of this.history) {
96
+ if (msg.role === 'user') {
97
+ historyLines.push(`\x1b[38;5;99mUser ❯\x1b[0m ${msg.content}`);
98
+ } else {
99
+ const lines = msg.content.split('\n');
100
+ for (const line of lines) {
101
+ historyLines.push(`\x1b[38;5;86mAgent ❯\x1b[0m ${line}`);
102
+ }
103
+ }
104
+ }
105
+
106
+ // Slice history to fit historyHeight
107
+ const startIdx = Math.max(0, historyLines.length - historyHeight);
108
+ const visibleHistory = historyLines.slice(startIdx, startIdx + historyHeight);
109
+
110
+ for (let i = 0; i < historyHeight; i++) {
111
+ if (i < visibleHistory.length) {
112
+ console.log(visibleHistory[i]);
113
+ } else {
114
+ console.log(''); // empty line
115
+ }
116
+ }
117
+
118
+ // 3. Draw Bottom Input Box (3 lines)
119
+ const border = '─'.repeat(cols - 2);
120
+ console.log(`\x1b[38;5;99m╭${border}╮\x1b[0m`);
121
+
122
+ const promptStr = this.isThinking ? ' \x1b[36m⠋ Thinking...\x1b[0m' : ` › ${this.input}`;
123
+ const visiblePromptLen = this.isThinking ? 14 : 3 + this.input.length;
124
+ const padding = ' '.repeat(Math.max(0, cols - 4 - visiblePromptLen));
125
+ console.log(`\x1b[38;5;99m│\x1b[0m${promptStr}${padding}\x1b[38;5;99m│\x1b[0m`);
126
+
127
+ console.log(`\x1b[38;5;99m╰${border}╯\x1b[0m`);
128
+
129
+ // Position cursor
130
+ if (!this.isThinking) {
131
+ const cursorCol = 4 + this.cursorIdx;
132
+ const cursorRow = rows - 1;
133
+ process.stdout.write(`\x1b[${cursorRow};${cursorCol}H`);
134
+ } else {
135
+ process.stdout.write('\x1b[?25l');
136
+ }
137
+ }
138
+
139
+ async handleKeypress(str: string, key: any) {
140
+ if (this.isThinking) return;
44
141
 
45
- const rl = readline.createInterface({
46
- input: process.stdin,
47
- output: process.stdout,
48
- prompt: '\x1b[38;5;99mclosed-loop\x1b[0m \x1b[38;5;86m❯\x1b[0m '
49
- });
142
+ if (key.ctrl && key.name === 'c') {
143
+ this.exit();
144
+ }
145
+
146
+ if (key.name === 'return') {
147
+ const cmd = this.input.trim();
148
+ if (!cmd) return;
149
+
150
+ if (cmd.toLowerCase() === 'exit' || cmd.toLowerCase() === 'quit') {
151
+ this.exit();
152
+ }
153
+
154
+ this.history.push({ role: 'user', content: cmd });
155
+ this.input = '';
156
+ this.cursorIdx = 0;
157
+ this.isThinking = true;
158
+ this.render();
159
+
160
+ try {
161
+ // Temporarily leave alternate screen buffer for execution
162
+ process.stdout.write('\x1b[?1049l');
163
+ process.stdout.write('\x1b[?25h');
164
+ if (process.stdin.isTTY) {
165
+ process.stdin.setRawMode(false);
166
+ }
167
+
168
+ console.log(`\n\x1b[35;1m=================== EXECUTION MODE ===================\x1b[0m`);
169
+ console.log(`\x1b[90mRunning Task:\x1b[0m ${cmd}\n`);
50
170
 
51
- const history: any[] = [];
52
- rl.prompt();
171
+ const response = await runTaskAgent(cmd, {
172
+ role: 'coder_codeact' as any,
173
+ effort: this.effort,
174
+ history: this.history.slice(0, -1)
175
+ });
53
176
 
54
- rl.on('line', async (line) => {
55
- const input = line.trim();
56
- if (!input) {
57
- rl.prompt();
177
+ // Re-enter TUI and raw mode
178
+ process.stdout.write('\x1b[?1049h');
179
+ if (process.stdin.isTTY) {
180
+ process.stdin.setRawMode(true);
181
+ }
182
+
183
+ this.history.push({ role: 'assistant', content: response.result });
184
+ } catch (err: any) {
185
+ // Re-enter TUI and raw mode
186
+ process.stdout.write('\x1b[?1049h');
187
+ if (process.stdin.isTTY) {
188
+ process.stdin.setRawMode(true);
189
+ }
190
+ this.history.push({ role: 'assistant', content: `Error: ${err.message}` });
191
+ }
192
+
193
+ this.isThinking = false;
194
+ process.stdout.write('\x1b[?25h');
195
+ this.render();
58
196
  return;
59
197
  }
60
198
 
61
- if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
62
- console.log('\x1b[90mGoodbye!\x1b[0m');
63
- process.exit(0);
199
+ if (key.name === 'backspace') {
200
+ if (this.cursorIdx > 0) {
201
+ this.input = this.input.slice(0, this.cursorIdx - 1) + this.input.slice(this.cursorIdx);
202
+ this.cursorIdx--;
203
+ this.render();
204
+ }
205
+ return;
64
206
  }
65
207
 
66
- rl.pause(); // Pause standard input while agent runs
67
- const spinner = new Spinner('Thinking...');
68
- spinner.start();
69
- try {
70
- const response = await runTaskAgent(input, {
71
- role: 'coder_codeact' as any, // Coder with command execution tools
72
- effort,
73
- history: history
74
- });
75
-
76
- spinner.stop(true, 'Done!');
77
-
78
- // Update history
79
- history.push({ role: 'user', content: input });
80
- history.push({ role: 'assistant', content: response.result });
81
-
82
- console.log(`\n\x1b[38;5;86m╭── Agent Response ─────────────────────────────────────────────────────────────╮\x1b[0m`);
83
- console.log(response.result);
84
- console.log(`\x1b[38;5;86m╰───────────────────────────────────────────────────────────────────────────────╯\x1b[0m\n`);
85
- } catch (err: any) {
86
- spinner.stop(false, 'Failed');
87
- console.error('\n\x1b[31m[Agent Error]:\x1b[0m', err.message);
208
+ if (key.name === 'left') {
209
+ if (this.cursorIdx > 0) {
210
+ this.cursorIdx--;
211
+ this.render();
212
+ }
213
+ return;
214
+ }
215
+
216
+ if (key.name === 'right') {
217
+ if (this.cursorIdx < this.input.length) {
218
+ this.cursorIdx++;
219
+ this.render();
220
+ }
221
+ return;
88
222
  }
89
- rl.resume();
90
- rl.prompt();
91
- }).on('close', () => {
92
- console.log('\n\x1b[90mGoodbye!\x1b[0m');
223
+
224
+ // Handle normal character inputs
225
+ if (str && str.length === 1 && !key.ctrl && !key.meta) {
226
+ this.input = this.input.slice(0, this.cursorIdx) + str + this.input.slice(this.cursorIdx);
227
+ this.cursorIdx++;
228
+ this.render();
229
+ }
230
+ }
231
+
232
+ exit() {
233
+ process.stdout.write('\x1b[?1049l');
234
+ process.stdout.write('\x1b[?25h');
235
+ console.log('Goodbye!');
93
236
  process.exit(0);
94
- });
237
+ }
238
+ }
239
+
240
+ async function startInteractiveCLI(effort: 'standard' | 'ultracode' = 'standard', codeactMode = false) {
241
+ const tui = new ClosedLoopTUI(effort);
242
+ tui.start();
95
243
  }
96
244
 
97
245
  function setupLogRedirection() {