@gilbert_oliveira/commit-wizard 2.12.1 → 2.13.0-canary.1

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.
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- var qe=Object.defineProperty;var A=(e,t)=>()=>(e&&(t=e(e=0)),t);var j=(e,t)=>{for(var o in t)qe(e,o,{get:t[o],enumerable:!0})};import Te from"path";import{fileURLToPath as Ne}from"url";var Le,Ue,C,f=A(()=>{"use strict";Le=()=>Ne(import.meta.url),Ue=()=>Te.dirname(Le()),C=Ue()});import{existsSync as ae,readFileSync as ce}from"fs";import{join as me}from"path";function ue(e){let t;try{t=me(process.cwd(),".commit-wizardrc")}catch{t="/tmp/.commit-wizardrc"}let o=me(process.env.HOME||process.env.USERPROFILE||"/tmp",".commit-wizardrc"),i={..._e};try{if(ae(o)){let s=ce(o,"utf-8"),a=JSON.parse(s);i=le(i,a)}}catch{console.warn("\u26A0\uFE0F Erro ao ler configura\xE7\xE3o global: Erro desconhecido")}let n=e||t;try{if(ae(n)){let s=ce(n,"utf-8"),a=JSON.parse(s);i=le(i,a)}}catch{console.warn("\u26A0\uFE0F Erro ao ler .commit-wizardrc: Erro desconhecido")}return i.openai.apiKey=process.env.OPENAI_API_KEY,process.env.COMMIT_WIZARD_DEBUG,process.env.COMMIT_WIZARD_DRY_RUN==="true"&&(i.dryRun=!0),i}function le(e,t){return{...e,...t,openai:{...e.openai,...t.openai},smartSplit:{...e.smartSplit,...t.smartSplit},cache:{...e.cache,...t.cache}}}function fe(e){let t=[];return e.openai.apiKey||t.push("OPENAI_API_KEY n\xE3o encontrada nas vari\xE1veis de ambiente"),(e.openai.maxTokens<10||e.openai.maxTokens>4e3)&&t.push("maxTokens deve estar entre 10 e 4000"),(e.openai.temperature<0||e.openai.temperature>2)&&t.push("temperature deve estar entre 0 e 2"),["pt","en","es","fr","de","it","ja","ko","zh"].includes(e.language)||t.push("language deve ser um idioma suportado (pt, en, es, fr, de, it, ja, ko, zh)"),["conventional","simple","detailed"].includes(e.commitStyle)||t.push("commitStyle deve ser conventional, simple ou detailed"),e.smartSplit.minGroupSize<1&&t.push("smartSplit.minGroupSize deve ser pelo menos 1"),(e.smartSplit.maxGroups<1||e.smartSplit.maxGroups>10)&&t.push("smartSplit.maxGroups deve estar entre 1 e 10"),(e.smartSplit.confidenceThreshold<0||e.smartSplit.confidenceThreshold>1)&&t.push("smartSplit.confidenceThreshold deve estar entre 0 e 1"),e.cache.ttl<1&&t.push("cache.ttl deve ser pelo menos 1 minuto"),e.cache.maxSize<1&&t.push("cache.maxSize deve ser pelo menos 1"),t}var _e,pe=A(()=>{"use strict";f();_e={openai:{model:"gpt-4o",maxTokens:150,temperature:.7,timeout:3e4,retries:2},language:"pt",commitStyle:"conventional",autoCommit:!1,splitCommits:!1,dryRun:!1,smartSplit:{enabled:!0,minGroupSize:1,maxGroups:5,confidenceThreshold:.7},cache:{enabled:!0,ttl:60,maxSize:100}}});var M={};j(M,{escapeShellArg:()=>D,executeCommit:()=>z,executeFileCommit:()=>k,getDiffStats:()=>K,getFileDiff:()=>Ve,getGitStatus:()=>J,isGitRepository:()=>H});import{execSync as E}from"child_process";function D(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function H(){try{return E("git rev-parse --git-dir",{stdio:"ignore"}),!0}catch{return!1}}function J(){try{let t=E("git diff --cached --name-only",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
3
- `).filter(i=>i.length>0),o=t.length>0?E("git diff --cached",{encoding:"utf-8",stdio:"pipe"}):"";return{hasStaged:t.length>0,stagedFiles:t,diff:o.trim()}}catch(e){throw new Error(`Erro ao obter status do Git: ${e instanceof Error?e.message:"Erro desconhecido"}`)}}function Ve(e){try{return E(`git diff --cached -- "${e}"`,{encoding:"utf-8",stdio:"pipe"})}catch(t){throw new Error(`Erro ao obter diff do arquivo ${e}: ${t instanceof Error?t.message:"Erro desconhecido"}`)}}function z(e){try{let t=D(e);return E(`git commit -m ${t}`,{stdio:"pipe"}),{success:!0,hash:E("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:e}}catch(t){return{success:!1,error:t instanceof Error?t.message:"Erro desconhecido ao executar commit"}}}function k(e,t){try{let o=D(t),i=D(e);return E(`git commit ${i} -m ${o}`,{stdio:"pipe"}),{success:!0,hash:E("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:t}}catch(o){return{success:!1,error:o instanceof Error?o.message:"Erro desconhecido ao executar commit do arquivo"}}}function K(){try{let t=E("git diff --cached --numstat",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
4
- `).filter(n=>n.length>0),o=0,i=0;return t.forEach(n=>{let[s,a]=n.split(" ");s&&s!=="-"&&(o+=parseInt(s)||0),a&&a!=="-"&&(i+=parseInt(a)||0)}),{added:o,removed:i,files:t.length}}catch{return{added:0,removed:0,files:0}}}var w=A(()=>{"use strict";f()});var Se={};j(Se,{buildPrompt:()=>ge,detectCommitType:()=>ye,extractCommitTypeFromMessage:()=>he,generateCommitMessage:()=>Ce,generateWithRetry:()=>q,processOpenAIMessage:()=>xe,smartFilterDiff:()=>de});function de(e,t){if(e.length<=t)return e;let o=[/^diff --git a\/package-lock\.json/m,/^diff --git a\/yarn\.lock/m,/^diff --git a\/pnpm-lock\.yaml/m,/^diff --git a\/composer\.lock/m,/^diff --git a\/Gemfile\.lock/m,/^diff --git a\/Cargo\.lock/m,/^diff --git a\/poetry\.lock/m,/^diff --git a\/go\.sum/m,/^diff --git a\/requirements(?:\.txt)?\.lock/m,/^diff --git a\/.*\.min\.js/m,/^diff --git a\/.*\.min\.css/m,/^diff --git a\/.*\.map/m,/^diff --git a\/dist\//m,/^diff --git a\/build\//m],i=e.split(/(?=^diff --git)/m).filter(m=>m.trim()),n=[],s=[];i.forEach(m=>{o.some(h=>h.test(m))?s.push(m):n.push(m)});let a="",c=t;for(let m of n)if(m.length<=c)a+=m,c-=m.length;else if(c>200){a+=m.substring(0,c-50),c=t-a.length;break}else break;if(!a&&s.length>0&&c>0)for(let m of s)if(m.length<=c)a+=m,c-=m.length;else if(c>200){a+=m.substring(0,c-50),c=t-a.length;break}else break;let r=`
5
- ... (diff otimizado para focar em mudan\xE7as principais)`;if(c>200&&s.length>0){let m=`
2
+ var Je=Object.defineProperty;var w=(e,t)=>()=>(e&&(t=e(e=0)),t);var G=(e,t)=>{for(var i in t)Je(e,i,{get:t[i],enumerable:!0})};import Ke from"path";import{fileURLToPath as Be}from"url";var Ye,Qe,y,f=w(()=>{"use strict";Ye=()=>Be(import.meta.url),Qe=()=>Ke.dirname(Ye()),y=Qe()});import{existsSync as de,readFileSync as pe}from"fs";import{join as ge}from"path";function ye(e){let t;try{t=ge(process.cwd(),".commit-wizardrc")}catch{t="/tmp/.commit-wizardrc"}let i=ge(process.env.HOME||process.env.USERPROFILE||"/tmp",".commit-wizardrc"),o={...Ze};try{if(de(i)){let r=pe(i,"utf-8"),a=JSON.parse(r);o=he(o,a)}}catch{console.warn("\u26A0\uFE0F Erro ao ler configura\xE7\xE3o global: Erro desconhecido")}let n=e||t;try{if(de(n)){let r=pe(n,"utf-8"),a=JSON.parse(r);o=he(o,a)}}catch{console.warn("\u26A0\uFE0F Erro ao ler .commit-wizardrc: Erro desconhecido")}return o.openai.apiKey=process.env.OPENAI_API_KEY,process.env.COMMIT_WIZARD_DEBUG,process.env.COMMIT_WIZARD_DRY_RUN==="true"&&(o.dryRun=!0),o}function he(e,t){return{...e,...t,openai:{...e.openai,...t.openai},smartSplit:{...e.smartSplit,...t.smartSplit},cache:{...e.cache,...t.cache},commitlint:{...e.commitlint,...t.commitlint}}}function Ce(e){let t=[];return e.openai.apiKey||t.push("OPENAI_API_KEY n\xE3o encontrada nas vari\xE1veis de ambiente"),(e.openai.maxTokens<10||e.openai.maxTokens>4e3)&&t.push("maxTokens deve estar entre 10 e 4000"),(e.openai.temperature<0||e.openai.temperature>2)&&t.push("temperature deve estar entre 0 e 2"),["pt","en","es","fr","de","it","ja","ko","zh"].includes(e.language)||t.push("language deve ser um idioma suportado (pt, en, es, fr, de, it, ja, ko, zh)"),["conventional","simple","detailed"].includes(e.commitStyle)||t.push("commitStyle deve ser conventional, simple ou detailed"),e.smartSplit.minGroupSize<1&&t.push("smartSplit.minGroupSize deve ser pelo menos 1"),(e.smartSplit.maxGroups<1||e.smartSplit.maxGroups>10)&&t.push("smartSplit.maxGroups deve estar entre 1 e 10"),(e.smartSplit.confidenceThreshold<0||e.smartSplit.confidenceThreshold>1)&&t.push("smartSplit.confidenceThreshold deve estar entre 0 e 1"),e.cache.ttl<1&&t.push("cache.ttl deve ser pelo menos 1 minuto"),e.cache.maxSize<1&&t.push("cache.maxSize deve ser pelo menos 1"),t}var Ze,ve=w(()=>{"use strict";f();Ze={openai:{model:"gpt-4o",maxTokens:150,temperature:.7,timeout:3e4,retries:2},language:"pt",commitStyle:"conventional",autoCommit:!1,splitCommits:!1,dryRun:!1,smartSplit:{enabled:!0,minGroupSize:1,maxGroups:5,confidenceThreshold:.7},cache:{enabled:!0,ttl:60,maxSize:100},commitlint:{enabled:!0}}});var M={};G(M,{escapeShellArg:()=>D,executeCommit:()=>z,executeFileCommit:()=>O,getDiffStats:()=>B,getFileDiff:()=>Xe,getGitStatus:()=>K,isGitRepository:()=>J});import{execSync as E}from"child_process";function D(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function J(){try{return E("git rev-parse --git-dir",{stdio:"ignore"}),!0}catch{return!1}}function K(){try{let t=E("git diff --cached --name-only",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
3
+ `).filter(o=>o.length>0),i=t.length>0?E("git diff --cached",{encoding:"utf-8",stdio:"pipe"}):"";return{hasStaged:t.length>0,stagedFiles:t,diff:i.trim()}}catch(e){throw new Error(`Erro ao obter status do Git: ${e instanceof Error?e.message:"Erro desconhecido"}`)}}function Xe(e){try{return E(`git diff --cached -- "${e}"`,{encoding:"utf-8",stdio:"pipe"})}catch(t){throw new Error(`Erro ao obter diff do arquivo ${e}: ${t instanceof Error?t.message:"Erro desconhecido"}`)}}function z(e){try{let t=D(e);return E(`git commit -m ${t}`,{stdio:"pipe"}),{success:!0,hash:E("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:e}}catch(t){return{success:!1,error:t instanceof Error?t.message:"Erro desconhecido ao executar commit"}}}function O(e,t){try{let i=D(t),o=D(e);return E(`git commit ${o} -m ${i}`,{stdio:"pipe"}),{success:!0,hash:E("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:t}}catch(i){return{success:!1,error:i instanceof Error?i.message:"Erro desconhecido ao executar commit do arquivo"}}}function B(){try{let t=E("git diff --cached --numstat",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
4
+ `).filter(n=>n.length>0),i=0,o=0;return t.forEach(n=>{let[r,a]=n.split(" ");r&&r!=="-"&&(i+=parseInt(r)||0),a&&a!=="-"&&(o+=parseInt(a)||0)}),{added:i,removed:o,files:t.length}}catch{return{added:0,removed:0,files:0}}}var $=w(()=>{"use strict";f()});var Ee={};G(Ee,{buildPrompt:()=>be,detectCommitType:()=>we,extractCommitTypeFromMessage:()=>Se,generateCommitMessage:()=>Ae,generateWithRetry:()=>T,processOpenAIMessage:()=>$e,smartFilterDiff:()=>xe});function xe(e,t){if(e.length<=t)return e;let i=[/^diff --git a\/package-lock\.json/m,/^diff --git a\/yarn\.lock/m,/^diff --git a\/pnpm-lock\.yaml/m,/^diff --git a\/composer\.lock/m,/^diff --git a\/Gemfile\.lock/m,/^diff --git a\/Cargo\.lock/m,/^diff --git a\/poetry\.lock/m,/^diff --git a\/go\.sum/m,/^diff --git a\/requirements(?:\.txt)?\.lock/m,/^diff --git a\/.*\.min\.js/m,/^diff --git a\/.*\.min\.css/m,/^diff --git a\/.*\.map/m,/^diff --git a\/dist\//m,/^diff --git a\/build\//m],o=e.split(/(?=^diff --git)/m).filter(m=>m.trim()),n=[],r=[];o.forEach(m=>{i.some(h=>h.test(m))?r.push(m):n.push(m)});let a="",c=t;for(let m of n)if(m.length<=c)a+=m,c-=m.length;else if(c>200){a+=m.substring(0,c-50),c=t-a.length;break}else break;if(!a&&r.length>0&&c>0)for(let m of r)if(m.length<=c)a+=m,c-=m.length;else if(c>200){a+=m.substring(0,c-50),c=t-a.length;break}else break;let s=`
5
+ ... (diff otimizado para focar em mudan\xE7as principais)`;if(c>200&&r.length>0){let m=`
6
6
 
7
- ... (${s.length} arquivo(s) de depend\xEAncias/build omitido(s): ${s.map(h=>{let b=h.match(/^diff --git a\/(.*?) b\//);return b?b[1]:""}).filter(Boolean).join(", ")})`,p=c-r.length;m.length<p&&(a+=m)}return a.trim()!==e.trim()?a+r:a}function ge(e,t,o){let i=t.language==="pt"?"portugu\xEAs":"english",n=He(t.commitStyle,t.language),a=de(e,6e3),c=o.length>10?`${o.length} arquivos: ${o.slice(0,5).join(", ")}...`:o.join(", ");return`Gere mensagem de commit em ${i} (${t.commitStyle}).
7
+ ... (${r.length} arquivo(s) de depend\xEAncias/build omitido(s): ${r.map(h=>{let b=h.match(/^diff --git a\/(.*?) b\//);return b?b[1]:""}).filter(Boolean).join(", ")})`,d=c-s.length;m.length<d&&(a+=m)}return a.trim()!==e.trim()?a+s:a}function be(e,t,i){let o=t.language==="pt"?"portugu\xEAs":"english",n=et(t.commitStyle,t.language),a=xe(e,6e3),c=i.length>10?`${i.length} arquivos: ${i.slice(0,5).join(", ")}...`:i.join(", ");return`Gere mensagem de commit em ${o} (${t.commitStyle}).
8
8
 
9
9
  Arquivos: ${c}
10
10
 
@@ -15,7 +15,7 @@ Diff:
15
15
  ${a}
16
16
  \`\`\`
17
17
 
18
- Mensagem:`}function He(e,t){let o={pt:{conventional:`- Use formato: tipo(escopo): descri\xE7\xE3o
18
+ Mensagem:`}function et(e,t){let i={pt:{conventional:`- Use formato: tipo(escopo): descri\xE7\xE3o
19
19
  - Tipos v\xE1lidos: feat, fix, docs, style, refactor, test, chore, build, ci
20
20
  - Exemplo: "feat(auth): adicionar valida\xE7\xE3o de email"
21
21
  - Mantenha a primeira linha com at\xE9 50 caracteres`,simple:`- Use formato simples e direto
@@ -33,27 +33,25 @@ Mensagem:`}function He(e,t){let o={pt:{conventional:`- Use formato: tipo(escopo)
33
33
  - Maximum 50 characters`,detailed:`- First line: summary under 50 characters
34
34
  - Add explanatory body if needed
35
35
  - Use imperative mood
36
- - Be descriptive but concise`}},i=t==="pt"?"pt":"en";return o[i][e]||o[i].conventional}function he(e){let t={feat:/^(feat|feature)(\([^)]+\))?:/i,fix:/^(fix|bugfix)(\([^)]+\))?:/i,docs:/^(docs|documentation)(\([^)]+\))?:/i,style:/^(style|format)(\([^)]+\))?:/i,refactor:/^(refactor|refactoring)(\([^)]+\))?:/i,test:/^(test|testing)(\([^)]+\))?:/i,chore:/^(chore|maintenance)(\([^)]+\))?:/i,build:/^(build|ci)(\([^)]+\))?:/i,ci:/^(ci|continuous-integration)(\([^)]+\))?:/i};for(let[o,i]of Object.entries(t))if(i.test(e))return o;return null}function ye(e,t){let o=e.toLowerCase(),i=t.join(" ").toLowerCase();return i.includes("test")||i.includes("spec")||o.includes("test(")?"test":i.includes("readme")||i.includes(".md")||i.includes("docs")?"docs":i.includes("package.json")||i.includes("dockerfile")||i.includes(".yml")||i.includes(".yaml")||i.includes("webpack")||i.includes("tsconfig")?"build":i.includes(".css")||i.includes(".scss")||o.includes("style")||o.includes("format")?"style":o.includes("fix")||o.includes("bug")||o.includes("error")||o.includes("issue")?"fix":o.includes("add")||o.includes("new")||o.includes("create")||o.includes("implement")?"feat":o.includes("refactor")||o.includes("restructure")||o.includes("rename")?"refactor":"chore"}function xe(e){if(e.startsWith("```")){let t=e.match(/^```[^\n`]*\n([\s\S]*?)\n\s*```([\s\S]*)$/);if(t){let o=t[1].trim(),i=t[2].trim();e=i?`${o}
36
+ - Be descriptive but concise`}},o=t==="pt"?"pt":"en";return i[o][e]||i[o].conventional}function Se(e){let t={feat:/^(feat|feature)(\([^)]+\))?:/i,fix:/^(fix|bugfix)(\([^)]+\))?:/i,docs:/^(docs|documentation)(\([^)]+\))?:/i,style:/^(style|format)(\([^)]+\))?:/i,refactor:/^(refactor|refactoring)(\([^)]+\))?:/i,test:/^(test|testing)(\([^)]+\))?:/i,chore:/^(chore|maintenance)(\([^)]+\))?:/i,build:/^(build|ci)(\([^)]+\))?:/i,ci:/^(ci|continuous-integration)(\([^)]+\))?:/i};for(let[i,o]of Object.entries(t))if(o.test(e))return i;return null}function we(e,t){let i=e.toLowerCase(),o=t.join(" ").toLowerCase();return o.includes("test")||o.includes("spec")||i.includes("test(")?"test":o.includes("readme")||o.includes(".md")||o.includes("docs")?"docs":o.includes("package.json")||o.includes("dockerfile")||o.includes(".yml")||o.includes(".yaml")||o.includes("webpack")||o.includes("tsconfig")?"build":o.includes(".css")||o.includes(".scss")||i.includes("style")||i.includes("format")?"style":i.includes("fix")||i.includes("bug")||i.includes("error")||i.includes("issue")?"fix":i.includes("add")||i.includes("new")||i.includes("create")||i.includes("implement")?"feat":i.includes("refactor")||i.includes("restructure")||i.includes("rename")?"refactor":"chore"}function $e(e){return e.match(/^```[\s\S]*```$/)&&(e=e.replace(/^```(?:plaintext|javascript|typescript|python|java|html|css|json|xml|yaml|yml|bash|shell|text)?\s*/,"").replace(/\s*```$/,"")),e=e.trim(),e}async function Ae(e,t,i){try{if(!t.openai.apiKey)return{success:!1,error:"Chave da OpenAI n\xE3o encontrada. Configure OPENAI_API_KEY nas vari\xE1veis de ambiente."};let o=be(e,t,i),n=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{Authorization:`Bearer ${t.openai.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:t.openai.model,messages:[{role:"user",content:o}],max_tokens:Math.min(t.openai.maxTokens,150),temperature:t.openai.temperature})});if(!n.ok){let l=await n.json().catch(()=>({}));return{success:!1,error:`Erro da OpenAI (${n.status}): ${l.error?.message||"Erro desconhecido"}`}}let a=(await n.json()).choices?.[0]?.message?.content?.trim();if(!a)return{success:!1,error:"OpenAI retornou resposta vazia"};a=$e(a);let c=Se(a),s=we(e,i);return{success:!0,suggestion:{message:a,type:c||s,confidence:.8}}}catch(o){return{success:!1,error:`Erro ao conectar com OpenAI: ${o instanceof Error?o.message:"Erro desconhecido"}`}}}async function T(e,t,i,o=3){let n="";for(let r=0;r<o;r++){let a=await Ae(e,t,i);if(a.success)return a;n=a.error||"Erro desconhecido",r<o-1&&await new Promise(c=>setTimeout(c,Math.pow(2,r)*1e3))}return{success:!1,error:`Falha ap\xF3s ${o} tentativas. \xDAltimo erro: ${n}`}}var Y=w(()=>{"use strict";f()});var X={};G(X,{askContinueCommits:()=>Z,confirmCommit:()=>st,copyToClipboard:()=>U,editCommitMessage:()=>L,selectFilesForCommit:()=>Q,showCancellation:()=>I,showCommitPreview:()=>N,showCommitResult:()=>x});import{text as tt,select as it,confirm as q,log as P,note as Me,cancel as ot,isCancel as F}from"@clack/prompts";import nt from"clipboardy";async function N(e){Me(`Tipo: ${e.type}
37
+ Mensagem: "${e.message}"`,"\u{1F4AD} Sugest\xE3o de Commit");let t=await it({message:"O que voc\xEA gostaria de fazer?",options:[{value:"commit",label:"\u2705 Fazer commit com esta mensagem",hint:"Executar git commit imediatamente"},{value:"edit",label:"\u270F\uFE0F Editar mensagem",hint:"Modificar a mensagem antes de commitar"},{value:"copy",label:"\u{1F4CB} Copiar para clipboard",hint:"Copiar mensagem e sair sem commitar"},{value:"cancel",label:"\u274C Cancelar",hint:"Sair sem fazer nada"}]});return F(t)?{action:"cancel"}:{action:t}}async function L(e){let t=await tt({message:"Edite a mensagem do commit:",initialValue:e,placeholder:"Digite a mensagem do commit...",validate:o=>{if(!o||o.trim().length===0)return"A mensagem n\xE3o pode estar vazia";if(o.trim().length>72)return"A mensagem est\xE1 muito longa (m\xE1ximo 72 caracteres recomendado)"}});if(F(t))return{action:"cancel"};let i=await q({message:`Confirma a mensagem editada: "${t}"?`});return F(i)||!i?{action:"cancel"}:{action:"commit",message:t}}async function U(e){try{return await nt.write(e),P.success("\u2705 Mensagem copiada para a \xE1rea de transfer\xEAncia!"),!0}catch(t){return P.error(`\u274C Erro ao copiar: ${t instanceof Error?t.message:"Erro desconhecido"}`),!1}}async function st(e){Me(`"${e}"`,"\u{1F680} Confirmar Commit");let t=await q({message:"Executar o commit agora?"});return F(t)?!1:t}function x(e,t,i){e&&t?(P.success("\u2705 Commit realizado com sucesso!"),P.info(`\u{1F517} Hash: ${t.substring(0,8)}`)):P.error(`\u274C Erro ao realizar commit: ${i||"Erro desconhecido"}`)}async function Q(e){P.info("\u{1F4CB} Modo Split: Selecione os arquivos para este commit");let t=[];for(let i of e){let o=await q({message:`Incluir "${i}" neste commit?`});if(F(o))break;o&&t.push(i)}return t}async function Z(e){if(e.length===0)return!1;P.info(`\u{1F4C4} Arquivos restantes: ${e.join(", ")}`);let t=await q({message:"Gerar commit para os arquivos restantes?"});return F(t)?!1:t}function I(){ot("Opera\xE7\xE3o cancelada pelo usu\xE1rio")}var k=w(()=>{"use strict";f()});var Ie={};G(Ie,{chooseSplitMode:()=>te,confirmGroupCommit:()=>ct,showSmartSplitGroups:()=>at,showSmartSplitProgress:()=>mt});import{select as Pe,confirm as rt,log as ze,note as Fe,isCancel as ee}from"@clack/prompts";async function te(){let e=await Pe({message:"Como voc\xEA gostaria de organizar os commits?",options:[{value:"smart",label:"\u{1F9E0} Smart Split (Recomendado)",hint:"IA analisa contexto e agrupa automaticamente"},{value:"manual",label:"\u270B Split Manual",hint:"Voc\xEA escolhe arquivos manualmente"},{value:"cancel",label:"\u274C Cancelar",hint:"Voltar ao modo normal"}]});return ee(e)?{action:"cancel"}:e==="manual"?{action:"manual"}:e==="smart"?{action:"proceed"}:{action:"cancel"}}async function at(e){Fe(`Identificamos ${e.length} grupo(s) l\xF3gico(s) para seus commits:
37
38
 
38
- ${i}`:o}else e.match(/^```[\s\S]*```$/)&&(e=e.replace(/^```(?:plaintext|javascript|typescript|python|java|html|css|json|xml|yaml|yml|bash|shell|text)?\s*/,"").replace(/\s*```$/,""))}return e=e.trim(),e}async function Ce(e,t,o){try{if(!t.openai.apiKey)return{success:!1,error:"Chave da OpenAI n\xE3o encontrada. Configure OPENAI_API_KEY nas vari\xE1veis de ambiente."};let i=ge(e,t,o),n=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{Authorization:`Bearer ${t.openai.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:t.openai.model,messages:[{role:"user",content:i}],max_tokens:Math.min(t.openai.maxTokens,150),temperature:t.openai.temperature})});if(!n.ok){let l=await n.json().catch(()=>({}));return{success:!1,error:`Erro da OpenAI (${n.status}): ${l.error?.message||"Erro desconhecido"}`}}let a=(await n.json()).choices?.[0]?.message?.content?.trim();if(!a)return{success:!1,error:"OpenAI retornou resposta vazia"};a=xe(a);let c=he(a),r=ye(e,o);return{success:!0,suggestion:{message:a,type:c||r,confidence:.8}}}catch(i){return{success:!1,error:`Erro ao conectar com OpenAI: ${i instanceof Error?i.message:"Erro desconhecido"}`}}}async function q(e,t,o,i=3){let n="";for(let s=0;s<i;s++){let a=await Ce(e,t,o);if(a.success)return a;n=a.error||"Erro desconhecido",s<i-1&&await new Promise(c=>setTimeout(c,Math.pow(2,s)*1e3))}return{success:!1,error:`Falha ap\xF3s ${i} tentativas. \xDAltimo erro: ${n}`}}var W=A(()=>{"use strict";f()});var Q={};j(Q,{askContinueCommits:()=>Y,confirmCommit:()=>Ye,copyToClipboard:()=>U,editCommitMessage:()=>L,selectFilesForCommit:()=>B,showCancellation:()=>R,showCommitPreview:()=>N,showCommitResult:()=>S});import{text as Je,select as Ke,confirm as T,log as F,note as be,cancel as We,isCancel as P}from"@clack/prompts";import Be from"clipboardy";async function N(e){be(`Tipo: ${e.type}
39
- Mensagem: "${e.message}"`,"\u{1F4AD} Sugest\xE3o de Commit");let t=await Ke({message:"O que voc\xEA gostaria de fazer?",options:[{value:"commit",label:"\u2705 Fazer commit com esta mensagem",hint:"Executar git commit imediatamente"},{value:"edit",label:"\u270F\uFE0F Editar mensagem",hint:"Modificar a mensagem antes de commitar"},{value:"copy",label:"\u{1F4CB} Copiar para clipboard",hint:"Copiar mensagem e sair sem commitar"},{value:"cancel",label:"\u274C Cancelar",hint:"Sair sem fazer nada"}]});return P(t)?{action:"cancel"}:{action:t}}async function L(e){let t=await Je({message:"Edite a mensagem do commit:",initialValue:e,placeholder:"Digite a mensagem do commit...",validate:i=>{if(!i||i.trim().length===0)return"A mensagem n\xE3o pode estar vazia";if(i.trim().length>72)return"A mensagem est\xE1 muito longa (m\xE1ximo 72 caracteres recomendado)"}});if(P(t))return{action:"cancel"};let o=await T({message:`Confirma a mensagem editada: "${t}"?`});return P(o)||!o?{action:"cancel"}:{action:"commit",message:t}}async function U(e){try{return await Be.write(e),F.success("\u2705 Mensagem copiada para a \xE1rea de transfer\xEAncia!"),!0}catch(t){return F.error(`\u274C Erro ao copiar: ${t instanceof Error?t.message:"Erro desconhecido"}`),!1}}async function Ye(e){be(`"${e}"`,"\u{1F680} Confirmar Commit");let t=await T({message:"Executar o commit agora?"});return P(t)?!1:t}function S(e,t,o){e&&t?(F.success("\u2705 Commit realizado com sucesso!"),F.info(`\u{1F517} Hash: ${t.substring(0,8)}`)):F.error(`\u274C Erro ao realizar commit: ${o||"Erro desconhecido"}`)}async function B(e){F.info("\u{1F4CB} Modo Split: Selecione os arquivos para este commit");let t=[];for(let o of e){let i=await T({message:`Incluir "${o}" neste commit?`});if(P(i))break;i&&t.push(o)}return t}async function Y(e){if(e.length===0)return!1;F.info(`\u{1F4C4} Arquivos restantes: ${e.join(", ")}`);let t=await T({message:"Gerar commit para os arquivos restantes?"});return P(t)?!1:t}function R(){We("Opera\xE7\xE3o cancelada pelo usu\xE1rio")}var O=A(()=>{"use strict";f()});var Ae={};j(Ae,{chooseSplitMode:()=>X,confirmGroupCommit:()=>Xe,showSmartSplitGroups:()=>Ze,showSmartSplitProgress:()=>et});import{select as we,confirm as Qe,log as ve,note as $e,isCancel as Z}from"@clack/prompts";async function X(){let e=await we({message:"Como voc\xEA gostaria de organizar os commits?",options:[{value:"smart",label:"\u{1F9E0} Smart Split (Recomendado)",hint:"IA analisa contexto e agrupa automaticamente"},{value:"manual",label:"\u270B Split Manual",hint:"Voc\xEA escolhe arquivos manualmente"},{value:"cancel",label:"\u274C Cancelar",hint:"Voltar ao modo normal"}]});return Z(e)?{action:"cancel"}:e==="manual"?{action:"manual"}:e==="smart"?{action:"proceed"}:{action:"cancel"}}async function Ze(e){$e(`Identificamos ${e.length} grupo(s) l\xF3gico(s) para seus commits:
39
+ `+e.map((i,o)=>`${o+1}. **${i.name}**
40
+ \u{1F4C4} ${i.files.join(", ")}
41
+ \u{1F4A1} ${i.description}
42
+ \u{1F3AF} Confian\xE7a: ${Math.round(i.confidence*100)}%`).join(`
40
43
 
41
- `+e.map((o,i)=>`${i+1}. **${o.name}**
42
- \u{1F4C4} ${o.files.join(", ")}
43
- \u{1F4A1} ${o.description}
44
- \u{1F3AF} Confian\xE7a: ${Math.round(o.confidence*100)}%`).join(`
45
-
46
- `),"\u{1F9E0} An\xE1lise de Contexto");let t=await we({message:"O que voc\xEA gostaria de fazer?",options:[{value:"proceed",label:"\u2705 Prosseguir com esta organiza\xE7\xE3o",hint:"Usar os grupos como sugeridos pela IA"},{value:"manual",label:"\u270B Fazer split manual",hint:"Escolher arquivos manualmente"},{value:"cancel",label:"\u274C Cancelar",hint:"Voltar ao modo normal"}]});return Z(t)?{action:"cancel"}:t==="proceed"?{action:"proceed",groups:e}:{action:t}}async function Xe(e,t){$e(`**Grupo:** ${e.name}
44
+ `),"\u{1F9E0} An\xE1lise de Contexto");let t=await Pe({message:"O que voc\xEA gostaria de fazer?",options:[{value:"proceed",label:"\u2705 Prosseguir com esta organiza\xE7\xE3o",hint:"Usar os grupos como sugeridos pela IA"},{value:"manual",label:"\u270B Fazer split manual",hint:"Escolher arquivos manualmente"},{value:"cancel",label:"\u274C Cancelar",hint:"Voltar ao modo normal"}]});return ee(t)?{action:"cancel"}:t==="proceed"?{action:"proceed",groups:e}:{action:t}}async function ct(e,t){Fe(`**Grupo:** ${e.name}
47
45
  **Arquivos:** ${e.files.join(", ")}
48
- **Mensagem:** "${t}"`,"\u{1F680} Confirmar Commit do Grupo");let o=await Qe({message:`Fazer commit para "${e.name}"?`});return Z(o)?!1:o}function et(e,t,o){let i=Math.round(e/t*100),n="\u2588".repeat(Math.floor(i/10))+"\u2591".repeat(10-Math.floor(i/10));ve.info(`\u{1F504} Progresso: [${n}] ${i}% (${e}/${t})`),ve.info(`\u{1F4CB} Processando: ${o}`)}var ee=A(()=>{"use strict";f()});import tt from"crypto";function Me(e){Ee=new te(e)}function ze(){return Ee}function Fe(e,t){let o=ze();return o?o.get(e,t):{hit:!1}}function Pe(e,t,o){let i=ze();i&&i.set(e,t,o)}var te,Ee,ie=A(()=>{"use strict";f();te=class{cache=new Map;config;constructor(t){this.config=t}generateHash(t,o){let i={files:t.sort(),diff:o.substring(0,1e3),model:this.config.openai.model,temperature:this.config.openai.temperature};return tt.createHash("md5").update(JSON.stringify(i)).digest("hex")}get(t,o){if(!this.config.cache.enabled)return{hit:!1};let i=this.generateHash(t,o),n=this.cache.get(i);if(!n)return{hit:!1};let s=Date.now(),a=this.config.cache.ttl*60*1e3;return s-n.timestamp>a?(this.cache.delete(i),{hit:!1}):{hit:!0,groups:n.groups}}set(t,o,i){if(!this.config.cache.enabled||(this.cache.size>=this.config.cache.maxSize&&this.cleanup(),this.cache.size>=this.config.cache.maxSize))return;let n=this.generateHash(t,o),s={groups:i,timestamp:Date.now(),hash:n};this.cache.set(n,s)}cleanup(){let t=Date.now(),o=this.config.cache.ttl*60*1e3;for(let[i,n]of this.cache.entries())t-n.timestamp>o&&this.cache.delete(i);if(this.cache.size>=this.config.cache.maxSize){let n=Array.from(this.cache.entries()).sort((s,a)=>s[1].timestamp-a[1].timestamp).slice(0,Math.ceil(this.config.cache.maxSize*.5));for(let[s]of n)this.cache.delete(s)}}clear(){this.cache.clear()}getStats(){return{size:this.cache.size,maxSize:this.config.cache.maxSize,enabled:this.config.cache.enabled}}},Ee=null});import{log as g}from"@clack/prompts";function it(e,t){let i=t.length>8e3?t.substring(0,8e3)+`
49
- ... (diff truncado)`:t,n=e.length,s=e.reduce((c,r)=>{let l=r.split(".").pop()||"sem-extensao";return c[l]=(c[l]||0)+1,c},{}),a=Object.entries(s).map(([c,r])=>`${c}: ${r}`).join(", ");return`Analise os arquivos modificados e agrupe em commits l\xF3gicos.
46
+ **Mensagem:** "${t}"`,"\u{1F680} Confirmar Commit do Grupo");let i=await rt({message:`Fazer commit para "${e.name}"?`});return ee(i)?!1:i}function mt(e,t,i){let o=Math.round(e/t*100),n="\u2588".repeat(Math.floor(o/10))+"\u2591".repeat(10-Math.floor(o/10));ze.info(`\u{1F504} Progresso: [${n}] ${o}% (${e}/${t})`),ze.info(`\u{1F4CB} Processando: ${i}`)}var ie=w(()=>{"use strict";f()});import lt from"crypto";function je(e){Re=new oe(e)}function Ge(){return Re}function Oe(e,t){let i=Ge();return i?i.get(e,t):{hit:!1}}function ke(e,t,i){let o=Ge();o&&o.set(e,t,i)}var oe,Re,ne=w(()=>{"use strict";f();oe=class{cache=new Map;config;constructor(t){this.config=t}generateHash(t,i){let o={files:t.sort(),diff:i.substring(0,1e3),model:this.config.openai.model,temperature:this.config.openai.temperature};return lt.createHash("md5").update(JSON.stringify(o)).digest("hex")}get(t,i){if(!this.config.cache.enabled)return{hit:!1};let o=this.generateHash(t,i),n=this.cache.get(o);if(!n)return{hit:!1};let r=Date.now(),a=this.config.cache.ttl*60*1e3;return r-n.timestamp>a?(this.cache.delete(o),{hit:!1}):{hit:!0,groups:n.groups}}set(t,i,o){if(!this.config.cache.enabled||(this.cache.size>=this.config.cache.maxSize&&this.cleanup(),this.cache.size>=this.config.cache.maxSize))return;let n=this.generateHash(t,i),r={groups:o,timestamp:Date.now(),hash:n};this.cache.set(n,r)}cleanup(){let t=Date.now(),i=this.config.cache.ttl*60*1e3;for(let[o,n]of this.cache.entries())t-n.timestamp>i&&this.cache.delete(o);if(this.cache.size>=this.config.cache.maxSize){let n=Array.from(this.cache.entries()).sort((r,a)=>r[1].timestamp-a[1].timestamp).slice(0,Math.ceil(this.config.cache.maxSize*.5));for(let[r]of n)this.cache.delete(r)}}clear(){this.cache.clear()}getStats(){return{size:this.cache.size,maxSize:this.config.cache.maxSize,enabled:this.config.cache.enabled}}},Re=null});import{log as g}from"@clack/prompts";function ut(e,t){let o=t.length>8e3?t.substring(0,8e3)+`
47
+ ... (diff truncado)`:t,n=e.length,r=e.reduce((c,s)=>{let l=s.split(".").pop()||"sem-extensao";return c[l]=(c[l]||0)+1,c},{}),a=Object.entries(r).map(([c,s])=>`${c}: ${s}`).join(", ");return`Analise os arquivos modificados e agrupe em commits l\xF3gicos.
50
48
 
51
49
  ARQUIVOS (${n}): ${e.join(", ")}
52
50
  TIPOS: ${a}
53
51
 
54
52
  DIFF RESUMIDO:
55
53
  \`\`\`
56
- ${i}
54
+ ${o}
57
55
  \`\`\`
58
56
 
59
57
  Agrupe arquivos relacionados. M\xE1ximo 5 grupos. Responda em JSON:
@@ -67,10 +65,10 @@ Agrupe arquivos relacionados. M\xE1ximo 5 grupos. Responda em JSON:
67
65
  "confidence": 0.8
68
66
  }
69
67
  ]
70
- }`}function ot(e){let t=e.reduce((i,n)=>{let s=n.split("/").slice(0,-1).join("/")||"root";return i[s]||(i[s]=[]),i[s].push(n),i},{});return`Agrupe estes arquivos em commits l\xF3gicos baseado nos diret\xF3rios:
68
+ }`}function ft(e){let t=e.reduce((o,n)=>{let r=n.split("/").slice(0,-1).join("/")||"root";return o[r]||(o[r]=[]),o[r].push(n),o},{});return`Agrupe estes arquivos em commits l\xF3gicos baseado nos diret\xF3rios:
71
69
 
72
70
  ARQUIVOS POR DIRET\xD3RIO:
73
- ${Object.entries(t).map(([i,n])=>`${i}: ${n.length} arquivo(s)`).join(`
71
+ ${Object.entries(t).map(([o,n])=>`${o}: ${n.length} arquivo(s)`).join(`
74
72
  `)}
75
73
 
76
74
  LISTA COMPLETA: ${e.join(", ")}
@@ -86,48 +84,54 @@ Agrupe por funcionalidade relacionada. M\xE1ximo 5 grupos. JSON:
86
84
  "confidence": 0.7
87
85
  }
88
86
  ]
89
- }`}async function st(e,t,o){try{if(!o.openai.apiKey)return{success:!1,error:"Chave da OpenAI n\xE3o encontrada"};let i=Fe(e,t);if(i.hit&&i.groups)return{success:!0,groups:i.groups};let s=t.length>6e3,a=s?ot(e):it(e,t);s&&console.warn(`\u26A0\uFE0F Diff muito grande (${t.length} chars), usando an\xE1lise baseada em nomes de arquivos`);let c=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{Authorization:`Bearer ${o.openai.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:o.openai.model,messages:[{role:"user",content:a}],max_tokens:800,temperature:.3})});if(!c.ok){let d=await c.json().catch(()=>({}));return{success:!1,error:`Erro da OpenAI (${c.status}): ${d.error?.message||"Erro desconhecido"}`}}let l=(await c.json()).choices?.[0]?.message?.content?.trim();if(!l)return{success:!1,error:"OpenAI retornou resposta vazia"};let m=l.match(/\{[\s\S]*\}/);if(!m)return{success:!1,error:"Resposta da OpenAI n\xE3o cont\xE9m JSON v\xE1lido"};let p=JSON.parse(m[0]);if(!p.groups||!Array.isArray(p.groups))return{success:!1,error:"Formato de resposta inv\xE1lido da OpenAI"};let h=p.groups.flatMap(d=>d.files||[]),b=e.filter(d=>!h.includes(d));b.length>0&&(p.groups[0].files=[...p.groups[0].files||[],...b]);let y=p.groups.map(d=>({id:d.id||`group-${Math.random().toString(36).substr(2,9)}`,name:d.name||"Grupo sem nome",description:d.description||"Sem descri\xE7\xE3o",files:d.files||[],diff:"",confidence:d.confidence||.5}));return Pe(e,t,y),{success:!0,groups:y}}catch(i){return{success:!1,error:`Erro ao analisar contexto: ${i instanceof Error?i.message:"Erro desconhecido"}`}}}async function nt(e){let{getFileDiff:t}=await Promise.resolve().then(()=>(w(),M)),o=e.files.map(s=>{try{let a=t(s),c=4e3;return a.length>c?a.substring(0,c)+`
90
- ... (diff truncado)`:a}catch{return""}}).filter(s=>s.length>0);if(o.length===0&&e.files.length>0){let{execSync:s}=await import("child_process"),a=e.files.filter(r=>{try{return s(`test -f "${r}"`,{stdio:"ignore"}),s(`git status --porcelain -- "${r}"`,{encoding:"utf-8",stdio:"pipe"}).trim().startsWith("??")}catch{return!1}});if(a.length>0)return a.map(r=>{try{let l=s(`cat "${r}"`,{encoding:"utf-8",stdio:"pipe"}),m=2e3,p=l.length>m?l.substring(0,m)+`
91
- ... (conte\xFAdo truncado)`:l;return`diff --git a/${r} b/${r}
87
+ }`}async function dt(e,t,i){try{if(!i.openai.apiKey)return{success:!1,error:"Chave da OpenAI n\xE3o encontrada"};let o=Oe(e,t);if(o.hit&&o.groups)return{success:!0,groups:o.groups};let r=t.length>6e3,a=r?ft(e):ut(e,t);r&&console.warn(`\u26A0\uFE0F Diff muito grande (${t.length} chars), usando an\xE1lise baseada em nomes de arquivos`);let c=await fetch("https://api.openai.com/v1/chat/completions",{method:"POST",headers:{Authorization:`Bearer ${i.openai.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify({model:i.openai.model,messages:[{role:"user",content:a}],max_tokens:800,temperature:.3})});if(!c.ok){let p=await c.json().catch(()=>({}));return{success:!1,error:`Erro da OpenAI (${c.status}): ${p.error?.message||"Erro desconhecido"}`}}let l=(await c.json()).choices?.[0]?.message?.content?.trim();if(!l)return{success:!1,error:"OpenAI retornou resposta vazia"};let m=l.match(/\{[\s\S]*\}/);if(!m)return{success:!1,error:"Resposta da OpenAI n\xE3o cont\xE9m JSON v\xE1lido"};let d=JSON.parse(m[0]);if(!d.groups||!Array.isArray(d.groups))return{success:!1,error:"Formato de resposta inv\xE1lido da OpenAI"};let h=d.groups.flatMap(p=>p.files||[]),b=e.filter(p=>!h.includes(p));b.length>0&&(d.groups[0].files=[...d.groups[0].files||[],...b]);let C=d.groups.map(p=>({id:p.id||`group-${Math.random().toString(36).substr(2,9)}`,name:p.name||"Grupo sem nome",description:p.description||"Sem descri\xE7\xE3o",files:p.files||[],diff:"",confidence:p.confidence||.5}));return ke(e,t,C),{success:!0,groups:C}}catch(o){return{success:!1,error:`Erro ao analisar contexto: ${o instanceof Error?o.message:"Erro desconhecido"}`}}}async function pt(e){let{getFileDiff:t}=await Promise.resolve().then(()=>($(),M)),i=e.files.map(r=>{try{let a=t(r),c=4e3;return a.length>c?a.substring(0,c)+`
88
+ ... (diff truncado)`:a}catch{return""}}).filter(r=>r.length>0);if(i.length===0&&e.files.length>0){let{execSync:r}=await import("child_process"),a=e.files.filter(s=>{try{return r(`test -f "${s}"`,{stdio:"ignore"}),r(`git status --porcelain -- "${s}"`,{encoding:"utf-8",stdio:"pipe"}).trim().startsWith("??")}catch{return!1}});if(a.length>0)return a.map(s=>{try{let l=r(`cat "${s}"`,{encoding:"utf-8",stdio:"pipe"}),m=2e3,d=l.length>m?l.substring(0,m)+`
89
+ ... (conte\xFAdo truncado)`:l;return`diff --git a/${s} b/${s}
92
90
  new file mode 100644
93
91
  index 0000000..${Math.random().toString(36).substr(2,7)}
94
92
  --- /dev/null
95
- +++ b/${r}
96
- @@ -0,0 +1,${p.split(`
93
+ +++ b/${s}
94
+ @@ -0,0 +1,${d.split(`
97
95
  `).length} @@
98
- ${p.split(`
96
+ ${d.split(`
99
97
  `).map(h=>`+${h}`).join(`
100
- `)}`}catch{return""}}).filter(r=>r.length>0).join(`
101
- `);let c=e.files.filter(r=>{try{return s(`test -f "${r}"`,{stdio:"ignore"}),s("git diff --cached --name-only",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
102
- `).includes(r)}catch{return!1}});if(c.length>0)return c.map(r=>{try{let l=s(`cat "${r}"`,{encoding:"utf-8",stdio:"pipe"}),m=2e3,p=l.length>m?l.substring(0,m)+`
103
- ... (conte\xFAdo truncado)`:l;return`diff --git a/${r} b/${r}
98
+ `)}`}catch{return""}}).filter(s=>s.length>0).join(`
99
+ `);let c=e.files.filter(s=>{try{return r(`test -f "${s}"`,{stdio:"ignore"}),r("git diff --cached --name-only",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
100
+ `).includes(s)}catch{return!1}});if(c.length>0)return c.map(s=>{try{let l=r(`cat "${s}"`,{encoding:"utf-8",stdio:"pipe"}),m=2e3,d=l.length>m?l.substring(0,m)+`
101
+ ... (conte\xFAdo truncado)`:l;return`diff --git a/${s} b/${s}
104
102
  index 0000000..${Math.random().toString(36).substr(2,7)} 100644
105
- --- a/${r}
106
- +++ b/${r}
107
- @@ -1 +1,${p.split(`
103
+ --- a/${s}
104
+ +++ b/${s}
105
+ @@ -1 +1,${d.split(`
108
106
  `).length} @@
109
- ${p.split(`
107
+ ${d.split(`
110
108
  `).map(h=>`+${h}`).join(`
111
- `)}`}catch{return""}}).filter(r=>r.length>0).join(`
112
- `)}let i=o.join(`
113
- `),n=8e3;return i.length>n?i.substring(0,n)+`
114
- ... (diff total truncado)`:i}async function oe(e,t,o){o.silent||g.info("\u{1F9E0} Modo Smart Split ativado - Agrupando arquivos por contexto"),o.silent||g.info("\u{1F916} Analisando contexto das mudan\xE7as...");let i=await st(e.stagedFiles,e.diff,t);if(!i.success){o.silent||g.error(`\u274C Erro na an\xE1lise de contexto: ${i.error}`);return}if(!i.groups||i.groups.length===0){o.silent||g.error("\u274C Nenhum grupo foi criado pela an\xE1lise");return}if(o.silent||(g.success(`\u2705 ${i.groups.length} grupo(s) identificado(s):`),i.groups.forEach((n,s)=>{g.info(` ${s+1}. ${n.name} (${n.files.length} arquivo(s))`),g.info(` \u{1F4C4} ${n.files.join(", ")}`)})),!o.yes&&!o.silent){let{showSmartSplitGroups:n}=await Promise.resolve().then(()=>(ee(),Ae)),s=await n(i.groups);if(s.action==="cancel"){o.silent||g.info("\u274C Opera\xE7\xE3o cancelada pelo usu\xE1rio");return}if(s.action==="manual"){let a={...o,split:!0,smartSplit:!1},{main:c}=await Promise.resolve().then(()=>(se(),Ge));await c(a);return}}for(let n=0;n<i.groups.length;n++){let s=i.groups[n];if(!s){o.silent||g.error(`\u274C Grupo ${n+1} \xE9 undefined`);continue}o.silent||g.info(`
115
- \u{1F504} Processando grupo ${n+1}/${i.groups.length}: ${s.name}`);let a=await nt(s);if(!a){o.silent||(g.warn(`\u26A0\uFE0F Nenhum diff encontrado para o grupo: ${s.name}`),g.info(` \u{1F4C4} Arquivos: ${s.files.join(", ")}`),g.info(" \u{1F4A1} Poss\xEDvel causa: arquivos novos, deletados/recriados, ou sem mudan\xE7as"));continue}o.silent||g.info(`\u{1F916} Gerando commit para: ${s.name}`);let{generateWithRetry:c}=await Promise.resolve().then(()=>(W(),Se)),r=await c(a,t,s.files);if(!r.success){o.silent||g.error(`\u274C Erro ao gerar commit para ${s.name}: ${r.error}`);continue}if(!r.suggestion){o.silent||g.error(`\u274C Nenhuma sugest\xE3o gerada para ${s.name}`);continue}if(t.dryRun){o.silent||(g.info(`\u{1F50D} Dry Run - Grupo: ${s.name}`),g.info(`\u{1F4C4} Arquivos: ${s.files.join(", ")}`),g.info(`\u{1F4AD} Mensagem: "${r.suggestion.message}"`));continue}if(o.yes){let{executeFileCommit:l}=await Promise.resolve().then(()=>(w(),M)),m;if(s.files.length===1&&s.files[0])m=l(s.files[0],r.suggestion.message||"");else{let{execSync:p}=await import("child_process"),{escapeShellArg:h}=await Promise.resolve().then(()=>(w(),M));try{let b=s.files.map(x=>h(x)).join(" "),y=h(r.suggestion.message||"");p(`git commit ${b} -m ${y}`,{stdio:"pipe"}),m={success:!0,hash:p("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:r.suggestion.message||""}}catch(b){m={success:!1,error:b instanceof Error?b.message:"Erro desconhecido ao executar commit"}}}S(m.success,m.hash,m.error)}else{let{showCommitPreview:l,editCommitMessage:m,copyToClipboard:p,showCancellation:h}=await Promise.resolve().then(()=>(O(),Q));switch((await l(r.suggestion)).action){case"commit":{let{executeFileCommit:y}=await Promise.resolve().then(()=>(w(),M)),d,x=r.suggestion.message||"Atualiza\xE7\xE3o de arquivos";if(s.files.length===1&&s.files[0])d=y(s.files[0],x);else{let{execSync:G}=await import("child_process"),{escapeShellArg:I}=await Promise.resolve().then(()=>(w(),M));try{let $=s.files.map(V=>I(V)).join(" "),_=I(x);G(`git commit ${$} -m ${_}`,{stdio:"pipe"}),d={success:!0,hash:G("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:x}}catch($){d={success:!1,error:$ instanceof Error?$.message:"Erro desconhecido ao executar commit"}}}S(d.success,d.hash,d.error);break}case"edit":{let y=await m(r.suggestion.message);if(y.action==="commit"&&y.message){let{executeFileCommit:d}=await Promise.resolve().then(()=>(w(),M)),x;if(s.files.length===1&&s.files[0])x=d(s.files[0],y.message||"");else{let{execSync:G}=await import("child_process"),{escapeShellArg:I}=await Promise.resolve().then(()=>(w(),M));try{let $=s.files.map(V=>I(V)).join(" "),_=I(y.message||"");G(`git commit ${$} -m ${_}`,{stdio:"pipe"}),x={success:!0,hash:G("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:y.message||""}}catch($){x={success:!1,error:$ instanceof Error?$.message:"Erro desconhecido ao executar commit"}}}S(x.success,x.hash,x.error)}break}case"copy":{await p(r.suggestion.message),o.silent||g.info("\u{1F3AF} Mensagem copiada para clipboard");break}case"cancel":{h();return}}}if(n<i.groups.length-1&&!o.yes){let{askContinueCommits:l}=await Promise.resolve().then(()=>(O(),Q)),m=i.groups.slice(n+1).filter(h=>h!==void 0).map(h=>h.name);if(!await l(m))break}}o.silent||g.success("\u2705 Smart Split conclu\xEDdo!")}var Re=A(()=>{"use strict";f();ie();O()});var Ge={};j(Ge,{main:()=>ne});import{log as u}from"@clack/prompts";async function ne(e={silent:!1,yes:!1,auto:!1,split:!1,smartSplit:!1,dryRun:!1,help:!1,version:!1}){e.silent||u.info("\u{1F680} Commit Wizard iniciado!"),H()||(u.error("\u274C N\xE3o foi encontrado um reposit\xF3rio Git neste diret\xF3rio."),e.silent||u.info("\u{1F4A1} Execute o comando em um diret\xF3rio com reposit\xF3rio Git inicializado."),process.exit(1)),e.silent||u.info("\u2699\uFE0F Carregando configura\xE7\xE3o...");let t=ue();Me(t),e.split&&(t.splitCommits=!0),e.dryRun&&(t.dryRun=!0);let o=fe(t);o.length>0&&(u.error("\u274C Erros na configura\xE7\xE3o:"),o.forEach(a=>u.error(` \u2022 ${a}`)),process.exit(1)),e.silent||u.success(`\u2705 Configura\xE7\xE3o carregada (modelo: ${t.openai.model}, idioma: ${t.language})`),e.silent||u.info("\u{1F4CB} Verificando arquivos staged...");let i=J();i.hasStaged||(u.warn("\u26A0\uFE0F Nenhum arquivo foi encontrado no stage."),e.silent||u.info("\u{1F4A1} Use `git add <arquivo>` para adicionar arquivos ao stage antes de gerar o commit."),process.exit(0));let n=K();if(e.silent||(u.success(`\u2705 Encontrados ${i.stagedFiles.length} arquivo(s) staged:`),i.stagedFiles.forEach(a=>u.info(` \u{1F4C4} ${a}`)),u.info(`\u{1F4CA} Estat\xEDsticas: +${n.added} -${n.removed} linhas`)),t.splitCommits||e.smartSplit){if(e.yes)return await oe(i,t,e);switch((await X()).action){case"proceed":return await oe(i,t,e);case"manual":return await rt(i,t,e);case"cancel":R();return}}e.silent||u.info("\u{1F916} Gerando mensagem de commit com IA...");let s=await q(i.diff,t,i.stagedFiles);if(s.success||(u.error(`\u274C Erro ao gerar commit: ${s.error}`),process.exit(1)),s.suggestion||(u.error("\u274C Nenhuma sugest\xE3o foi gerada"),process.exit(1)),e.silent||u.success("\u2728 Mensagem de commit gerada!"),t.dryRun){u.info("\u{1F50D} Modo Dry Run - Mensagem gerada:"),u.info(`"${s.suggestion.message}"`),u.info("\u{1F4A1} Execute sem --dry-run para fazer o commit");return}if(e.yes){let a=z(s.suggestion.message);S(a.success,a.hash,a.error);return}for(;;)switch((await N(s.suggestion)).action){case"commit":{let c=z(s.suggestion.message);S(c.success,c.hash,c.error);return}case"edit":{let c=await L(s.suggestion.message);if(c.action==="cancel"){R();return}if(c.action==="commit"&&c.message){let r=z(c.message);S(r.success,r.hash,r.error);return}break}case"copy":{await U(s.suggestion.message),e.silent||u.info('\u{1F3AF} Voc\xEA pode usar a mensagem copiada com: git commit -m "mensagem"');return}case"cancel":{R();return}}}async function rt(e,t,o){o.silent||u.info("\u{1F504} Modo Split ativado - Commits separados por arquivo");let i=[...e.stagedFiles];for(;i.length>0;){let n=o.yes?[i[0]]:await B(i);if(n.length===0){o.silent||u.info("\u274C Nenhum arquivo selecionado");break}let{getFileDiff:s}=await Promise.resolve().then(()=>(w(),M)),a=n.filter(r=>r!==void 0).map(r=>{try{return s(r)}catch(l){return u.error(`\u274C Erro ao obter diff do arquivo ${r}: ${l instanceof Error?l.message:"Erro desconhecido"}`),""}}).filter(r=>r.length>0).join(`
116
- `);if(!a){o.silent||u.warn("\u26A0\uFE0F Nenhum diff encontrado para os arquivos selecionados"),i=i.filter(r=>!n.includes(r));continue}o.silent||u.info(`\u{1F916} Gerando commit para: ${n.join(", ")}`);let c=await q(a,t,n.filter(r=>r!==void 0));if(!c.success){u.error(`\u274C Erro ao gerar commit: ${c.error}`),i=i.filter(r=>!n.includes(r));continue}if(!c.suggestion){u.error("\u274C Nenhuma sugest\xE3o foi gerada"),i=i.filter(r=>!n.includes(r));continue}if(t.dryRun){u.info(`\u{1F50D} Dry Run - Mensagem para ${n.join(", ")}:`),u.info(`"${c.suggestion.message}"`),i=i.filter(r=>!n.includes(r));continue}if(o.yes){let r=n.length===1&&n[0]?await k(n[0],c.suggestion.message):await z(c.suggestion.message);S(r.success,r.hash,r.error)}else{let r=await N(c.suggestion);if(r.action==="commit"){let l=n.length===1&&n[0]?await k(n[0],c.suggestion.message):await z(c.suggestion.message);S(l.success,l.hash,l.error)}else if(r.action==="edit"){let l=await L(c.suggestion.message);if(l.action==="commit"&&l.message){let m=n.length===1&&n[0]?await k(n[0],l.message):await z(l.message);S(m.success,m.hash,m.error)}}else if(r.action==="copy")await U(c.suggestion.message),o.silent||u.info("\u{1F3AF} Mensagem copiada para clipboard");else if(r.action==="cancel"){R();return}}if(i=i.filter(r=>!n.includes(r)),i.length>0&&!o.yes&&!await Y(i))break}o.silent||u.success("\u2705 Modo Split conclu\xEDdo!")}var se=A(()=>{"use strict";f();pe();w();W();O();ee();Re();ie()});f();se();import{intro as ct,outro as mt,log as lt}from"@clack/prompts";f();f();import{readFileSync as at}from"fs";import{join as re}from"path";function Ie(){try{let e=[re(process.cwd(),"package.json"),re(process.cwd(),"..","package.json"),re(C,"..","..","package.json")];for(let t of e)try{let o=at(t,"utf-8"),i=JSON.parse(o);if(i.name==="@gilbert_oliveira/commit-wizard")return i.version}catch{continue}throw new Error("Package.json n\xE3o encontrado")}catch{return console.warn("\u26A0\uFE0F N\xE3o foi poss\xEDvel ler a vers\xE3o do package.json, usando vers\xE3o padr\xE3o"),"0.0.0"}}function je(e){return{silent:e.includes("--silent")||e.includes("-s"),yes:e.includes("--yes")||e.includes("-y"),auto:e.includes("--auto")||e.includes("-a"),split:e.includes("--split"),smartSplit:e.includes("--smart-split"),dryRun:e.includes("--dry-run")||e.includes("-n"),help:e.includes("--help")||e.includes("-h"),version:e.includes("--version")||e.includes("-v")}}function ke(){console.log(`
109
+ `)}`}catch{return""}}).filter(s=>s.length>0).join(`
110
+ `)}let o=i.join(`
111
+ `),n=8e3;return o.length>n?o.substring(0,n)+`
112
+ ... (diff total truncado)`:o}async function se(e,t,i){i.silent||g.info("\u{1F9E0} Modo Smart Split ativado - Agrupando arquivos por contexto"),i.silent||g.info("\u{1F916} Analisando contexto das mudan\xE7as...");let o=await dt(e.stagedFiles,e.diff,t);if(!o.success){i.silent||g.error(`\u274C Erro na an\xE1lise de contexto: ${o.error}`);return}if(!o.groups||o.groups.length===0){i.silent||g.error("\u274C Nenhum grupo foi criado pela an\xE1lise");return}if(i.silent||(g.success(`\u2705 ${o.groups.length} grupo(s) identificado(s):`),o.groups.forEach((n,r)=>{g.info(` ${r+1}. ${n.name} (${n.files.length} arquivo(s))`),g.info(` \u{1F4C4} ${n.files.join(", ")}`)})),!i.yes&&!i.silent){let{showSmartSplitGroups:n}=await Promise.resolve().then(()=>(ie(),Ie)),r=await n(o.groups);if(r.action==="cancel"){i.silent||g.info("\u274C Opera\xE7\xE3o cancelada pelo usu\xE1rio");return}if(r.action==="manual"){let a={...i,split:!0,smartSplit:!1},{main:c}=await Promise.resolve().then(()=>(re(),Te));await c(a);return}}for(let n=0;n<o.groups.length;n++){let r=o.groups[n];if(!r){i.silent||g.error(`\u274C Grupo ${n+1} \xE9 undefined`);continue}i.silent||g.info(`
113
+ \u{1F504} Processando grupo ${n+1}/${o.groups.length}: ${r.name}`);let a=await pt(r);if(!a){i.silent||(g.warn(`\u26A0\uFE0F Nenhum diff encontrado para o grupo: ${r.name}`),g.info(` \u{1F4C4} Arquivos: ${r.files.join(", ")}`),g.info(" \u{1F4A1} Poss\xEDvel causa: arquivos novos, deletados/recriados, ou sem mudan\xE7as"));continue}i.silent||g.info(`\u{1F916} Gerando commit para: ${r.name}`);let{generateWithRetry:c}=await Promise.resolve().then(()=>(Y(),Ee)),s=await c(a,t,r.files);if(!s.success){i.silent||g.error(`\u274C Erro ao gerar commit para ${r.name}: ${s.error}`);continue}if(!s.suggestion){i.silent||g.error(`\u274C Nenhuma sugest\xE3o gerada para ${r.name}`);continue}if(t.dryRun){i.silent||(g.info(`\u{1F50D} Dry Run - Grupo: ${r.name}`),g.info(`\u{1F4C4} Arquivos: ${r.files.join(", ")}`),g.info(`\u{1F4AD} Mensagem: "${s.suggestion.message}"`));continue}if(i.yes){let{executeFileCommit:l}=await Promise.resolve().then(()=>($(),M)),m;if(r.files.length===1&&r.files[0])m=l(r.files[0],s.suggestion.message||"");else{let{execSync:d}=await import("child_process"),{escapeShellArg:h}=await Promise.resolve().then(()=>($(),M));try{let b=r.files.map(v=>h(v)).join(" "),C=h(s.suggestion.message||"");d(`git commit ${b} -m ${C}`,{stdio:"pipe"}),m={success:!0,hash:d("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:s.suggestion.message||""}}catch(b){m={success:!1,error:b instanceof Error?b.message:"Erro desconhecido ao executar commit"}}}x(m.success,m.hash,m.error)}else{let{showCommitPreview:l,editCommitMessage:m,copyToClipboard:d,showCancellation:h}=await Promise.resolve().then(()=>(k(),X));switch((await l(s.suggestion)).action){case"commit":{let{executeFileCommit:C}=await Promise.resolve().then(()=>($(),M)),p,v=s.suggestion.message||"Atualiza\xE7\xE3o de arquivos";if(r.files.length===1&&r.files[0])p=C(r.files[0],v);else{let{execSync:R}=await import("child_process"),{escapeShellArg:j}=await Promise.resolve().then(()=>($(),M));try{let A=r.files.map(W=>j(W)).join(" "),H=j(v);R(`git commit ${A} -m ${H}`,{stdio:"pipe"}),p={success:!0,hash:R("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:v}}catch(A){p={success:!1,error:A instanceof Error?A.message:"Erro desconhecido ao executar commit"}}}x(p.success,p.hash,p.error);break}case"edit":{let C=await m(s.suggestion.message);if(C.action==="commit"&&C.message){let{executeFileCommit:p}=await Promise.resolve().then(()=>($(),M)),v;if(r.files.length===1&&r.files[0])v=p(r.files[0],C.message||"");else{let{execSync:R}=await import("child_process"),{escapeShellArg:j}=await Promise.resolve().then(()=>($(),M));try{let A=r.files.map(W=>j(W)).join(" "),H=j(C.message||"");R(`git commit ${A} -m ${H}`,{stdio:"pipe"}),v={success:!0,hash:R("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:C.message||""}}catch(A){v={success:!1,error:A instanceof Error?A.message:"Erro desconhecido ao executar commit"}}}x(v.success,v.hash,v.error)}break}case"copy":{await d(s.suggestion.message),i.silent||g.info("\u{1F3AF} Mensagem copiada para clipboard");break}case"cancel":{h();return}}}if(n<o.groups.length-1&&!i.yes){let{askContinueCommits:l}=await Promise.resolve().then(()=>(k(),X)),m=o.groups.slice(n+1).filter(h=>h!==void 0).map(h=>h.name);if(!await l(m))break}}i.silent||g.success("\u2705 Smart Split conclu\xEDdo!")}var De=w(()=>{"use strict";f();ne();k()});import{existsSync as ae,writeFileSync as gt}from"fs";import{join as _}from"path";import{spawnSync as ht}from"child_process";function qe(e){let t=e||process.cwd();for(let i of yt){let o=_(t,i);if(ae(o))return o}return null}function ce(e){let t=e||process.cwd(),i=_(t,"node_modules",".bin","commitlint");return ae(i)}function Ne(e,t){let i=t||process.cwd(),o=_(i,"node_modules",".bin","commitlint"),n=ht(o,[],{input:e,encoding:"utf-8",cwd:i});if(n.status===0)return{valid:!0,errors:[],warnings:[]};let a=((n.stdout||"")+(n.stderr||"")).split(`
114
+ `),c=[],s=[];for(let l of a){let m=l.trim();m&&(m.startsWith("\u2716")||m.includes("[error]")?c.push(m):(m.startsWith("\u26A0")||m.includes("[warning]"))&&s.push(m))}if(c.length===0&&n.status!==0){let l=a.map(m=>m.trim()).filter(m=>m.length>0&&!m.startsWith("\u29D7")&&!m.startsWith("\u2714"));c.push(...l)}return{valid:!1,errors:c,warnings:s}}function Le(e){let t=e||process.cwd(),i=_(t,"commitlint.config.js");if(ae(i))throw new Error("commitlint.config.js j\xE1 existe neste diret\xF3rio. Remova-o antes de continuar.");gt(i,`export default {
115
+ extends: ['@commitlint/config-conventional'],
116
+ };
117
+ `,"utf-8")}var yt,me=w(()=>{"use strict";f();yt=["commitlint.config.js","commitlint.config.ts","commitlint.config.mjs","commitlint.config.cjs",".commitlintrc",".commitlintrc.js",".commitlintrc.json",".commitlintrc.yml",".commitlintrc.yaml"]});var Te={};G(Te,{main:()=>ue});import{log as u}from"@clack/prompts";function Ct(e,t){return!t.commitlint.enabled||!ce()?!1:e.validate?!0:qe()!==null}function le(e,t){let i=Ne(e);return i.valid?(t||u.success("\u2705 Mensagem validada pelo commitlint!"),!0):(u.error("\u274C A mensagem n\xE3o passou na valida\xE7\xE3o do commitlint:"),i.errors.forEach(o=>u.error(` ${o}`)),i.warnings.length>0&&i.warnings.forEach(o=>u.info(` \u26A0\uFE0F ${o}`)),!1)}async function ue(e={silent:!1,yes:!1,auto:!1,split:!1,smartSplit:!1,dryRun:!1,help:!1,version:!1,validate:!1,initCommitlint:!1}){e.silent||u.info("\u{1F680} Commit Wizard iniciado!"),J()||(u.error("\u274C N\xE3o foi encontrado um reposit\xF3rio Git neste diret\xF3rio."),e.silent||u.info("\u{1F4A1} Execute o comando em um diret\xF3rio com reposit\xF3rio Git inicializado."),process.exit(1)),e.silent||u.info("\u2699\uFE0F Carregando configura\xE7\xE3o...");let t=ye();je(t),e.split&&(t.splitCommits=!0),e.dryRun&&(t.dryRun=!0);let i=Ce(t);i.length>0&&(u.error("\u274C Erros na configura\xE7\xE3o:"),i.forEach(c=>u.error(` \u2022 ${c}`)),process.exit(1)),e.silent||u.success(`\u2705 Configura\xE7\xE3o carregada (modelo: ${t.openai.model}, idioma: ${t.language})`);let o=Ct(e,t);e.silent||(e.validate&&!ce()?u.info("\u26A0\uFE0F --validate especificado mas commitlint n\xE3o est\xE1 instalado. Valida\xE7\xE3o ignorada."):o&&u.info("\u{1F50D} Valida\xE7\xE3o commitlint ativa")),e.silent||u.info("\u{1F4CB} Verificando arquivos staged...");let n=K();n.hasStaged||(u.warn("\u26A0\uFE0F Nenhum arquivo foi encontrado no stage."),e.silent||u.info("\u{1F4A1} Use `git add <arquivo>` para adicionar arquivos ao stage antes de gerar o commit."),process.exit(0));let r=B();if(e.silent||(u.success(`\u2705 Encontrados ${n.stagedFiles.length} arquivo(s) staged:`),n.stagedFiles.forEach(c=>u.info(` \u{1F4C4} ${c}`)),u.info(`\u{1F4CA} Estat\xEDsticas: +${r.added} -${r.removed} linhas`)),t.splitCommits||e.smartSplit){if(e.yes)return await se(n,t,e);switch((await te()).action){case"proceed":return await se(n,t,e);case"manual":return await vt(n,t,e);case"cancel":I();return}}e.silent||u.info("\u{1F916} Gerando mensagem de commit com IA...");let a=await T(n.diff,t,n.stagedFiles);if(a.success||(u.error(`\u274C Erro ao gerar commit: ${a.error}`),process.exit(1)),a.suggestion||(u.error("\u274C Nenhuma sugest\xE3o foi gerada"),process.exit(1)),e.silent||u.success("\u2728 Mensagem de commit gerada!"),t.dryRun){u.info("\u{1F50D} Modo Dry Run - Mensagem gerada:"),u.info(`"${a.suggestion.message}"`),u.info("\u{1F4A1} Execute sem --dry-run para fazer o commit");return}if(e.yes){o&&(le(a.suggestion.message,e.silent)||process.exit(1));let c=z(a.suggestion.message);x(c.success,c.hash,c.error);return}for(;;)switch((await N(a.suggestion)).action){case"commit":{if(o&&!le(a.suggestion.message,e.silent))break;let s=z(a.suggestion.message);x(s.success,s.hash,s.error);return}case"edit":{let s=await L(a.suggestion.message);if(s.action==="cancel"){I();return}if(s.action==="commit"&&s.message){if(o&&!le(s.message,e.silent))break;let l=z(s.message);x(l.success,l.hash,l.error);return}break}case"copy":{await U(a.suggestion.message),e.silent||u.info('\u{1F3AF} Voc\xEA pode usar a mensagem copiada com: git commit -m "mensagem"');return}case"cancel":{I();return}}}async function vt(e,t,i){i.silent||u.info("\u{1F504} Modo Split ativado - Commits separados por arquivo");let o=[...e.stagedFiles];for(;o.length>0;){let n=i.yes?[o[0]]:await Q(o);if(n.length===0){i.silent||u.info("\u274C Nenhum arquivo selecionado");break}let{getFileDiff:r}=await Promise.resolve().then(()=>($(),M)),a=n.filter(s=>s!==void 0).map(s=>{try{return r(s)}catch(l){return u.error(`\u274C Erro ao obter diff do arquivo ${s}: ${l instanceof Error?l.message:"Erro desconhecido"}`),""}}).filter(s=>s.length>0).join(`
118
+ `);if(!a){i.silent||u.warn("\u26A0\uFE0F Nenhum diff encontrado para os arquivos selecionados"),o=o.filter(s=>!n.includes(s));continue}i.silent||u.info(`\u{1F916} Gerando commit para: ${n.join(", ")}`);let c=await T(a,t,n.filter(s=>s!==void 0));if(!c.success){u.error(`\u274C Erro ao gerar commit: ${c.error}`),o=o.filter(s=>!n.includes(s));continue}if(!c.suggestion){u.error("\u274C Nenhuma sugest\xE3o foi gerada"),o=o.filter(s=>!n.includes(s));continue}if(t.dryRun){u.info(`\u{1F50D} Dry Run - Mensagem para ${n.join(", ")}:`),u.info(`"${c.suggestion.message}"`),o=o.filter(s=>!n.includes(s));continue}if(i.yes){let s=n.length===1&&n[0]?await O(n[0],c.suggestion.message):await z(c.suggestion.message);x(s.success,s.hash,s.error)}else{let s=await N(c.suggestion);if(s.action==="commit"){let l=n.length===1&&n[0]?await O(n[0],c.suggestion.message):await z(c.suggestion.message);x(l.success,l.hash,l.error)}else if(s.action==="edit"){let l=await L(c.suggestion.message);if(l.action==="commit"&&l.message){let m=n.length===1&&n[0]?await O(n[0],l.message):await z(l.message);x(m.success,m.hash,m.error)}}else if(s.action==="copy")await U(c.suggestion.message),i.silent||u.info("\u{1F3AF} Mensagem copiada para clipboard");else if(s.action==="cancel"){I();return}}if(o=o.filter(s=>!n.includes(s)),o.length>0&&!i.yes&&!await Z(o))break}i.silent||u.success("\u2705 Modo Split conclu\xEDdo!")}var re=w(()=>{"use strict";f();ve();$();Y();k();ie();De();ne();me()});f();re();import{intro as bt,outro as St,log as V}from"@clack/prompts";f();f();import{readFileSync as xt}from"fs";import{join as fe}from"path";function Ue(){try{let e=[fe(process.cwd(),"package.json"),fe(process.cwd(),"..","package.json"),fe(y,"..","..","package.json")];for(let t of e)try{let i=xt(t,"utf-8"),o=JSON.parse(i);if(o.name==="@gilbert_oliveira/commit-wizard")return o.version}catch{continue}throw new Error("Package.json n\xE3o encontrado")}catch{return console.warn("\u26A0\uFE0F N\xE3o foi poss\xEDvel ler a vers\xE3o do package.json, usando vers\xE3o padr\xE3o"),"0.0.0"}}function _e(e){return{silent:e.includes("--silent")||e.includes("-s"),yes:e.includes("--yes")||e.includes("-y"),auto:e.includes("--auto")||e.includes("-a"),split:e.includes("--split"),smartSplit:e.includes("--smart-split"),dryRun:e.includes("--dry-run")||e.includes("-n"),help:e.includes("--help")||e.includes("-h"),version:e.includes("--version")||e.includes("-v"),validate:e.includes("--validate"),initCommitlint:e.includes("--init-commitlint")}}function Ve(){console.log(`
117
119
  \u{1F9D9}\u200D\u2642\uFE0F Commit Wizard - Gerador inteligente de mensagens de commit
118
120
 
119
121
  USAGE:
120
122
  commit-wizard [OPTIONS]
121
123
 
122
124
  OPTIONS:
123
- -s, --silent Modo silencioso (sem logs detalhados)
124
- -y, --yes Confirmar automaticamente sem prompts
125
- -a, --auto Modo autom\xE1tico (--yes + --silent)
126
- --split Modo split manual (commits separados por arquivo)
127
- --smart-split Modo smart split (IA agrupa por contexto)
128
- -n, --dry-run Visualizar mensagem sem fazer commit
129
- -h, --help Mostrar esta ajuda
130
- -v, --version Mostrar vers\xE3o
125
+ -s, --silent Modo silencioso (sem logs detalhados)
126
+ -y, --yes Confirmar automaticamente sem prompts
127
+ -a, --auto Modo autom\xE1tico (--yes + --silent)
128
+ --split Modo split manual (commits separados por arquivo)
129
+ --smart-split Modo smart split (IA agrupa por contexto)
130
+ -n, --dry-run Visualizar mensagem sem fazer commit
131
+ --validate Habilitar valida\xE7\xE3o com commitlint
132
+ --init-commitlint Criar configura\xE7\xE3o padr\xE3o do commitlint
133
+ -h, --help Mostrar esta ajuda
134
+ -v, --version Mostrar vers\xE3o
131
135
 
132
136
  EXAMPLES:
133
137
  commit-wizard # Modo interativo padr\xE3o
@@ -136,6 +140,8 @@ EXAMPLES:
136
140
  commit-wizard --smart-split # Smart split com IA
137
141
  commit-wizard --dry-run # Apenas visualizar mensagem
138
142
  commit-wizard --auto # Modo totalmente autom\xE1tico
143
+ commit-wizard --validate # Habilitar valida\xE7\xE3o com commitlint
144
+ commit-wizard --init-commitlint # Criar configura\xE7\xE3o padr\xE3o do commitlint
139
145
 
140
146
  Para mais informa\xE7\xF5es, visite: https://github.com/user/commit-wizard
141
- `)}function Oe(){let e=Ie();console.log(`commit-wizard v${e}`)}async function ut(){try{let e=je(process.argv.slice(2));e.help&&(ke(),process.exit(0)),e.version&&(Oe(),process.exit(0)),e.auto&&(e.silent=!0,e.yes=!0),e.silent||ct("\u{1F9D9}\u200D\u2642\uFE0F Commit Wizard"),await ne(e),e.silent||mt("At\xE9 logo! \u2728")}catch(e){lt.error(`Erro: ${e instanceof Error?e.message:"Erro desconhecido"}`),process.exit(1)}}ut();
147
+ `)}function He(){let e=Ue();console.log(`commit-wizard v${e}`)}me();async function wt(){try{let e=_e(process.argv.slice(2));if(e.help&&(Ve(),process.exit(0)),e.version&&(He(),process.exit(0)),e.initCommitlint){try{Le(),V.success("\u2705 commitlint.config.js criado com sucesso!"),V.info("\u{1F4A1} Instale as depend\xEAncias necess\xE1rias: npm install --save-dev @commitlint/cli @commitlint/config-conventional")}catch(t){V.error(`\u274C ${t instanceof Error?t.message:"Erro ao criar configura\xE7\xE3o do commitlint"}`),process.exit(1)}process.exit(0)}e.auto&&(e.silent=!0,e.yes=!0),e.silent||bt("\u{1F9D9}\u200D\u2642\uFE0F Commit Wizard"),await ue(e),e.silent||St("At\xE9 logo! \u2728")}catch(e){V.error(`Erro: ${e instanceof Error?e.message:"Erro desconhecido"}`),process.exit(1)}}wt();
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "displayName": "Commit Wizard",
4
4
  "publisher": "gilbert-oliveira",
5
5
  "description": "CLI inteligente para gerar mensagens de commit usando OpenAI",
6
- "version": "2.12.1",
6
+ "version": "2.13.0-canary.1",
7
7
  "categories": [
8
8
  "Other",
9
9
  "SCM Providers"
@@ -3,6 +3,7 @@
3
3
  import { intro, outro, log } from '@clack/prompts';
4
4
  import { main } from '@core/index';
5
5
  import { parseArgs, showHelp, showVersion } from '../utils/args';
6
+ import { initCommitlintConfig } from '../commitlint/index';
6
7
 
7
8
  async function run() {
8
9
  try {
@@ -21,6 +22,25 @@ async function run() {
21
22
  process.exit(0);
22
23
  }
23
24
 
25
+ // Inicializar configuração do commitlint
26
+ if (args.initCommitlint) {
27
+ try {
28
+ initCommitlintConfig();
29
+ log.success(
30
+ '✅ commitlint.config.js criado com sucesso!'
31
+ );
32
+ log.info(
33
+ '💡 Instale as dependências necessárias: npm install --save-dev @commitlint/cli @commitlint/config-conventional'
34
+ );
35
+ } catch (error) {
36
+ log.error(
37
+ `❌ ${error instanceof Error ? error.message : 'Erro ao criar configuração do commitlint'}`
38
+ );
39
+ process.exit(1);
40
+ }
41
+ process.exit(0);
42
+ }
43
+
24
44
  // Modo automático = silent + yes
25
45
  if (args.auto) {
26
46
  args.silent = true;
@@ -0,0 +1,99 @@
1
+ import { existsSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { spawnSync } from 'child_process';
4
+
5
+ export interface CommitlintResult {
6
+ valid: boolean;
7
+ errors: string[];
8
+ warnings: string[];
9
+ }
10
+
11
+ const COMMITLINT_CONFIG_FILES = [
12
+ 'commitlint.config.js',
13
+ 'commitlint.config.ts',
14
+ 'commitlint.config.mjs',
15
+ 'commitlint.config.cjs',
16
+ '.commitlintrc',
17
+ '.commitlintrc.js',
18
+ '.commitlintrc.json',
19
+ '.commitlintrc.yml',
20
+ '.commitlintrc.yaml',
21
+ ];
22
+
23
+ export function findCommitlintConfig(cwd?: string): string | null {
24
+ const dir = cwd || process.cwd();
25
+ for (const file of COMMITLINT_CONFIG_FILES) {
26
+ const fullPath = join(dir, file);
27
+ if (existsSync(fullPath)) {
28
+ return fullPath;
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+
34
+ export function isCommitlintInstalled(cwd?: string): boolean {
35
+ const dir = cwd || process.cwd();
36
+ const binPath = join(dir, 'node_modules', '.bin', 'commitlint');
37
+ return existsSync(binPath);
38
+ }
39
+
40
+ export function validateCommitMessage(
41
+ message: string,
42
+ cwd?: string
43
+ ): CommitlintResult {
44
+ const dir = cwd || process.cwd();
45
+ const binPath = join(dir, 'node_modules', '.bin', 'commitlint');
46
+
47
+ const result = spawnSync(binPath, [], {
48
+ input: message,
49
+ encoding: 'utf-8',
50
+ cwd: dir,
51
+ });
52
+
53
+ if (result.status === 0) {
54
+ return { valid: true, errors: [], warnings: [] };
55
+ }
56
+
57
+ const output = (result.stdout || '') + (result.stderr || '');
58
+ const lines = output.split('\n');
59
+ const errors: string[] = [];
60
+ const warnings: string[] = [];
61
+
62
+ for (const line of lines) {
63
+ const trimmed = line.trim();
64
+ if (!trimmed) continue;
65
+ if (trimmed.startsWith('✖') || trimmed.includes('[error]')) {
66
+ errors.push(trimmed);
67
+ } else if (trimmed.startsWith('⚠') || trimmed.includes('[warning]')) {
68
+ warnings.push(trimmed);
69
+ }
70
+ }
71
+
72
+ // If we couldn't parse individual errors, include raw output
73
+ if (errors.length === 0 && result.status !== 0) {
74
+ const rawErrors = lines
75
+ .map((l) => l.trim())
76
+ .filter((l) => l.length > 0 && !l.startsWith('⧗') && !l.startsWith('✔'));
77
+ errors.push(...rawErrors);
78
+ }
79
+
80
+ return { valid: false, errors, warnings };
81
+ }
82
+
83
+ export function initCommitlintConfig(cwd?: string): void {
84
+ const dir = cwd || process.cwd();
85
+ const configPath = join(dir, 'commitlint.config.js');
86
+
87
+ if (existsSync(configPath)) {
88
+ throw new Error(
89
+ 'commitlint.config.js já existe neste diretório. Remova-o antes de continuar.'
90
+ );
91
+ }
92
+
93
+ const content = `export default {
94
+ extends: ['@commitlint/config-conventional'],
95
+ };
96
+ `;
97
+
98
+ writeFileSync(configPath, content, 'utf-8');
99
+ }
@@ -30,6 +30,9 @@ export interface Config {
30
30
  ttl: number; // Time to live em minutos
31
31
  maxSize: number; // Número máximo de entradas
32
32
  };
33
+ commitlint: {
34
+ enabled: boolean;
35
+ };
33
36
  }
34
37
 
35
38
  const DEFAULT_CONFIG: Config = {
@@ -56,6 +59,9 @@ const DEFAULT_CONFIG: Config = {
56
59
  ttl: 60, // 1 hora
57
60
  maxSize: 100,
58
61
  },
62
+ commitlint: {
63
+ enabled: true,
64
+ },
59
65
  };
60
66
 
61
67
  // Adicionar interface para userConfig
@@ -68,6 +74,7 @@ interface UserConfig {
68
74
  dryRun?: boolean;
69
75
  smartSplit?: Partial<Config['smartSplit']>;
70
76
  cache?: Partial<Config['cache']>;
77
+ commitlint?: Partial<Config['commitlint']>;
71
78
  }
72
79
 
73
80
  export function loadConfig(configPath?: string): Config {
@@ -142,6 +149,10 @@ function mergeConfig(defaultConfig: Config, userConfig: UserConfig): Config {
142
149
  ...defaultConfig.cache,
143
150
  ...userConfig.cache,
144
151
  },
152
+ commitlint: {
153
+ ...defaultConfig.commitlint,
154
+ ...userConfig.commitlint,
155
+ },
145
156
  };
146
157
  }
147
158
 
package/src/core/index.ts CHANGED
@@ -22,6 +22,45 @@ import { handleSmartSplitMode } from './smart-split';
22
22
  import { initializeCache } from './cache';
23
23
  import type { CLIArgs } from '../utils/args';
24
24
  import type { Config } from '../config/index';
25
+ import {
26
+ isCommitlintInstalled,
27
+ findCommitlintConfig,
28
+ validateCommitMessage,
29
+ } from '../commitlint/index';
30
+
31
+ function shouldRunCommitlint(args: CLIArgs, config: Config): boolean {
32
+ // Explicitly disabled in config
33
+ if (!config.commitlint.enabled) return false;
34
+
35
+ // Commitlint binary must be installed
36
+ if (!isCommitlintInstalled()) return false;
37
+
38
+ // Explicitly enabled via --validate flag
39
+ if (args.validate) return true;
40
+
41
+ // Auto-detect: validate if a commitlint config file exists
42
+ return findCommitlintConfig() !== null;
43
+ }
44
+
45
+ function runCommitlintValidation(
46
+ message: string,
47
+ silent: boolean
48
+ ): boolean {
49
+ const result = validateCommitMessage(message);
50
+ if (result.valid) {
51
+ if (!silent) {
52
+ log.success('✅ Mensagem validada pelo commitlint!');
53
+ }
54
+ return true;
55
+ }
56
+
57
+ log.error('❌ A mensagem não passou na validação do commitlint:');
58
+ result.errors.forEach((err) => log.error(` ${err}`));
59
+ if (result.warnings.length > 0) {
60
+ result.warnings.forEach((warn) => log.info(` ⚠️ ${warn}`));
61
+ }
62
+ return false;
63
+ }
25
64
 
26
65
  export async function main(
27
66
  args: CLIArgs = {
@@ -33,6 +72,8 @@ export async function main(
33
72
  dryRun: false,
34
73
  help: false,
35
74
  version: false,
75
+ validate: false,
76
+ initCommitlint: false,
36
77
  }
37
78
  ) {
38
79
  if (!args.silent) {
@@ -81,6 +122,18 @@ export async function main(
81
122
  );
82
123
  }
83
124
 
125
+ // Informar sobre validação commitlint
126
+ const useCommitlint = shouldRunCommitlint(args, config);
127
+ if (!args.silent) {
128
+ if (args.validate && !isCommitlintInstalled()) {
129
+ log.info(
130
+ '⚠️ --validate especificado mas commitlint não está instalado. Validação ignorada.'
131
+ );
132
+ } else if (useCommitlint) {
133
+ log.info('🔍 Validação commitlint ativa');
134
+ }
135
+ }
136
+
84
137
  // Verificar arquivos staged
85
138
  if (!args.silent) {
86
139
  log.info('📋 Verificando arquivos staged...');
@@ -164,6 +217,15 @@ export async function main(
164
217
 
165
218
  // Modo automático: commit direto
166
219
  if (args.yes) {
220
+ if (useCommitlint) {
221
+ const valid = runCommitlintValidation(
222
+ result.suggestion.message,
223
+ args.silent
224
+ );
225
+ if (!valid) {
226
+ process.exit(1);
227
+ }
228
+ }
167
229
  const commitResult = executeCommit(result.suggestion.message);
168
230
  showCommitResult(
169
231
  commitResult.success,
@@ -179,6 +241,14 @@ export async function main(
179
241
 
180
242
  switch (uiAction.action) {
181
243
  case 'commit': {
244
+ // Validar com commitlint se necessário
245
+ if (useCommitlint) {
246
+ const valid = runCommitlintValidation(
247
+ result.suggestion.message,
248
+ args.silent
249
+ );
250
+ if (!valid) break; // Voltar ao menu para editar
251
+ }
182
252
  // Commit direto com mensagem gerada
183
253
  const commitResult = executeCommit(result.suggestion.message);
184
254
  showCommitResult(
@@ -196,6 +266,13 @@ export async function main(
196
266
  return;
197
267
  }
198
268
  if (editAction.action === 'commit' && editAction.message) {
269
+ if (useCommitlint) {
270
+ const valid = runCommitlintValidation(
271
+ editAction.message,
272
+ args.silent
273
+ );
274
+ if (!valid) break; // Voltar ao menu para editar
275
+ }
199
276
  const editCommitResult = executeCommit(editAction.message);
200
277
  showCommitResult(
201
278
  editCommitResult.success,
@@ -328,22 +328,13 @@ export function detectCommitType(
328
328
  * Processa a mensagem retornada pela OpenAI removendo formatação desnecessária
329
329
  */
330
330
  export function processOpenAIMessage(message: string): string {
331
- // Remover backticks de código quando a mensagem começa com um bloco de código
332
- // Isso cobre tanto o caso em que toda a mensagem está envolvida em backticks,
333
- // quanto o caso em que há texto de corpo após o bloco de código (ex: ```\nfix: ...\n```\n\nCorpo)
334
- if (message.startsWith('```')) {
335
- // Tentar extrair conteúdo de um bloco de código com possível texto de corpo após ele
336
- const blockWithBodyMatch = message.match(/^```[^\n`]*\n([\s\S]*?)\n\s*```([\s\S]*)$/);
337
- if (blockWithBodyMatch) {
338
- const codeContent = blockWithBodyMatch[1].trim();
339
- const afterBlock = blockWithBodyMatch[2].trim();
340
- message = afterBlock ? `${codeContent}\n\n${afterBlock}` : codeContent;
341
- } else if (message.match(/^```[\s\S]*```$/)) {
342
- // Fallback para blocos sem quebra de linha (ex: ```feat: ...```)
343
- message = message
344
- .replace(/^```(?:plaintext|javascript|typescript|python|java|html|css|json|xml|yaml|yml|bash|shell|text)?\s*/, '')
345
- .replace(/\s*```$/, '');
346
- }
331
+ // Remover backticks de código APENAS se a mensagem inteira está envolvida em backticks
332
+ // Isso preserva nomes de funções, métodos e variáveis dentro da mensagem
333
+ if (message.match(/^```[\s\S]*```$/)) {
334
+ // Se a mensagem inteira está em um bloco de código, remover as marcações
335
+ message = message
336
+ .replace(/^```(?:plaintext|javascript|typescript|python|java|html|css|json|xml|yaml|yml|bash|shell|text)?\s*/, '')
337
+ .replace(/\s*```$/, '');
347
338
  }
348
339
 
349
340
  // Remover quebras de linha extras do início e fim
package/src/utils/args.ts CHANGED
@@ -7,6 +7,8 @@ export interface CLIArgs {
7
7
  dryRun: boolean;
8
8
  help: boolean;
9
9
  version: boolean;
10
+ validate: boolean;
11
+ initCommitlint: boolean;
10
12
  }
11
13
 
12
14
  export function parseArgs(args: string[]): CLIArgs {
@@ -19,6 +21,8 @@ export function parseArgs(args: string[]): CLIArgs {
19
21
  dryRun: args.includes('--dry-run') || args.includes('-n'),
20
22
  help: args.includes('--help') || args.includes('-h'),
21
23
  version: args.includes('--version') || args.includes('-v'),
24
+ validate: args.includes('--validate'),
25
+ initCommitlint: args.includes('--init-commitlint'),
22
26
  };
23
27
  }
24
28
 
@@ -30,14 +34,16 @@ USAGE:
30
34
  commit-wizard [OPTIONS]
31
35
 
32
36
  OPTIONS:
33
- -s, --silent Modo silencioso (sem logs detalhados)
34
- -y, --yes Confirmar automaticamente sem prompts
35
- -a, --auto Modo automático (--yes + --silent)
36
- --split Modo split manual (commits separados por arquivo)
37
- --smart-split Modo smart split (IA agrupa por contexto)
38
- -n, --dry-run Visualizar mensagem sem fazer commit
39
- -h, --help Mostrar esta ajuda
40
- -v, --version Mostrar versão
37
+ -s, --silent Modo silencioso (sem logs detalhados)
38
+ -y, --yes Confirmar automaticamente sem prompts
39
+ -a, --auto Modo automático (--yes + --silent)
40
+ --split Modo split manual (commits separados por arquivo)
41
+ --smart-split Modo smart split (IA agrupa por contexto)
42
+ -n, --dry-run Visualizar mensagem sem fazer commit
43
+ --validate Habilitar validação com commitlint
44
+ --init-commitlint Criar configuração padrão do commitlint
45
+ -h, --help Mostrar esta ajuda
46
+ -v, --version Mostrar versão
41
47
 
42
48
  EXAMPLES:
43
49
  commit-wizard # Modo interativo padrão
@@ -46,6 +52,8 @@ EXAMPLES:
46
52
  commit-wizard --smart-split # Smart split com IA
47
53
  commit-wizard --dry-run # Apenas visualizar mensagem
48
54
  commit-wizard --auto # Modo totalmente automático
55
+ commit-wizard --validate # Habilitar validação com commitlint
56
+ commit-wizard --init-commitlint # Criar configuração padrão do commitlint
49
57
 
50
58
  Para mais informações, visite: https://github.com/user/commit-wizard
51
59
  `);