Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agent-code-review/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ async function main(): Promise<void> {
console.log('[DRY-RUN] Preview mode active - no files will be written\n');
}

const scanSpinner = ora('🔍 Resolviendo provider...').start();
const scanSpinner = ora('🔍 Resolving provider...').start();

const resolved = await resolveProvider();
const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider);
Expand Down
30 changes: 26 additions & 4 deletions agent-code-review/src/aiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ export interface CreateAIClientOptions {
const PLACEHOLDER_KEY = 'your_gemini_api_key_here';
const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434';

// Convierte el body de un error HTTP en una razon amigable de una sola linea.
// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON,
// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes.
function formatApiError(providerLabel: string, status: number, body: string): string {
try {
const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string };
const reason =
typeof parsed.error === 'string'
? parsed.error
: typeof parsed.error === 'object' && parsed.error?.message
? parsed.error.message
: typeof parsed.message === 'string'
? parsed.message
: undefined;
if (reason) return `${providerLabel} API error ${status}: ${reason}`;
} catch {
// body no es JSON, usar texto plano truncado
}
const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body;
return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`;
}

export class GeminiClient implements AIClient {
private ai: GoogleGenAI;

Expand Down Expand Up @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`OpenAI API error ${res.status}: ${body}`);
throw new Error(formatApiError('OpenAI', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Anthropic API error ${res.status}: ${body}`);
throw new Error(formatApiError('Anthropic', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`DeepSeek API error ${res.status}: ${body}`);
throw new Error(formatApiError('DeepSeek', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Ollama API error ${res.status}: ${body}`);
throw new Error(formatApiError('Ollama', res.status, body));
}

const data = (await res.json()) as {
Expand Down
7 changes: 3 additions & 4 deletions agent-code-review/src/reviewGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function generateReview(
}

if (content.length > maxChars) {
console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`);
console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`);
}

const trimmedContent = content.length > maxChars
Expand Down Expand Up @@ -64,13 +64,12 @@ export async function generateReview(
}

if (attempt < MAX_RETRIES) {
console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`);
console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`);
await sleep(RETRY_DELAY_MS);
continue;
}

console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`);
console.error(` ${error.message}`);
console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`);
return null;
}
}
Expand Down
2 changes: 1 addition & 1 deletion agent-code-review/tests/reviewGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ describe('generateReview', () => {
expect(warnSpy).toHaveBeenCalled();
const warnMsg = warnSpy.mock.calls[0]![0] as string;
expect(warnMsg).toContain('long.ts');
expect(warnMsg).toContain('truncado');
expect(warnMsg).toContain('truncated to');
expect(warnMsg).toContain('20000');
warnSpy.mockRestore();
});
Expand Down
4 changes: 2 additions & 2 deletions agent-doc-generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ async function main(): Promise<void> {
console.log('[DRY-RUN] Preview mode active - no files will be written\n');
}

const scanSpinner = ora('🔍 Resolviendo provider...').start();
const scanSpinner = ora('🔍 Resolving provider...').start();

const resolved = await resolveProvider();
const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider);
Expand Down Expand Up @@ -287,7 +287,7 @@ async function main(): Promise<void> {
const docRelative = path.relative(projectRoot, docPath);
writeSpinner.succeed(`Documentation generated: ${docRelative}`);
} else {
writeSpinner.fail('Error al escribir DOCS.md');
writeSpinner.fail('Error writing DOCS.md');
}
}
}
Expand Down
30 changes: 26 additions & 4 deletions agent-doc-generator/src/aiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ export interface CreateAIClientOptions {
const PLACEHOLDER_KEY = 'your_gemini_api_key_here';
const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434';

// Convierte el body de un error HTTP en una razon amigable de una sola linea.
// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON,
// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes.
function formatApiError(providerLabel: string, status: number, body: string): string {
try {
const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string };
const reason =
typeof parsed.error === 'string'
? parsed.error
: typeof parsed.error === 'object' && parsed.error?.message
? parsed.error.message
: typeof parsed.message === 'string'
? parsed.message
: undefined;
if (reason) return `${providerLabel} API error ${status}: ${reason}`;
} catch {
// body no es JSON, usar texto plano truncado
}
const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body;
return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`;
}

export class GeminiClient implements AIClient {
private ai: GoogleGenAI;

Expand Down Expand Up @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`OpenAI API error ${res.status}: ${body}`);
throw new Error(formatApiError('OpenAI', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Anthropic API error ${res.status}: ${body}`);
throw new Error(formatApiError('Anthropic', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`DeepSeek API error ${res.status}: ${body}`);
throw new Error(formatApiError('DeepSeek', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Ollama API error ${res.status}: ${body}`);
throw new Error(formatApiError('Ollama', res.status, body));
}

const data = (await res.json()) as {
Expand Down
7 changes: 3 additions & 4 deletions agent-doc-generator/src/docGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function generateDocumentation(
}

if (content.length > maxChars) {
console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`);
console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`);
}

const trimmedContent = content.length > maxChars
Expand Down Expand Up @@ -62,13 +62,12 @@ export async function generateDocumentation(
}

if (attempt < MAX_RETRIES) {
console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`);
console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`);
await sleep(RETRY_DELAY_MS);
continue;
}

