brito-cli 0.6.3 → 0.6.4
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/agent/claude-cli.js +3 -3
- package/dist/agent/context.js +2 -2
- package/dist/agent/loop.js +2 -2
- package/dist/repl.js +7 -6
- package/dist/ui/dock.js +2 -2
- package/dist/util/images.js +10 -5
- package/package.json +1 -1
package/dist/agent/claude-cli.js
CHANGED
|
@@ -144,11 +144,11 @@ export class ClaudeSession {
|
|
|
144
144
|
ui.turnStart();
|
|
145
145
|
const { ok: imgs, tooBig } = splitOversize(findImages(text, this.o.root));
|
|
146
146
|
for (const i of tooBig)
|
|
147
|
-
ui.warn(`${i.name} passa
|
|
147
|
+
ui.warn(`${i.name} passa do limite da API e não foi anexado`);
|
|
148
148
|
if (imgs.length)
|
|
149
|
-
ui.info(
|
|
149
|
+
ui.info(`📎 ${imgs.map((i) => `${i.name} (${fmtBytes(i.bytes)})`).join(", ")} anexado${imgs.length > 1 ? "s" : ""}`);
|
|
150
150
|
// O claude vê a imagem pela tool Read: o prompt diz quais arquivos são imagens anexadas pelo usuário.
|
|
151
|
-
const prompt = imgs.length ? `${text}\n\n[O usuário anexou ${imgs.length > 1 ? "
|
|
151
|
+
const prompt = imgs.length ? `${text}\n\n[O usuário anexou ${imgs.length > 1 ? "estes arquivos" : "este arquivo"}; abra com a tool Read para vê-${imgs.length > 1 ? "las" : "la"}:\n${imgs.map((i) => `- ${i.path}`).join("\n")}]` : text;
|
|
152
152
|
const cmd = claudeCommand(this.args(prompt, [...new Set(imgs.map((i) => path.dirname(i.path)))]));
|
|
153
153
|
const child = (this.child = spawn(cmd.command, cmd.args, {
|
|
154
154
|
cwd: this.o.root, stdio: ["ignore", "pipe", "pipe"],
|
package/dist/agent/context.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Imagem em base64 tem megabytes, mas custa ~1,6 mil tokens: troca o conteúdo por um marcador antes de medir ou resumir. */
|
|
2
|
-
const stubImages = (_k, v) => (v && typeof v === "object" && v.type === "image" ? { type:
|
|
2
|
+
const stubImages = (_k, v) => (v && typeof v === "object" && (v.type === "image" || v.type === "document") && v.source ? { type: v.type, note: "x".repeat(6400) } : v);
|
|
3
3
|
export const estimateTokens = (msgs) => Math.ceil(JSON.stringify(msgs, stubImages).length / 4);
|
|
4
4
|
const MAX_OLD_RESULT = 1500;
|
|
5
5
|
/** Fase 1 (barata): encurta tool_results antigos, preservando as últimas `keep` mensagens. */
|
|
@@ -35,7 +35,7 @@ export async function summarizeMiddle(client, model, msgs, keepTail = 6) {
|
|
|
35
35
|
model,
|
|
36
36
|
max_tokens: 2000,
|
|
37
37
|
system: "Resuma a conversa de um agente de programação. Preserve: objetivo, decisões, arquivos/símbolos tocados, erros encontrados, estado atual e próximos passos. Seja denso.",
|
|
38
|
-
messages: [{ role: "user", content: JSON.stringify(middle, (_k, v) => (v && typeof v === "object" && v.type === "image" ? { type:
|
|
38
|
+
messages: [{ role: "user", content: JSON.stringify(middle, (_k, v) => (v && typeof v === "object" && (v.type === "image" || v.type === "document") && v.source ? { type: v.type, note: "[anexo omitido]" } : v)).slice(0, 150_000) }],
|
|
39
39
|
});
|
|
40
40
|
const summary = res.content.map((b) => (b.type === "text" ? b.text : "")).join("");
|
|
41
41
|
return [...head, { role: "user", content: `[Resumo da conversa anterior]\n${summary}` }, { role: "assistant", content: "Entendido, continuando a partir do resumo." }, ...tail];
|
package/dist/agent/loop.js
CHANGED
|
@@ -63,9 +63,9 @@ export class Agent {
|
|
|
63
63
|
// (usado na reflexão) guarda só o texto, para não carregar base64.
|
|
64
64
|
const { ok, tooBig } = splitOversize(findImages(userText, this.ctx.root));
|
|
65
65
|
for (const i of tooBig)
|
|
66
|
-
this.log(`⚠ ${i.name} passa
|
|
66
|
+
this.log(`⚠ ${i.name} passa do limite da API e não foi anexado`);
|
|
67
67
|
if (ok.length) {
|
|
68
|
-
this.log(
|
|
68
|
+
this.log(`📎 ${ok.map((i) => `${i.name} (${fmtBytes(i.bytes)})`).join(", ")} anexado${ok.length > 1 ? "s" : ""}`);
|
|
69
69
|
this.messages.push({ role: "user", content: [...ok.map(imageBlock), { type: "text", text: userText }] });
|
|
70
70
|
turn.messages.push({ role: "user", content: userText });
|
|
71
71
|
}
|
package/dist/repl.js
CHANGED
|
@@ -188,15 +188,16 @@ export async function runRepl(b, o) {
|
|
|
188
188
|
await shutdown();
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (!run) {
|
|
194
|
-
ui.warn(`comando desconhecido: ${cmd}. Digite /help.`);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
191
|
+
const run = line.startsWith("/") ? common[key] ?? b.commands[key]?.run : undefined;
|
|
192
|
+
if (run) {
|
|
197
193
|
await run(args);
|
|
198
194
|
return;
|
|
199
195
|
}
|
|
196
|
+
// "/Users/x/arquivo.pdf" é caminho, não comando: segue como mensagem ao agente
|
|
197
|
+
if (line.startsWith("/") && !cmd.slice(1).includes("/")) {
|
|
198
|
+
ui.warn(`comando desconhecido: ${cmd}. Digite /help.`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
200
201
|
const ac = (running = new AbortController());
|
|
201
202
|
dock?.setBusy(true);
|
|
202
203
|
try {
|
package/dist/ui/dock.js
CHANGED
|
@@ -8,7 +8,7 @@ const graphemes = (s) => Array.from(seg.segment(s), (x) => x.segment);
|
|
|
8
8
|
const ANSI = /\x1b\[[0-9;]*[A-Za-z]/y;
|
|
9
9
|
const MAX_INPUT_ROWS = 8;
|
|
10
10
|
const MAX_MENU = 7;
|
|
11
|
-
const PASTE_RE = /\[(?:colado|imagem) #(\d+): [^\]]*\]/g;
|
|
11
|
+
const PASTE_RE = /\[(?:colado|imagem|anexo) #(\d+): [^\]]*\]/g;
|
|
12
12
|
export class Dock {
|
|
13
13
|
opts;
|
|
14
14
|
onSubmit = () => { };
|
|
@@ -415,7 +415,7 @@ export class Dock {
|
|
|
415
415
|
if (imgs.length) {
|
|
416
416
|
const n = ++this.pasteN;
|
|
417
417
|
this.pastes.set(n, imgs.map((i) => (/\s/.test(i.path) ? `"${i.path}"` : i.path)).join(" "));
|
|
418
|
-
this.insert(`[
|
|
418
|
+
this.insert(`[anexo #${n}: ${imgs.length === 1 ? imgs[0].name : `${imgs.length} arquivos`}] `);
|
|
419
419
|
}
|
|
420
420
|
else if (lines >= 2 || text.length > 300) {
|
|
421
421
|
const n = ++this.pasteN;
|
package/dist/util/images.js
CHANGED
|
@@ -2,10 +2,13 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
const TYPES = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp" };
|
|
6
|
-
const IMG_EXT = /\.(png|jpe?g|gif|webp)$/i;
|
|
7
|
-
/** Limite da API por imagem (5 MB) e teto de anexos por mensagem. */
|
|
5
|
+
const TYPES = { png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", pdf: "application/pdf" };
|
|
6
|
+
const IMG_EXT = /\.(png|jpe?g|gif|webp|pdf)$/i;
|
|
7
|
+
/** Limite da API por imagem (5 MB), por PDF (32 MB) e teto de anexos por mensagem. */
|
|
8
8
|
export const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
9
|
+
export const MAX_PDF_BYTES = 32 * 1024 * 1024;
|
|
10
|
+
export const limitOf = (i) => (i.mediaType === "application/pdf" ? MAX_PDF_BYTES : MAX_IMAGE_BYTES);
|
|
11
|
+
export const isPdf = (i) => i.mediaType === "application/pdf";
|
|
9
12
|
export const MAX_IMAGES = 5;
|
|
10
13
|
/** Divide o texto em tokens, respeitando aspas e (fora do Windows) `\ ` como espaço do nome do arquivo. */
|
|
11
14
|
function tokens(text) {
|
|
@@ -67,9 +70,11 @@ export function droppedImages(text, root = process.cwd()) {
|
|
|
67
70
|
export const fmtBytes = (n) => (n >= 1_048_576 ? `${(n / 1_048_576).toFixed(1)} MB` : `${Math.max(1, Math.round(n / 1024))} kB`);
|
|
68
71
|
/** Separa o que dá para enviar do que passa do limite da API. */
|
|
69
72
|
export function splitOversize(imgs) {
|
|
70
|
-
return { ok: imgs.filter((i) => i.bytes <=
|
|
73
|
+
return { ok: imgs.filter((i) => i.bytes <= limitOf(i)), tooBig: imgs.filter((i) => i.bytes > limitOf(i)) };
|
|
71
74
|
}
|
|
72
|
-
/** Bloco `image` da API da Anthropic, com o arquivo em base64. */
|
|
75
|
+
/** Bloco `image` (ou `document`, para PDF) da API da Anthropic, com o arquivo em base64. */
|
|
73
76
|
export function imageBlock(img) {
|
|
77
|
+
if (isPdf(img))
|
|
78
|
+
return { type: "document", source: { type: "base64", media_type: "application/pdf", data: fs.readFileSync(img.path).toString("base64") } };
|
|
74
79
|
return { type: "image", source: { type: "base64", media_type: img.mediaType, data: fs.readFileSync(img.path).toString("base64") } };
|
|
75
80
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brito-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Coding-agent CLI with a code knowledge graph, memory and reviewable skills / CLI de agente de código com grafo de conhecimento, memória e skills revisáveis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|