UNPKG

taskmanager-ai

Version:

A CLI tool to manage your projects tasks with AI

250 lines (213 loc) 8.7 kB
/** * Módulo para integração com Perplexity AI * * Este módulo implementa a análise avançada de tarefas usando a API da Perplexity, * permitindo uma subdivisão mais inteligente e precisa das tarefas em subtarefas. */ import fetch from 'node-fetch'; import chalk from 'chalk'; import fs from 'fs/promises'; import path from 'path'; import dotenv from 'dotenv'; import { getTaskManagerDir } from '../config.js'; // Carrega a chave da API do arquivo .env async function loadPerplexityApiKey() { try { const taskManagerDir = getTaskManagerDir(); const envPath = path.join(taskManagerDir, '.env'); // Verifica se o arquivo .env existe try { await fs.access(envPath); } catch (error) { throw new Error('Arquivo .env não encontrado. Configure PERPLEXITY_API_KEY no arquivo .env'); } // Carrega as variáveis de ambiente const envConfig = dotenv.parse(await fs.readFile(envPath, 'utf-8')); // Verifica se a chave da API está definida if (!envConfig.PERPLEXITY_API_KEY) { throw new Error('PERPLEXITY_API_KEY não encontrada no arquivo .env'); } return envConfig.PERPLEXITY_API_KEY; } catch (error) { console.error(chalk.red(`Erro ao carregar chave da API Perplexity: ${error.message}`)); throw error; } } /** * Chama a API da Perplexity para análise avançada da tarefa * @param {Object} taskContext - Contexto da tarefa a ser analisada * @param {Number} numSubtasks - Número de subtarefas a serem geradas * @returns {Object} Resultado da análise (análise, subtarefas e melhorias) */ export async function callPerplexityAPI(taskContext, numSubtasks) { try { // Carrega a chave da API const apiKey = await loadPerplexityApiKey(); // Cria o prompt de análise da tarefa const prompt = createTaskAnalysisPrompt(taskContext, numSubtasks); // Chama a API da Perplexity const response = await fetch('https://api.perplexity.ai/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` }, body: JSON.stringify({ model: 'sonar-reasoning-pro', messages: [ { role: 'system', content: 'Você é um especialista em gerenciamento de projetos e análise de tarefas. Sua função é analisar detalhadamente uma tarefa e dividi-la em subtarefas lógicas e bem estruturadas.' }, { role: 'user', content: prompt } ], temperature: 0.1, // Baixa temperatura para respostas mais determinísticas max_tokens: 2048 }) }); // Verifica se a resposta foi bem-sucedida if (!response.ok) { const errorData = await response.json(); throw new Error(`Erro na API Perplexity: ${response.status} - ${JSON.stringify(errorData)}`); } // Processa a resposta const responseData = await response.json(); return parsePerplexityResponse(responseData); } catch (error) { console.error(chalk.red(`Erro ao chamar API Perplexity: ${error.message}`)); throw error; } } /** * Cria o prompt para análise da tarefa * @param {Object} taskContext - Contexto da tarefa a ser analisada * @param {Number} numSubtasks - Número de subtarefas a serem geradas * @returns {String} Prompt formatado */ function createTaskAnalysisPrompt(taskContext, numSubtasks) { // Extrai as informações da tarefa const { title, description, details, category, testStrategy, existingSubtasks } = taskContext; // Formato das subtarefas existentes para o prompt let existingSubtasksText = ''; if (existingSubtasks && existingSubtasks.length > 0) { existingSubtasksText = '\nSubtarefas existentes:\n' + existingSubtasks.map(st => `- ${st.title}: ${st.description || 'Sem descrição'}` ).join('\n'); } // Constrói o prompt return ` # Análise Técnica e Decomposição de Tarefa ## Detalhes da Tarefa - Título: ${title} - Descrição: ${description || 'Não fornecida'} - Categoria: ${category || 'Não especificada'} - Detalhes de Implementação: ${details || 'Não fornecidos'} - Estratégia de Teste: ${testStrategy || 'Não fornecida'} ${existingSubtasksText} ## Diretrizes para Análise Analise a tarefa considerando os seguintes aspectos técnicos: 1. Ambiente Docker e Infraestrutura: - Configuração completa de serviços (databases, cache, filas, etc.) - Uso de LocalStack para emulação de serviços cloud - Scripts de automação para setup com um comando 2. Especificações Técnicas: - Bibliotecas e frameworks específicos com versões - Padrões de implementação e design patterns - Estrutura de diretórios e convenções de nomenclatura - Interfaces e contratos de comunicação 3. Clean Architecture: - Separação clara de camadas (adapters, application, domain, infrastructure) - Definição de interfaces entre camadas - Injeção de dependências e inversão de controle - Domain-driven design patterns 4. Automação e DevOps: - Scripts de setup de ambiente - Seeds e migrations - Hooks de git e formatação - CI/CD e validações automatizadas 5. Testes e Qualidade: - Configuração de testes por camada - Mocks e fixtures padronizados - Cobertura de código - Análise estática e linting 6. Documentação: - Geração automática de docs - Swagger/OpenAPI - Diagramas e fluxos - Exemplos de uso ## Instruções para Subtarefas Decomponha a tarefa em exatamente ${numSubtasks} subtarefas técnicas, onde cada subtarefa deve: 1. Ter um título técnico específico que indica exatamente o que será feito 2. Incluir uma descrição detalhada com: - Tecnologias e versões específicas - Estrutura de arquivos e diretórios - Padrões e convenções a seguir - Dependências e pré-requisitos - Critérios de aceitação técnicos - Estratégia de teste específica - Pontos de integração com outras subtarefas 3. Seguir uma sequência lógica de implementação que priorize: - Setup inicial de ambiente e ferramentas - Modelagem de domínio e regras de negócio - Implementação de camadas internas para externas - Integração de serviços e infraestrutura - Automação e documentação ## Formato da Resposta Retorne a resposta em formato JSON com a seguinte estrutura: { "analysis": "Análise técnica detalhada da tarefa (texto em markdown)", "subtasks": [ { "title": "Título técnico específico", "description": "Descrição técnica detalhada incluindo todos os pontos solicitados", "technicalSpecs": { "technologies": ["lista de tecnologias com versões"], "patterns": ["padrões a serem seguidos"], "directory": "estrutura de diretórios", "testStrategy": "estratégia específica de testes", "acceptanceCriteria": ["critérios técnicos de aceitação"] } } ], "taskImprovements": { "description": "Sugestão de melhoria técnica para a descrição", "details": "Sugestão de especificações técnicas adicionais", "testStrategy": "Sugestão de estratégia de teste mais abrangente" } } IMPORTANTE: A resposta DEVE conter APENAS o JSON solicitado, sem texto adicional antes ou depois. `; } /** * Processa a resposta da API da Perplexity * @param {Object} responseData - Dados da resposta da API * @returns {Object} Dados processados (análise, subtarefas e melhorias) */ function parsePerplexityResponse(responseData) { try { // Extrai o conteúdo da resposta const content = responseData.choices[0].message.content; // Encontra o JSON na resposta const jsonMatch = content.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error('Formato de resposta inválido: JSON não encontrado'); } // Parse do JSON const result = JSON.parse(jsonMatch[0]); // Verifica se as propriedades esperadas estão presentes if (!result.analysis || !result.subtasks || !Array.isArray(result.subtasks)) { throw new Error('Formato de resposta inválido: propriedades obrigatórias ausentes'); } // Verifica se o número correto de subtarefas foi gerado if (result.subtasks.length === 0) { throw new Error('Nenhuma subtarefa foi gerada'); } // Verifica se cada subtarefa tem título e descrição for (const subtask of result.subtasks) { if (!subtask.title || !subtask.description) { throw new Error('Formato de subtarefa inválido: título ou descrição ausente'); } } return result; } catch (error) { console.error(chalk.red(`Erro ao processar resposta da API: ${error.message}`)); throw error; } }