console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`);
console.error(` ${error.message}`);
console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`);
return null;
}
}
Expand Down
2 changes: 1 addition & 1 deletion agent-doc-generator/tests/docGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ describe('generateDocumentation', () => {
expect(warnSpy).toHaveBeenCalled();
const warnMsg = warnSpy.mock.calls[0]![0] as string;
expect(warnMsg).toContain('long.ts');
expect(warnMsg).toContain('truncado');
expect(warnMsg).toContain('truncated to');
expect(warnMsg).toContain('20000');
warnSpy.mockRestore();
});
Expand Down
30 changes: 26 additions & 4 deletions agent-refactor/src/aiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ export interface CreateAIClientOptions {
const PLACEHOLDER_KEY = 'your_gemini_api_key_here';
const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434';

// Convierte el body de un error HTTP en una razon amigable de una sola linea.
// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON,
// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes.
function formatApiError(providerLabel: string, status: number, body: string): string {
try {
const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string };
const reason =
typeof parsed.error === 'string'
? parsed.error
: typeof parsed.error === 'object' && parsed.error?.message
? parsed.error.message
: typeof parsed.message === 'string'
? parsed.message
: undefined;
if (reason) return `${providerLabel} API error ${status}: ${reason}`;
} catch {
// body no es JSON, usar texto plano truncado
}
const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body;
return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`;
}

export class GeminiClient implements AIClient {
private ai: GoogleGenAI;

Expand Down Expand Up @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`OpenAI API error ${res.status}: ${body}`);
throw new Error(formatApiError('OpenAI', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Anthropic API error ${res.status}: ${body}`);
throw new Error(formatApiError('Anthropic', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`DeepSeek API error ${res.status}: ${body}`);
throw new Error(formatApiError('DeepSeek', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Ollama API error ${res.status}: ${body}`);
throw new Error(formatApiError('Ollama', res.status, body));
}

const data = (await res.json()) as {
Expand Down
7 changes: 3 additions & 4 deletions agent-refactor/src/refactorGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function generateRefactor(
}

if (content.length > maxChars) {
console.warn(`[WARN] ${filePath}: ${content.length} chars truncado a ${maxChars}.`);
console.warn(`[WARN] ${filePath}: ${content.length} chars truncated to ${maxChars}.`);
}

const trimmedContent = content.length > maxChars
Expand Down Expand Up @@ -64,13 +64,12 @@ export async function generateRefactor(
}

if (attempt < MAX_RETRIES) {
console.warn(`[WARN] Attempt ${attempt} failed: ${error.message}. Retrying...`);
console.warn(`[WARN] Attempt ${attempt} failed. Retrying...`);
await sleep(RETRY_DELAY_MS);
continue;
}

console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}:`);
console.error(` ${error.message}`);
console.error(`[ERROR] All ${MAX_RETRIES} attempts failed for ${filePath}: ${error.message}`);
return null;
}
}
Expand Down
2 changes: 1 addition & 1 deletion agent-refactor/tests/refactorGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ describe('generateRefactor', () => {
expect(warnSpy).toHaveBeenCalled();
const warnMsg = warnSpy.mock.calls[0]![0] as string;
expect(warnMsg).toContain('long.ts');
expect(warnMsg).toContain('truncado');
expect(warnMsg).toContain('truncated to');
expect(warnMsg).toContain('20000');
warnSpy.mockRestore();
});
Expand Down
2 changes: 1 addition & 1 deletion agent-security-audit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ async function main(): Promise<void> {
console.log('[DRY-RUN] Preview mode active - no files will be written\n');
}

const scanSpinner = ora('🔍 Resolviendo provider...').start();
const scanSpinner = ora('🔍 Resolving provider...').start();

const resolved = await resolveProvider();
const apiKey = options.apiKey || getApiKeyForProvider(resolved.provider);
Expand Down
30 changes: 26 additions & 4 deletions agent-security-audit/src/aiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ export interface CreateAIClientOptions {
const PLACEHOLDER_KEY = 'your_gemini_api_key_here';
const DEFAULT_LOCAL_BASE_URL = 'http://localhost:11434';

// Convierte el body de un error HTTP en una razon amigable de una sola linea.
// Ollama y otros providers devuelven JSON tipo {"error": "..."}; si no es JSON,
// se trunca el texto plano a 200 caracteres para no volcar respuestas gigantes.
function formatApiError(providerLabel: string, status: number, body: string): string {
try {
const parsed = JSON.parse(body) as { error?: string | { message?: string }; message?: string };
const reason =
typeof parsed.error === 'string'
? parsed.error
: typeof parsed.error === 'object' && parsed.error?.message
? parsed.error.message
: typeof parsed.message === 'string'
? parsed.message
: undefined;
if (reason) return `${providerLabel} API error ${status}: ${reason}`;
} catch {
// body no es JSON, usar texto plano truncado
}
const truncated = body.length > 200 ? `${body.slice(0, 200)}...` : body;
return `${providerLabel} API error ${status}: ${truncated || 'unknown error'}`;
}

export class GeminiClient implements AIClient {
private ai: GoogleGenAI;

Expand Down Expand Up @@ -51,7 +73,7 @@ export class OpenAIClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`OpenAI API error ${res.status}: ${body}`);
throw new Error(formatApiError('OpenAI', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -85,7 +107,7 @@ export class AnthropicClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Anthropic API error ${res.status}: ${body}`);
throw new Error(formatApiError('Anthropic', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -118,7 +140,7 @@ export class DeepSeekClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`DeepSeek API error ${res.status}: ${body}`);
throw new Error(formatApiError('DeepSeek', res.status, body));
}

const data = (await res.json()) as {
Expand Down Expand Up @@ -154,7 +176,7 @@ export class OllamaClient implements AIClient {

if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`Ollama API error ${res.status}: ${body}`);
throw new Error(formatApiError('Ollama', res.status, body));
}

const data = (await res.json()) as {
Expand Down
Loading
Loading