@averagejoeslab/puppuccino-tui 0.1.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.cjs +578 -0
- package/dist/index.js +532 -0
- package/package.json +41 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Cmd: () => import_tui2.Cmd,
|
|
24
|
+
DarkTheme: () => DarkTheme,
|
|
25
|
+
KALDI: () => KALDI,
|
|
26
|
+
KALDI_COLORS: () => KALDI_COLORS,
|
|
27
|
+
KaldiTheme: () => KaldiTheme,
|
|
28
|
+
Key: () => import_tui2.Key,
|
|
29
|
+
LightTheme: () => LightTheme,
|
|
30
|
+
Program: () => import_tui2.Program,
|
|
31
|
+
Style: () => import_style3.Style,
|
|
32
|
+
createApp: () => createApp,
|
|
33
|
+
getGoodbyeBanner: () => getGoodbyeBanner,
|
|
34
|
+
getKaldi: () => getKaldi,
|
|
35
|
+
getPrompt: () => getPrompt,
|
|
36
|
+
getTheme: () => getTheme,
|
|
37
|
+
getWelcomeBanner: () => getWelcomeBanner,
|
|
38
|
+
init: () => init,
|
|
39
|
+
renderMarkdown: () => import_markdown2.render,
|
|
40
|
+
themes: () => themes,
|
|
41
|
+
update: () => update,
|
|
42
|
+
view: () => view
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(index_exports);
|
|
45
|
+
|
|
46
|
+
// src/app.ts
|
|
47
|
+
var import_tui = require("@averagejoeslab/tui");
|
|
48
|
+
var import_widgets = require("@averagejoeslab/widgets");
|
|
49
|
+
var import_markdown = require("@averagejoeslab/markdown");
|
|
50
|
+
var import_term = require("@averagejoeslab/term");
|
|
51
|
+
|
|
52
|
+
// src/themes/index.ts
|
|
53
|
+
var import_style = require("@averagejoeslab/style");
|
|
54
|
+
var KaldiTheme = {
|
|
55
|
+
name: "kaldi",
|
|
56
|
+
colors: {
|
|
57
|
+
primary: [255, 250, 240],
|
|
58
|
+
// Cream white (like Kaldi's fur)
|
|
59
|
+
secondary: [139, 119, 101],
|
|
60
|
+
// Light brown
|
|
61
|
+
accent: [210, 180, 140],
|
|
62
|
+
// Tan
|
|
63
|
+
text: [245, 245, 245],
|
|
64
|
+
// Off-white
|
|
65
|
+
textDim: [169, 169, 169],
|
|
66
|
+
// Gray
|
|
67
|
+
background: [30, 30, 30],
|
|
68
|
+
// Dark
|
|
69
|
+
success: [144, 238, 144],
|
|
70
|
+
// Light green
|
|
71
|
+
error: [255, 99, 71],
|
|
72
|
+
// Tomato red
|
|
73
|
+
warning: [255, 215, 0],
|
|
74
|
+
// Gold
|
|
75
|
+
info: [135, 206, 235]
|
|
76
|
+
// Sky blue
|
|
77
|
+
},
|
|
78
|
+
styles: {
|
|
79
|
+
heading: new import_style.Style().foregroundRGB(255, 250, 240).bold(),
|
|
80
|
+
text: new import_style.Style().foregroundRGB(245, 245, 245),
|
|
81
|
+
dim: new import_style.Style().foregroundRGB(169, 169, 169),
|
|
82
|
+
accent: new import_style.Style().foregroundRGB(210, 180, 140),
|
|
83
|
+
success: new import_style.Style().foregroundRGB(144, 238, 144),
|
|
84
|
+
error: new import_style.Style().foregroundRGB(255, 99, 71),
|
|
85
|
+
warning: new import_style.Style().foregroundRGB(255, 215, 0),
|
|
86
|
+
info: new import_style.Style().foregroundRGB(135, 206, 235),
|
|
87
|
+
code: new import_style.Style().foregroundRGB(255, 250, 240).background(52),
|
|
88
|
+
link: new import_style.Style().foregroundRGB(135, 206, 235).underline(),
|
|
89
|
+
prompt: new import_style.Style().foregroundRGB(210, 180, 140).bold(),
|
|
90
|
+
toolName: new import_style.Style().foregroundRGB(135, 206, 235).bold(),
|
|
91
|
+
toolOutput: new import_style.Style().foregroundRGB(169, 169, 169)
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var DarkTheme = {
|
|
95
|
+
name: "dark",
|
|
96
|
+
colors: {
|
|
97
|
+
primary: [97, 175, 239],
|
|
98
|
+
// Blue
|
|
99
|
+
secondary: [152, 195, 121],
|
|
100
|
+
// Green
|
|
101
|
+
accent: [229, 192, 123],
|
|
102
|
+
// Yellow
|
|
103
|
+
text: [220, 220, 220],
|
|
104
|
+
textDim: [128, 128, 128],
|
|
105
|
+
background: [30, 30, 30],
|
|
106
|
+
success: [152, 195, 121],
|
|
107
|
+
error: [224, 108, 117],
|
|
108
|
+
warning: [229, 192, 123],
|
|
109
|
+
info: [97, 175, 239]
|
|
110
|
+
},
|
|
111
|
+
styles: {
|
|
112
|
+
heading: new import_style.Style().foregroundRGB(97, 175, 239).bold(),
|
|
113
|
+
text: new import_style.Style().foregroundRGB(220, 220, 220),
|
|
114
|
+
dim: new import_style.Style().foregroundRGB(128, 128, 128),
|
|
115
|
+
accent: new import_style.Style().foregroundRGB(229, 192, 123),
|
|
116
|
+
success: new import_style.Style().foregroundRGB(152, 195, 121),
|
|
117
|
+
error: new import_style.Style().foregroundRGB(224, 108, 117),
|
|
118
|
+
warning: new import_style.Style().foregroundRGB(229, 192, 123),
|
|
119
|
+
info: new import_style.Style().foregroundRGB(97, 175, 239),
|
|
120
|
+
code: new import_style.Style().foregroundRGB(220, 220, 220).background(236),
|
|
121
|
+
link: new import_style.Style().foregroundRGB(97, 175, 239).underline(),
|
|
122
|
+
prompt: new import_style.Style().foregroundRGB(152, 195, 121).bold(),
|
|
123
|
+
toolName: new import_style.Style().foregroundRGB(97, 175, 239).bold(),
|
|
124
|
+
toolOutput: new import_style.Style().foregroundRGB(128, 128, 128)
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var LightTheme = {
|
|
128
|
+
name: "light",
|
|
129
|
+
colors: {
|
|
130
|
+
primary: [0, 95, 184],
|
|
131
|
+
// Blue
|
|
132
|
+
secondary: [40, 167, 69],
|
|
133
|
+
// Green
|
|
134
|
+
accent: [202, 138, 4],
|
|
135
|
+
// Amber
|
|
136
|
+
text: [33, 33, 33],
|
|
137
|
+
textDim: [117, 117, 117],
|
|
138
|
+
background: [255, 255, 255],
|
|
139
|
+
success: [40, 167, 69],
|
|
140
|
+
error: [220, 53, 69],
|
|
141
|
+
warning: [202, 138, 4],
|
|
142
|
+
info: [0, 95, 184]
|
|
143
|
+
},
|
|
144
|
+
styles: {
|
|
145
|
+
heading: new import_style.Style().foregroundRGB(0, 95, 184).bold(),
|
|
146
|
+
text: new import_style.Style().foregroundRGB(33, 33, 33),
|
|
147
|
+
dim: new import_style.Style().foregroundRGB(117, 117, 117),
|
|
148
|
+
accent: new import_style.Style().foregroundRGB(202, 138, 4),
|
|
149
|
+
success: new import_style.Style().foregroundRGB(40, 167, 69),
|
|
150
|
+
error: new import_style.Style().foregroundRGB(220, 53, 69),
|
|
151
|
+
warning: new import_style.Style().foregroundRGB(202, 138, 4),
|
|
152
|
+
info: new import_style.Style().foregroundRGB(0, 95, 184),
|
|
153
|
+
code: new import_style.Style().foregroundRGB(33, 33, 33).background(253),
|
|
154
|
+
link: new import_style.Style().foregroundRGB(0, 95, 184).underline(),
|
|
155
|
+
prompt: new import_style.Style().foregroundRGB(40, 167, 69).bold(),
|
|
156
|
+
toolName: new import_style.Style().foregroundRGB(0, 95, 184).bold(),
|
|
157
|
+
toolOutput: new import_style.Style().foregroundRGB(117, 117, 117)
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
function getTheme(name) {
|
|
161
|
+
switch (name) {
|
|
162
|
+
case "kaldi":
|
|
163
|
+
return KaldiTheme;
|
|
164
|
+
case "light":
|
|
165
|
+
return LightTheme;
|
|
166
|
+
case "dark":
|
|
167
|
+
default:
|
|
168
|
+
return DarkTheme;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
var themes = {
|
|
172
|
+
kaldi: KaldiTheme,
|
|
173
|
+
dark: DarkTheme,
|
|
174
|
+
light: LightTheme
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/components/kaldi.ts
|
|
178
|
+
var import_style2 = require("@averagejoeslab/style");
|
|
179
|
+
var KALDI = {
|
|
180
|
+
normal: `
|
|
181
|
+
/ \\__
|
|
182
|
+
( @\\___
|
|
183
|
+
/ O
|
|
184
|
+
/ (_____/
|
|
185
|
+
/_____/ U
|
|
186
|
+
`,
|
|
187
|
+
thinking: `
|
|
188
|
+
/ \\__
|
|
189
|
+
( @\\___ ...
|
|
190
|
+
/ O
|
|
191
|
+
/ (_____/
|
|
192
|
+
/_____/ U
|
|
193
|
+
`,
|
|
194
|
+
happy: `
|
|
195
|
+
/ \\__
|
|
196
|
+
( @\\___ woof!
|
|
197
|
+
/ O
|
|
198
|
+
/ (_____/
|
|
199
|
+
/_____/ U
|
|
200
|
+
`,
|
|
201
|
+
working: `
|
|
202
|
+
/ \\__
|
|
203
|
+
( @\\___ *typing*
|
|
204
|
+
/ O
|
|
205
|
+
/ (_____/
|
|
206
|
+
/_____/ U
|
|
207
|
+
`,
|
|
208
|
+
error: `
|
|
209
|
+
/ \\__
|
|
210
|
+
( @\\___ :(
|
|
211
|
+
/ O
|
|
212
|
+
/ (_____/
|
|
213
|
+
/_____/ U
|
|
214
|
+
`,
|
|
215
|
+
small: ` /\\_/\\
|
|
216
|
+
( o.o )
|
|
217
|
+
> ^ <`
|
|
218
|
+
};
|
|
219
|
+
var KALDI_COLORS = {
|
|
220
|
+
body: [255, 250, 240],
|
|
221
|
+
// Cream/white
|
|
222
|
+
nose: [0, 0, 0],
|
|
223
|
+
// Black
|
|
224
|
+
accent: [139, 119, 101]
|
|
225
|
+
// Light brown
|
|
226
|
+
};
|
|
227
|
+
function getKaldi(variant = "normal") {
|
|
228
|
+
const art = KALDI[variant];
|
|
229
|
+
const style = new import_style2.Style().foregroundRGB(...KALDI_COLORS.body).bold();
|
|
230
|
+
return style.render(art);
|
|
231
|
+
}
|
|
232
|
+
function getWelcomeBanner(version = "0.1.0") {
|
|
233
|
+
const style = new import_style2.Style().foregroundRGB(...KALDI_COLORS.body);
|
|
234
|
+
const accentStyle = new import_style2.Style().foregroundRGB(...KALDI_COLORS.accent);
|
|
235
|
+
const banner = `
|
|
236
|
+
${style.render(KALDI.normal)}
|
|
237
|
+
${accentStyle.render(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
|
|
238
|
+
${accentStyle.render(" \u2551")} ${style.render("Puppuccino")} ${accentStyle.render("- AI Coding Assistant")} ${accentStyle.render("\u2551")}
|
|
239
|
+
${accentStyle.render(" \u2551")} ${style.render(`v${version}`)} ${accentStyle.render("| Powered by Kaldi")} ${accentStyle.render("\u2551")}
|
|
240
|
+
${accentStyle.render(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
|
|
241
|
+
`;
|
|
242
|
+
return banner;
|
|
243
|
+
}
|
|
244
|
+
function getGoodbyeBanner() {
|
|
245
|
+
const style = new import_style2.Style().foregroundRGB(...KALDI_COLORS.body);
|
|
246
|
+
return `
|
|
247
|
+
${style.render(KALDI.happy)}
|
|
248
|
+
${style.render("Goodbye! Kaldi will miss you!")}
|
|
249
|
+
`;
|
|
250
|
+
}
|
|
251
|
+
function getPrompt() {
|
|
252
|
+
return "\u{1F415}\u200D\u{1F9BA}\u276F ";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// src/app.ts
|
|
256
|
+
function init(options) {
|
|
257
|
+
const { width, height } = (0, import_term.getTerminalSize)();
|
|
258
|
+
const theme = getTheme(options?.theme || "kaldi");
|
|
259
|
+
const model = {
|
|
260
|
+
input: import_widgets.TextInput.init({
|
|
261
|
+
placeholder: "Ask Kaldi anything...",
|
|
262
|
+
width: width - 4
|
|
263
|
+
}),
|
|
264
|
+
viewport: import_widgets.Viewport.init({
|
|
265
|
+
width: width - 2,
|
|
266
|
+
height: height - 6
|
|
267
|
+
}),
|
|
268
|
+
spinner: import_widgets.Spinner.init({ style: "dots" }),
|
|
269
|
+
status: "idle",
|
|
270
|
+
messages: [],
|
|
271
|
+
theme,
|
|
272
|
+
showWelcome: true,
|
|
273
|
+
width,
|
|
274
|
+
height
|
|
275
|
+
};
|
|
276
|
+
const spinnerCmd = import_widgets.Spinner.tick(model.spinner);
|
|
277
|
+
return [model, spinnerCmd ? () => ({ type: "spinner", msg: spinnerCmd }) : null];
|
|
278
|
+
}
|
|
279
|
+
function update(msg, model) {
|
|
280
|
+
switch (msg.type) {
|
|
281
|
+
case "key": {
|
|
282
|
+
if (msg.key.ctrl && msg.key.name === "c") {
|
|
283
|
+
return [model, () => ({ type: "quit" })];
|
|
284
|
+
}
|
|
285
|
+
if (msg.key.name === "escape") {
|
|
286
|
+
if (model.showWelcome) {
|
|
287
|
+
return [{ ...model, showWelcome: false }, null];
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (msg.key.name === "enter" && model.status === "idle") {
|
|
291
|
+
const text = import_widgets.TextInput.value(model.input);
|
|
292
|
+
if (text.trim()) {
|
|
293
|
+
return update({ type: "submit" }, model);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (model.showWelcome) {
|
|
297
|
+
return [{ ...model, showWelcome: false }, null];
|
|
298
|
+
}
|
|
299
|
+
const inputMsg = import_widgets.TextInput.handleKey(msg.key);
|
|
300
|
+
if (inputMsg) {
|
|
301
|
+
return update({ type: "input", msg: inputMsg }, model);
|
|
302
|
+
}
|
|
303
|
+
return [model, null];
|
|
304
|
+
}
|
|
305
|
+
case "input": {
|
|
306
|
+
const [newInput, inputCmd] = import_widgets.TextInput.update(msg.msg, model.input);
|
|
307
|
+
const cmd = inputCmd ? () => ({ type: "input", msg: inputCmd }) : null;
|
|
308
|
+
return [{ ...model, input: newInput }, cmd];
|
|
309
|
+
}
|
|
310
|
+
case "viewport": {
|
|
311
|
+
const [newViewport, viewportCmd] = import_widgets.Viewport.update(msg.msg, model.viewport);
|
|
312
|
+
const cmd = viewportCmd ? () => ({ type: "viewport", msg: viewportCmd }) : null;
|
|
313
|
+
return [{ ...model, viewport: newViewport }, cmd];
|
|
314
|
+
}
|
|
315
|
+
case "spinner": {
|
|
316
|
+
const [newSpinner, spinnerCmd] = import_widgets.Spinner.update(msg.msg, model.spinner);
|
|
317
|
+
const cmd = spinnerCmd ? () => ({ type: "spinner", msg: spinnerCmd }) : null;
|
|
318
|
+
return [{ ...model, spinner: newSpinner }, cmd];
|
|
319
|
+
}
|
|
320
|
+
case "submit": {
|
|
321
|
+
const text = import_widgets.TextInput.value(model.input);
|
|
322
|
+
if (!text.trim()) return [model, null];
|
|
323
|
+
if (text.startsWith("/")) {
|
|
324
|
+
return handleCommand(text.slice(1), model);
|
|
325
|
+
}
|
|
326
|
+
const userMessage = {
|
|
327
|
+
role: "user",
|
|
328
|
+
content: text,
|
|
329
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
330
|
+
};
|
|
331
|
+
return [{
|
|
332
|
+
...model,
|
|
333
|
+
input: import_widgets.TextInput.init({
|
|
334
|
+
placeholder: "Ask Kaldi anything...",
|
|
335
|
+
width: model.width - 4
|
|
336
|
+
}),
|
|
337
|
+
messages: [...model.messages, userMessage],
|
|
338
|
+
status: "thinking"
|
|
339
|
+
}, null];
|
|
340
|
+
}
|
|
341
|
+
case "agent_event": {
|
|
342
|
+
return handleAgentEvent(msg.event, model);
|
|
343
|
+
}
|
|
344
|
+
case "resize": {
|
|
345
|
+
return [{
|
|
346
|
+
...model,
|
|
347
|
+
width: msg.width,
|
|
348
|
+
height: msg.height,
|
|
349
|
+
input: import_widgets.TextInput.init({
|
|
350
|
+
placeholder: "Ask Kaldi anything...",
|
|
351
|
+
width: msg.width - 4,
|
|
352
|
+
value: import_widgets.TextInput.value(model.input)
|
|
353
|
+
}),
|
|
354
|
+
viewport: import_widgets.Viewport.init({
|
|
355
|
+
width: msg.width - 2,
|
|
356
|
+
height: msg.height - 6,
|
|
357
|
+
content: model.viewport.content
|
|
358
|
+
})
|
|
359
|
+
}, null];
|
|
360
|
+
}
|
|
361
|
+
case "clear": {
|
|
362
|
+
return [{
|
|
363
|
+
...model,
|
|
364
|
+
messages: [],
|
|
365
|
+
status: "idle",
|
|
366
|
+
errorMessage: void 0
|
|
367
|
+
}, null];
|
|
368
|
+
}
|
|
369
|
+
case "error": {
|
|
370
|
+
return [{
|
|
371
|
+
...model,
|
|
372
|
+
status: "error",
|
|
373
|
+
errorMessage: msg.error
|
|
374
|
+
}, null];
|
|
375
|
+
}
|
|
376
|
+
case "dismiss_welcome": {
|
|
377
|
+
return [{ ...model, showWelcome: false }, null];
|
|
378
|
+
}
|
|
379
|
+
case "quit": {
|
|
380
|
+
return [model, null];
|
|
381
|
+
}
|
|
382
|
+
default:
|
|
383
|
+
return [model, null];
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function handleCommand(cmd, model) {
|
|
387
|
+
const [command, ...args] = cmd.split(/\s+/);
|
|
388
|
+
switch (command.toLowerCase()) {
|
|
389
|
+
case "clear":
|
|
390
|
+
case "c":
|
|
391
|
+
return update({ type: "clear" }, model);
|
|
392
|
+
case "quit":
|
|
393
|
+
case "q":
|
|
394
|
+
case "exit":
|
|
395
|
+
return update({ type: "quit" }, model);
|
|
396
|
+
case "help":
|
|
397
|
+
case "h":
|
|
398
|
+
case "?": {
|
|
399
|
+
const helpMessage = {
|
|
400
|
+
role: "assistant",
|
|
401
|
+
content: `## Available Commands
|
|
402
|
+
|
|
403
|
+
- \`/clear\` or \`/c\` - Clear conversation
|
|
404
|
+
- \`/quit\` or \`/q\` - Exit Puppuccino
|
|
405
|
+
- \`/help\` or \`/?\` - Show this help
|
|
406
|
+
|
|
407
|
+
Just type your message and press Enter to chat with Kaldi!`,
|
|
408
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
409
|
+
};
|
|
410
|
+
return [{
|
|
411
|
+
...model,
|
|
412
|
+
input: import_widgets.TextInput.init({
|
|
413
|
+
placeholder: "Ask Kaldi anything...",
|
|
414
|
+
width: model.width - 4
|
|
415
|
+
}),
|
|
416
|
+
messages: [...model.messages, helpMessage]
|
|
417
|
+
}, null];
|
|
418
|
+
}
|
|
419
|
+
default: {
|
|
420
|
+
const errorMessage = {
|
|
421
|
+
role: "assistant",
|
|
422
|
+
content: `Unknown command: \`/${command}\`. Type \`/help\` for available commands.`,
|
|
423
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
424
|
+
};
|
|
425
|
+
return [{
|
|
426
|
+
...model,
|
|
427
|
+
input: import_widgets.TextInput.init({
|
|
428
|
+
placeholder: "Ask Kaldi anything...",
|
|
429
|
+
width: model.width - 4
|
|
430
|
+
}),
|
|
431
|
+
messages: [...model.messages, errorMessage]
|
|
432
|
+
}, null];
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function handleAgentEvent(event, model) {
|
|
437
|
+
switch (event.type) {
|
|
438
|
+
case "thinking":
|
|
439
|
+
return [{ ...model, status: "thinking" }, null];
|
|
440
|
+
case "text": {
|
|
441
|
+
const messages = [...model.messages];
|
|
442
|
+
const lastMsg = messages[messages.length - 1];
|
|
443
|
+
if (lastMsg && lastMsg.role === "assistant" && !lastMsg.toolName) {
|
|
444
|
+
lastMsg.content += event.content;
|
|
445
|
+
} else {
|
|
446
|
+
messages.push({
|
|
447
|
+
role: "assistant",
|
|
448
|
+
content: event.content,
|
|
449
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
return [{ ...model, messages, status: "idle" }, null];
|
|
453
|
+
}
|
|
454
|
+
case "tool_start":
|
|
455
|
+
return [{
|
|
456
|
+
...model,
|
|
457
|
+
status: "tool",
|
|
458
|
+
currentTool: event.name
|
|
459
|
+
}, null];
|
|
460
|
+
case "tool_result": {
|
|
461
|
+
const toolMessage = {
|
|
462
|
+
role: "tool",
|
|
463
|
+
content: event.result.success ? event.result.output : `Error: ${event.result.error}`,
|
|
464
|
+
toolName: event.name,
|
|
465
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
466
|
+
};
|
|
467
|
+
return [{
|
|
468
|
+
...model,
|
|
469
|
+
messages: [...model.messages, toolMessage],
|
|
470
|
+
status: "thinking",
|
|
471
|
+
currentTool: void 0
|
|
472
|
+
}, null];
|
|
473
|
+
}
|
|
474
|
+
case "tool_denied": {
|
|
475
|
+
const deniedMessage = {
|
|
476
|
+
role: "tool",
|
|
477
|
+
content: `Permission denied: ${event.reason}`,
|
|
478
|
+
toolName: event.name,
|
|
479
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
480
|
+
};
|
|
481
|
+
return [{
|
|
482
|
+
...model,
|
|
483
|
+
messages: [...model.messages, deniedMessage],
|
|
484
|
+
status: "thinking",
|
|
485
|
+
currentTool: void 0
|
|
486
|
+
}, null];
|
|
487
|
+
}
|
|
488
|
+
case "error":
|
|
489
|
+
return [{
|
|
490
|
+
...model,
|
|
491
|
+
status: "error",
|
|
492
|
+
errorMessage: event.error
|
|
493
|
+
}, null];
|
|
494
|
+
case "done":
|
|
495
|
+
return [{ ...model, status: "idle" }, null];
|
|
496
|
+
default:
|
|
497
|
+
return [model, null];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function view(model) {
|
|
501
|
+
const { theme } = model;
|
|
502
|
+
const lines = [];
|
|
503
|
+
if (model.showWelcome) {
|
|
504
|
+
return getWelcomeBanner() + "\n\nPress any key to continue...";
|
|
505
|
+
}
|
|
506
|
+
lines.push(theme.styles.heading.render("\u2500".repeat(model.width)));
|
|
507
|
+
const chatLines = [];
|
|
508
|
+
for (const msg of model.messages) {
|
|
509
|
+
chatLines.push(renderMessage(msg, theme, model.width - 4));
|
|
510
|
+
chatLines.push("");
|
|
511
|
+
}
|
|
512
|
+
if (model.status === "thinking") {
|
|
513
|
+
const spinner = import_widgets.Spinner.view(model.spinner);
|
|
514
|
+
chatLines.push(`${spinner} ${theme.styles.dim.render("Kaldi is thinking...")}`);
|
|
515
|
+
} else if (model.status === "tool" && model.currentTool) {
|
|
516
|
+
const spinner = import_widgets.Spinner.view(model.spinner);
|
|
517
|
+
chatLines.push(`${spinner} ${theme.styles.toolName.render(model.currentTool)}`);
|
|
518
|
+
} else if (model.status === "error" && model.errorMessage) {
|
|
519
|
+
chatLines.push(theme.styles.error.render(`Error: ${model.errorMessage}`));
|
|
520
|
+
}
|
|
521
|
+
const viewportModel = import_widgets.Viewport.setContent(model.viewport, chatLines.join("\n"));
|
|
522
|
+
lines.push(import_widgets.Viewport.view(viewportModel));
|
|
523
|
+
lines.push(theme.styles.heading.render("\u2500".repeat(model.width)));
|
|
524
|
+
const prompt = getPrompt();
|
|
525
|
+
const inputView = import_widgets.TextInput.view(model.input);
|
|
526
|
+
lines.push(`${prompt}${inputView}`);
|
|
527
|
+
return lines.join("\n");
|
|
528
|
+
}
|
|
529
|
+
function renderMessage(msg, theme, width) {
|
|
530
|
+
const lines = [];
|
|
531
|
+
if (msg.role === "user") {
|
|
532
|
+
lines.push(theme.styles.accent.render("You:"));
|
|
533
|
+
lines.push(msg.content);
|
|
534
|
+
} else if (msg.role === "assistant") {
|
|
535
|
+
lines.push(theme.styles.heading.render("Kaldi:"));
|
|
536
|
+
lines.push((0, import_markdown.render)(msg.content, { width }));
|
|
537
|
+
} else if (msg.role === "tool") {
|
|
538
|
+
lines.push(theme.styles.toolName.render(`[${msg.toolName}]`));
|
|
539
|
+
const output = msg.content.length > 500 ? msg.content.slice(0, 500) + "\n...(truncated)" : msg.content;
|
|
540
|
+
lines.push(theme.styles.toolOutput.render(output));
|
|
541
|
+
}
|
|
542
|
+
return lines.join("\n");
|
|
543
|
+
}
|
|
544
|
+
function createApp(options) {
|
|
545
|
+
return new import_tui.Program({
|
|
546
|
+
init: () => init(options),
|
|
547
|
+
update,
|
|
548
|
+
view
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/index.ts
|
|
553
|
+
var import_tui2 = require("@averagejoeslab/tui");
|
|
554
|
+
var import_style3 = require("@averagejoeslab/style");
|
|
555
|
+
var import_markdown2 = require("@averagejoeslab/markdown");
|
|
556
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
557
|
+
0 && (module.exports = {
|
|
558
|
+
Cmd,
|
|
559
|
+
DarkTheme,
|
|
560
|
+
KALDI,
|
|
561
|
+
KALDI_COLORS,
|
|
562
|
+
KaldiTheme,
|
|
563
|
+
Key,
|
|
564
|
+
LightTheme,
|
|
565
|
+
Program,
|
|
566
|
+
Style,
|
|
567
|
+
createApp,
|
|
568
|
+
getGoodbyeBanner,
|
|
569
|
+
getKaldi,
|
|
570
|
+
getPrompt,
|
|
571
|
+
getTheme,
|
|
572
|
+
getWelcomeBanner,
|
|
573
|
+
init,
|
|
574
|
+
renderMarkdown,
|
|
575
|
+
themes,
|
|
576
|
+
update,
|
|
577
|
+
view
|
|
578
|
+
});
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
// src/app.ts
|
|
2
|
+
import { Program } from "@averagejoeslab/tui";
|
|
3
|
+
import { Spinner, TextInput, Viewport } from "@averagejoeslab/widgets";
|
|
4
|
+
import { render as renderMarkdown } from "@averagejoeslab/markdown";
|
|
5
|
+
import { getTerminalSize } from "@averagejoeslab/term";
|
|
6
|
+
|
|
7
|
+
// src/themes/index.ts
|
|
8
|
+
import { Style } from "@averagejoeslab/style";
|
|
9
|
+
var KaldiTheme = {
|
|
10
|
+
name: "kaldi",
|
|
11
|
+
colors: {
|
|
12
|
+
primary: [255, 250, 240],
|
|
13
|
+
// Cream white (like Kaldi's fur)
|
|
14
|
+
secondary: [139, 119, 101],
|
|
15
|
+
// Light brown
|
|
16
|
+
accent: [210, 180, 140],
|
|
17
|
+
// Tan
|
|
18
|
+
text: [245, 245, 245],
|
|
19
|
+
// Off-white
|
|
20
|
+
textDim: [169, 169, 169],
|
|
21
|
+
// Gray
|
|
22
|
+
background: [30, 30, 30],
|
|
23
|
+
// Dark
|
|
24
|
+
success: [144, 238, 144],
|
|
25
|
+
// Light green
|
|
26
|
+
error: [255, 99, 71],
|
|
27
|
+
// Tomato red
|
|
28
|
+
warning: [255, 215, 0],
|
|
29
|
+
// Gold
|
|
30
|
+
info: [135, 206, 235]
|
|
31
|
+
// Sky blue
|
|
32
|
+
},
|
|
33
|
+
styles: {
|
|
34
|
+
heading: new Style().foregroundRGB(255, 250, 240).bold(),
|
|
35
|
+
text: new Style().foregroundRGB(245, 245, 245),
|
|
36
|
+
dim: new Style().foregroundRGB(169, 169, 169),
|
|
37
|
+
accent: new Style().foregroundRGB(210, 180, 140),
|
|
38
|
+
success: new Style().foregroundRGB(144, 238, 144),
|
|
39
|
+
error: new Style().foregroundRGB(255, 99, 71),
|
|
40
|
+
warning: new Style().foregroundRGB(255, 215, 0),
|
|
41
|
+
info: new Style().foregroundRGB(135, 206, 235),
|
|
42
|
+
code: new Style().foregroundRGB(255, 250, 240).background(52),
|
|
43
|
+
link: new Style().foregroundRGB(135, 206, 235).underline(),
|
|
44
|
+
prompt: new Style().foregroundRGB(210, 180, 140).bold(),
|
|
45
|
+
toolName: new Style().foregroundRGB(135, 206, 235).bold(),
|
|
46
|
+
toolOutput: new Style().foregroundRGB(169, 169, 169)
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var DarkTheme = {
|
|
50
|
+
name: "dark",
|
|
51
|
+
colors: {
|
|
52
|
+
primary: [97, 175, 239],
|
|
53
|
+
// Blue
|
|
54
|
+
secondary: [152, 195, 121],
|
|
55
|
+
// Green
|
|
56
|
+
accent: [229, 192, 123],
|
|
57
|
+
// Yellow
|
|
58
|
+
text: [220, 220, 220],
|
|
59
|
+
textDim: [128, 128, 128],
|
|
60
|
+
background: [30, 30, 30],
|
|
61
|
+
success: [152, 195, 121],
|
|
62
|
+
error: [224, 108, 117],
|
|
63
|
+
warning: [229, 192, 123],
|
|
64
|
+
info: [97, 175, 239]
|
|
65
|
+
},
|
|
66
|
+
styles: {
|
|
67
|
+
heading: new Style().foregroundRGB(97, 175, 239).bold(),
|
|
68
|
+
text: new Style().foregroundRGB(220, 220, 220),
|
|
69
|
+
dim: new Style().foregroundRGB(128, 128, 128),
|
|
70
|
+
accent: new Style().foregroundRGB(229, 192, 123),
|
|
71
|
+
success: new Style().foregroundRGB(152, 195, 121),
|
|
72
|
+
error: new Style().foregroundRGB(224, 108, 117),
|
|
73
|
+
warning: new Style().foregroundRGB(229, 192, 123),
|
|
74
|
+
info: new Style().foregroundRGB(97, 175, 239),
|
|
75
|
+
code: new Style().foregroundRGB(220, 220, 220).background(236),
|
|
76
|
+
link: new Style().foregroundRGB(97, 175, 239).underline(),
|
|
77
|
+
prompt: new Style().foregroundRGB(152, 195, 121).bold(),
|
|
78
|
+
toolName: new Style().foregroundRGB(97, 175, 239).bold(),
|
|
79
|
+
toolOutput: new Style().foregroundRGB(128, 128, 128)
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
var LightTheme = {
|
|
83
|
+
name: "light",
|
|
84
|
+
colors: {
|
|
85
|
+
primary: [0, 95, 184],
|
|
86
|
+
// Blue
|
|
87
|
+
secondary: [40, 167, 69],
|
|
88
|
+
// Green
|
|
89
|
+
accent: [202, 138, 4],
|
|
90
|
+
// Amber
|
|
91
|
+
text: [33, 33, 33],
|
|
92
|
+
textDim: [117, 117, 117],
|
|
93
|
+
background: [255, 255, 255],
|
|
94
|
+
success: [40, 167, 69],
|
|
95
|
+
error: [220, 53, 69],
|
|
96
|
+
warning: [202, 138, 4],
|
|
97
|
+
info: [0, 95, 184]
|
|
98
|
+
},
|
|
99
|
+
styles: {
|
|
100
|
+
heading: new Style().foregroundRGB(0, 95, 184).bold(),
|
|
101
|
+
text: new Style().foregroundRGB(33, 33, 33),
|
|
102
|
+
dim: new Style().foregroundRGB(117, 117, 117),
|
|
103
|
+
accent: new Style().foregroundRGB(202, 138, 4),
|
|
104
|
+
success: new Style().foregroundRGB(40, 167, 69),
|
|
105
|
+
error: new Style().foregroundRGB(220, 53, 69),
|
|
106
|
+
warning: new Style().foregroundRGB(202, 138, 4),
|
|
107
|
+
info: new Style().foregroundRGB(0, 95, 184),
|
|
108
|
+
code: new Style().foregroundRGB(33, 33, 33).background(253),
|
|
109
|
+
link: new Style().foregroundRGB(0, 95, 184).underline(),
|
|
110
|
+
prompt: new Style().foregroundRGB(40, 167, 69).bold(),
|
|
111
|
+
toolName: new Style().foregroundRGB(0, 95, 184).bold(),
|
|
112
|
+
toolOutput: new Style().foregroundRGB(117, 117, 117)
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
function getTheme(name) {
|
|
116
|
+
switch (name) {
|
|
117
|
+
case "kaldi":
|
|
118
|
+
return KaldiTheme;
|
|
119
|
+
case "light":
|
|
120
|
+
return LightTheme;
|
|
121
|
+
case "dark":
|
|
122
|
+
default:
|
|
123
|
+
return DarkTheme;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
var themes = {
|
|
127
|
+
kaldi: KaldiTheme,
|
|
128
|
+
dark: DarkTheme,
|
|
129
|
+
light: LightTheme
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/components/kaldi.ts
|
|
133
|
+
import { Style as Style2 } from "@averagejoeslab/style";
|
|
134
|
+
var KALDI = {
|
|
135
|
+
normal: `
|
|
136
|
+
/ \\__
|
|
137
|
+
( @\\___
|
|
138
|
+
/ O
|
|
139
|
+
/ (_____/
|
|
140
|
+
/_____/ U
|
|
141
|
+
`,
|
|
142
|
+
thinking: `
|
|
143
|
+
/ \\__
|
|
144
|
+
( @\\___ ...
|
|
145
|
+
/ O
|
|
146
|
+
/ (_____/
|
|
147
|
+
/_____/ U
|
|
148
|
+
`,
|
|
149
|
+
happy: `
|
|
150
|
+
/ \\__
|
|
151
|
+
( @\\___ woof!
|
|
152
|
+
/ O
|
|
153
|
+
/ (_____/
|
|
154
|
+
/_____/ U
|
|
155
|
+
`,
|
|
156
|
+
working: `
|
|
157
|
+
/ \\__
|
|
158
|
+
( @\\___ *typing*
|
|
159
|
+
/ O
|
|
160
|
+
/ (_____/
|
|
161
|
+
/_____/ U
|
|
162
|
+
`,
|
|
163
|
+
error: `
|
|
164
|
+
/ \\__
|
|
165
|
+
( @\\___ :(
|
|
166
|
+
/ O
|
|
167
|
+
/ (_____/
|
|
168
|
+
/_____/ U
|
|
169
|
+
`,
|
|
170
|
+
small: ` /\\_/\\
|
|
171
|
+
( o.o )
|
|
172
|
+
> ^ <`
|
|
173
|
+
};
|
|
174
|
+
var KALDI_COLORS = {
|
|
175
|
+
body: [255, 250, 240],
|
|
176
|
+
// Cream/white
|
|
177
|
+
nose: [0, 0, 0],
|
|
178
|
+
// Black
|
|
179
|
+
accent: [139, 119, 101]
|
|
180
|
+
// Light brown
|
|
181
|
+
};
|
|
182
|
+
function getKaldi(variant = "normal") {
|
|
183
|
+
const art = KALDI[variant];
|
|
184
|
+
const style = new Style2().foregroundRGB(...KALDI_COLORS.body).bold();
|
|
185
|
+
return style.render(art);
|
|
186
|
+
}
|
|
187
|
+
function getWelcomeBanner(version = "0.1.0") {
|
|
188
|
+
const style = new Style2().foregroundRGB(...KALDI_COLORS.body);
|
|
189
|
+
const accentStyle = new Style2().foregroundRGB(...KALDI_COLORS.accent);
|
|
190
|
+
const banner = `
|
|
191
|
+
${style.render(KALDI.normal)}
|
|
192
|
+
${accentStyle.render(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
|
|
193
|
+
${accentStyle.render(" \u2551")} ${style.render("Puppuccino")} ${accentStyle.render("- AI Coding Assistant")} ${accentStyle.render("\u2551")}
|
|
194
|
+
${accentStyle.render(" \u2551")} ${style.render(`v${version}`)} ${accentStyle.render("| Powered by Kaldi")} ${accentStyle.render("\u2551")}
|
|
195
|
+
${accentStyle.render(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
|
|
196
|
+
`;
|
|
197
|
+
return banner;
|
|
198
|
+
}
|
|
199
|
+
function getGoodbyeBanner() {
|
|
200
|
+
const style = new Style2().foregroundRGB(...KALDI_COLORS.body);
|
|
201
|
+
return `
|
|
202
|
+
${style.render(KALDI.happy)}
|
|
203
|
+
${style.render("Goodbye! Kaldi will miss you!")}
|
|
204
|
+
`;
|
|
205
|
+
}
|
|
206
|
+
function getPrompt() {
|
|
207
|
+
return "\u{1F415}\u200D\u{1F9BA}\u276F ";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/app.ts
|
|
211
|
+
function init(options) {
|
|
212
|
+
const { width, height } = getTerminalSize();
|
|
213
|
+
const theme = getTheme(options?.theme || "kaldi");
|
|
214
|
+
const model = {
|
|
215
|
+
input: TextInput.init({
|
|
216
|
+
placeholder: "Ask Kaldi anything...",
|
|
217
|
+
width: width - 4
|
|
218
|
+
}),
|
|
219
|
+
viewport: Viewport.init({
|
|
220
|
+
width: width - 2,
|
|
221
|
+
height: height - 6
|
|
222
|
+
}),
|
|
223
|
+
spinner: Spinner.init({ style: "dots" }),
|
|
224
|
+
status: "idle",
|
|
225
|
+
messages: [],
|
|
226
|
+
theme,
|
|
227
|
+
showWelcome: true,
|
|
228
|
+
width,
|
|
229
|
+
height
|
|
230
|
+
};
|
|
231
|
+
const spinnerCmd = Spinner.tick(model.spinner);
|
|
232
|
+
return [model, spinnerCmd ? () => ({ type: "spinner", msg: spinnerCmd }) : null];
|
|
233
|
+
}
|
|
234
|
+
function update(msg, model) {
|
|
235
|
+
switch (msg.type) {
|
|
236
|
+
case "key": {
|
|
237
|
+
if (msg.key.ctrl && msg.key.name === "c") {
|
|
238
|
+
return [model, () => ({ type: "quit" })];
|
|
239
|
+
}
|
|
240
|
+
if (msg.key.name === "escape") {
|
|
241
|
+
if (model.showWelcome) {
|
|
242
|
+
return [{ ...model, showWelcome: false }, null];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (msg.key.name === "enter" && model.status === "idle") {
|
|
246
|
+
const text = TextInput.value(model.input);
|
|
247
|
+
if (text.trim()) {
|
|
248
|
+
return update({ type: "submit" }, model);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (model.showWelcome) {
|
|
252
|
+
return [{ ...model, showWelcome: false }, null];
|
|
253
|
+
}
|
|
254
|
+
const inputMsg = TextInput.handleKey(msg.key);
|
|
255
|
+
if (inputMsg) {
|
|
256
|
+
return update({ type: "input", msg: inputMsg }, model);
|
|
257
|
+
}
|
|
258
|
+
return [model, null];
|
|
259
|
+
}
|
|
260
|
+
case "input": {
|
|
261
|
+
const [newInput, inputCmd] = TextInput.update(msg.msg, model.input);
|
|
262
|
+
const cmd = inputCmd ? () => ({ type: "input", msg: inputCmd }) : null;
|
|
263
|
+
return [{ ...model, input: newInput }, cmd];
|
|
264
|
+
}
|
|
265
|
+
case "viewport": {
|
|
266
|
+
const [newViewport, viewportCmd] = Viewport.update(msg.msg, model.viewport);
|
|
267
|
+
const cmd = viewportCmd ? () => ({ type: "viewport", msg: viewportCmd }) : null;
|
|
268
|
+
return [{ ...model, viewport: newViewport }, cmd];
|
|
269
|
+
}
|
|
270
|
+
case "spinner": {
|
|
271
|
+
const [newSpinner, spinnerCmd] = Spinner.update(msg.msg, model.spinner);
|
|
272
|
+
const cmd = spinnerCmd ? () => ({ type: "spinner", msg: spinnerCmd }) : null;
|
|
273
|
+
return [{ ...model, spinner: newSpinner }, cmd];
|
|
274
|
+
}
|
|
275
|
+
case "submit": {
|
|
276
|
+
const text = TextInput.value(model.input);
|
|
277
|
+
if (!text.trim()) return [model, null];
|
|
278
|
+
if (text.startsWith("/")) {
|
|
279
|
+
return handleCommand(text.slice(1), model);
|
|
280
|
+
}
|
|
281
|
+
const userMessage = {
|
|
282
|
+
role: "user",
|
|
283
|
+
content: text,
|
|
284
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
285
|
+
};
|
|
286
|
+
return [{
|
|
287
|
+
...model,
|
|
288
|
+
input: TextInput.init({
|
|
289
|
+
placeholder: "Ask Kaldi anything...",
|
|
290
|
+
width: model.width - 4
|
|
291
|
+
}),
|
|
292
|
+
messages: [...model.messages, userMessage],
|
|
293
|
+
status: "thinking"
|
|
294
|
+
}, null];
|
|
295
|
+
}
|
|
296
|
+
case "agent_event": {
|
|
297
|
+
return handleAgentEvent(msg.event, model);
|
|
298
|
+
}
|
|
299
|
+
case "resize": {
|
|
300
|
+
return [{
|
|
301
|
+
...model,
|
|
302
|
+
width: msg.width,
|
|
303
|
+
height: msg.height,
|
|
304
|
+
input: TextInput.init({
|
|
305
|
+
placeholder: "Ask Kaldi anything...",
|
|
306
|
+
width: msg.width - 4,
|
|
307
|
+
value: TextInput.value(model.input)
|
|
308
|
+
}),
|
|
309
|
+
viewport: Viewport.init({
|
|
310
|
+
width: msg.width - 2,
|
|
311
|
+
height: msg.height - 6,
|
|
312
|
+
content: model.viewport.content
|
|
313
|
+
})
|
|
314
|
+
}, null];
|
|
315
|
+
}
|
|
316
|
+
case "clear": {
|
|
317
|
+
return [{
|
|
318
|
+
...model,
|
|
319
|
+
messages: [],
|
|
320
|
+
status: "idle",
|
|
321
|
+
errorMessage: void 0
|
|
322
|
+
}, null];
|
|
323
|
+
}
|
|
324
|
+
case "error": {
|
|
325
|
+
return [{
|
|
326
|
+
...model,
|
|
327
|
+
status: "error",
|
|
328
|
+
errorMessage: msg.error
|
|
329
|
+
}, null];
|
|
330
|
+
}
|
|
331
|
+
case "dismiss_welcome": {
|
|
332
|
+
return [{ ...model, showWelcome: false }, null];
|
|
333
|
+
}
|
|
334
|
+
case "quit": {
|
|
335
|
+
return [model, null];
|
|
336
|
+
}
|
|
337
|
+
default:
|
|
338
|
+
return [model, null];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function handleCommand(cmd, model) {
|
|
342
|
+
const [command, ...args] = cmd.split(/\s+/);
|
|
343
|
+
switch (command.toLowerCase()) {
|
|
344
|
+
case "clear":
|
|
345
|
+
case "c":
|
|
346
|
+
return update({ type: "clear" }, model);
|
|
347
|
+
case "quit":
|
|
348
|
+
case "q":
|
|
349
|
+
case "exit":
|
|
350
|
+
return update({ type: "quit" }, model);
|
|
351
|
+
case "help":
|
|
352
|
+
case "h":
|
|
353
|
+
case "?": {
|
|
354
|
+
const helpMessage = {
|
|
355
|
+
role: "assistant",
|
|
356
|
+
content: `## Available Commands
|
|
357
|
+
|
|
358
|
+
- \`/clear\` or \`/c\` - Clear conversation
|
|
359
|
+
- \`/quit\` or \`/q\` - Exit Puppuccino
|
|
360
|
+
- \`/help\` or \`/?\` - Show this help
|
|
361
|
+
|
|
362
|
+
Just type your message and press Enter to chat with Kaldi!`,
|
|
363
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
364
|
+
};
|
|
365
|
+
return [{
|
|
366
|
+
...model,
|
|
367
|
+
input: TextInput.init({
|
|
368
|
+
placeholder: "Ask Kaldi anything...",
|
|
369
|
+
width: model.width - 4
|
|
370
|
+
}),
|
|
371
|
+
messages: [...model.messages, helpMessage]
|
|
372
|
+
}, null];
|
|
373
|
+
}
|
|
374
|
+
default: {
|
|
375
|
+
const errorMessage = {
|
|
376
|
+
role: "assistant",
|
|
377
|
+
content: `Unknown command: \`/${command}\`. Type \`/help\` for available commands.`,
|
|
378
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
379
|
+
};
|
|
380
|
+
return [{
|
|
381
|
+
...model,
|
|
382
|
+
input: TextInput.init({
|
|
383
|
+
placeholder: "Ask Kaldi anything...",
|
|
384
|
+
width: model.width - 4
|
|
385
|
+
}),
|
|
386
|
+
messages: [...model.messages, errorMessage]
|
|
387
|
+
}, null];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function handleAgentEvent(event, model) {
|
|
392
|
+
switch (event.type) {
|
|
393
|
+
case "thinking":
|
|
394
|
+
return [{ ...model, status: "thinking" }, null];
|
|
395
|
+
case "text": {
|
|
396
|
+
const messages = [...model.messages];
|
|
397
|
+
const lastMsg = messages[messages.length - 1];
|
|
398
|
+
if (lastMsg && lastMsg.role === "assistant" && !lastMsg.toolName) {
|
|
399
|
+
lastMsg.content += event.content;
|
|
400
|
+
} else {
|
|
401
|
+
messages.push({
|
|
402
|
+
role: "assistant",
|
|
403
|
+
content: event.content,
|
|
404
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
return [{ ...model, messages, status: "idle" }, null];
|
|
408
|
+
}
|
|
409
|
+
case "tool_start":
|
|
410
|
+
return [{
|
|
411
|
+
...model,
|
|
412
|
+
status: "tool",
|
|
413
|
+
currentTool: event.name
|
|
414
|
+
}, null];
|
|
415
|
+
case "tool_result": {
|
|
416
|
+
const toolMessage = {
|
|
417
|
+
role: "tool",
|
|
418
|
+
content: event.result.success ? event.result.output : `Error: ${event.result.error}`,
|
|
419
|
+
toolName: event.name,
|
|
420
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
421
|
+
};
|
|
422
|
+
return [{
|
|
423
|
+
...model,
|
|
424
|
+
messages: [...model.messages, toolMessage],
|
|
425
|
+
status: "thinking",
|
|
426
|
+
currentTool: void 0
|
|
427
|
+
}, null];
|
|
428
|
+
}
|
|
429
|
+
case "tool_denied": {
|
|
430
|
+
const deniedMessage = {
|
|
431
|
+
role: "tool",
|
|
432
|
+
content: `Permission denied: ${event.reason}`,
|
|
433
|
+
toolName: event.name,
|
|
434
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
435
|
+
};
|
|
436
|
+
return [{
|
|
437
|
+
...model,
|
|
438
|
+
messages: [...model.messages, deniedMessage],
|
|
439
|
+
status: "thinking",
|
|
440
|
+
currentTool: void 0
|
|
441
|
+
}, null];
|
|
442
|
+
}
|
|
443
|
+
case "error":
|
|
444
|
+
return [{
|
|
445
|
+
...model,
|
|
446
|
+
status: "error",
|
|
447
|
+
errorMessage: event.error
|
|
448
|
+
}, null];
|
|
449
|
+
case "done":
|
|
450
|
+
return [{ ...model, status: "idle" }, null];
|
|
451
|
+
default:
|
|
452
|
+
return [model, null];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function view(model) {
|
|
456
|
+
const { theme } = model;
|
|
457
|
+
const lines = [];
|
|
458
|
+
if (model.showWelcome) {
|
|
459
|
+
return getWelcomeBanner() + "\n\nPress any key to continue...";
|
|
460
|
+
}
|
|
461
|
+
lines.push(theme.styles.heading.render("\u2500".repeat(model.width)));
|
|
462
|
+
const chatLines = [];
|
|
463
|
+
for (const msg of model.messages) {
|
|
464
|
+
chatLines.push(renderMessage(msg, theme, model.width - 4));
|
|
465
|
+
chatLines.push("");
|
|
466
|
+
}
|
|
467
|
+
if (model.status === "thinking") {
|
|
468
|
+
const spinner = Spinner.view(model.spinner);
|
|
469
|
+
chatLines.push(`${spinner} ${theme.styles.dim.render("Kaldi is thinking...")}`);
|
|
470
|
+
} else if (model.status === "tool" && model.currentTool) {
|
|
471
|
+
const spinner = Spinner.view(model.spinner);
|
|
472
|
+
chatLines.push(`${spinner} ${theme.styles.toolName.render(model.currentTool)}`);
|
|
473
|
+
} else if (model.status === "error" && model.errorMessage) {
|
|
474
|
+
chatLines.push(theme.styles.error.render(`Error: ${model.errorMessage}`));
|
|
475
|
+
}
|
|
476
|
+
const viewportModel = Viewport.setContent(model.viewport, chatLines.join("\n"));
|
|
477
|
+
lines.push(Viewport.view(viewportModel));
|
|
478
|
+
lines.push(theme.styles.heading.render("\u2500".repeat(model.width)));
|
|
479
|
+
const prompt = getPrompt();
|
|
480
|
+
const inputView = TextInput.view(model.input);
|
|
481
|
+
lines.push(`${prompt}${inputView}`);
|
|
482
|
+
return lines.join("\n");
|
|
483
|
+
}
|
|
484
|
+
function renderMessage(msg, theme, width) {
|
|
485
|
+
const lines = [];
|
|
486
|
+
if (msg.role === "user") {
|
|
487
|
+
lines.push(theme.styles.accent.render("You:"));
|
|
488
|
+
lines.push(msg.content);
|
|
489
|
+
} else if (msg.role === "assistant") {
|
|
490
|
+
lines.push(theme.styles.heading.render("Kaldi:"));
|
|
491
|
+
lines.push(renderMarkdown(msg.content, { width }));
|
|
492
|
+
} else if (msg.role === "tool") {
|
|
493
|
+
lines.push(theme.styles.toolName.render(`[${msg.toolName}]`));
|
|
494
|
+
const output = msg.content.length > 500 ? msg.content.slice(0, 500) + "\n...(truncated)" : msg.content;
|
|
495
|
+
lines.push(theme.styles.toolOutput.render(output));
|
|
496
|
+
}
|
|
497
|
+
return lines.join("\n");
|
|
498
|
+
}
|
|
499
|
+
function createApp(options) {
|
|
500
|
+
return new Program({
|
|
501
|
+
init: () => init(options),
|
|
502
|
+
update,
|
|
503
|
+
view
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// src/index.ts
|
|
508
|
+
import { Program as Program2, Key as Key2, Cmd as Cmd2 } from "@averagejoeslab/tui";
|
|
509
|
+
import { Style as Style3 } from "@averagejoeslab/style";
|
|
510
|
+
import { render } from "@averagejoeslab/markdown";
|
|
511
|
+
export {
|
|
512
|
+
Cmd2 as Cmd,
|
|
513
|
+
DarkTheme,
|
|
514
|
+
KALDI,
|
|
515
|
+
KALDI_COLORS,
|
|
516
|
+
KaldiTheme,
|
|
517
|
+
Key2 as Key,
|
|
518
|
+
LightTheme,
|
|
519
|
+
Program2 as Program,
|
|
520
|
+
Style3 as Style,
|
|
521
|
+
createApp,
|
|
522
|
+
getGoodbyeBanner,
|
|
523
|
+
getKaldi,
|
|
524
|
+
getPrompt,
|
|
525
|
+
getTheme,
|
|
526
|
+
getWelcomeBanner,
|
|
527
|
+
init,
|
|
528
|
+
render as renderMarkdown,
|
|
529
|
+
themes,
|
|
530
|
+
update,
|
|
531
|
+
view
|
|
532
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@averagejoeslab/puppuccino-tui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Terminal UI for Puppuccino AI coding agent",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
18
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"clean": "rm -rf dist"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@averagejoeslab/puppuccino-core": "^0.1.0",
|
|
25
|
+
"@averagejoeslab/ansi": "^1.0.0",
|
|
26
|
+
"@averagejoeslab/term": "^1.0.0",
|
|
27
|
+
"@averagejoeslab/style": "^1.0.0",
|
|
28
|
+
"@averagejoeslab/tui": "^1.0.0",
|
|
29
|
+
"@averagejoeslab/widgets": "^1.0.0",
|
|
30
|
+
"@averagejoeslab/markdown": "^1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.4.0",
|
|
36
|
+
"vitest": "^2.0.0"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
]
|
|
41
|
+
}
|