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.
- package/dist/index.js +183 -58
- package/package.json +1 -1
- 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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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 (
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
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(
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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 (
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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() {
|