chocolatito-code 1.3.1 → 1.3.3
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.js +56 -12
- package/dist/ui/marco.js +57 -16
- package/dist/ui/modes.d.ts +9 -1
- package/dist/ui/modes.js +11 -3
- package/dist/ui/motorActual.d.ts +42 -0
- package/dist/ui/motorActual.js +66 -0
- package/dist/ui/prompt.js +21 -3
- package/dist/ui/selector.d.ts +37 -0
- package/dist/ui/selector.js +81 -0
- package/dist/ui/spinner.js +5 -16
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,9 @@ import { buildSystemPrompt } from "./prompts/systemPrompt.js";
|
|
|
13
13
|
import { AgentLoop } from "./agent/loop.js";
|
|
14
14
|
import { codigoDeSalida, EXIT_OK, EXIT_FALLO, EXIT_USO, EXIT_LICENCIA } from "./agent/result.js";
|
|
15
15
|
import { showBanner, animateStartupBanner, showHelp, showEffortMenu } from "./ui/banner.js";
|
|
16
|
+
import { fijarMotor } from "./ui/motorActual.js";
|
|
17
|
+
import { elegirDeLista } from "./ui/selector.js";
|
|
18
|
+
import { reafirmarModoCrudo } from "./ui/keyboardGuard.js";
|
|
16
19
|
import { askInteractivePrompt, SLASH_COMMANDS } from "./ui/prompt.js";
|
|
17
20
|
import { changeDir } from "./tools/changeDir.js";
|
|
18
21
|
import { gitDiff, gitCommit } from "./tools/gitTools.js";
|
|
@@ -233,6 +236,9 @@ ${nodeViejo}
|
|
|
233
236
|
const sessionManager = new SessionManager();
|
|
234
237
|
const sessionId = `session_${Date.now()}`;
|
|
235
238
|
let currentModel = DEFAULT_ESPECTRO;
|
|
239
|
+
// La barra de estado lo lee de aqui. Fijarlo al arrancar y no solo al
|
|
240
|
+
// cambiarlo: si no, hasta el primer /effort la barra no dice nada.
|
|
241
|
+
fijarMotor(currentModel.name, currentModel.effortLevel);
|
|
236
242
|
let isPlanMode = false;
|
|
237
243
|
let systemPrompt = buildSystemPrompt(context, currentModel, skillManager, memoryManager, isPlanMode);
|
|
238
244
|
const agent = new AgentLoop(apiKey, getEngineBaseUrl(), currentModel, systemPrompt);
|
|
@@ -429,6 +435,7 @@ ${sessionData}
|
|
|
429
435
|
const restauracion = prepararRestauracion(loaded, { cwd: agent.currentCwd, modelId: currentModel.id });
|
|
430
436
|
agent.currentCwd = restauracion.cwd;
|
|
431
437
|
currentModel = ESPECTRO_MODELS[restauracion.modelId] || currentModel;
|
|
438
|
+
fijarMotor(currentModel.name, currentModel.effortLevel);
|
|
432
439
|
const refreshedContext = await getProjectContext(agent.currentCwd);
|
|
433
440
|
systemPrompt = buildSystemPrompt(refreshedContext, currentModel, skillManager, memoryManager, isPlanMode);
|
|
434
441
|
agent.messages = [{ role: "system", content: systemPrompt }, ...restauracion.conversacion];
|
|
@@ -580,20 +587,57 @@ Directivas de ejecución:
|
|
|
580
587
|
}
|
|
581
588
|
if (trimmed.startsWith("/effort") || trimmed.startsWith("/espectro") || trimmed === "/model") {
|
|
582
589
|
const parts = trimmed.split(/\s+/);
|
|
583
|
-
|
|
590
|
+
let targetId = parts[1]?.trim();
|
|
591
|
+
// Sin argumento se ABRE UN MENU, no se imprime una lista.
|
|
592
|
+
//
|
|
593
|
+
// Antes se volcaban las cuatro opciones con console.log y una frase al
|
|
594
|
+
// final que decia "selecciona con /effort 2.0". En pantalla se veia una
|
|
595
|
+
// lista, la gente pulsaba las flechas -que es lo unico razonable- y no
|
|
596
|
+
// pasaba nada.
|
|
584
597
|
if (!targetId || !ESPECTRO_MODELS[targetId]) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
598
|
+
const opciones = Object.values(ESPECTRO_MODELS).map((m) => ({
|
|
599
|
+
id: m.id,
|
|
600
|
+
titulo: `${m.name} ${m.id === currentModel.id ? "· activo" : ""}`.trimEnd(),
|
|
601
|
+
detalle: `Esfuerzo ${m.effortLevel} — ${m.description}`,
|
|
602
|
+
}));
|
|
603
|
+
const indiceActual = Object.values(ESPECTRO_MODELS).findIndex((m) => m.id === currentModel.id);
|
|
604
|
+
const elegido = await elegirDeLista("POTENCIA DEL MOTOR — CHOCOLATITO CODE", opciones, Math.max(0, indiceActual));
|
|
605
|
+
// Sin terminal el menu no puede abrirse: ahi se cae a la lista de
|
|
606
|
+
// siempre, que al menos dice como elegir escribiendolo.
|
|
607
|
+
if (elegido === null) {
|
|
608
|
+
if (!process.stdin.isTTY)
|
|
609
|
+
showEffortMenu(currentModel.id);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
targetId = elegido;
|
|
596
613
|
}
|
|
614
|
+
currentModel = ESPECTRO_MODELS[targetId];
|
|
615
|
+
// Sin esto la barra de abajo sigue anunciando el motor anterior.
|
|
616
|
+
fijarMotor(currentModel.name, currentModel.effortLevel);
|
|
617
|
+
const refreshedContext = await getProjectContext(agent.currentCwd);
|
|
618
|
+
const updatedPrompt = buildSystemPrompt(refreshedContext, currentModel, skillManager, memoryManager, isPlanMode);
|
|
619
|
+
agent.setEspectroModel(currentModel, updatedPrompt);
|
|
620
|
+
console.log(chalk.green(`
|
|
621
|
+
✔ Nivel actualizado a `) +
|
|
622
|
+
chalk.bold.hex("#A855F7")(currentModel.name) +
|
|
623
|
+
chalk.gray(` (${currentModel.effortLevel})`) +
|
|
624
|
+
`
|
|
625
|
+
${chalk.italic.gray(currentModel.description)}
|
|
626
|
+
`);
|
|
627
|
+
// EL TECLADO SE QUEDABA TRABADO AL CAMBIAR DE MOTOR
|
|
628
|
+
//
|
|
629
|
+
// "una vez cambiado de effort se queda trabado y con un enter se va la
|
|
630
|
+
// traba". Ese "con un enter se arregla" es la firma del modo COCIDO: la
|
|
631
|
+
// consola retiene lo tecleado hasta el salto de linea, asi que el prompt
|
|
632
|
+
// no recibe nada hasta que pulsas Enter.
|
|
633
|
+
//
|
|
634
|
+
// Aqui en medio se refresca el contexto del proyecto, que lanza git. Un
|
|
635
|
+
// hijo que hereda la consola puede devolverla en cocido al terminar, y
|
|
636
|
+
// Node no se entera: `process.stdin.isRaw` sigue diciendo true. Por eso el
|
|
637
|
+
// vigilante del prompt no lo arregla —solo actua cuando SABE que se
|
|
638
|
+
// perdio— y hace falta reafirmar, que pasa por false antes de poner true
|
|
639
|
+
// y por tanto no se lo come la cache de libuv.
|
|
640
|
+
reafirmarModoCrudo(process.stdin);
|
|
597
641
|
continue;
|
|
598
642
|
}
|
|
599
643
|
if (trimmed === "/clear") {
|
package/dist/ui/marco.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { describeMode, getMode } from "./modes.js";
|
|
2
|
+
import { describeMode, getMode, TRIANGULOS } from "./modes.js";
|
|
3
|
+
import { ESFUERZO_MAXIMO, esfuerzoActual, marcaDeMotor, motorActual } from "./motorActual.js";
|
|
3
4
|
/**
|
|
4
5
|
* EL MARCO DE LA BARRA, EN UN SOLO SITIO
|
|
5
6
|
*
|
|
@@ -121,27 +122,67 @@ export function buildPromptFrame(opts) {
|
|
|
121
122
|
// ancha que el terminal. En una consola de 80 columnas el pie media 87 y se
|
|
122
123
|
// partia en dos filas desde el primer dibujado.
|
|
123
124
|
const modeLook = describeMode(getMode());
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
// 3. La linea de estado, con POSICIONES FIJAS.
|
|
126
|
+
//
|
|
127
|
+
// Antes iban los atajos a la izquierda y el modo y el motor apelotonados a la
|
|
128
|
+
// derecha, y encima la linea se reordenaba sola en cuatro variantes segun el
|
|
129
|
+
// ancho: al estrecharse la ventana, el modo saltaba de sitio. El usuario lo
|
|
130
|
+
// pidio asi: "el boton del modo debe ir a la izquierda... y el modelo a la
|
|
131
|
+
// derecha, pero deben ser fijos siempre y nunca cambiar".
|
|
132
|
+
//
|
|
133
|
+
// Ahora el modo esta SIEMPRE pegado a la izquierda y el motor SIEMPRE pegado a
|
|
134
|
+
// la derecha. Lo unico que aparece y desaparece es la pista del medio, que es
|
|
135
|
+
// lo prescindible: quien ya se sabe los atajos no la necesita, y quien no, la
|
|
136
|
+
// tiene en /help.
|
|
137
|
+
const modePlain = `${TRIANGULOS} modo ${modeLook.label}`;
|
|
138
|
+
// Estaba escrito a mano y por eso no cambiaba nunca. Vacio si nadie lo ha
|
|
139
|
+
// fijado: mejor un hueco que un nombre que no es el que se esta usando.
|
|
140
|
+
//
|
|
141
|
+
// El nivel de esfuerzo va PEGADO al motor y no en una linea aparte: es un dato
|
|
142
|
+
// del mismo hecho -con que potencia trabajas-, y una fila mas en el pie es una
|
|
143
|
+
// fila menos de conversacion en pantalla.
|
|
144
|
+
const nivel = esfuerzoActual();
|
|
145
|
+
const marca = marcaDeMotor(nivel);
|
|
146
|
+
const motor = motorActual();
|
|
147
|
+
const enginePlain = motor ? `${marca ? `${marca} ` : ""}${motor}${nivel ? ` · ${nivel}` : ""}` : "";
|
|
148
|
+
const hintPlain = "shift+tab cambia · esc cancela · ? /help";
|
|
128
149
|
const modeTag = chalk.hex(modeLook.color)(modePlain);
|
|
129
|
-
|
|
150
|
+
// En el maximo se pinta encendido, no en gris: es el unico nivel que conviene
|
|
151
|
+
// que se note, porque es el que se come la cuota.
|
|
152
|
+
const engine = nivel === ESFUERZO_MAXIMO ? chalk.hex("#FFA500")(enginePlain) : chalk.gray(enginePlain);
|
|
153
|
+
// Dos columnas de respiro. Los triangulos son un glifo de anchura ambigua y
|
|
154
|
+
// hay terminales que lo pintan doble: si la linea llegara justa al ancho, el
|
|
155
|
+
// terminal la partiria en dos filas y el repintado del marco se descuadraria
|
|
156
|
+
// entero. Ese fallo ya costo una barra huerfana por pulsacion.
|
|
157
|
+
const util = Math.max(0, cols - 2);
|
|
130
158
|
let footerLine;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
159
|
+
const huecoConPista = util - modePlain.length - 1 - hintPlain.length - enginePlain.length;
|
|
160
|
+
const hueco = util - modePlain.length - enginePlain.length;
|
|
161
|
+
if (!enginePlain) {
|
|
162
|
+
// Todavia nadie ha dicho que motor hay. Se enseña el modo y ya, pero SIN
|
|
163
|
+
// saltarse el recorte: "⏵⏵ modo acepta ediciones" son 24 columnas y en una
|
|
164
|
+
// consola de 20 se partiria en dos filas, que es lo que descuadra el marco.
|
|
165
|
+
const conPista = util - modePlain.length - 1 - hintPlain.length;
|
|
166
|
+
if (modePlain.length > util) {
|
|
167
|
+
footerLine = chalk.hex(modeLook.color)(truncatePlain(modePlain, util));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
footerLine = conPista >= 0 ? `${modeTag} ${chalk.gray(hintPlain)}` : modeTag;
|
|
171
|
+
}
|
|
134
172
|
}
|
|
135
|
-
else if (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
173
|
+
else if (huecoConPista >= 1) {
|
|
174
|
+
footerLine = `${modeTag} ${chalk.gray(hintPlain)}${" ".repeat(huecoConPista)}${engine}`;
|
|
175
|
+
}
|
|
176
|
+
else if (hueco >= 1) {
|
|
177
|
+
footerLine = `${modeTag}${" ".repeat(hueco)}${engine}`;
|
|
139
178
|
}
|
|
140
|
-
else if (modePlain.length <=
|
|
141
|
-
|
|
179
|
+
else if (modePlain.length <= util) {
|
|
180
|
+
// Si hay que sacrificar uno, se va el motor: el modo es lo que decide si te
|
|
181
|
+
// van a preguntar antes de tocar tus archivos.
|
|
182
|
+
footerLine = modeTag;
|
|
142
183
|
}
|
|
143
184
|
else {
|
|
144
|
-
footerLine = chalk.hex(modeLook.color)(truncatePlain(modePlain,
|
|
185
|
+
footerLine = chalk.hex(modeLook.color)(truncatePlain(modePlain, util));
|
|
145
186
|
}
|
|
146
187
|
const lines = [bar, inputLine, ...dropdownLines, bar, footerLine];
|
|
147
188
|
// 4. Donde queda el cursor, en filas fisicas. Antes se hacia cursorCol =
|
package/dist/ui/modes.d.ts
CHANGED
|
@@ -22,7 +22,15 @@ export interface ModeLook {
|
|
|
22
22
|
color: string;
|
|
23
23
|
}
|
|
24
24
|
export declare function describeMode(mode?: InteractiveMode): ModeLook;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* El simbolo del modo.
|
|
27
|
+
*
|
|
28
|
+
* Dos triangulos, no la flecha de tabulacion. El "⇥" de antes es el simbolo del
|
|
29
|
+
* tabulador: dice COMO se cambia de modo, no QUE modo hay puesto. Y como ademas
|
|
30
|
+
* el atajo es shift+tab, se leia como si el modo fuera un atajo mas.
|
|
31
|
+
*/
|
|
32
|
+
export declare const TRIANGULOS = "\u23F5\u23F5";
|
|
33
|
+
/** Etiqueta de una linea del modo activo. */
|
|
26
34
|
export declare function modeBadge(mode?: InteractiveMode): string;
|
|
27
35
|
/** Aviso completo, para cuando el usuario acaba de cambiar de modo. */
|
|
28
36
|
export declare function modeAnnouncement(mode?: InteractiveMode): string;
|
package/dist/ui/modes.js
CHANGED
|
@@ -43,15 +43,23 @@ const LOOKS = {
|
|
|
43
43
|
export function describeMode(mode = current) {
|
|
44
44
|
return LOOKS[mode];
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* El simbolo del modo.
|
|
48
|
+
*
|
|
49
|
+
* Dos triangulos, no la flecha de tabulacion. El "⇥" de antes es el simbolo del
|
|
50
|
+
* tabulador: dice COMO se cambia de modo, no QUE modo hay puesto. Y como ademas
|
|
51
|
+
* el atajo es shift+tab, se leia como si el modo fuera un atajo mas.
|
|
52
|
+
*/
|
|
53
|
+
export const TRIANGULOS = "⏵⏵";
|
|
54
|
+
/** Etiqueta de una linea del modo activo. */
|
|
47
55
|
export function modeBadge(mode = current) {
|
|
48
56
|
if (mode === "default")
|
|
49
57
|
return "";
|
|
50
58
|
const look = LOOKS[mode];
|
|
51
|
-
return chalk.hex(look.color)(
|
|
59
|
+
return chalk.hex(look.color)(`${TRIANGULOS} modo ${look.label}`);
|
|
52
60
|
}
|
|
53
61
|
/** Aviso completo, para cuando el usuario acaba de cambiar de modo. */
|
|
54
62
|
export function modeAnnouncement(mode = current) {
|
|
55
63
|
const look = LOOKS[mode];
|
|
56
|
-
return `${chalk.bold.hex(look.color)(
|
|
64
|
+
return `${chalk.bold.hex(look.color)(`${TRIANGULOS} modo ${look.label}`)} ${chalk.gray(`· ${look.hint}`)} ${chalk.gray("· shift+tab para cambiar")}`;
|
|
57
65
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUE MOTOR ESTA PUESTO
|
|
3
|
+
*
|
|
4
|
+
* La linea de estado del marco lo llevaba escrito a mano:
|
|
5
|
+
*
|
|
6
|
+
* const enginePlain = "Espectro 1.0 (Ágil)";
|
|
7
|
+
*
|
|
8
|
+
* Asi que decia siempre lo mismo pasara lo que pasara. Cambiabas de motor con
|
|
9
|
+
* /effort, el agente empezaba a usar otro de verdad, y la barra seguia
|
|
10
|
+
* anunciando el primero. Lo vio el usuario: "al poner el modelo, la parte de
|
|
11
|
+
* abajo que es fija no se actualiza con el modelo que se pone".
|
|
12
|
+
*
|
|
13
|
+
* Un indicador que miente es peor que no tener indicador: te hace tomar
|
|
14
|
+
* decisiones -esperar mas, gastar mas- creyendo que estas en otro sitio.
|
|
15
|
+
*
|
|
16
|
+
* Vive aparte y no en el marco por lo mismo que el modo (ver modes.ts): lo
|
|
17
|
+
* escribe quien cambia de motor y lo lee quien pinta, y ninguno de los dos tiene
|
|
18
|
+
* por que conocer al otro.
|
|
19
|
+
*/
|
|
20
|
+
/** Lo pone index.ts al arrancar y cada vez que se cambia de motor. */
|
|
21
|
+
export declare function fijarMotor(nuevo: string, nivel?: string): void;
|
|
22
|
+
/** El nivel de esfuerzo activo ("Normal", "Alto", "Extendido", "Máximo"). */
|
|
23
|
+
export declare function esfuerzoActual(): string;
|
|
24
|
+
/** El nivel mas alto. Es el unico que se anuncia distinto. */
|
|
25
|
+
export declare const ESFUERZO_MAXIMO = "M\u00E1ximo";
|
|
26
|
+
/**
|
|
27
|
+
* La marca que precede al nombre del motor.
|
|
28
|
+
*
|
|
29
|
+
* Recibe el reloj en vez de leerlo: asi se puede comprobar fotograma a
|
|
30
|
+
* fotograma, que dentro de un terminal no se revisa a ojo.
|
|
31
|
+
*
|
|
32
|
+
* Cambia cada 180 ms, no cada 120 como el spinner. Dos cosas latiendo al mismo
|
|
33
|
+
* ritmo se leen como una sola parpadeando; a ritmos distintos se distinguen.
|
|
34
|
+
*/
|
|
35
|
+
export declare function marcaDeMotor(nivel?: string, ahora?: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* El nombre del motor activo.
|
|
38
|
+
*
|
|
39
|
+
* Vacio hasta que alguien lo fije. Quien pinta decide que hacer con eso: mejor
|
|
40
|
+
* no enseñar nada que enseñar un nombre inventado.
|
|
41
|
+
*/
|
|
42
|
+
export declare function motorActual(): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUE MOTOR ESTA PUESTO
|
|
3
|
+
*
|
|
4
|
+
* La linea de estado del marco lo llevaba escrito a mano:
|
|
5
|
+
*
|
|
6
|
+
* const enginePlain = "Espectro 1.0 (Ágil)";
|
|
7
|
+
*
|
|
8
|
+
* Asi que decia siempre lo mismo pasara lo que pasara. Cambiabas de motor con
|
|
9
|
+
* /effort, el agente empezaba a usar otro de verdad, y la barra seguia
|
|
10
|
+
* anunciando el primero. Lo vio el usuario: "al poner el modelo, la parte de
|
|
11
|
+
* abajo que es fija no se actualiza con el modelo que se pone".
|
|
12
|
+
*
|
|
13
|
+
* Un indicador que miente es peor que no tener indicador: te hace tomar
|
|
14
|
+
* decisiones -esperar mas, gastar mas- creyendo que estas en otro sitio.
|
|
15
|
+
*
|
|
16
|
+
* Vive aparte y no en el marco por lo mismo que el modo (ver modes.ts): lo
|
|
17
|
+
* escribe quien cambia de motor y lo lee quien pinta, y ninguno de los dos tiene
|
|
18
|
+
* por que conocer al otro.
|
|
19
|
+
*/
|
|
20
|
+
let nombre = "";
|
|
21
|
+
let esfuerzo = "";
|
|
22
|
+
/** Lo pone index.ts al arrancar y cada vez que se cambia de motor. */
|
|
23
|
+
export function fijarMotor(nuevo, nivel = "") {
|
|
24
|
+
if (nuevo && nuevo.trim())
|
|
25
|
+
nombre = nuevo.trim();
|
|
26
|
+
if (nivel && nivel.trim())
|
|
27
|
+
esfuerzo = nivel.trim();
|
|
28
|
+
}
|
|
29
|
+
/** El nivel de esfuerzo activo ("Normal", "Alto", "Extendido", "Máximo"). */
|
|
30
|
+
export function esfuerzoActual() {
|
|
31
|
+
return esfuerzo;
|
|
32
|
+
}
|
|
33
|
+
/** El nivel mas alto. Es el unico que se anuncia distinto. */
|
|
34
|
+
export const ESFUERZO_MAXIMO = "Máximo";
|
|
35
|
+
/**
|
|
36
|
+
* Los cuatro fotogramas del latido del maximo.
|
|
37
|
+
*
|
|
38
|
+
* Solo se anima el nivel mas alto, y a proposito: si parpadeara siempre seria
|
|
39
|
+
* ruido de fondo que se deja de ver a los dos minutos. Animando solo el maximo,
|
|
40
|
+
* el movimiento SIGNIFICA algo -"estas gastando el motor caro"- en vez de ser
|
|
41
|
+
* decoracion.
|
|
42
|
+
*/
|
|
43
|
+
const LATIDO = ["◇", "◈", "◆", "◈"];
|
|
44
|
+
/**
|
|
45
|
+
* La marca que precede al nombre del motor.
|
|
46
|
+
*
|
|
47
|
+
* Recibe el reloj en vez de leerlo: asi se puede comprobar fotograma a
|
|
48
|
+
* fotograma, que dentro de un terminal no se revisa a ojo.
|
|
49
|
+
*
|
|
50
|
+
* Cambia cada 180 ms, no cada 120 como el spinner. Dos cosas latiendo al mismo
|
|
51
|
+
* ritmo se leen como una sola parpadeando; a ritmos distintos se distinguen.
|
|
52
|
+
*/
|
|
53
|
+
export function marcaDeMotor(nivel = esfuerzo, ahora = Date.now()) {
|
|
54
|
+
if (nivel !== ESFUERZO_MAXIMO)
|
|
55
|
+
return "";
|
|
56
|
+
return LATIDO[Math.floor(ahora / 180) % LATIDO.length];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* El nombre del motor activo.
|
|
60
|
+
*
|
|
61
|
+
* Vacio hasta que alguien lo fije. Quien pinta decide que hacer con eso: mejor
|
|
62
|
+
* no enseñar nada que enseñar un nombre inventado.
|
|
63
|
+
*/
|
|
64
|
+
export function motorActual() {
|
|
65
|
+
return nombre;
|
|
66
|
+
}
|
package/dist/ui/prompt.js
CHANGED
|
@@ -462,10 +462,28 @@ export async function askInteractivePrompt(promptSymbol = "❯", cwd = process.c
|
|
|
462
462
|
render();
|
|
463
463
|
return;
|
|
464
464
|
}
|
|
465
|
-
|
|
466
|
-
|
|
465
|
+
// EL DESPLEGABLE MANDA SOBRE LO TECLEADO
|
|
466
|
+
//
|
|
467
|
+
// Antes esto solo entraba con el buffer EXACTAMENTE en "/". Si habias
|
|
468
|
+
// escrito "/e" y el desplegable te estaba señalando "/effort", el Enter
|
|
469
|
+
// enviaba "/e" tal cual: un comando que no existe, mientras en pantalla
|
|
470
|
+
// ponia otra cosa. Lo conto el usuario asi: "me recomienda effort pero
|
|
471
|
+
// al dar enter se manda como /e".
|
|
472
|
+
const mencionActiva = getAtMention().active;
|
|
473
|
+
const escribiendoComando = inputBuffer.startsWith("/") && !inputBuffer.includes(" ");
|
|
474
|
+
const soloLaBarra = inputBuffer === "/";
|
|
475
|
+
if (items.length > 0 && (escribiendoComando || mencionActiva)) {
|
|
476
|
+
const elegido = items[selectedIndex];
|
|
477
|
+
const yaEstaEntero = Boolean(elegido) && inputBuffer === elegido.insertText;
|
|
478
|
+
if (!yaEstaEntero && applySelectedDropdownItem()) {
|
|
467
479
|
render();
|
|
468
|
-
|
|
480
|
+
// Se completa Y SE ENVIA, que es lo que uno espera al ver el comando
|
|
481
|
+
// señalado y pulsar Enter. Dos excepciones que solo completan:
|
|
482
|
+
// - una mencion de archivo casi siempre lleva mas texto detras;
|
|
483
|
+
// - con solo "/" escrito no has elegido nada todavia, y enviar ahi
|
|
484
|
+
// ejecutaria el primero de la lista sin querer.
|
|
485
|
+
if (mencionActiva || soloLaBarra)
|
|
486
|
+
return;
|
|
469
487
|
}
|
|
470
488
|
}
|
|
471
489
|
scheduleSubmit();
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UNA LISTA POR LA QUE SE PUEDE SUBIR Y BAJAR
|
|
3
|
+
*
|
|
4
|
+
* `/effort` no era un menu: era un `console.log` con las cuatro opciones y una
|
|
5
|
+
* frase al final que decia "selecciona con /effort 2.0". El usuario lo vio en
|
|
6
|
+
* pantalla como una lista, hizo lo unico razonable -pulsar las flechas- y no
|
|
7
|
+
* paso nada. Sus palabras: "me salen en pantalla los modelos pero no puedo
|
|
8
|
+
* subir y bajar entre ellos con las flechitas".
|
|
9
|
+
*
|
|
10
|
+
* Una lista que parece elegible y no lo es engaña dos veces: primero no
|
|
11
|
+
* funciona, y despues hay que leerse la letra pequeña para descubrir que habia
|
|
12
|
+
* que escribir el comando otra vez con un argumento.
|
|
13
|
+
*
|
|
14
|
+
* POR QUE NO SE REUTILIZO EL DIALOGO DE PERMISOS
|
|
15
|
+
*
|
|
16
|
+
* Hace lo mismo -flechas y Enter- pero tiene dentro toda la logica de aprobar,
|
|
17
|
+
* rechazar, recordar patrones y pedir explicacion por escrito. Sacar de ahi la
|
|
18
|
+
* parte de elegir habria sido tocar el camino por el que pasa CADA accion con
|
|
19
|
+
* consecuencias del agente, y por un menu que se abre de higos a brevas. Esto
|
|
20
|
+
* son sesenta lineas que no tocan nada de eso.
|
|
21
|
+
*/
|
|
22
|
+
export interface OpcionDeLista {
|
|
23
|
+
/** Lo que se devuelve al elegirla. */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Primera linea, la que se lee. */
|
|
26
|
+
titulo: string;
|
|
27
|
+
/** Segunda linea en gris. Opcional. */
|
|
28
|
+
detalle?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Enseña la lista y devuelve el id elegido. `null` si se sale con Esc.
|
|
32
|
+
*
|
|
33
|
+
* Sin terminal no hay nada que elegir: devuelve null en el acto en vez de
|
|
34
|
+
* quedarse esperando una tecla que no va a llegar nunca. Es lo que pasaria al
|
|
35
|
+
* correrlo desde un script o dentro de las pruebas.
|
|
36
|
+
*/
|
|
37
|
+
export declare function elegirDeLista(titulo: string, opciones: OpcionDeLista[], indiceInicial?: number): Promise<string | null>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import readline from "node:readline";
|
|
4
|
+
import { dibujar } from "./salida.js";
|
|
5
|
+
/**
|
|
6
|
+
* Enseña la lista y devuelve el id elegido. `null` si se sale con Esc.
|
|
7
|
+
*
|
|
8
|
+
* Sin terminal no hay nada que elegir: devuelve null en el acto en vez de
|
|
9
|
+
* quedarse esperando una tecla que no va a llegar nunca. Es lo que pasaria al
|
|
10
|
+
* correrlo desde un script o dentro de las pruebas.
|
|
11
|
+
*/
|
|
12
|
+
export async function elegirDeLista(titulo, opciones, indiceInicial = 0) {
|
|
13
|
+
if (opciones.length === 0)
|
|
14
|
+
return null;
|
|
15
|
+
if (!process.stdin.isTTY)
|
|
16
|
+
return null;
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
let elegido = Math.max(0, Math.min(indiceInicial, opciones.length - 1));
|
|
19
|
+
if (!readline.__choco_keypress) {
|
|
20
|
+
readline.emitKeypressEvents(process.stdin);
|
|
21
|
+
readline.__choco_keypress = true;
|
|
22
|
+
}
|
|
23
|
+
const eraCrudo = process.stdin.isRaw;
|
|
24
|
+
if (process.stdin.setRawMode)
|
|
25
|
+
process.stdin.setRawMode(true);
|
|
26
|
+
process.stdin.resume();
|
|
27
|
+
/** Filas del ultimo dibujo. Se repinta encima, sin dejar restos. */
|
|
28
|
+
let filas = 0;
|
|
29
|
+
function pintar() {
|
|
30
|
+
const lineas = ["", chalk.bold.hex("#E4CDEC")(titulo), ""];
|
|
31
|
+
opciones.forEach((o, i) => {
|
|
32
|
+
const puesto = i === elegido;
|
|
33
|
+
const punta = puesto ? chalk.bold.hex("#D97757")(" ❯ ") : " ";
|
|
34
|
+
const nombre = puesto ? chalk.bold.white(o.titulo) : chalk.hex("#A855F7")(o.titulo);
|
|
35
|
+
lineas.push(`${punta}${nombre}`);
|
|
36
|
+
if (o.detalle)
|
|
37
|
+
lineas.push(` ${chalk.gray(o.detalle)}`);
|
|
38
|
+
});
|
|
39
|
+
lineas.push("", chalk.gray(" ↑↓ para moverte · enter para elegir · esc para salir"), "");
|
|
40
|
+
// Se sube justo lo que se escribio la vez anterior: ni una fila mas, ni
|
|
41
|
+
// una menos. Contar de mas se come la linea de encima.
|
|
42
|
+
const salida = (filas > 0 ? `\x1b[${filas}A` : "") + lineas.map((l) => `\r\x1b[2K${l}`).join("\n") + "\n";
|
|
43
|
+
dibujar(salida);
|
|
44
|
+
filas = lineas.length;
|
|
45
|
+
}
|
|
46
|
+
function terminar(valor) {
|
|
47
|
+
process.stdin.removeListener("keypress", alPulsar);
|
|
48
|
+
// Se devuelve el modo que habia, no se fuerza uno: si el prompt tenia el
|
|
49
|
+
// teclado en crudo y aqui se dejara en cocido, la sesion se queda sin
|
|
50
|
+
// poder escribir y no se sabe por que.
|
|
51
|
+
if (process.stdin.setRawMode)
|
|
52
|
+
process.stdin.setRawMode(eraCrudo || false);
|
|
53
|
+
resolve(valor);
|
|
54
|
+
}
|
|
55
|
+
function alPulsar(_str, key) {
|
|
56
|
+
if (!key)
|
|
57
|
+
return;
|
|
58
|
+
if (key.name === "up" || (key.name === "k" && !key.ctrl)) {
|
|
59
|
+
elegido = (elegido - 1 + opciones.length) % opciones.length;
|
|
60
|
+
pintar();
|
|
61
|
+
}
|
|
62
|
+
else if (key.name === "down" || (key.name === "j" && !key.ctrl)) {
|
|
63
|
+
elegido = (elegido + 1) % opciones.length;
|
|
64
|
+
pintar();
|
|
65
|
+
}
|
|
66
|
+
else if (key.name === "return" || key.name === "enter") {
|
|
67
|
+
terminar(opciones[elegido].id);
|
|
68
|
+
}
|
|
69
|
+
else if (key.name === "escape") {
|
|
70
|
+
terminar(null);
|
|
71
|
+
}
|
|
72
|
+
else if (key.ctrl && key.name === "c") {
|
|
73
|
+
// Ctrl+C aqui es "sal del menu", no "cierra el programa": estas
|
|
74
|
+
// eligiendo, no ejecutando nada.
|
|
75
|
+
terminar(null);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
pintar();
|
|
79
|
+
process.stdin.on("keypress", alPulsar);
|
|
80
|
+
});
|
|
81
|
+
}
|
package/dist/ui/spinner.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { typedText, queuedCount } from "./typeAhead.js";
|
|
4
|
-
import { modeBadge } from "./modes.js";
|
|
5
4
|
import { fijarSecciones, quitarSeccion } from "./pieFijo.js";
|
|
6
5
|
import { buildPromptFrame } from "./marco.js";
|
|
7
6
|
/**
|
|
@@ -32,9 +31,6 @@ const VERBS = [
|
|
|
32
31
|
"Tejiendo",
|
|
33
32
|
"Cocinando",
|
|
34
33
|
];
|
|
35
|
-
function sinColores(texto) {
|
|
36
|
-
return texto.replace(/\x1b\[[0-9;]*m/g, "");
|
|
37
|
-
}
|
|
38
34
|
export class DynamicSpinner {
|
|
39
35
|
timer = null;
|
|
40
36
|
glyphIdx = 0;
|
|
@@ -81,24 +77,17 @@ export class DynamicSpinner {
|
|
|
81
77
|
* salian todas como "Rumiando…", indistinguibles de una peticion normal.
|
|
82
78
|
*/
|
|
83
79
|
lineaDelSpinner(elapsedSec) {
|
|
84
|
-
const cols = Math.max(20, process.stdout.columns || 80);
|
|
85
80
|
const glyph = chalk.hex("#D97757")(GLYPHS[this.glyphIdx]);
|
|
86
81
|
const verbo = this.prefix || VERBS[this.verbIdx];
|
|
87
82
|
const verb = chalk.italic.hex("#D97757")(`${verbo}…`);
|
|
88
83
|
// El contador va hacia abajo: son los tokens que ESTAN LLEGANDO del modelo.
|
|
89
84
|
const tokenStr = this.tokenCount > 0 ? ` · ↓ ${(this.tokenCount / 1000).toFixed(1)}k tokens` : "";
|
|
90
85
|
const cola = chalk.gray(`(${elapsedSec}s${tokenStr} · esc para pausar)`);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
const badge = modeBadge();
|
|
97
|
-
if (!badge)
|
|
98
|
-
return izquierda;
|
|
99
|
-
const anchoBadge = sinColores(badge).length;
|
|
100
|
-
const hueco = cols - anchoIzq - anchoBadge;
|
|
101
|
-
return hueco >= 2 ? `${izquierda}${" ".repeat(hueco)}${badge}` : izquierda;
|
|
86
|
+
// El modo YA NO va aqui. Vive en la linea de estado de la barra, que ahora
|
|
87
|
+
// es la misma en los dos estados: repetirlo era decir dos veces lo mismo en
|
|
88
|
+
// dos sitios que no siempre coincidian, y ademas obligaba a cuadrar anchos a
|
|
89
|
+
// mano justo en la linea mas propensa a partirse en dos filas.
|
|
90
|
+
return `${glyph} ${verb} ${cola}`;
|
|
102
91
|
}
|
|
103
92
|
/**
|
|
104
93
|
* LA BARRA. La MISMA que se ve al escribir una orden, no una parecida.
|
package/extension/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"minimum_chrome_version": "102",
|
|
4
4
|
"name": "Chocolatito Code",
|
|
5
|
-
"version": "1.3.
|
|
5
|
+
"version": "1.3.3",
|
|
6
6
|
"description": "Deja que Chocolatito Code trabaje dentro de tu Chrome, en segundo plano y con tus sesiones ya iniciadas.",
|
|
7
7
|
"permissions": [
|
|
8
8
|
"tabs",
|
package/package.json
CHANGED