chocolatito-code 1.0.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/.mcp.json.ejemplo +19 -0
- package/COMPUTER-USE.md +484 -0
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/agent/context.d.ts +9 -0
- package/dist/agent/context.js +123 -0
- package/dist/agent/fileTracker.d.ts +16 -0
- package/dist/agent/fileTracker.js +53 -0
- package/dist/agent/loop.d.ts +25 -0
- package/dist/agent/loop.js +524 -0
- package/dist/agent/mentions.d.ts +11 -0
- package/dist/agent/mentions.js +43 -0
- package/dist/agent/retry.d.ts +17 -0
- package/dist/agent/retry.js +57 -0
- package/dist/agent/subagent.d.ts +2 -0
- package/dist/agent/subagent.js +116 -0
- package/dist/agent/syntaxValidator.d.ts +5 -0
- package/dist/agent/syntaxValidator.js +71 -0
- package/dist/agent/tracker.d.ts +19 -0
- package/dist/agent/tracker.js +38 -0
- package/dist/agent/undoManager.d.ts +37 -0
- package/dist/agent/undoManager.js +95 -0
- package/dist/config/constants.d.ts +77 -0
- package/dist/config/constants.js +146 -0
- package/dist/config/engine.d.ts +49 -0
- package/dist/config/engine.js +86 -0
- package/dist/config/env.d.ts +13 -0
- package/dist/config/env.js +103 -0
- package/dist/config/license.d.ts +39 -0
- package/dist/config/license.js +306 -0
- package/dist/config/permissions.d.ts +38 -0
- package/dist/config/permissions.js +239 -0
- package/dist/config/quota.d.ts +58 -0
- package/dist/config/quota.js +156 -0
- package/dist/hooks/manager.d.ts +60 -0
- package/dist/hooks/manager.js +193 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +470 -0
- package/dist/mcp/client.d.ts +55 -0
- package/dist/mcp/client.js +282 -0
- package/dist/mcp/manager.d.ts +26 -0
- package/dist/mcp/manager.js +167 -0
- package/dist/memory/manager.d.ts +9 -0
- package/dist/memory/manager.js +59 -0
- package/dist/prompts/systemPrompt.d.ts +5 -0
- package/dist/prompts/systemPrompt.js +177 -0
- package/dist/sessions/manager.d.ts +21 -0
- package/dist/sessions/manager.js +54 -0
- package/dist/skills/manager.d.ts +14 -0
- package/dist/skills/manager.js +86 -0
- package/dist/tools/browserCdp.d.ts +26 -0
- package/dist/tools/browserCdp.js +610 -0
- package/dist/tools/browserExtension.d.ts +58 -0
- package/dist/tools/browserExtension.js +479 -0
- package/dist/tools/browserLive.d.ts +12 -0
- package/dist/tools/browserLive.js +265 -0
- package/dist/tools/changeDir.d.ts +5 -0
- package/dist/tools/changeDir.js +63 -0
- package/dist/tools/computerUse.d.ts +58 -0
- package/dist/tools/computerUse.js +567 -0
- package/dist/tools/createImage.d.ts +1 -0
- package/dist/tools/createImage.js +34 -0
- package/dist/tools/definitions.d.ts +8 -0
- package/dist/tools/definitions.js +401 -0
- package/dist/tools/deleteFile.d.ts +1 -0
- package/dist/tools/deleteFile.js +17 -0
- package/dist/tools/editFile.d.ts +5 -0
- package/dist/tools/editFile.js +88 -0
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/findFiles.js +33 -0
- package/dist/tools/getSystemInfo.d.ts +1 -0
- package/dist/tools/getSystemInfo.js +23 -0
- package/dist/tools/gitTools.d.ts +3 -0
- package/dist/tools/gitTools.js +47 -0
- package/dist/tools/grepSearch.d.ts +1 -0
- package/dist/tools/grepSearch.js +37 -0
- package/dist/tools/listDir.d.ts +1 -0
- package/dist/tools/listDir.js +33 -0
- package/dist/tools/moveCopyFile.d.ts +1 -0
- package/dist/tools/moveCopyFile.js +21 -0
- package/dist/tools/runCommand.d.ts +1 -0
- package/dist/tools/runCommand.js +61 -0
- package/dist/tools/runner.d.ts +5 -0
- package/dist/tools/runner.js +177 -0
- package/dist/tools/safety.d.ts +17 -0
- package/dist/tools/safety.js +33 -0
- package/dist/tools/todoTool.d.ts +8 -0
- package/dist/tools/todoTool.js +27 -0
- package/dist/tools/toolDefsComputer.d.ts +13 -0
- package/dist/tools/toolDefsComputer.js +205 -0
- package/dist/tools/truncator.d.ts +11 -0
- package/dist/tools/truncator.js +24 -0
- package/dist/tools/viewFile.d.ts +1 -0
- package/dist/tools/viewFile.js +45 -0
- package/dist/tools/visionBridge.d.ts +37 -0
- package/dist/tools/visionBridge.js +107 -0
- package/dist/tools/webFetch.d.ts +1 -0
- package/dist/tools/webFetch.js +25 -0
- package/dist/tools/webSearch.d.ts +1 -0
- package/dist/tools/webSearch.js +35 -0
- package/dist/tools/win/hostClient.d.ts +31 -0
- package/dist/tools/win/hostClient.js +158 -0
- package/dist/tools/win/hostScript.d.ts +1 -0
- package/dist/tools/win/hostScript.js +925 -0
- package/dist/tools/writeFile.d.ts +1 -0
- package/dist/tools/writeFile.js +13 -0
- package/dist/ui/banner.d.ts +10 -0
- package/dist/ui/banner.js +165 -0
- package/dist/ui/diffViewer.d.ts +1 -0
- package/dist/ui/diffViewer.js +60 -0
- package/dist/ui/interrupt.d.ts +17 -0
- package/dist/ui/interrupt.js +156 -0
- package/dist/ui/markdownStream.d.ts +23 -0
- package/dist/ui/markdownStream.js +0 -0
- package/dist/ui/modes.d.ts +28 -0
- package/dist/ui/modes.js +57 -0
- package/dist/ui/permissionPrompt.d.ts +35 -0
- package/dist/ui/permissionPrompt.js +271 -0
- package/dist/ui/prompt.d.ts +7 -0
- package/dist/ui/prompt.js +466 -0
- package/dist/ui/reasoningStream.d.ts +6 -0
- package/dist/ui/reasoningStream.js +38 -0
- package/dist/ui/renderer.d.ts +10 -0
- package/dist/ui/renderer.js +142 -0
- package/dist/ui/spinner.d.ts +24 -0
- package/dist/ui/spinner.js +111 -0
- package/dist/ui/theme.d.ts +36 -0
- package/dist/ui/theme.js +37 -0
- package/dist/ui/typeAhead.d.ts +31 -0
- package/dist/ui/typeAhead.js +87 -0
- package/extension/README.md +74 -0
- package/extension/background.js +575 -0
- package/extension/content.js +446 -0
- package/extension/manifest.json +36 -0
- package/extension/popup.html +22 -0
- package/extension/popup.js +12 -0
- package/package.json +66 -0
- package/skills/complex-refactor/SKILL.md +13 -0
- package/skills/deep-debugger/SKILL.md +14 -0
- package/skills/software-architect/SKILL.md +20 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import readline from "node:readline";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { cycleMode, describeMode, getMode } from "./modes.js";
|
|
8
|
+
import { releaseKeyboard } from "./interrupt.js";
|
|
9
|
+
import { clearTyped } from "./typeAhead.js";
|
|
10
|
+
export const SLASH_COMMANDS = [
|
|
11
|
+
{ name: "/mcp", description: "Ver servidores MCP conectados y sus herramientas" },
|
|
12
|
+
{ name: "/undo", description: "Deshacer las últimas modificaciones de archivos de la sesión" },
|
|
13
|
+
{ name: "/export", description: "Exportar bitácora de la sesión a archivo Markdown", args: "[nombre.md]" },
|
|
14
|
+
{ name: "/plan", description: "Alternar Modo Plan (solo lectura e investigación previa)" },
|
|
15
|
+
{ name: "/resume", description: "Reanudar una sesión anterior guardada", args: "[id]" },
|
|
16
|
+
{ name: "/skills", description: "Ver skills disponibles y cómo usarlos" },
|
|
17
|
+
{ name: "/memory", description: "Consultar la memoria persistente transversal (~/.chocolatito/memory/)" },
|
|
18
|
+
{ name: "/effort", description: "Regular potencia del motor (1.0, 1.5, 2.0, 2.5)", args: "[nivel]" },
|
|
19
|
+
{ name: "/goal", description: "Ejecutar meta compleja autónoma con auto-corrección", args: "<meta>" },
|
|
20
|
+
{ name: "/compact", description: "Comprimir contexto y optimizar memoria de la sesión" },
|
|
21
|
+
{ name: "/diff", description: "Ver diferencias de código (Git diff) del proyecto" },
|
|
22
|
+
{ name: "/commit", description: "Crear un commit en Git con mensaje semántico", args: "<mensaje>" },
|
|
23
|
+
{ name: "/cd", description: "Cambiar el directorio de trabajo activo", args: "<ruta>" },
|
|
24
|
+
{ name: "/cost", description: "Ver consumo detallado de tokens e inversión en USD" },
|
|
25
|
+
{ name: "/clear", description: "Limpiar historial y reiniciar contexto" },
|
|
26
|
+
{ name: "/init", description: "Crear archivo de directivas CHOCOLATITO.md" },
|
|
27
|
+
{ name: "/help", description: "Mostrar guía completa de comandos y ayuda" },
|
|
28
|
+
{ name: "/exit", description: "Cerrar sesión de Chocolatito Code" },
|
|
29
|
+
];
|
|
30
|
+
let isKeypressInitialized = false;
|
|
31
|
+
function stripAnsi(str) {
|
|
32
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
33
|
+
}
|
|
34
|
+
function getHistoryFilePath() {
|
|
35
|
+
const dir = path.join(os.homedir(), ".chocolatito");
|
|
36
|
+
if (!fs.existsSync(dir))
|
|
37
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
38
|
+
return path.join(dir, "history");
|
|
39
|
+
}
|
|
40
|
+
function loadHistory() {
|
|
41
|
+
const file = getHistoryFilePath();
|
|
42
|
+
if (fs.existsSync(file)) {
|
|
43
|
+
try {
|
|
44
|
+
return fs.readFileSync(file, "utf-8").split("\n").filter((l) => l.trim().length > 0);
|
|
45
|
+
}
|
|
46
|
+
catch { }
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
function appendHistory(prompt) {
|
|
51
|
+
if (!prompt.trim())
|
|
52
|
+
return;
|
|
53
|
+
const file = getHistoryFilePath();
|
|
54
|
+
try {
|
|
55
|
+
fs.appendFileSync(file, `${prompt.trim()}\n`, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
}
|
|
59
|
+
function scanProjectFiles(dir, maxResults = 40) {
|
|
60
|
+
const results = [];
|
|
61
|
+
const ignore = new Set(["node_modules", ".git", "dist", "build", ".next", "out", "venv", "__pycache__", ".turbo"]);
|
|
62
|
+
function walk(current, depth = 0) {
|
|
63
|
+
if (depth > 6 || results.length >= maxResults)
|
|
64
|
+
return;
|
|
65
|
+
try {
|
|
66
|
+
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
67
|
+
for (const ent of entries) {
|
|
68
|
+
if (ent.name.startsWith(".") && ent.name !== ".env")
|
|
69
|
+
continue;
|
|
70
|
+
if (ignore.has(ent.name))
|
|
71
|
+
continue;
|
|
72
|
+
const full = path.join(current, ent.name);
|
|
73
|
+
const rel = path.relative(dir, full).replace(/\\/g, "/");
|
|
74
|
+
if (ent.isDirectory()) {
|
|
75
|
+
walk(full, depth + 1);
|
|
76
|
+
}
|
|
77
|
+
else if (ent.isFile()) {
|
|
78
|
+
results.push(rel);
|
|
79
|
+
if (results.length >= maxResults)
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { }
|
|
85
|
+
}
|
|
86
|
+
walk(dir);
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
export async function askInteractivePrompt(promptSymbol = "❯", cwd = process.cwd()) {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
let inputBuffer = "";
|
|
92
|
+
let cursorIndex = 0;
|
|
93
|
+
let selectedIndex = 0;
|
|
94
|
+
let isFinished = false;
|
|
95
|
+
let lastRenderedLines = [];
|
|
96
|
+
let lastCursorLineIndex = 1;
|
|
97
|
+
const history = loadHistory();
|
|
98
|
+
let historyIdx = history.length;
|
|
99
|
+
// Cache project files for @ autocompletion
|
|
100
|
+
let projectFiles = [];
|
|
101
|
+
try {
|
|
102
|
+
projectFiles = scanProjectFiles(cwd);
|
|
103
|
+
}
|
|
104
|
+
catch { }
|
|
105
|
+
if (!isKeypressInitialized) {
|
|
106
|
+
readline.emitKeypressEvents(process.stdin);
|
|
107
|
+
isKeypressInitialized = true;
|
|
108
|
+
}
|
|
109
|
+
// El prompt reclama el teclado antes de dibujarse. Si un vigilante de la
|
|
110
|
+
// generacion anterior se quedo escuchando (el bucle tiene muchas salidas y
|
|
111
|
+
// basta que una se escape), el usuario teclearia y no aparecerian sus letras
|
|
112
|
+
// aqui: se las estaria comiendo el vigilante. Reclamarlo cuesta nada y
|
|
113
|
+
// convierte veinte sitios que hay que acertar en uno.
|
|
114
|
+
releaseKeyboard();
|
|
115
|
+
// Lo que quedara a medio escribir del turno anterior no es de este prompt.
|
|
116
|
+
clearTyped();
|
|
117
|
+
const wasRaw = process.stdin.isRaw;
|
|
118
|
+
if (process.stdin.setRawMode) {
|
|
119
|
+
process.stdin.setRawMode(true);
|
|
120
|
+
}
|
|
121
|
+
process.stdin.resume();
|
|
122
|
+
function getAtMention() {
|
|
123
|
+
const textBeforeCursor = inputBuffer.slice(0, cursorIndex);
|
|
124
|
+
const match = textBeforeCursor.match(/@([^\s]*)$/);
|
|
125
|
+
if (match && match.index !== undefined) {
|
|
126
|
+
return {
|
|
127
|
+
active: true,
|
|
128
|
+
query: match[1].toLowerCase(),
|
|
129
|
+
startIndex: match.index,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return { active: false, query: "", startIndex: -1 };
|
|
133
|
+
}
|
|
134
|
+
function getDropdownItems() {
|
|
135
|
+
// 1. Slash commands
|
|
136
|
+
if (inputBuffer.startsWith("/")) {
|
|
137
|
+
const query = inputBuffer.toLowerCase();
|
|
138
|
+
return SLASH_COMMANDS
|
|
139
|
+
.filter((cmd) => cmd.name.toLowerCase().startsWith(query) || query === "/")
|
|
140
|
+
.map((cmd) => ({
|
|
141
|
+
type: "command",
|
|
142
|
+
label: `${cmd.name}${cmd.args ? " " + cmd.args : ""}`,
|
|
143
|
+
secondary: cmd.description,
|
|
144
|
+
insertText: cmd.name,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
// 2. @ File mentions
|
|
148
|
+
const at = getAtMention();
|
|
149
|
+
if (at.active) {
|
|
150
|
+
return projectFiles
|
|
151
|
+
.filter((f) => f.toLowerCase().includes(at.query))
|
|
152
|
+
.slice(0, 8)
|
|
153
|
+
.map((f) => ({
|
|
154
|
+
type: "file",
|
|
155
|
+
label: `@${f}`,
|
|
156
|
+
secondary: "Archivo del proyecto",
|
|
157
|
+
insertText: `@${f} `,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
function render() {
|
|
163
|
+
if (isFinished)
|
|
164
|
+
return;
|
|
165
|
+
const cols = Math.max(20, process.stdout.columns || 80);
|
|
166
|
+
// 1. Top separator line
|
|
167
|
+
const topBar = chalk.gray("─".repeat(cols));
|
|
168
|
+
// 2. Prompt symbol & input text
|
|
169
|
+
const symbolStr = promptSymbol.includes("plan")
|
|
170
|
+
? `${chalk.cyan.bold("plan ❯")} `
|
|
171
|
+
: `${chalk.bold.hex("#D97757")("❯")} `;
|
|
172
|
+
const rawSymbolLen = promptSymbol.includes("plan") ? 7 : 2;
|
|
173
|
+
const placeholderText = "Escribe una instrucción, '@' para archivos o '/' para comandos...";
|
|
174
|
+
let formattedInput = "";
|
|
175
|
+
if (inputBuffer.length === 0) {
|
|
176
|
+
formattedInput = chalk.gray(placeholderText);
|
|
177
|
+
}
|
|
178
|
+
else if (inputBuffer.startsWith("/")) {
|
|
179
|
+
const parts = inputBuffer.split(" ");
|
|
180
|
+
const cmdPart = chalk.bold.hex("#A855F7")(parts[0]);
|
|
181
|
+
const restPart = parts.slice(1).join(" ");
|
|
182
|
+
formattedInput = restPart ? `${cmdPart} ${chalk.white(restPart)}` : cmdPart;
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
formattedInput = inputBuffer.replace(/(@[^\s]+)/g, (m) => chalk.bold.hex("#D97757")(m));
|
|
186
|
+
}
|
|
187
|
+
// 3. Dropdown items (if any)
|
|
188
|
+
const items = getDropdownItems();
|
|
189
|
+
const dropdownLines = [];
|
|
190
|
+
if (items.length > 0) {
|
|
191
|
+
if (selectedIndex >= items.length)
|
|
192
|
+
selectedIndex = 0;
|
|
193
|
+
if (selectedIndex < 0)
|
|
194
|
+
selectedIndex = items.length - 1;
|
|
195
|
+
items.forEach((item, idx) => {
|
|
196
|
+
const isSelected = idx === selectedIndex;
|
|
197
|
+
const isFile = item.type === "file";
|
|
198
|
+
const colorHex = isFile ? "#D97757" : "#A855F7";
|
|
199
|
+
const pointer = isSelected
|
|
200
|
+
? chalk.bold.hex(colorHex)(" ❯ ")
|
|
201
|
+
: chalk.gray(" ");
|
|
202
|
+
const label = isSelected
|
|
203
|
+
? chalk.bold.white(item.label)
|
|
204
|
+
: chalk.hex(colorHex)(item.label);
|
|
205
|
+
const desc = isSelected
|
|
206
|
+
? chalk.whiteBright(` ${item.secondary || ""}`)
|
|
207
|
+
: chalk.gray(` ${item.secondary || ""}`);
|
|
208
|
+
dropdownLines.push(`${pointer}${label}${desc}`);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// 4. Bottom separator line
|
|
212
|
+
const bottomBar = chalk.gray("─".repeat(cols));
|
|
213
|
+
// 5. Footer status line
|
|
214
|
+
const modeLook = describeMode(getMode());
|
|
215
|
+
const leftFooter = chalk.gray("? /help para atajos · shift+tab modo · esc cancelar");
|
|
216
|
+
const rightFooter = `${chalk.hex(modeLook.color)(`⇥ modo ${modeLook.label}`)} ${chalk.gray("· Espectro 1.0 (Ágil)")}`;
|
|
217
|
+
const leftLen = stripAnsi(leftFooter).length;
|
|
218
|
+
const rightLen = stripAnsi(rightFooter).length;
|
|
219
|
+
const padLen = Math.max(1, cols - leftLen - rightLen);
|
|
220
|
+
const footerLine = `${leftFooter}${" ".repeat(padLen)}${rightFooter}`;
|
|
221
|
+
// Build current frame array
|
|
222
|
+
const currentFrame = [topBar, symbolStr + formattedInput];
|
|
223
|
+
if (dropdownLines.length > 0) {
|
|
224
|
+
currentFrame.push(...dropdownLines);
|
|
225
|
+
}
|
|
226
|
+
currentFrame.push(bottomBar, footerLine);
|
|
227
|
+
const inputLineIdx = 1;
|
|
228
|
+
const totalCharsCursor = rawSymbolLen + (inputBuffer.length === 0 ? 0 : cursorIndex);
|
|
229
|
+
const cursorCol = totalCharsCursor % cols;
|
|
230
|
+
if (lastRenderedLines.length === 0) {
|
|
231
|
+
// First render: write frame and position cursor on input line
|
|
232
|
+
process.stdout.write(currentFrame.join("\n"));
|
|
233
|
+
const moveUp = currentFrame.length - 1 - inputLineIdx;
|
|
234
|
+
process.stdout.write(`\x1b[${moveUp}A\r\x1b[${cursorCol}C`);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Subsequent render: re-draw lines in place with 0 scrolling drift
|
|
238
|
+
if (lastCursorLineIndex > 0) {
|
|
239
|
+
process.stdout.write(`\x1b[${lastCursorLineIndex}A\r`);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
process.stdout.write("\r");
|
|
243
|
+
}
|
|
244
|
+
const maxLines = Math.max(currentFrame.length, lastRenderedLines.length);
|
|
245
|
+
for (let i = 0; i < maxLines; i++) {
|
|
246
|
+
const content = i < currentFrame.length ? currentFrame[i] : "";
|
|
247
|
+
process.stdout.write(`\x1b[2K${content}`);
|
|
248
|
+
if (i < maxLines - 1) {
|
|
249
|
+
process.stdout.write("\x1b[1E");
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const moveUp = (maxLines - 1) - inputLineIdx;
|
|
253
|
+
if (moveUp > 0) {
|
|
254
|
+
process.stdout.write(`\x1b[${moveUp}A\r\x1b[${cursorCol}C`);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
process.stdout.write(`\r\x1b[${cursorCol}C`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
lastRenderedLines = currentFrame;
|
|
261
|
+
lastCursorLineIndex = inputLineIdx;
|
|
262
|
+
}
|
|
263
|
+
function cleanup() {
|
|
264
|
+
isFinished = true;
|
|
265
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
266
|
+
if (process.stdin.setRawMode) {
|
|
267
|
+
process.stdin.setRawMode(wasRaw || false);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function applySelectedDropdownItem() {
|
|
271
|
+
const items = getDropdownItems();
|
|
272
|
+
if (items.length === 0)
|
|
273
|
+
return false;
|
|
274
|
+
const item = items[selectedIndex];
|
|
275
|
+
if (item.type === "command") {
|
|
276
|
+
inputBuffer = item.insertText;
|
|
277
|
+
cursorIndex = inputBuffer.length;
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
else if (item.type === "file") {
|
|
281
|
+
const at = getAtMention();
|
|
282
|
+
if (at.active) {
|
|
283
|
+
inputBuffer = inputBuffer.slice(0, at.startIndex) + item.insertText + inputBuffer.slice(cursorIndex);
|
|
284
|
+
cursorIndex = at.startIndex + item.insertText.length;
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
function finalizePromptOnSubmit(submittedText) {
|
|
291
|
+
const cols = Math.max(20, process.stdout.columns || 80);
|
|
292
|
+
const symbolStr = promptSymbol.includes("plan")
|
|
293
|
+
? `${chalk.cyan.bold("plan ❯")} `
|
|
294
|
+
: `${chalk.bold.hex("#D97757")("❯")} `;
|
|
295
|
+
// Move cursor to top of frame
|
|
296
|
+
if (lastCursorLineIndex > 0) {
|
|
297
|
+
process.stdout.write(`\x1b[${lastCursorLineIndex}A\r`);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
process.stdout.write("\r");
|
|
301
|
+
}
|
|
302
|
+
// Draw clean submitted prompt box in terminal history
|
|
303
|
+
process.stdout.write(`\x1b[2K${chalk.gray("─".repeat(cols))}\x1b[1E`);
|
|
304
|
+
process.stdout.write(`\x1b[2K${symbolStr}${chalk.bold.white(submittedText)}\x1b[1E`);
|
|
305
|
+
process.stdout.write(`\x1b[2K${chalk.gray("─".repeat(cols))}\x1b[1E`);
|
|
306
|
+
// Clear any leftover rows (dropdown, old footer)
|
|
307
|
+
const extraLines = Math.max(0, lastRenderedLines.length - 3);
|
|
308
|
+
for (let i = 0; i < extraLines; i++) {
|
|
309
|
+
process.stdout.write(`\x1b[2K`);
|
|
310
|
+
if (i < extraLines - 1)
|
|
311
|
+
process.stdout.write("\x1b[1E");
|
|
312
|
+
}
|
|
313
|
+
// Move cursor directly below the bottom bar
|
|
314
|
+
if (extraLines > 0) {
|
|
315
|
+
process.stdout.write(`\x1b[${extraLines}A\r\x1b[1E\n`);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
process.stdout.write("\n");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function onKeypress(str, key) {
|
|
322
|
+
if (isFinished)
|
|
323
|
+
return;
|
|
324
|
+
const items = getDropdownItems();
|
|
325
|
+
// Enter key
|
|
326
|
+
if (key && (key.name === "return" || key.name === "enter")) {
|
|
327
|
+
// Multi-line continuation if ends with \
|
|
328
|
+
if (inputBuffer.endsWith("\\")) {
|
|
329
|
+
inputBuffer = inputBuffer.slice(0, -1) + "\n";
|
|
330
|
+
cursorIndex = inputBuffer.length;
|
|
331
|
+
render();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (items.length > 0 && (inputBuffer === "/" || getAtMention().active)) {
|
|
335
|
+
if (applySelectedDropdownItem()) {
|
|
336
|
+
render();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
appendHistory(inputBuffer);
|
|
341
|
+
cleanup();
|
|
342
|
+
finalizePromptOnSubmit(inputBuffer);
|
|
343
|
+
resolve(inputBuffer);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
// Shift+Tab: cambia el modo de permisos instantáneamente
|
|
347
|
+
const isShiftTab = Boolean(key && key.name === "tab" && key.shift) ||
|
|
348
|
+
str === "\x1b[Z" ||
|
|
349
|
+
Boolean(key && key.sequence === "\x1b[Z");
|
|
350
|
+
if (isShiftTab) {
|
|
351
|
+
cycleMode();
|
|
352
|
+
render();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
// Tab autocompletion
|
|
356
|
+
if (key && key.name === "tab") {
|
|
357
|
+
if (items.length > 0) {
|
|
358
|
+
if (applySelectedDropdownItem()) {
|
|
359
|
+
render();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Up arrow
|
|
365
|
+
if (key && key.name === "up") {
|
|
366
|
+
if (items.length > 0) {
|
|
367
|
+
selectedIndex = (selectedIndex - 1 + items.length) % items.length;
|
|
368
|
+
render();
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
else if (history.length > 0 && historyIdx > 0) {
|
|
372
|
+
historyIdx--;
|
|
373
|
+
inputBuffer = history[historyIdx] || "";
|
|
374
|
+
cursorIndex = inputBuffer.length;
|
|
375
|
+
render();
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Down arrow
|
|
380
|
+
if (key && key.name === "down") {
|
|
381
|
+
if (items.length > 0) {
|
|
382
|
+
selectedIndex = (selectedIndex + 1) % items.length;
|
|
383
|
+
render();
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
else if (historyIdx < history.length - 1) {
|
|
387
|
+
historyIdx++;
|
|
388
|
+
inputBuffer = history[historyIdx] || "";
|
|
389
|
+
cursorIndex = inputBuffer.length;
|
|
390
|
+
render();
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
else if (historyIdx === history.length - 1) {
|
|
394
|
+
historyIdx = history.length;
|
|
395
|
+
inputBuffer = "";
|
|
396
|
+
cursorIndex = 0;
|
|
397
|
+
render();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// Left arrow
|
|
402
|
+
if (key && key.name === "left") {
|
|
403
|
+
if (cursorIndex > 0) {
|
|
404
|
+
cursorIndex--;
|
|
405
|
+
render();
|
|
406
|
+
}
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
// Right arrow
|
|
410
|
+
if (key && key.name === "right") {
|
|
411
|
+
if (cursorIndex < inputBuffer.length) {
|
|
412
|
+
cursorIndex++;
|
|
413
|
+
render();
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// Backspace
|
|
418
|
+
if (key && (key.name === "backspace" || (key.ctrl && key.name === "h"))) {
|
|
419
|
+
if (cursorIndex > 0) {
|
|
420
|
+
inputBuffer = inputBuffer.slice(0, cursorIndex - 1) + inputBuffer.slice(cursorIndex);
|
|
421
|
+
cursorIndex--;
|
|
422
|
+
selectedIndex = 0;
|
|
423
|
+
render();
|
|
424
|
+
}
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
// Ctrl + C / Esc to exit
|
|
428
|
+
if ((key && key.ctrl && key.name === "c") || (key && key.name === "escape" && inputBuffer.length === 0)) {
|
|
429
|
+
cleanup();
|
|
430
|
+
finalizePromptOnSubmit(inputBuffer);
|
|
431
|
+
process.exit(0);
|
|
432
|
+
}
|
|
433
|
+
// Esc when text is written: clear line
|
|
434
|
+
if (key && key.name === "escape" && inputBuffer.length > 0) {
|
|
435
|
+
inputBuffer = "";
|
|
436
|
+
cursorIndex = 0;
|
|
437
|
+
selectedIndex = 0;
|
|
438
|
+
render();
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Ctrl + L (clear screen)
|
|
442
|
+
if (key && key.ctrl && key.name === "l") {
|
|
443
|
+
console.clear();
|
|
444
|
+
lastRenderedLines = [];
|
|
445
|
+
lastCursorLineIndex = 1;
|
|
446
|
+
render();
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
// Printable characters or pasted text
|
|
450
|
+
if (str &&
|
|
451
|
+
!isShiftTab &&
|
|
452
|
+
str !== "\x1b[Z" &&
|
|
453
|
+
(!key || (!key.ctrl && !key.meta) || (key.ctrl && key.meta) || (!key.name && str.length > 0))) {
|
|
454
|
+
const cleanStr = str.replace(/[\r\n]/g, "");
|
|
455
|
+
if (cleanStr.length > 0) {
|
|
456
|
+
inputBuffer = inputBuffer.slice(0, cursorIndex) + cleanStr + inputBuffer.slice(cursorIndex);
|
|
457
|
+
cursorIndex += cleanStr.length;
|
|
458
|
+
selectedIndex = 0;
|
|
459
|
+
render();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
process.stdin.on("keypress", onKeypress);
|
|
464
|
+
render();
|
|
465
|
+
});
|
|
466
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
export class ReasoningStream {
|
|
4
|
+
hasStarted = false;
|
|
5
|
+
atLineStart = true;
|
|
6
|
+
push(chunk) {
|
|
7
|
+
if (!chunk)
|
|
8
|
+
return;
|
|
9
|
+
if (!this.hasStarted) {
|
|
10
|
+
this.hasStarted = true;
|
|
11
|
+
process.stdout.write(`\n ${chalk.bold.hex("#8B5CF6")("💭 Razonando:")}\n`);
|
|
12
|
+
}
|
|
13
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
14
|
+
const char = chunk[i];
|
|
15
|
+
if (this.atLineStart) {
|
|
16
|
+
process.stdout.write(` ${chalk.hex("#8B5CF6")("│")} `);
|
|
17
|
+
this.atLineStart = false;
|
|
18
|
+
}
|
|
19
|
+
if (char === "\n") {
|
|
20
|
+
process.stdout.write("\n");
|
|
21
|
+
this.atLineStart = true;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
process.stdout.write(chalk.gray(char));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
flush() {
|
|
29
|
+
if (this.hasStarted) {
|
|
30
|
+
if (!this.atLineStart) {
|
|
31
|
+
process.stdout.write("\n");
|
|
32
|
+
this.atLineStart = true;
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write("\n");
|
|
35
|
+
this.hasStarted = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function renderUserPrompt(text: string): void;
|
|
2
|
+
export declare function renderToolStart(name: string, summary: string): void;
|
|
3
|
+
export declare function renderToolSuccess(name: string, resultSummary: string): void;
|
|
4
|
+
export declare function renderToolError(name: string, errorSummary: string): void;
|
|
5
|
+
export declare function renderDiff(filePath: string, diffText: string): void;
|
|
6
|
+
export declare function renderReasoning(reasoningText: string): void;
|
|
7
|
+
export declare function renderAssistantResponse(markdownText: string): void;
|
|
8
|
+
export declare function renderFooter(costStr: string, outTokens: number, cachedTokens: number, modelName: string, durationMs?: number, contextPercent?: number, quotaStr?: string): void;
|
|
9
|
+
export declare function renderError(message: string): void;
|
|
10
|
+
export declare function formatToolName(name: string): string;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Marked } from "marked";
|
|
3
|
+
import { markedTerminal } from "marked-terminal";
|
|
4
|
+
const marked = new Marked(markedTerminal({
|
|
5
|
+
heading: chalk.bold.hex("#D97757"),
|
|
6
|
+
firstHeading: chalk.bold.hex("#D97757").underline,
|
|
7
|
+
secondHeading: chalk.bold.hex("#FFA500"),
|
|
8
|
+
thirdHeading: chalk.bold.white,
|
|
9
|
+
code: chalk.cyan,
|
|
10
|
+
codespan: (text) => chalk.bgHex("#27272A").hex("#F4F4F5")(` ${text} `),
|
|
11
|
+
strong: chalk.bold.white,
|
|
12
|
+
em: chalk.italic.hex("#D97757"),
|
|
13
|
+
hr: () => chalk.gray("\n ──────────────────────────────────────────────────────────\n"),
|
|
14
|
+
unescape: true,
|
|
15
|
+
tableOptions: {
|
|
16
|
+
style: {
|
|
17
|
+
head: ["cyan", "bold"],
|
|
18
|
+
border: ["gray"],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
export function renderUserPrompt(text) {
|
|
23
|
+
const symbol = chalk.bold.hex("#D97757")("❯");
|
|
24
|
+
const shaded = chalk.bgHex("#242730").hex("#F4F4F5").bold(` ${text.trim()} `);
|
|
25
|
+
console.log(`\n${symbol} ${shaded}\n`);
|
|
26
|
+
}
|
|
27
|
+
// Tool call rendering as specified in §B5
|
|
28
|
+
export function renderToolStart(name, summary) {
|
|
29
|
+
const bullet = chalk.hex("#FFA500")("●"); // Amber while starting
|
|
30
|
+
const toolName = chalk.bold.white(formatToolName(name));
|
|
31
|
+
const argPart = summary ? chalk.gray(`(${summary.length > 60 ? summary.slice(0, 57) + '...' : summary})`) : "";
|
|
32
|
+
console.log(`\n${bullet} ${toolName}${argPart}`);
|
|
33
|
+
}
|
|
34
|
+
export function renderToolSuccess(name, resultSummary) {
|
|
35
|
+
const rawLines = resultSummary.trim().split("\n");
|
|
36
|
+
const maxVisibleLines = 5;
|
|
37
|
+
const visibleLines = rawLines.slice(0, maxVisibleLines);
|
|
38
|
+
const remainingCount = rawLines.length - maxVisibleLines;
|
|
39
|
+
const indented = visibleLines.map((line, idx) => {
|
|
40
|
+
const prefix = idx === 0 ? ` ${chalk.gray("⎿")} ` : " ";
|
|
41
|
+
return `${prefix}${colorizeText(line)}`;
|
|
42
|
+
}).join("\n");
|
|
43
|
+
console.log(indented);
|
|
44
|
+
if (remainingCount > 0) {
|
|
45
|
+
console.log(chalk.gray(` … +${remainingCount} líneas más`));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function renderToolError(name, errorSummary) {
|
|
49
|
+
console.log(` ${chalk.red("⎿")} ${chalk.red(errorSummary)}`);
|
|
50
|
+
}
|
|
51
|
+
export function renderDiff(filePath, diffText) {
|
|
52
|
+
console.log(`\n${diffText}\n`);
|
|
53
|
+
}
|
|
54
|
+
export function renderReasoning(reasoningText) {
|
|
55
|
+
if (!reasoningText.trim())
|
|
56
|
+
return;
|
|
57
|
+
const lines = reasoningText.trim().split("\n");
|
|
58
|
+
const formatted = lines.map((l) => ` ${chalk.hex("#8B5CF6")("│")} ${chalk.gray(l)}`).join("\n");
|
|
59
|
+
console.log(`\n ${chalk.bold.hex("#8B5CF6")("💭 Razonamiento:")}\n${formatted}\n`);
|
|
60
|
+
}
|
|
61
|
+
export function renderAssistantResponse(markdownText) {
|
|
62
|
+
try {
|
|
63
|
+
// Aggressively clean triple asterisks, triple underscores, and excessive separators
|
|
64
|
+
let cleaned = markdownText
|
|
65
|
+
.replace(/\r\n/g, "\n")
|
|
66
|
+
.replace(/^\s*(\*\*\*|---|___|\*\s+\*\s+\*)\s*$/gm, "\n---\n")
|
|
67
|
+
.replace(/\n{3,}/g, "\n\n");
|
|
68
|
+
let parsed = String(marked.parse(cleaned));
|
|
69
|
+
// Convert raw asterisks bullets to terracotta bullets
|
|
70
|
+
parsed = parsed.replace(/^(\s*)\*\s+/gm, `$1${chalk.hex("#D97757")("•")} `);
|
|
71
|
+
console.log(`\n${parsed}`);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
console.log(`\n${markdownText.trim()}\n`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function renderFooter(costStr, outTokens, cachedTokens, modelName, durationMs, contextPercent, quotaStr) {
|
|
78
|
+
const durationPart = durationMs ? `${(durationMs / 1000).toFixed(1)}s · ` : "";
|
|
79
|
+
const outPart = `${outTokens.toLocaleString()} tokens salida`;
|
|
80
|
+
const cachePart = cachedTokens > 0 ? ` (${(cachedTokens / 1000).toFixed(1)}k en caché)` : "";
|
|
81
|
+
const contextPart = contextPercent !== undefined ? ` · ${contextPercent}% contexto libre` : "";
|
|
82
|
+
const quotaPart = quotaStr ? ` · ${quotaStr}` : "";
|
|
83
|
+
const line = chalk.gray(` ${chalk.hex("#D97757")("⚡")} ${durationPart}${outPart}${cachePart} · ${chalk.green(costStr)} · ${modelName}${contextPart}${quotaPart}`);
|
|
84
|
+
console.log(`${line}\n`);
|
|
85
|
+
}
|
|
86
|
+
export function renderError(message) {
|
|
87
|
+
console.log(`\n ${chalk.bgRed.white.bold(" ERROR ")} ${chalk.red(message)}\n`);
|
|
88
|
+
}
|
|
89
|
+
function colorizeText(text) {
|
|
90
|
+
// Highlight file paths (C:\..., /path/...) in cyan
|
|
91
|
+
return text
|
|
92
|
+
.replace(/([a-zA-Z]:\\[^\s"']+)/g, (m) => chalk.cyan(m))
|
|
93
|
+
.replace(/(\b\w+\.(?:ts|js|json|png|py|md|html|css|tsx|jsx)\b)/g, (m) => chalk.bold.white(m));
|
|
94
|
+
}
|
|
95
|
+
export function formatToolName(name) {
|
|
96
|
+
switch (name) {
|
|
97
|
+
case "todo_write":
|
|
98
|
+
return "Task";
|
|
99
|
+
case "use_skill":
|
|
100
|
+
return "Skill";
|
|
101
|
+
case "save_memory":
|
|
102
|
+
return "MemorySave";
|
|
103
|
+
case "read_memory":
|
|
104
|
+
return "MemoryRead";
|
|
105
|
+
case "spawn_agent":
|
|
106
|
+
return "SubAgent";
|
|
107
|
+
case "browser_live":
|
|
108
|
+
return "BrowserLive";
|
|
109
|
+
case "computer_use":
|
|
110
|
+
return "ComputerUse";
|
|
111
|
+
case "change_dir":
|
|
112
|
+
return "Cd";
|
|
113
|
+
case "create_image":
|
|
114
|
+
return "CreateImage";
|
|
115
|
+
case "web_search":
|
|
116
|
+
return "SearchWeb";
|
|
117
|
+
case "find_files":
|
|
118
|
+
return "Glob";
|
|
119
|
+
case "view_file":
|
|
120
|
+
return "Read";
|
|
121
|
+
case "edit_file":
|
|
122
|
+
return "Edit";
|
|
123
|
+
case "write_file":
|
|
124
|
+
return "Write";
|
|
125
|
+
case "delete_file":
|
|
126
|
+
return "Delete";
|
|
127
|
+
case "move_copy_file":
|
|
128
|
+
return "Move";
|
|
129
|
+
case "run_command":
|
|
130
|
+
return "Bash";
|
|
131
|
+
case "list_dir":
|
|
132
|
+
return "ListDir";
|
|
133
|
+
case "grep_search":
|
|
134
|
+
return "Grep";
|
|
135
|
+
case "web_fetch":
|
|
136
|
+
return "WebFetch";
|
|
137
|
+
case "get_system_info":
|
|
138
|
+
return "SystemInfo";
|
|
139
|
+
default:
|
|
140
|
+
return name;
|
|
141
|
+
}
|
|
142
|
+
}
|