@vierratale/ai 0.1.0-beta.14 → 0.1.0-beta.16
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/README.md +19 -132
- package/bin/vierrataleai.js +1 -76
- package/build.sh +17 -0
- package/package.json +16 -22
- package/src/catalog.js +49 -78
- package/src/cli.js +121 -1007
- package/src/config.js +10 -26
- package/src/index.js +1 -2
- package/src/installer.js +105 -0
- package/src/prompts/system.json +2 -2
- package/src/prompts/system.md +0 -42
- package/src/providers/anthropic.js +34 -65
- package/src/providers/base.js +0 -10
- package/src/providers/cortex.js +40 -223
- package/src/providers/gemini.js +31 -62
- package/src/providers/openai.js +33 -64
- package/src/session.js +15 -195
- package/src/ui/branding.js +1 -1
- package/src/utils/intents.js +1 -61
- package/src/utils/webfetch.js +24 -576
- package/src/utils/websearch.js +44 -213
- package/LICENSE +0 -21
- package/src/cmd/agent.js +0 -1825
- package/src/cmd/executor.js +0 -271
- package/src/cmd/todos.js +0 -64
- package/src/cmd/tools.js +0 -204
- package/src/engine.js +0 -150
- package/src/ui/chatbox.js +0 -280
- package/src/ui/input.js +0 -501
- package/src/utils/downloader.js +0 -364
- package/src/utils/filetree.js +0 -44
- package/src/utils/filewriter.js +0 -97
- package/src/utils/logger.js +0 -67
- package/src/utils/math.js +0 -142
package/src/ui/chatbox.js
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { Branding } from './branding.js';
|
|
2
|
-
|
|
3
|
-
const C = Branding.colors;
|
|
4
|
-
|
|
5
|
-
const visible = (s) => s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
6
|
-
|
|
7
|
-
function padRight(text, width) {
|
|
8
|
-
const t = String(text ?? '');
|
|
9
|
-
return t + ' '.repeat(Math.max(0, width - visible(t)));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function wrapLines(text, width) {
|
|
13
|
-
const out = [];
|
|
14
|
-
const src = String(text ?? '');
|
|
15
|
-
for (const raw of src.split('\n')) {
|
|
16
|
-
if (!raw.trim()) {
|
|
17
|
-
out.push('');
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
const words = raw.split(/\s+/).filter(Boolean);
|
|
21
|
-
let cur = '';
|
|
22
|
-
for (const word of words) {
|
|
23
|
-
if (word.length >= width) {
|
|
24
|
-
if (cur) {
|
|
25
|
-
out.push(cur);
|
|
26
|
-
cur = '';
|
|
27
|
-
}
|
|
28
|
-
for (let i = 0; i < word.length; i += width) out.push(word.slice(i, i + width));
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const next = cur ? `${cur} ${word}` : word;
|
|
32
|
-
if (visible(next) > width) {
|
|
33
|
-
if (cur) out.push(cur);
|
|
34
|
-
cur = word;
|
|
35
|
-
} else {
|
|
36
|
-
cur = next;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (cur) out.push(cur);
|
|
40
|
-
}
|
|
41
|
-
return out;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function hardWrap(text, width) {
|
|
45
|
-
const out = [];
|
|
46
|
-
const src = String(text ?? '').replace(/\t/g, ' ');
|
|
47
|
-
for (const raw of src.split('\n')) {
|
|
48
|
-
if (visible(raw) <= width) {
|
|
49
|
-
out.push(raw);
|
|
50
|
-
} else {
|
|
51
|
-
for (let i = 0; i < raw.length; i += width) out.push(raw.slice(i, i + width));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return out;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Column geometry: true-full width, then a centered column like opencode.
|
|
58
|
-
export function columnWidth(termWidth) {
|
|
59
|
-
const w = termWidth && termWidth > 44 ? termWidth : 96;
|
|
60
|
-
return Math.max(40, Math.min(100, w - 8));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function centerPad(col, termWidth) {
|
|
64
|
-
const w = termWidth && termWidth > 44 ? termWidth : 96;
|
|
65
|
-
const pad = Math.max(0, Math.floor((w - col) / 2));
|
|
66
|
-
return ' '.repeat(pad);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function highlight(text) {
|
|
70
|
-
return String(text)
|
|
71
|
-
.replace(/\*\*([^*]+)\*\*/g, `${C.bold}$1${C.reset}`)
|
|
72
|
-
.replace(/`([^`]+)`/g, `${C.accent}$1${C.reset}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function splitBlocks(text) {
|
|
76
|
-
const blocks = [];
|
|
77
|
-
const re = /```([\w./+-]*)[^\n]*\n?([\s\S]*?)(?:```|$)/g;
|
|
78
|
-
let last = 0;
|
|
79
|
-
let m;
|
|
80
|
-
while ((m = re.exec(String(text)))) {
|
|
81
|
-
if (m.index > last) blocks.push({ code: false, text: text.slice(last, m.index) });
|
|
82
|
-
blocks.push({ code: true, lang: m[1] || 'code', content: m[2] });
|
|
83
|
-
last = m.index + m[0].length;
|
|
84
|
-
}
|
|
85
|
-
if (last < text.length) blocks.push({ code: false, text: text.slice(last) });
|
|
86
|
-
return blocks;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export class FrameThrottle {
|
|
90
|
-
constructor(render, min = 40) {
|
|
91
|
-
this.render = render;
|
|
92
|
-
this.min = min;
|
|
93
|
-
this.last = 0;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
schedule() {
|
|
97
|
-
const now = performance.now();
|
|
98
|
-
if (now - this.last >= this.min) {
|
|
99
|
-
this.last = now;
|
|
100
|
-
this.render();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
flush() {
|
|
105
|
-
this.render();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export class ChatUI {
|
|
110
|
-
constructor({ model = '', engine = '' } = {}) {
|
|
111
|
-
this.model = model;
|
|
112
|
-
this.engine = engine;
|
|
113
|
-
this.status = '';
|
|
114
|
-
this.notices = [];
|
|
115
|
-
this.streaming = null;
|
|
116
|
-
this.thinking = false;
|
|
117
|
-
this.compositor = null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
setCompositor(fn) {
|
|
121
|
-
this.compositor = fn;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
width() {
|
|
125
|
-
const w = process.stdout.columns;
|
|
126
|
-
return w && w > 44 ? w : 96;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
setModel(model, engine) {
|
|
130
|
-
this.model = model;
|
|
131
|
-
this.engine = engine;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
setStatus(text) {
|
|
135
|
-
this.status = String(text ?? '');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
setThinking(flag) {
|
|
139
|
-
this.thinking = !!flag;
|
|
140
|
-
this.streaming = null;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
setStreaming(text) {
|
|
144
|
-
this.streaming = String(text ?? '');
|
|
145
|
-
this.thinking = false;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
notify(text) {
|
|
149
|
-
if (text) this.notices.push(String(text));
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
clearNotices() {
|
|
153
|
-
this.notices = [];
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
_codeBox(lang, content, w) {
|
|
157
|
-
const out = [];
|
|
158
|
-
const c = C.success;
|
|
159
|
-
// The box lives inside the centered column and carries its own 4-space
|
|
160
|
-
// indent, so draw it at (w - 4) to stay inside the column.
|
|
161
|
-
const box = Math.max(8, w - 4);
|
|
162
|
-
const inner = Math.max(1, box - 8);
|
|
163
|
-
const head = ` ${c}╭─${C.reset} ${C.bold}${c}${lang || 'code'}${C.reset}${c} `;
|
|
164
|
-
const fill = Math.max(0, box - visible(head) - 1);
|
|
165
|
-
out.push(`${head}${'─'.repeat(fill)}╮${C.reset}`);
|
|
166
|
-
const lines = hardWrap(content, inner);
|
|
167
|
-
while (lines.length && lines[lines.length - 1].trim() === '') lines.pop();
|
|
168
|
-
if (!lines.length) lines.push('');
|
|
169
|
-
for (const line of lines) {
|
|
170
|
-
out.push(` ${c}│${C.reset} ${padRight(line, inner)} ${c}│${C.reset}`);
|
|
171
|
-
}
|
|
172
|
-
out.push(` ${c}╰${'─'.repeat(Math.max(0, box - 6))}╯${C.reset}`);
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
_contentRows(content, w) {
|
|
177
|
-
const rows = [];
|
|
178
|
-
const width = Math.max(20, w - 4);
|
|
179
|
-
const blocks = splitBlocks(content);
|
|
180
|
-
for (const b of blocks) {
|
|
181
|
-
if (b.code) {
|
|
182
|
-
rows.push(...this._codeBox(b.lang, b.content, w));
|
|
183
|
-
} else {
|
|
184
|
-
const lines = wrapLines(b.text, width);
|
|
185
|
-
if (lines.length === 0) lines.push('');
|
|
186
|
-
for (const line of lines) {
|
|
187
|
-
rows.push(line.trim() === '' ? '' : ` ${highlight(line)}`);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return rows;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
_centered(row) {
|
|
195
|
-
return `${centerPad(columnWidth(process.stdout.columns), process.stdout.columns)}${row}`;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Banner drawn as part of the full clear+draw cycle so it can never be
|
|
199
|
-
// wiped by a later render (the old showBanner() was erased instantly).
|
|
200
|
-
_bannerRows(col) {
|
|
201
|
-
const out = [];
|
|
202
|
-
// Center the art as a BLOCK: pad every line to the widest one, then use a
|
|
203
|
-
// single shared left indent so the llama's rows stay glued together.
|
|
204
|
-
const art = Branding.BANNER.split('\n').map((l) => l.trimEnd());
|
|
205
|
-
const artW = Math.max(...art.map((l) => visible(l)));
|
|
206
|
-
const padL = Math.max(0, Math.floor((col - artW) / 2));
|
|
207
|
-
for (const line of art) {
|
|
208
|
-
out.push(`${' '.repeat(padL)}${C.primary}${line}${C.reset}`);
|
|
209
|
-
}
|
|
210
|
-
const center = (s) => {
|
|
211
|
-
const p = Math.max(0, Math.floor((col - visible(s)) / 2));
|
|
212
|
-
return `${' '.repeat(p)}${s}`;
|
|
213
|
-
};
|
|
214
|
-
out.push(center(`${C.bold}${C.accent}▸ ${Branding.APP_NAME} · ${this.model} · ${this.engine}${C.reset}`));
|
|
215
|
-
out.push('');
|
|
216
|
-
const box = Math.max(24, col - 6);
|
|
217
|
-
const padR = (s) => s + ' '.repeat(Math.max(1, box - visible(s)));
|
|
218
|
-
// Border is ` ┌` + (box+2) dashes + `┐` = box+6 wide, matching every row.
|
|
219
|
-
const borderLine = ` ${C.primary}┌${'─'.repeat(box + 2)}┐${C.reset}`;
|
|
220
|
-
out.push(center(borderLine));
|
|
221
|
-
out.push(center(` ${C.primary}│${C.reset} ${padR(' ' + C.bold + C.primary + 'Model' + C.reset)} ${C.primary}│${C.reset}`));
|
|
222
|
-
out.push(center(` ${C.primary}│${C.reset} ${padR(' ' + C.accent + this.model + C.reset)} ${C.primary}│${C.reset}`));
|
|
223
|
-
out.push(center(` ${C.primary}│${C.reset} ${padR(' ' + C.bold + C.primary + 'Engine' + C.reset)} ${C.primary}│${C.reset}`));
|
|
224
|
-
out.push(center(` ${C.primary}│${C.reset} ${padR(' ' + C.accent + this.engine + C.reset)} ${C.primary}│${C.reset}`));
|
|
225
|
-
out.push(center(` ${C.primary}└${'─'.repeat(box + 2)}┘${C.reset}`));
|
|
226
|
-
out.push('');
|
|
227
|
-
out.push(center(`${C.dim}Type /help for commands · /quit to exit${C.reset}`));
|
|
228
|
-
out.push('');
|
|
229
|
-
return out;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
render(messages) {
|
|
233
|
-
const w = this.width();
|
|
234
|
-
const col = columnWidth(w);
|
|
235
|
-
const out = [];
|
|
236
|
-
out.push(`\x1b[2J\x1b[H`);
|
|
237
|
-
// Banner (opencode style header) at the top of the centered column.
|
|
238
|
-
out.push(...this._bannerRows(col));
|
|
239
|
-
if (this.status) out.push(this._centered(` ${C.dim}${this.status}${C.reset}`));
|
|
240
|
-
out.push('');
|
|
241
|
-
|
|
242
|
-
for (const m of messages || []) {
|
|
243
|
-
if (!m || m.hidden || !m.content) continue;
|
|
244
|
-
const title = m.role === 'user' ? 'You' : Branding.AI_PROMPT;
|
|
245
|
-
const color = m.role === 'user' ? C.accent : C.primary;
|
|
246
|
-
out.push(this._centered(` ${C.bold}${color}${title}${C.reset}`));
|
|
247
|
-
for (const row of this._contentRows(m.content, col)) {
|
|
248
|
-
out.push(this._centered(row));
|
|
249
|
-
}
|
|
250
|
-
out.push('');
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (this.thinking) {
|
|
254
|
-
out.push(this._centered(` ${C.bold}${C.primary}${Branding.AI_PROMPT}${C.reset}`));
|
|
255
|
-
out.push(this._centered(` ${C.dim}···${C.reset}`));
|
|
256
|
-
out.push('');
|
|
257
|
-
} else if (this.streaming !== null) {
|
|
258
|
-
out.push(this._centered(` ${C.bold}${C.primary}${Branding.AI_PROMPT}${C.reset}`));
|
|
259
|
-
const rows = this._contentRows(this.streaming, col);
|
|
260
|
-
for (const r of rows.length ? rows : [' ']) out.push(this._centered(r));
|
|
261
|
-
out.push('');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
for (const n of this.notices) {
|
|
265
|
-
for (const line of wrapLines(n, col - 4)) out.push(this._centered(` ${C.dim}${line}${C.reset}`));
|
|
266
|
-
}
|
|
267
|
-
if (this.notices.length) out.push('');
|
|
268
|
-
|
|
269
|
-
// Keep the latest content pinned to the bottom of the terminal so streaming
|
|
270
|
-
// never re-scrolls through older messages (which caused a flash).
|
|
271
|
-
const isTty = process.stdout.isTTY && process.stdout.rows > 8;
|
|
272
|
-
const rows = isTty ? process.stdout.rows : 999;
|
|
273
|
-
const reserved = this.compositor ? Math.max(0, this.compositor.reservedRows()) : 0;
|
|
274
|
-
const available = Math.max(3, rows - reserved);
|
|
275
|
-
const lines = out.join('\n').split('\n').slice(1);
|
|
276
|
-
const body = lines.length > available ? lines.slice(lines.length - available) : lines;
|
|
277
|
-
process.stdout.write('\x1b[2J\x1b[H' + body.join('\n') + '\n');
|
|
278
|
-
if (this.compositor) this.compositor.draw();
|
|
279
|
-
}
|
|
280
|
-
}
|