@gilbert_oliveira/commit-wizard 2.13.0-canary.2 → 2.13.0-canary.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,23 @@
1
1
  #!/usr/bin/env node
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 $t from"path";import{fileURLToPath as Et}from"url";var f=w(()=>{"use strict"});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={...Ke};try{if(de(i)){let s=pe(i,"utf-8"),a=JSON.parse(s);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 s=pe(n,"utf-8"),a=JSON.parse(s);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 Ke,ve=w(()=>{"use strict";f();Ke={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:()=>k,getDiffStats:()=>B,getFileDiff:()=>Be,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 Be(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 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[s,a]=n.split(" ");s&&s!=="-"&&(i+=parseInt(s)||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=[],s=[];o.forEach(m=>{i.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 Ze=Object.defineProperty;var S=(e,t)=>()=>(e&&(t=e(e=0)),t);var O=(e,t)=>{for(var i in t)Ze(e,i,{get:t[i],enumerable:!0})};import Pt from"path";import{fileURLToPath as It}from"url";var p=S(()=>{"use strict"});import{existsSync as be,readFileSync as ve}from"fs";import{join as Ce}from"path";function Se(e){let t;try{t=Ce(process.cwd(),".commit-wizardrc")}catch{t="/tmp/.commit-wizardrc"}let i=Ce(process.env.HOME||process.env.USERPROFILE||"/tmp",".commit-wizardrc"),o={...Xe};try{if(be(i)){let s=ve(i,"utf-8"),r=JSON.parse(s);o=xe(o,r)}}catch{console.warn("\u26A0\uFE0F Erro ao ler configura\xE7\xE3o global: Erro desconhecido")}let n=e||t;try{if(be(n)){let s=ve(n,"utf-8"),r=JSON.parse(s);o=xe(o,r)}}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 xe(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 we(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 Xe,$e=S(()=>{"use strict";p();Xe={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 j={};O(j,{escapeShellArg:()=>T,executeCommit:()=>M,executeFileCommit:()=>G,getDiffStats:()=>Y,getFileDiff:()=>et,getGitStatus:()=>K,isGitRepository:()=>B});import{execSync as E}from"child_process";function T(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function B(){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 et(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 M(e){try{let t=T(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 G(e,t){try{let i=T(t),o=T(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 Y(){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[s,r]=n.split(" ");s&&s!=="-"&&(i+=parseInt(s)||0),r&&r!=="-"&&(o+=parseInt(r)||0)}),{added:i,removed:o,files:t.length}}catch{return{added:0,removed:0,files:0}}}var w=S(()=>{"use strict";p()});var ze={};O(ze,{buildPrompt:()=>Ee,detectCommitType:()=>Re,extractCommitTypeFromMessage:()=>je,generateCommitMessage:()=>Fe,generateWithRetry:()=>D,processOpenAIMessage:()=>Me,smartFilterDiff:()=>Ae});function Ae(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(a=>a.trim()),n=[],s=[];o.forEach(a=>{i.some(h=>h.test(a))?s.push(a):n.push(a)});let r="",l=t;for(let a of n)if(a.length<=l)r+=a,l-=a.length;else if(l>200){r+=a.substring(0,l-50),l=t-r.length;break}else break;if(!r&&s.length>0&&l>0)for(let a of s)if(a.length<=l)r+=a,l-=a.length;else if(l>200){r+=a.substring(0,l-50),l=t-r.length;break}else break;let m=`
5
+ ... (diff otimizado para focar em mudan\xE7as principais)`;if(l>200&&s.length>0){let a=`
6
6
 
7
- ... (${s.length} arquivo(s) de depend\xEAncias/build omitido(s): ${s.map(h=>{let x=h.match(/^diff --git a\/(.*?) b\//);return x?x[1]:""}).filter(Boolean).join(", ")})`,d=c-r.length;m.length<d&&(a+=m)}return a.trim()!==e.trim()?a+r:a}function be(e,t,i){let o=t.language==="pt"?"portugu\xEAs":"english",n=Ye(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}).
7
+ ... (${s.length} arquivo(s) de depend\xEAncias/build omitido(s): ${s.map(h=>{let y=h.match(/^diff --git a\/(.*?) b\//);return y?y[1]:""}).filter(Boolean).join(", ")})`,u=l-m.length;a.length<u&&(r+=a)}return r.trim()!==e.trim()?r+m:r}function Ee(e,t,i,o){let n=t.language==="pt"?"portugu\xEAs":"english",s=o!=null?tt(t.commitStyle,t.language,o):it(t.commitStyle,t.language),l=Ae(e,6e3),m=i.length>10?`${i.length} arquivos: ${i.slice(0,5).join(", ")}...`:i.join(", ");return`Gere mensagem de commit em ${n} (${t.commitStyle}).
8
8
 
9
- Arquivos: ${c}
9
+ Arquivos: ${m}
10
10
 
11
- ${n}
11
+ ${s}
12
12
 
13
13
  Diff:
14
14
  \`\`\`
15
- ${a}
15
+ ${l}
16
16
  \`\`\`
17
17
 
18
- Mensagem:`}function Ye(e,t){let i={pt:{conventional:`- Use formato: tipo(escopo): descri\xE7\xE3o
19
- - Tipos v\xE1lidos: feat, fix, docs, style, refactor, test, chore, build, ci
18
+ Mensagem:`}function tt(e,t,i){let o=t==="pt",n=[];if(e==="conventional"){n.push(o?"- Use formato: tipo(escopo): descri\xE7\xE3o":"- Use format: type(scope): description");let s=i.typeEnum?.join(", ")??Q;n.push(o?`- Tipos v\xE1lidos: ${s}`:`- Valid types: ${s}`);let r=i.headerMaxLength??72;n.push(o?`- Mantenha a primeira linha com at\xE9 ${r} caracteres`:`- Keep first line under ${r} characters`);let l=i.typeEnum?.[0]??"feat";if(n.push(o?`- Exemplo: "${l}(auth): adicionar valida\xE7\xE3o de email"`:`- Example: "${l}(auth): add email validation"`),i.subjectCase){let{condition:m,cases:c}=i.subjectCase,a=c.join(", ");m==="never"?n.push(o?`- Subject nunca em: ${a}`:`- Subject must never be: ${a}`):m==="always"&&n.push(o?`- Subject deve estar em: ${a}`:`- Subject must be in: ${a}`)}if(i.subjectFullStop){let{condition:m,value:c}=i.subjectFullStop,a=c??".";m==="never"?n.push(o?`- N\xE3o termine o subject com "${a}"`:`- Do not end subject with "${a}"`):m==="always"&&n.push(o?`- Termine o subject com "${a}"`:`- End subject with "${a}"`)}i.bodyLeadingBlank===!0&&n.push(o?"- Adicione uma linha em branco entre o header e o body":"- Add a blank line between header and body"),i.scopeEnum&&i.scopeEnum.length>0&&n.push(o?`- Escopos permitidos: ${i.scopeEnum.join(", ")}`:`- Allowed scopes: ${i.scopeEnum.join(", ")}`)}else if(e==="simple"){let s=i.headerMaxLength??50;o?(n.push("- Use formato simples e direto"),n.push("- Comece com verbo no infinitivo"),n.push('- Exemplo: "corrigir valida\xE7\xE3o de formul\xE1rio"'),n.push(`- M\xE1ximo ${s} caracteres`)):(n.push("- Use simple and direct format"),n.push("- Start with imperative verb"),n.push('- Example: "fix form validation"'),n.push(`- Maximum ${s} characters`))}else{let s=i.headerMaxLength??72;o?(n.push(`- Primeira linha: resumo em at\xE9 ${s} caracteres`),n.push("- Se necess\xE1rio, adicione corpo explicativo"),n.push("- Use presente do indicativo"),n.push("- Seja descritivo mas conciso")):(n.push(`- First line: summary under ${s} characters`),n.push("- Add explanatory body if needed"),n.push("- Use imperative mood"),n.push("- Be descriptive but concise"))}return n.join(`
19
+ `)}function it(e,t){let i={pt:{conventional:`- Use formato: tipo(escopo): descri\xE7\xE3o
20
+ - Tipos v\xE1lidos: ${Q}
20
21
  - Exemplo: "feat(auth): adicionar valida\xE7\xE3o de email"
21
22
  - Mantenha a primeira linha com at\xE9 50 caracteres`,simple:`- Use formato simples e direto
22
23
  - Comece com verbo no infinitivo
@@ -25,7 +26,7 @@ Mensagem:`}function Ye(e,t){let i={pt:{conventional:`- Use formato: tipo(escopo)
25
26
  - Se necess\xE1rio, adicione corpo explicativo
26
27
  - Use presente do indicativo
27
28
  - Seja descritivo mas conciso`},en:{conventional:`- Use format: type(scope): description
28
- - Valid types: feat, fix, docs, style, refactor, test, chore, build, ci
29
+ - Valid types: ${Q}
29
30
  - Example: "feat(auth): add email validation"
30
31
  - Keep first line under 50 characters`,simple:`- Use simple and direct format
31
32
  - Start with imperative verb
@@ -33,23 +34,23 @@ Mensagem:`}function Ye(e,t){let i={pt:{conventional:`- Use formato: tipo(escopo)
33
34
  - Maximum 50 characters`,detailed:`- First line: summary under 50 characters
34
35
  - Add explanatory body if needed
35
36
  - Use imperative mood
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){if(e.startsWith("```")){let t=e.match(/^```[^\n`]*\n([\s\S]*?)\n\s*```([\s\S]*)$/);if(t){let i=t[1].trim(),o=t[2].trim();e=o?`${i}
37
+ - Be descriptive but concise`}},o=t==="pt"?"pt":"en";return i[o][e]||i[o].conventional}function je(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 Re(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 Me(e){if(e.startsWith("```")){let t=e.match(/^```[^\n`]*\n([\s\S]*?)\n\s*```([\s\S]*)$/);if(t){let i=t[1].trim(),o=t[2].trim();e=o?`${i}
37
38
 
38
- ${o}`:i}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 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),r=we(e,i);return{success:!0,suggestion:{message:a,type:c||r,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 s=0;s<o;s++){let a=await Ae(e,t,i);if(a.success)return a;n=a.error||"Erro desconhecido",s<o-1&&await new Promise(c=>setTimeout(c,Math.pow(2,s)*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:()=>tt,copyToClipboard:()=>_,editCommitMessage:()=>N,selectFilesForCommit:()=>Q,showCancellation:()=>F,showCommitPreview:()=>L,showCommitResult:()=>v});import{text as Qe,select as Ze,confirm as q,log as P,note as Me,cancel as Xe,isCancel as R}from"@clack/prompts";import et from"clipboardy";async function L(e){Me(`Tipo: ${e.type}
39
- Mensagem: "${e.message}"`,"\u{1F4AD} Sugest\xE3o de Commit");let t=await Ze({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 R(t)?{action:"cancel"}:{action:t}}async function N(e){let t=await Qe({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(R(t))return{action:"cancel"};let i=await q({message:`Confirma a mensagem editada: "${t}"?`});return R(i)||!i?{action:"cancel"}:{action:"commit",message:t}}async function _(e){try{return await et.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 tt(e){Me(`"${e}"`,"\u{1F680} Confirmar Commit");let t=await q({message:"Executar o commit agora?"});return R(t)?!1:t}function v(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(R(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 R(t)?!1:t}function F(){Xe("Opera\xE7\xE3o cancelada pelo usu\xE1rio")}var O=w(()=>{"use strict";f()});var Fe={};G(Fe,{chooseSplitMode:()=>te,confirmGroupCommit:()=>nt,showSmartSplitGroups:()=>ot,showSmartSplitProgress:()=>st});import{select as Pe,confirm as it,log as ze,note as Re,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 ot(e){Re(`Identificamos ${e.length} grupo(s) l\xF3gico(s) para seus commits:
39
+ ${o}`:i}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 Fe(e,t,i,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 n=Ee(e,t,i,o),s=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:n}],max_tokens:Math.min(t.openai.maxTokens,150),temperature:t.openai.temperature})});if(!s.ok){let a=await s.json().catch(()=>({}));return{success:!1,error:`Erro da OpenAI (${s.status}): ${a.error?.message||"Erro desconhecido"}`}}let l=(await s.json()).choices?.[0]?.message?.content?.trim();if(!l)return{success:!1,error:"OpenAI retornou resposta vazia"};l=Me(l);let m=je(l),c=Re(e,i);return{success:!0,suggestion:{message:l,type:m||c,confidence:.8}}}catch(n){return{success:!1,error:`Erro ao conectar com OpenAI: ${n instanceof Error?n.message:"Erro desconhecido"}`}}}async function D(e,t,i,o=3,n){let s="";for(let r=0;r<o;r++){let l=await Fe(e,t,i,n);if(l.success)return l;s=l.error||"Erro desconhecido",r<o-1&&await new Promise(m=>setTimeout(m,Math.pow(2,r)*1e3))}return{success:!1,error:`Falha ap\xF3s ${o} tentativas. \xDAltimo erro: ${s}`}}var Q,Z=S(()=>{"use strict";p();Q="feat, fix, docs, style, refactor, test, chore, build, ci"});var te={};O(te,{askContinueCommits:()=>ee,confirmCommit:()=>at,copyToClipboard:()=>_,editCommitMessage:()=>U,selectFilesForCommit:()=>X,showCancellation:()=>P,showCommitPreview:()=>N,showCommitResult:()=>v});import{text as ot,select as nt,confirm as q,log as F,note as Pe,cancel as st,isCancel as z}from"@clack/prompts";import rt from"clipboardy";async function N(e){Pe(`Tipo: ${e.type}
40
+ Mensagem: "${e.message}"`,"\u{1F4AD} Sugest\xE3o de Commit");let t=await nt({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 z(t)?{action:"cancel"}:{action:t}}async function U(e){let t=await ot({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(z(t))return{action:"cancel"};let i=await q({message:`Confirma a mensagem editada: "${t}"?`});return z(i)||!i?{action:"cancel"}:{action:"commit",message:t}}async function _(e){try{return await rt.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 at(e){Pe(`"${e}"`,"\u{1F680} Confirmar Commit");let t=await q({message:"Executar o commit agora?"});return z(t)?!1:t}function v(e,t,i){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: ${i||"Erro desconhecido"}`)}async function X(e){F.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(z(o))break;o&&t.push(i)}return t}async function ee(e){if(e.length===0)return!1;F.info(`\u{1F4C4} Arquivos restantes: ${e.join(", ")}`);let t=await q({message:"Gerar commit para os arquivos restantes?"});return z(t)?!1:t}function P(){st("Opera\xE7\xE3o cancelada pelo usu\xE1rio")}var L=S(()=>{"use strict";p()});var Ge={};O(Ge,{chooseSplitMode:()=>oe,confirmGroupCommit:()=>lt,showSmartSplitGroups:()=>mt,showSmartSplitProgress:()=>ut});import{select as Ie,confirm as ct,log as ke,note as Oe,isCancel as ie}from"@clack/prompts";async function oe(){let e=await Ie({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 ie(e)?{action:"cancel"}:e==="manual"?{action:"manual"}:e==="smart"?{action:"proceed"}:{action:"cancel"}}async function mt(e){Oe(`Identificamos ${e.length} grupo(s) l\xF3gico(s) para seus commits:
40
41
 
41
42
  `+e.map((i,o)=>`${o+1}. **${i.name}**
42
43
  \u{1F4C4} ${i.files.join(", ")}
43
44
  \u{1F4A1} ${i.description}
44
45
  \u{1F3AF} Confian\xE7a: ${Math.round(i.confidence*100)}%`).join(`
45
46
 
46
- `),"\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 nt(e,t){Re(`**Grupo:** ${e.name}
47
+ `),"\u{1F9E0} An\xE1lise de Contexto");let t=await Ie({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 ie(t)?{action:"cancel"}:t==="proceed"?{action:"proceed",groups:e}:{action:t}}async function lt(e,t){Oe(`**Grupo:** ${e.name}
47
48
  **Arquivos:** ${e.files.join(", ")}
48
- **Mensagem:** "${t}"`,"\u{1F680} Confirmar Commit do Grupo");let i=await it({message:`Fazer commit para "${e.name}"?`});return ee(i)?!1:i}function st(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 rt from"crypto";function je(e){Ie=new oe(e)}function Ge(){return Ie}function ke(e,t){let i=Ge();return i?i.get(e,t):{hit:!1}}function Oe(e,t,i){let o=Ge();o&&o.set(e,t,i)}var oe,Ie,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 rt.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 s=Date.now(),a=this.config.cache.ttl*60*1e3;return s-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),s={groups:o,timestamp:Date.now(),hash:n};this.cache.set(n,s)}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((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}}},Ie=null});import{log as g}from"@clack/prompts";function at(e,t){let o=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.
49
+ **Mensagem:** "${t}"`,"\u{1F680} Confirmar Commit do Grupo");let i=await ct({message:`Fazer commit para "${e.name}"?`});return ie(i)?!1:i}function ut(e,t,i){let o=Math.round(e/t*100),n="\u2588".repeat(Math.floor(o/10))+"\u2591".repeat(10-Math.floor(o/10));ke.info(`\u{1F504} Progresso: [${n}] ${o}% (${e}/${t})`),ke.info(`\u{1F4CB} Processando: ${i}`)}var ne=S(()=>{"use strict";p()});import ft from"crypto";function Te(e){Le=new se(e)}function De(){return Le}function qe(e,t){let i=De();return i?i.get(e,t):{hit:!1}}function Ne(e,t,i){let o=De();o&&o.set(e,t,i)}var se,Le,re=S(()=>{"use strict";p();se=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 ft.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 s=Date.now(),r=this.config.cache.ttl*60*1e3;return s-n.timestamp>r?(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),s={groups:o,timestamp:Date.now(),hash:n};this.cache.set(n,s)}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((s,r)=>s[1].timestamp-r[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}}},Le=null});import{log as g}from"@clack/prompts";function pt(e,t){let o=t.length>8e3?t.substring(0,8e3)+`
50
+ ... (diff truncado)`:t,n=e.length,s=e.reduce((l,m)=>{let c=m.split(".").pop()||"sem-extensao";return l[c]=(l[c]||0)+1,l},{}),r=Object.entries(s).map(([l,m])=>`${l}: ${m}`).join(", ");return`Analise os arquivos modificados e agrupe em commits l\xF3gicos.
50
51
 
51
52
  ARQUIVOS (${n}): ${e.join(", ")}
52
- TIPOS: ${a}
53
+ TIPOS: ${r}
53
54
 
54
55
  DIFF RESUMIDO:
55
56
  \`\`\`
@@ -67,7 +68,7 @@ Agrupe arquivos relacionados. M\xE1ximo 5 grupos. Responda em JSON:
67
68
  "confidence": 0.8
68
69
  }
69
70
  ]
70
- }`}function ct(e){let t=e.reduce((o,n)=>{let s=n.split("/").slice(0,-1).join("/")||"root";return o[s]||(o[s]=[]),o[s].push(n),o},{});return`Agrupe estes arquivos em commits l\xF3gicos baseado nos diret\xF3rios:
71
+ }`}function dt(e){let t=e.reduce((o,n)=>{let s=n.split("/").slice(0,-1).join("/")||"root";return o[s]||(o[s]=[]),o[s].push(n),o},{});return`Agrupe estes arquivos em commits l\xF3gicos baseado nos diret\xF3rios:
71
72
 
72
73
  ARQUIVOS POR DIRET\xD3RIO:
73
74
  ${Object.entries(t).map(([o,n])=>`${o}: ${n.length} arquivo(s)`).join(`
@@ -86,38 +87,40 @@ Agrupe por funcionalidade relacionada. M\xE1ximo 5 grupos. JSON:
86
87
  "confidence": 0.7
87
88
  }
88
89
  ]
89
- }`}async function mt(e,t,i){try{if(!i.openai.apiKey)return{success:!1,error:"Chave da OpenAI n\xE3o encontrada"};let o=ke(e,t);if(o.hit&&o.groups)return{success:!0,groups:o.groups};let s=t.length>6e3,a=s?ct(e):at(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 ${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||[]),x=e.filter(p=>!h.includes(p));x.length>0&&(d.groups[0].files=[...d.groups[0].files||[],...x]);let y=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 Oe(e,t,y),{success:!0,groups:y}}catch(o){return{success:!1,error:`Erro ao analisar contexto: ${o instanceof Error?o.message:"Erro desconhecido"}`}}}async function lt(e){let{getFileDiff:t}=await Promise.resolve().then(()=>($(),M)),i=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(i.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,d=l.length>m?l.substring(0,m)+`
91
- ... (conte\xFAdo truncado)`:l;return`diff --git a/${r} b/${r}
90
+ }`}async function gt(e,t,i){try{if(!i.openai.apiKey)return{success:!1,error:"Chave da OpenAI n\xE3o encontrada"};let o=qe(e,t);if(o.hit&&o.groups)return{success:!0,groups:o.groups};let s=t.length>6e3,r=s?dt(e):pt(e,t);s&&console.warn(`\u26A0\uFE0F Diff muito grande (${t.length} chars), usando an\xE1lise baseada em nomes de arquivos`);let l=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:r}],max_tokens:800,temperature:.3})});if(!l.ok){let d=await l.json().catch(()=>({}));return{success:!1,error:`Erro da OpenAI (${l.status}): ${d.error?.message||"Erro desconhecido"}`}}let c=(await l.json()).choices?.[0]?.message?.content?.trim();if(!c)return{success:!1,error:"OpenAI retornou resposta vazia"};let a=c.match(/\{[\s\S]*\}/);if(!a)return{success:!1,error:"Resposta da OpenAI n\xE3o cont\xE9m JSON v\xE1lido"};let u=JSON.parse(a[0]);if(!u.groups||!Array.isArray(u.groups))return{success:!1,error:"Formato de resposta inv\xE1lido da OpenAI"};let h=u.groups.flatMap(d=>d.files||[]),y=e.filter(d=>!h.includes(d));y.length>0&&(u.groups[0].files=[...u.groups[0].files||[],...y]);let R=u.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 Ne(e,t,R),{success:!0,groups:R}}catch(o){return{success:!1,error:`Erro ao analisar contexto: ${o instanceof Error?o.message:"Erro desconhecido"}`}}}async function ht(e){let{getFileDiff:t}=await Promise.resolve().then(()=>(w(),j)),i=e.files.map(s=>{try{let r=t(s),l=4e3;return r.length>l?r.substring(0,l)+`
91
+ ... (diff truncado)`:r}catch{return""}}).filter(s=>s.length>0);if(i.length===0&&e.files.length>0){let{execSync:s}=await import("child_process"),r=e.files.filter(m=>{try{return s(`test -f "${m}"`,{stdio:"ignore"}),s(`git status --porcelain -- "${m}"`,{encoding:"utf-8",stdio:"pipe"}).trim().startsWith("??")}catch{return!1}});if(r.length>0)return r.map(m=>{try{let c=s(`cat "${m}"`,{encoding:"utf-8",stdio:"pipe"}),a=2e3,u=c.length>a?c.substring(0,a)+`
92
+ ... (conte\xFAdo truncado)`:c;return`diff --git a/${m} b/${m}
92
93
  new file mode 100644
93
94
  index 0000000..${Math.random().toString(36).substr(2,7)}
94
95
  --- /dev/null
95
- +++ b/${r}
96
- @@ -0,0 +1,${d.split(`
96
+ +++ b/${m}
97
+ @@ -0,0 +1,${u.split(`
97
98
  `).length} @@
98
- ${d.split(`
99
+ ${u.split(`
99
100
  `).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,d=l.length>m?l.substring(0,m)+`
103
- ... (conte\xFAdo truncado)`:l;return`diff --git a/${r} b/${r}
101
+ `)}`}catch{return""}}).filter(m=>m.length>0).join(`
102
+ `);let l=e.files.filter(m=>{try{return s(`test -f "${m}"`,{stdio:"ignore"}),s("git diff --cached --name-only",{encoding:"utf-8",stdio:"pipe"}).trim().split(`
103
+ `).includes(m)}catch{return!1}});if(l.length>0)return l.map(m=>{try{let c=s(`cat "${m}"`,{encoding:"utf-8",stdio:"pipe"}),a=2e3,u=c.length>a?c.substring(0,a)+`
104
+ ... (conte\xFAdo truncado)`:c;return`diff --git a/${m} b/${m}
104
105
  index 0000000..${Math.random().toString(36).substr(2,7)} 100644
105
- --- a/${r}
106
- +++ b/${r}
107
- @@ -1 +1,${d.split(`
106
+ --- a/${m}
107
+ +++ b/${m}
108
+ @@ -1 +1,${u.split(`
108
109
  `).length} @@
109
- ${d.split(`
110
+ ${u.split(`
110
111
  `).map(h=>`+${h}`).join(`
111
- `)}`}catch{return""}}).filter(r=>r.length>0).join(`
112
+ `)}`}catch{return""}}).filter(m=>m.length>0).join(`
112
113
  `)}let o=i.join(`
113
114
  `),n=8e3;return o.length>n?o.substring(0,n)+`
114
- ... (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 mt(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,s)=>{g.info(` ${s+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(),Fe)),s=await n(o.groups);if(s.action==="cancel"){i.silent||g.info("\u274C Opera\xE7\xE3o cancelada pelo usu\xE1rio");return}if(s.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 s=o.groups[n];if(!s){i.silent||g.error(`\u274C Grupo ${n+1} \xE9 undefined`);continue}i.silent||g.info(`
115
- \u{1F504} Processando grupo ${n+1}/${o.groups.length}: ${s.name}`);let a=await lt(s);if(!a){i.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}i.silent||g.info(`\u{1F916} Gerando commit para: ${s.name}`);let{generateWithRetry:c}=await Promise.resolve().then(()=>(Y(),Ee)),r=await c(a,t,s.files);if(!r.success){i.silent||g.error(`\u274C Erro ao gerar commit para ${s.name}: ${r.error}`);continue}if(!r.suggestion){i.silent||g.error(`\u274C Nenhuma sugest\xE3o gerada para ${s.name}`);continue}if(t.dryRun){i.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(i.yes){let{executeFileCommit:l}=await Promise.resolve().then(()=>($(),M)),m;if(s.files.length===1&&s.files[0])m=l(s.files[0],r.suggestion.message||"");else{let{execSync:d}=await import("child_process"),{escapeShellArg:h}=await Promise.resolve().then(()=>($(),M));try{let x=s.files.map(C=>h(C)).join(" "),y=h(r.suggestion.message||"");d(`git commit ${x} -m ${y}`,{stdio:"pipe"}),m={success:!0,hash:d("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:r.suggestion.message||""}}catch(x){m={success:!1,error:x instanceof Error?x.message:"Erro desconhecido ao executar commit"}}}v(m.success,m.hash,m.error)}else{let{showCommitPreview:l,editCommitMessage:m,copyToClipboard:d,showCancellation:h}=await Promise.resolve().then(()=>(O(),X));switch((await l(r.suggestion)).action){case"commit":{let{executeFileCommit:y}=await Promise.resolve().then(()=>($(),M)),p,C=r.suggestion.message||"Atualiza\xE7\xE3o de arquivos";if(s.files.length===1&&s.files[0])p=y(s.files[0],C);else{let{execSync:I}=await import("child_process"),{escapeShellArg:j}=await Promise.resolve().then(()=>($(),M));try{let A=s.files.map(W=>j(W)).join(" "),H=j(C);I(`git commit ${A} -m ${H}`,{stdio:"pipe"}),p={success:!0,hash:I("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:C}}catch(A){p={success:!1,error:A instanceof Error?A.message:"Erro desconhecido ao executar commit"}}}v(p.success,p.hash,p.error);break}case"edit":{let y=await m(r.suggestion.message);if(y.action==="commit"&&y.message){let{executeFileCommit:p}=await Promise.resolve().then(()=>($(),M)),C;if(s.files.length===1&&s.files[0])C=p(s.files[0],y.message||"");else{let{execSync:I}=await import("child_process"),{escapeShellArg:j}=await Promise.resolve().then(()=>($(),M));try{let A=s.files.map(W=>j(W)).join(" "),H=j(y.message||"");I(`git commit ${A} -m ${H}`,{stdio:"pipe"}),C={success:!0,hash:I("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:y.message||""}}catch(A){C={success:!1,error:A instanceof Error?A.message:"Erro desconhecido ao executar commit"}}}v(C.success,C.hash,C.error)}break}case"copy":{await d(r.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(()=>(O(),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();O()});import{existsSync as ae,writeFileSync as ut}from"fs";import{join as U}from"path";import{spawnSync as ft}from"child_process";function qe(e){let t=e||process.cwd();for(let i of dt){let o=U(t,i);if(ae(o))return o}return null}function ce(e){let t=e||process.cwd(),i=U(t,"node_modules",".bin","commitlint");return ae(i)}function Le(e,t){let i=t||process.cwd(),o=U(i,"node_modules",".bin","commitlint"),n=ft(o,[],{input:e,encoding:"utf-8",cwd:i});if(n.status===0)return{valid:!0,errors:[],warnings:[]};let a=((n.stdout||"")+(n.stderr||"")).split(`
116
- `),c=[],r=[];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]"))&&r.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:r}}function Ne(e){let t=e||process.cwd(),i=U(t,"commitlint.config.js");if(ae(i))throw new Error("commitlint.config.js j\xE1 existe neste diret\xF3rio. Remova-o antes de continuar.");ut(i,`export default {
115
+ ... (diff total truncado)`:o}async function ae(e,t,i,o){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 n=await gt(e.stagedFiles,e.diff,t);if(!n.success){i.silent||g.error(`\u274C Erro na an\xE1lise de contexto: ${n.error}`);return}if(!n.groups||n.groups.length===0){i.silent||g.error("\u274C Nenhum grupo foi criado pela an\xE1lise");return}if(i.silent||(g.success(`\u2705 ${n.groups.length} grupo(s) identificado(s):`),n.groups.forEach((s,r)=>{g.info(` ${r+1}. ${s.name} (${s.files.length} arquivo(s))`),g.info(` \u{1F4C4} ${s.files.join(", ")}`)})),!i.yes&&!i.silent){let{showSmartSplitGroups:s}=await Promise.resolve().then(()=>(ne(),Ge)),r=await s(n.groups);if(r.action==="cancel"){i.silent||g.info("\u274C Opera\xE7\xE3o cancelada pelo usu\xE1rio");return}if(r.action==="manual"){let l={...i,split:!0,smartSplit:!1},{main:m}=await Promise.resolve().then(()=>(ce(),_e));await m(l);return}}for(let s=0;s<n.groups.length;s++){let r=n.groups[s];if(!r){i.silent||g.error(`\u274C Grupo ${s+1} \xE9 undefined`);continue}i.silent||g.info(`
116
+ \u{1F504} Processando grupo ${s+1}/${n.groups.length}: ${r.name}`);let l=await ht(r);if(!l){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:m}=await Promise.resolve().then(()=>(Z(),ze)),c=await m(l,t,r.files,3,o);if(!c.success){i.silent||g.error(`\u274C Erro ao gerar commit para ${r.name}: ${c.error}`);continue}if(!c.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: "${c.suggestion.message}"`));continue}if(i.yes){let{executeFileCommit:a}=await Promise.resolve().then(()=>(w(),j)),u;if(r.files.length===1&&r.files[0])u=a(r.files[0],c.suggestion.message||"");else{let{execSync:h}=await import("child_process"),{escapeShellArg:y}=await Promise.resolve().then(()=>(w(),j));try{let R=r.files.map(b=>y(b)).join(" "),d=y(c.suggestion.message||"");h(`git commit ${R} -m ${d}`,{stdio:"pipe"}),u={success:!0,hash:h("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:c.suggestion.message||""}}catch(R){u={success:!1,error:R instanceof Error?R.message:"Erro desconhecido ao executar commit"}}}v(u.success,u.hash,u.error)}else{let{showCommitPreview:a,editCommitMessage:u,copyToClipboard:h,showCancellation:y}=await Promise.resolve().then(()=>(L(),te));switch((await a(c.suggestion)).action){case"commit":{let{executeFileCommit:d}=await Promise.resolve().then(()=>(w(),j)),$,b=c.suggestion.message||"Atualiza\xE7\xE3o de arquivos";if(r.files.length===1&&r.files[0])$=d(r.files[0],b);else{let{execSync:k}=await import("child_process"),{escapeShellArg:I}=await Promise.resolve().then(()=>(w(),j));try{let A=r.files.map(W=>I(W)).join(" "),H=I(b);k(`git commit ${A} -m ${H}`,{stdio:"pipe"}),$={success:!0,hash:k("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:b}}catch(A){$={success:!1,error:A instanceof Error?A.message:"Erro desconhecido ao executar commit"}}}v($.success,$.hash,$.error);break}case"edit":{let d=await u(c.suggestion.message);if(d.action==="commit"&&d.message){let{executeFileCommit:$}=await Promise.resolve().then(()=>(w(),j)),b;if(r.files.length===1&&r.files[0])b=$(r.files[0],d.message||"");else{let{execSync:k}=await import("child_process"),{escapeShellArg:I}=await Promise.resolve().then(()=>(w(),j));try{let A=r.files.map(W=>I(W)).join(" "),H=I(d.message||"");k(`git commit ${A} -m ${H}`,{stdio:"pipe"}),b={success:!0,hash:k("git rev-parse HEAD",{encoding:"utf-8",stdio:"pipe"}).trim(),message:d.message||""}}catch(A){b={success:!1,error:A instanceof Error?A.message:"Erro desconhecido ao executar commit"}}}v(b.success,b.hash,b.error)}break}case"copy":{await h(c.suggestion.message),i.silent||g.info("\u{1F3AF} Mensagem copiada para clipboard");break}case"cancel":{y();return}}}if(s<n.groups.length-1&&!i.yes){let{askContinueCommits:a}=await Promise.resolve().then(()=>(L(),te)),u=n.groups.slice(s+1).filter(y=>y!==void 0).map(y=>y.name);if(!await a(u))break}}i.silent||g.success("\u2705 Smart Split conclu\xEDdo!")}var Ue=S(()=>{"use strict";p();re();L()});import{existsSync as ue,writeFileSync as yt,readFileSync as bt}from"fs";import{join as J,extname as vt,basename as Ct}from"path";import{spawnSync as le}from"child_process";function me(e){let t={},i=e?.rules??{},o=i["type-enum"];Array.isArray(o)&&Array.isArray(o[2])&&(t.typeEnum=o[2]);let n=i["header-max-length"];Array.isArray(n)&&typeof n[2]=="number"&&(t.headerMaxLength=n[2]);let s=i["subject-case"];if(Array.isArray(s)){let[c,a,u]=s;Array.isArray(u)?t.subjectCase={severity:c,condition:a,cases:u}:typeof u=="string"&&(t.subjectCase={severity:c,condition:a,cases:[u]})}let r=i["subject-full-stop"];if(Array.isArray(r)){let[c,a,u]=r;t.subjectFullStop={severity:c,condition:a,value:u??"."}}let l=i["body-leading-blank"];Array.isArray(l)&&(t.bodyLeadingBlank=l[1]==="always");let m=i["scope-enum"];return Array.isArray(m)&&Array.isArray(m[2])&&(t.scopeEnum=m[2]),t}function Je(e){try{let t=vt(e).toLowerCase(),i=Ct(e);if(t===".json"||t===""&&i===".commitlintrc")try{let o=bt(e,"utf-8"),n=JSON.parse(o);return me(n)}catch{return null}if(t===".js"||t===".mjs"||t===".cjs"){let o=["import { pathToFileURL } from 'url';",`const mod = await import(pathToFileURL(${JSON.stringify(e)}).href);`,"const cfg = mod.default ?? mod;","process.stdout.write(JSON.stringify(cfg));"].join(`
117
+ `),n=le(process.execPath,["--input-type=module"],{input:o,encoding:"utf-8",timeout:5e3});if(n.status===0&&n.stdout)try{let l=JSON.parse(n.stdout);return me(l)}catch{}let s=["try {",` const c = require(${JSON.stringify(e)});`," process.stdout.write(JSON.stringify(c.default ?? c));","} catch (e) {"," process.exit(1);","}"].join(`
118
+ `),r=le(process.execPath,["-e",s],{encoding:"utf-8",timeout:5e3});if(r.status===0&&r.stdout)try{let l=JSON.parse(r.stdout);return me(l)}catch{}}return null}catch{return null}}function fe(e){let t=e||process.cwd();for(let i of xt){let o=J(t,i);if(ue(o))return o}return null}function pe(e){let t=e||process.cwd(),i=J(t,"node_modules",".bin","commitlint");return ue(i)}function Ve(e,t){let i=t||process.cwd(),o=J(i,"node_modules",".bin","commitlint"),n=le(o,[],{input:e,encoding:"utf-8",cwd:i});if(n.status===0)return{valid:!0,errors:[],warnings:[]};let r=((n.stdout||"")+(n.stderr||"")).split(`
119
+ `),l=[],m=[];for(let c of r){let a=c.trim();a&&(a.startsWith("\u2716")||a.includes("[error]")?l.push(a):(a.startsWith("\u26A0")||a.includes("[warning]"))&&m.push(a))}if(l.length===0&&n.status!==0){let c=r.map(a=>a.trim()).filter(a=>a.length>0&&!a.startsWith("\u29D7")&&!a.startsWith("\u2714"));l.push(...c)}return{valid:!1,errors:l,warnings:m}}function He(e){let t=e||process.cwd(),i=J(t,"commitlint.config.js");if(ue(i))throw new Error("commitlint.config.js j\xE1 existe neste diret\xF3rio. Remova-o antes de continuar.");yt(i,`export default {
117
120
  extends: ['@commitlint/config-conventional'],
118
121
  };
119
- `,"utf-8")}var dt,me=w(()=>{"use strict";f();dt=["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 pt(e,t){return!t.commitlint.enabled||!ce()?!1:e.validate?!0:qe()!==null}function le(e,t){let i=Le(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=pt(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 s=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: +${s.added} -${s.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 gt(n,t,e);case"cancel":F();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);v(c.success,c.hash,c.error);return}for(;;)switch((await L(a.suggestion)).action){case"commit":{if(o&&!le(a.suggestion.message,e.silent))break;let r=z(a.suggestion.message);v(r.success,r.hash,r.error);return}case"edit":{let r=await N(a.suggestion.message);if(r.action==="cancel"){F();return}if(r.action==="commit"&&r.message){if(o&&!le(r.message,e.silent))break;let l=z(r.message);v(l.success,l.hash,l.error);return}break}case"copy":{await _(a.suggestion.message),e.silent||u.info('\u{1F3AF} Voc\xEA pode usar a mensagem copiada com: git commit -m "mensagem"');return}case"cancel":{F();return}}}async function gt(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:s}=await Promise.resolve().then(()=>($(),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(`
120
- `);if(!a){i.silent||u.warn("\u26A0\uFE0F Nenhum diff encontrado para os arquivos selecionados"),o=o.filter(r=>!n.includes(r));continue}i.silent||u.info(`\u{1F916} Gerando commit para: ${n.join(", ")}`);let c=await T(a,t,n.filter(r=>r!==void 0));if(!c.success){u.error(`\u274C Erro ao gerar commit: ${c.error}`),o=o.filter(r=>!n.includes(r));continue}if(!c.suggestion){u.error("\u274C Nenhuma sugest\xE3o foi gerada"),o=o.filter(r=>!n.includes(r));continue}if(t.dryRun){u.info(`\u{1F50D} Dry Run - Mensagem para ${n.join(", ")}:`),u.info(`"${c.suggestion.message}"`),o=o.filter(r=>!n.includes(r));continue}if(i.yes){let r=n.length===1&&n[0]?await k(n[0],c.suggestion.message):await z(c.suggestion.message);v(r.success,r.hash,r.error)}else{let r=await L(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);v(l.success,l.hash,l.error)}else if(r.action==="edit"){let l=await N(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);v(m.success,m.hash,m.error)}}else if(r.action==="copy")await _(c.suggestion.message),i.silent||u.info("\u{1F3AF} Mensagem copiada para clipboard");else if(r.action==="cancel"){F();return}}if(o=o.filter(r=>!n.includes(r)),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();O();ie();De();ne();me()});f();re();import{intro as vt,outro as xt,log as V}from"@clack/prompts";f();f();import{readFileSync as ht}from"fs";import{join as fe,dirname as yt}from"path";import{fileURLToPath as Ct}from"url";function _e(){try{let e=Ct(import.meta.url),t=yt(e),i=[fe(t,"..","package.json"),fe(t,"..","..","package.json"),fe(process.cwd(),"package.json")];for(let o of i)try{let n=ht(o,"utf-8"),s=JSON.parse(n);if(s.name==="@gilbert_oliveira/commit-wizard")return s.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 Ue(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(`
122
+ `,"utf-8")}var xt,de=S(()=>{"use strict";p();xt=["commitlint.config.js","commitlint.config.ts","commitlint.config.mjs","commitlint.config.cjs",".commitlintrc",".commitlintrc.js",".commitlintrc.json",".commitlintrc.yml",".commitlintrc.yaml"]});var _e={};O(_e,{main:()=>he});import{log as f}from"@clack/prompts";function St(e,t){return!t.commitlint.enabled||!pe()?!1:e.validate?!0:fe()!==null}function ge(e,t){let i=Ve(e);return i.valid?(t||f.success("\u2705 Mensagem validada pelo commitlint!"),!0):(f.error("\u274C A mensagem n\xE3o passou na valida\xE7\xE3o do commitlint:"),i.errors.forEach(o=>f.error(` ${o}`)),i.warnings.length>0&&i.warnings.forEach(o=>f.info(` \u26A0\uFE0F ${o}`)),!1)}async function he(e={silent:!1,yes:!1,auto:!1,split:!1,smartSplit:!1,dryRun:!1,help:!1,version:!1,validate:!1,initCommitlint:!1}){e.silent||f.info("\u{1F680} Commit Wizard iniciado!"),B()||(f.error("\u274C N\xE3o foi encontrado um reposit\xF3rio Git neste diret\xF3rio."),e.silent||f.info("\u{1F4A1} Execute o comando em um diret\xF3rio com reposit\xF3rio Git inicializado."),process.exit(1)),e.silent||f.info("\u2699\uFE0F Carregando configura\xE7\xE3o...");let t=Se();Te(t),e.split&&(t.splitCommits=!0),e.dryRun&&(t.dryRun=!0);let i=we(t);i.length>0&&(f.error("\u274C Erros na configura\xE7\xE3o:"),i.forEach(c=>f.error(` \u2022 ${c}`)),process.exit(1)),e.silent||f.success(`\u2705 Configura\xE7\xE3o carregada (modelo: ${t.openai.model}, idioma: ${t.language})`);let o=St(e,t);e.silent||(e.validate&&!pe()?f.info("\u26A0\uFE0F --validate especificado mas commitlint n\xE3o est\xE1 instalado. Valida\xE7\xE3o ignorada."):o&&f.info("\u{1F50D} Valida\xE7\xE3o commitlint ativa"));let n=fe(),s=n?Je(n):null;e.silent||f.info("\u{1F4CB} Verificando arquivos staged...");let r=K();r.hasStaged||(f.warn("\u26A0\uFE0F Nenhum arquivo foi encontrado no stage."),e.silent||f.info("\u{1F4A1} Use `git add <arquivo>` para adicionar arquivos ao stage antes de gerar o commit."),process.exit(0));let l=Y();if(e.silent||(f.success(`\u2705 Encontrados ${r.stagedFiles.length} arquivo(s) staged:`),r.stagedFiles.forEach(c=>f.info(` \u{1F4C4} ${c}`)),f.info(`\u{1F4CA} Estat\xEDsticas: +${l.added} -${l.removed} linhas`)),t.splitCommits||e.smartSplit){if(e.yes)return await ae(r,t,e,s);switch((await oe()).action){case"proceed":return await ae(r,t,e,s);case"manual":return await wt(r,t,e,s);case"cancel":P();return}}e.silent||f.info("\u{1F916} Gerando mensagem de commit com IA...");let m=await D(r.diff,t,r.stagedFiles,3,s);if(m.success||(f.error(`\u274C Erro ao gerar commit: ${m.error}`),process.exit(1)),m.suggestion||(f.error("\u274C Nenhuma sugest\xE3o foi gerada"),process.exit(1)),e.silent||f.success("\u2728 Mensagem de commit gerada!"),t.dryRun){f.info("\u{1F50D} Modo Dry Run - Mensagem gerada:"),f.info(`"${m.suggestion.message}"`),f.info("\u{1F4A1} Execute sem --dry-run para fazer o commit");return}if(e.yes){o&&(ge(m.suggestion.message,e.silent)||process.exit(1));let c=M(m.suggestion.message);v(c.success,c.hash,c.error);return}for(;;)switch((await N(m.suggestion)).action){case"commit":{if(o&&!ge(m.suggestion.message,e.silent))break;let a=M(m.suggestion.message);v(a.success,a.hash,a.error);return}case"edit":{let a=await U(m.suggestion.message);if(a.action==="cancel"){P();return}if(a.action==="commit"&&a.message){if(o&&!ge(a.message,e.silent))break;let u=M(a.message);v(u.success,u.hash,u.error);return}break}case"copy":{await _(m.suggestion.message),e.silent||f.info('\u{1F3AF} Voc\xEA pode usar a mensagem copiada com: git commit -m "mensagem"');return}case"cancel":{P();return}}}async function wt(e,t,i,o){i.silent||f.info("\u{1F504} Modo Split ativado - Commits separados por arquivo");let n=[...e.stagedFiles];for(;n.length>0;){let s=i.yes?[n[0]]:await X(n);if(s.length===0){i.silent||f.info("\u274C Nenhum arquivo selecionado");break}let{getFileDiff:r}=await Promise.resolve().then(()=>(w(),j)),l=s.filter(c=>c!==void 0).map(c=>{try{return r(c)}catch(a){return f.error(`\u274C Erro ao obter diff do arquivo ${c}: ${a instanceof Error?a.message:"Erro desconhecido"}`),""}}).filter(c=>c.length>0).join(`
123
+ `);if(!l){i.silent||f.warn("\u26A0\uFE0F Nenhum diff encontrado para os arquivos selecionados"),n=n.filter(c=>!s.includes(c));continue}i.silent||f.info(`\u{1F916} Gerando commit para: ${s.join(", ")}`);let m=await D(l,t,s.filter(c=>c!==void 0),3,o);if(!m.success){f.error(`\u274C Erro ao gerar commit: ${m.error}`),n=n.filter(c=>!s.includes(c));continue}if(!m.suggestion){f.error("\u274C Nenhuma sugest\xE3o foi gerada"),n=n.filter(c=>!s.includes(c));continue}if(t.dryRun){f.info(`\u{1F50D} Dry Run - Mensagem para ${s.join(", ")}:`),f.info(`"${m.suggestion.message}"`),n=n.filter(c=>!s.includes(c));continue}if(i.yes){let c=s.length===1&&s[0]?await G(s[0],m.suggestion.message):await M(m.suggestion.message);v(c.success,c.hash,c.error)}else{let c=await N(m.suggestion);if(c.action==="commit"){let a=s.length===1&&s[0]?await G(s[0],m.suggestion.message):await M(m.suggestion.message);v(a.success,a.hash,a.error)}else if(c.action==="edit"){let a=await U(m.suggestion.message);if(a.action==="commit"&&a.message){let u=s.length===1&&s[0]?await G(s[0],a.message):await M(a.message);v(u.success,u.hash,u.error)}}else if(c.action==="copy")await _(m.suggestion.message),i.silent||f.info("\u{1F3AF} Mensagem copiada para clipboard");else if(c.action==="cancel"){P();return}}if(n=n.filter(c=>!s.includes(c)),n.length>0&&!i.yes&&!await ee(n))break}i.silent||f.success("\u2705 Modo Split conclu\xEDdo!")}var ce=S(()=>{"use strict";p();$e();w();Z();L();ne();Ue();re();de()});p();ce();import{intro as jt,outro as Rt,log as V}from"@clack/prompts";p();p();import{readFileSync as $t}from"fs";import{join as ye,dirname as At}from"path";import{fileURLToPath as Et}from"url";function We(){try{let e=Et(import.meta.url),t=At(e),i=[ye(t,"..","package.json"),ye(t,"..","..","package.json"),ye(process.cwd(),"package.json")];for(let o of i)try{let n=$t(o,"utf-8"),s=JSON.parse(n);if(s.name==="@gilbert_oliveira/commit-wizard")return s.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 Be(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 Ke(){console.log(`
121
124
  \u{1F9D9}\u200D\u2642\uFE0F Commit Wizard - Gerador inteligente de mensagens de commit
122
125
 
123
126
  USAGE:
@@ -146,4 +149,4 @@ EXAMPLES:
146
149
  commit-wizard --init-commitlint # Criar configura\xE7\xE3o padr\xE3o do commitlint
147
150
 
148
151
  Para mais informa\xE7\xF5es, visite: https://github.com/user/commit-wizard
149
- `)}function He(){let e=_e();console.log(`commit-wizard v${e}`)}me();async function bt(){try{let e=Ue(process.argv.slice(2));if(e.help&&(Ve(),process.exit(0)),e.version&&(He(),process.exit(0)),e.initCommitlint){try{Ne(),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||vt("\u{1F9D9}\u200D\u2642\uFE0F Commit Wizard"),await ue(e),e.silent||xt("At\xE9 logo! \u2728")}catch(e){V.error(`Erro: ${e instanceof Error?e.message:"Erro desconhecido"}`),process.exit(1)}}bt();
152
+ `)}function Ye(){let e=We();console.log(`commit-wizard v${e}`)}de();async function Mt(){try{let e=Be(process.argv.slice(2));if(e.help&&(Ke(),process.exit(0)),e.version&&(Ye(),process.exit(0)),e.initCommitlint){try{He(),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||jt("\u{1F9D9}\u200D\u2642\uFE0F Commit Wizard"),await he(e),e.silent||Rt("At\xE9 logo! \u2728")}catch(e){V.error(`Erro: ${e instanceof Error?e.message:"Erro desconhecido"}`),process.exit(1)}}Mt();
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.13.0-canary.2",
6
+ "version": "2.13.0-canary.4",
7
7
  "categories": [
8
8
  "Other",
9
9
  "SCM Providers"
@@ -71,6 +71,8 @@
71
71
  },
72
72
  "devDependencies": {
73
73
  "@changesets/cli": "^2.29.5",
74
+ "@commitlint/cli": "^20.4.2",
75
+ "@commitlint/config-conventional": "^20.4.2",
74
76
  "@types/jest": "^30.0.0",
75
77
  "@types/node": "^24.0.13",
76
78
  "@typescript-eslint/eslint-plugin": "^8.36.0",
@@ -1,5 +1,5 @@
1
- import { existsSync, writeFileSync } from 'fs';
2
- import { join } from 'path';
1
+ import { existsSync, writeFileSync, readFileSync } from 'fs';
2
+ import { join, extname, basename } from 'path';
3
3
  import { spawnSync } from 'child_process';
4
4
 
5
5
  export interface CommitlintResult {
@@ -20,6 +20,150 @@ const COMMITLINT_CONFIG_FILES = [
20
20
  '.commitlintrc.yaml',
21
21
  ];
22
22
 
23
+ export interface CommitlintRules {
24
+ typeEnum?: string[];
25
+ headerMaxLength?: number;
26
+ subjectCase?: { severity: number; condition: string; cases: string[] };
27
+ subjectFullStop?: { severity: number; condition: string; value: string };
28
+ bodyLeadingBlank?: boolean;
29
+ scopeEnum?: string[];
30
+ }
31
+
32
+ function extractRulesFromConfig(config: unknown): CommitlintRules {
33
+ const rules: CommitlintRules = {};
34
+ const rawRules =
35
+ (config as { rules?: Record<string, unknown[]> })?.rules ?? {};
36
+
37
+ const typeEnumRule = rawRules['type-enum'];
38
+ if (Array.isArray(typeEnumRule) && Array.isArray(typeEnumRule[2])) {
39
+ rules.typeEnum = typeEnumRule[2] as string[];
40
+ }
41
+
42
+ const headerMaxLengthRule = rawRules['header-max-length'];
43
+ if (
44
+ Array.isArray(headerMaxLengthRule) &&
45
+ typeof headerMaxLengthRule[2] === 'number'
46
+ ) {
47
+ rules.headerMaxLength = headerMaxLengthRule[2];
48
+ }
49
+
50
+ const subjectCaseRule = rawRules['subject-case'];
51
+ if (Array.isArray(subjectCaseRule)) {
52
+ const [severity, condition, cases] = subjectCaseRule;
53
+ if (Array.isArray(cases)) {
54
+ rules.subjectCase = {
55
+ severity: severity as number,
56
+ condition: condition as string,
57
+ cases: cases as string[],
58
+ };
59
+ } else if (typeof cases === 'string') {
60
+ rules.subjectCase = {
61
+ severity: severity as number,
62
+ condition: condition as string,
63
+ cases: [cases],
64
+ };
65
+ }
66
+ }
67
+
68
+ const subjectFullStopRule = rawRules['subject-full-stop'];
69
+ if (Array.isArray(subjectFullStopRule)) {
70
+ const [severity, condition, value] = subjectFullStopRule;
71
+ rules.subjectFullStop = {
72
+ severity: severity as number,
73
+ condition: condition as string,
74
+ value: (value as string) ?? '.',
75
+ };
76
+ }
77
+
78
+ const bodyLeadingBlankRule = rawRules['body-leading-blank'];
79
+ if (Array.isArray(bodyLeadingBlankRule)) {
80
+ rules.bodyLeadingBlank = bodyLeadingBlankRule[1] === 'always';
81
+ }
82
+
83
+ const scopeEnumRule = rawRules['scope-enum'];
84
+ if (Array.isArray(scopeEnumRule) && Array.isArray(scopeEnumRule[2])) {
85
+ rules.scopeEnum = scopeEnumRule[2] as string[];
86
+ }
87
+
88
+ return rules;
89
+ }
90
+
91
+ /**
92
+ * Reads and parses a commitlint config file, returning the extracted rules.
93
+ * Returns null if the file cannot be parsed or the format is unsupported.
94
+ */
95
+ export function parseCommitlintRules(configPath: string): CommitlintRules | null {
96
+ try {
97
+ const ext = extname(configPath).toLowerCase();
98
+ const base = basename(configPath);
99
+
100
+ // JSON format: .commitlintrc.json or .commitlintrc (extensionless, try JSON)
101
+ if (ext === '.json' || (ext === '' && base === '.commitlintrc')) {
102
+ try {
103
+ const content = readFileSync(configPath, 'utf-8');
104
+ const config = JSON.parse(content) as unknown;
105
+ return extractRulesFromConfig(config);
106
+ } catch {
107
+ return null;
108
+ }
109
+ }
110
+
111
+ // JS / MJS / CJS: use a Node.js subprocess to evaluate the config
112
+ if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
113
+ // Try ESM dynamic import first (handles export default)
114
+ const esmScript = [
115
+ "import { pathToFileURL } from 'url';",
116
+ `const mod = await import(pathToFileURL(${JSON.stringify(configPath)}).href);`,
117
+ 'const cfg = mod.default ?? mod;',
118
+ 'process.stdout.write(JSON.stringify(cfg));',
119
+ ].join('\n');
120
+
121
+ const esmResult = spawnSync(process.execPath, ['--input-type=module'], {
122
+ input: esmScript,
123
+ encoding: 'utf-8',
124
+ timeout: 5000,
125
+ });
126
+
127
+ if (esmResult.status === 0 && esmResult.stdout) {
128
+ try {
129
+ const config = JSON.parse(esmResult.stdout) as unknown;
130
+ return extractRulesFromConfig(config);
131
+ } catch {
132
+ // JSON parse error – fall through to CJS
133
+ }
134
+ }
135
+
136
+ // Fallback: try CommonJS require
137
+ const cjsScript = [
138
+ 'try {',
139
+ ` const c = require(${JSON.stringify(configPath)});`,
140
+ ' process.stdout.write(JSON.stringify(c.default ?? c));',
141
+ '} catch (e) {',
142
+ ' process.exit(1);',
143
+ '}',
144
+ ].join('\n');
145
+ const cjsResult = spawnSync(process.execPath, ['-e', cjsScript], {
146
+ encoding: 'utf-8',
147
+ timeout: 5000,
148
+ });
149
+
150
+ if (cjsResult.status === 0 && cjsResult.stdout) {
151
+ try {
152
+ const config = JSON.parse(cjsResult.stdout) as unknown;
153
+ return extractRulesFromConfig(config);
154
+ } catch {
155
+ // JSON parse error
156
+ }
157
+ }
158
+ }
159
+
160
+ // Unsupported format (e.g. YAML, TypeScript): return null gracefully
161
+ return null;
162
+ } catch {
163
+ return null;
164
+ }
165
+ }
166
+
23
167
  export function findCommitlintConfig(cwd?: string): string | null {
24
168
  const dir = cwd || process.cwd();
25
169
  for (const file of COMMITLINT_CONFIG_FILES) {
package/src/core/index.ts CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  isCommitlintInstalled,
27
27
  findCommitlintConfig,
28
28
  validateCommitMessage,
29
+ parseCommitlintRules,
29
30
  } from '../commitlint/index';
30
31
 
31
32
  function shouldRunCommitlint(args: CLIArgs, config: Config): boolean {
@@ -134,6 +135,12 @@ export async function main(
134
135
  }
135
136
  }
136
137
 
138
+ // Parse commitlint rules for AI prompt (auto-detected, no validation required)
139
+ const commitlintConfigPath = findCommitlintConfig();
140
+ const commitlintRules = commitlintConfigPath
141
+ ? parseCommitlintRules(commitlintConfigPath)
142
+ : null;
143
+
137
144
  // Verificar arquivos staged
138
145
  if (!args.silent) {
139
146
  log.info('📋 Verificando arquivos staged...');
@@ -165,16 +172,16 @@ export async function main(
165
172
  if (config.splitCommits || args.smartSplit) {
166
173
  if (args.yes) {
167
174
  // Modo automático: usar smart split
168
- return await handleSmartSplitMode(gitStatus, config, args);
175
+ return await handleSmartSplitMode(gitStatus, config, args, commitlintRules);
169
176
  } else {
170
177
  // Modo interativo: perguntar qual tipo de split
171
178
  const splitAction = await chooseSplitMode();
172
179
 
173
180
  switch (splitAction.action) {
174
181
  case 'proceed':
175
- return await handleSmartSplitMode(gitStatus, config, args);
182
+ return await handleSmartSplitMode(gitStatus, config, args, commitlintRules);
176
183
  case 'manual':
177
- return await handleSplitMode(gitStatus, config, args);
184
+ return await handleSplitMode(gitStatus, config, args, commitlintRules);
178
185
  case 'cancel':
179
186
  showCancellation();
180
187
  return;
@@ -190,7 +197,9 @@ export async function main(
190
197
  const result = await generateWithRetry(
191
198
  gitStatus.diff,
192
199
  config,
193
- gitStatus.stagedFiles
200
+ gitStatus.stagedFiles,
201
+ 3,
202
+ commitlintRules
194
203
  );
195
204
 
196
205
  if (!result.success) {
@@ -302,7 +311,7 @@ export async function main(
302
311
  }
303
312
  }
304
313
 
305
- async function handleSplitMode(gitStatus: any, config: any, args: CLIArgs) {
314
+ async function handleSplitMode(gitStatus: any, config: any, args: CLIArgs, commitlintRules?: import('../commitlint/index').CommitlintRules | null) {
306
315
  if (!args.silent) {
307
316
  log.info('🔄 Modo Split ativado - Commits separados por arquivo');
308
317
  }
@@ -358,7 +367,9 @@ async function handleSplitMode(gitStatus: any, config: any, args: CLIArgs) {
358
367
  const result = await generateWithRetry(
359
368
  fileDiffs,
360
369
  config,
361
- selectedFiles.filter((file): file is string => file !== undefined)
370
+ selectedFiles.filter((file): file is string => file !== undefined),
371
+ 3,
372
+ commitlintRules
362
373
  );
363
374
 
364
375
  if (!result.success) {
@@ -1,4 +1,7 @@
1
1
  import type { Config } from '../config/index';
2
+ import type { CommitlintRules } from '../commitlint/index';
3
+
4
+ const DEFAULT_COMMIT_TYPES = 'feat, fix, docs, style, refactor, test, chore, build, ci';
2
5
 
3
6
  export interface CommitSuggestion {
4
7
  message: string;
@@ -133,13 +136,18 @@ export function smartFilterDiff(diff: string, maxLength: number): string {
133
136
  export function buildPrompt(
134
137
  diff: string,
135
138
  config: Config,
136
- filenames: string[]
139
+ filenames: string[],
140
+ commitlintRules?: CommitlintRules | null
137
141
  ): string {
138
142
  const language = config.language === 'pt' ? 'português' : 'english';
139
- const styleInstructions = getStyleInstructions(
140
- config.commitStyle,
141
- config.language
142
- );
143
+ const styleInstructions =
144
+ commitlintRules != null
145
+ ? getStyleInstructionsFromRules(
146
+ config.commitStyle,
147
+ config.language,
148
+ commitlintRules
149
+ )
150
+ : getStyleInstructions(config.commitStyle, config.language);
143
151
 
144
152
  // Limitar tamanho do diff para economizar tokens usando filtro inteligente
145
153
  const maxDiffLength = 6000;
@@ -167,6 +175,127 @@ Mensagem:`;
167
175
  return prompt;
168
176
  }
169
177
 
178
+ /**
179
+ * Builds style instructions dynamically from parsed commitlint rules.
180
+ * Falls back to sensible defaults for any rules not explicitly configured.
181
+ */
182
+ function getStyleInstructionsFromRules(
183
+ style: string,
184
+ language: string,
185
+ rules: CommitlintRules
186
+ ): string {
187
+ const isPt = language === 'pt';
188
+ const lines: string[] = [];
189
+
190
+ if (style === 'conventional') {
191
+ lines.push(
192
+ isPt
193
+ ? '- Use formato: tipo(escopo): descrição'
194
+ : '- Use format: type(scope): description'
195
+ );
196
+
197
+ const types = rules.typeEnum?.join(', ') ?? DEFAULT_COMMIT_TYPES;
198
+ lines.push(
199
+ isPt ? `- Tipos válidos: ${types}` : `- Valid types: ${types}`
200
+ );
201
+
202
+ const maxLen = rules.headerMaxLength ?? 72;
203
+ lines.push(
204
+ isPt
205
+ ? `- Mantenha a primeira linha com até ${maxLen} caracteres`
206
+ : `- Keep first line under ${maxLen} characters`
207
+ );
208
+
209
+ const exampleType = rules.typeEnum?.[0] ?? 'feat';
210
+ lines.push(
211
+ isPt
212
+ ? `- Exemplo: "${exampleType}(auth): adicionar validação de email"`
213
+ : `- Example: "${exampleType}(auth): add email validation"`
214
+ );
215
+
216
+ if (rules.subjectCase) {
217
+ const { condition, cases } = rules.subjectCase;
218
+ const caseList = cases.join(', ');
219
+ if (condition === 'never') {
220
+ lines.push(
221
+ isPt
222
+ ? `- Subject nunca em: ${caseList}`
223
+ : `- Subject must never be: ${caseList}`
224
+ );
225
+ } else if (condition === 'always') {
226
+ lines.push(
227
+ isPt
228
+ ? `- Subject deve estar em: ${caseList}`
229
+ : `- Subject must be in: ${caseList}`
230
+ );
231
+ }
232
+ }
233
+
234
+ if (rules.subjectFullStop) {
235
+ const { condition, value } = rules.subjectFullStop;
236
+ const char = value ?? '.';
237
+ if (condition === 'never') {
238
+ lines.push(
239
+ isPt
240
+ ? `- Não termine o subject com "${char}"`
241
+ : `- Do not end subject with "${char}"`
242
+ );
243
+ } else if (condition === 'always') {
244
+ lines.push(
245
+ isPt
246
+ ? `- Termine o subject com "${char}"`
247
+ : `- End subject with "${char}"`
248
+ );
249
+ }
250
+ }
251
+
252
+ if (rules.bodyLeadingBlank === true) {
253
+ lines.push(
254
+ isPt
255
+ ? '- Adicione uma linha em branco entre o header e o body'
256
+ : '- Add a blank line between header and body'
257
+ );
258
+ }
259
+
260
+ if (rules.scopeEnum && rules.scopeEnum.length > 0) {
261
+ lines.push(
262
+ isPt
263
+ ? `- Escopos permitidos: ${rules.scopeEnum.join(', ')}`
264
+ : `- Allowed scopes: ${rules.scopeEnum.join(', ')}`
265
+ );
266
+ }
267
+ } else if (style === 'simple') {
268
+ const maxLen = rules.headerMaxLength ?? 50;
269
+ if (isPt) {
270
+ lines.push('- Use formato simples e direto');
271
+ lines.push('- Comece com verbo no infinitivo');
272
+ lines.push('- Exemplo: "corrigir validação de formulário"');
273
+ lines.push(`- Máximo ${maxLen} caracteres`);
274
+ } else {
275
+ lines.push('- Use simple and direct format');
276
+ lines.push('- Start with imperative verb');
277
+ lines.push('- Example: "fix form validation"');
278
+ lines.push(`- Maximum ${maxLen} characters`);
279
+ }
280
+ } else {
281
+ // detailed
282
+ const maxLen = rules.headerMaxLength ?? 72;
283
+ if (isPt) {
284
+ lines.push(`- Primeira linha: resumo em até ${maxLen} caracteres`);
285
+ lines.push('- Se necessário, adicione corpo explicativo');
286
+ lines.push('- Use presente do indicativo');
287
+ lines.push('- Seja descritivo mas conciso');
288
+ } else {
289
+ lines.push(`- First line: summary under ${maxLen} characters`);
290
+ lines.push('- Add explanatory body if needed');
291
+ lines.push('- Use imperative mood');
292
+ lines.push('- Be descriptive but concise');
293
+ }
294
+ }
295
+
296
+ return lines.join('\n');
297
+ }
298
+
170
299
  /**
171
300
  * Obtém instruções específicas baseadas no estilo de commit
172
301
  */
@@ -174,7 +303,7 @@ function getStyleInstructions(style: string, language: string): string {
174
303
  const instructions = {
175
304
  pt: {
176
305
  conventional: `- Use formato: tipo(escopo): descrição
177
- - Tipos válidos: feat, fix, docs, style, refactor, test, chore, build, ci
306
+ - Tipos válidos: ${DEFAULT_COMMIT_TYPES}
178
307
  - Exemplo: "feat(auth): adicionar validação de email"
179
308
  - Mantenha a primeira linha com até 50 caracteres`,
180
309
 
@@ -190,7 +319,7 @@ function getStyleInstructions(style: string, language: string): string {
190
319
  },
191
320
  en: {
192
321
  conventional: `- Use format: type(scope): description
193
- - Valid types: feat, fix, docs, style, refactor, test, chore, build, ci
322
+ - Valid types: ${DEFAULT_COMMIT_TYPES}
194
323
  - Example: "feat(auth): add email validation"
195
324
  - Keep first line under 50 characters`,
196
325
 
@@ -358,7 +487,8 @@ export function processOpenAIMessage(message: string): string {
358
487
  export async function generateCommitMessage(
359
488
  diff: string,
360
489
  config: Config,
361
- filenames: string[]
490
+ filenames: string[],
491
+ commitlintRules?: CommitlintRules | null
362
492
  ): Promise<OpenAIResponse> {
363
493
  try {
364
494
  if (!config.openai.apiKey) {
@@ -369,7 +499,7 @@ export async function generateCommitMessage(
369
499
  };
370
500
  }
371
501
 
372
- const prompt = buildPrompt(diff, config, filenames);
502
+ const prompt = buildPrompt(diff, config, filenames, commitlintRules);
373
503
 
374
504
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
375
505
  method: 'POST',
@@ -438,12 +568,13 @@ export async function generateWithRetry(
438
568
  diff: string,
439
569
  config: Config,
440
570
  filenames: string[],
441
- maxRetries: number = 3
571
+ maxRetries: number = 3,
572
+ commitlintRules?: CommitlintRules | null
442
573
  ): Promise<OpenAIResponse> {
443
574
  let lastError = '';
444
575
 
445
576
  for (let i = 0; i < maxRetries; i++) {
446
- const result = await generateCommitMessage(diff, config, filenames);
577
+ const result = await generateCommitMessage(diff, config, filenames, commitlintRules);
447
578
 
448
579
  if (result.success) {
449
580
  return result;
@@ -1,5 +1,6 @@
1
1
  import type { Config } from '../config/index';
2
2
  import type { CLIArgs } from '../utils/args';
3
+ import type { CommitlintRules } from '../commitlint/index';
3
4
  import { getCachedAnalysis, setCachedAnalysis } from './cache';
4
5
  import { showCommitResult } from '../ui/index';
5
6
  import { log } from '@clack/prompts';
@@ -386,7 +387,8 @@ export async function generateGroupDiff(group: FileGroup): Promise<string> {
386
387
  export async function handleSmartSplitMode(
387
388
  gitStatus: any,
388
389
  config: Config,
389
- args: CLIArgs
390
+ args: CLIArgs,
391
+ commitlintRules?: CommitlintRules | null
390
392
  ): Promise<void> {
391
393
  if (!args.silent) {
392
394
  log.info('🧠 Modo Smart Split ativado - Agrupando arquivos por contexto');
@@ -487,7 +489,7 @@ export async function handleSmartSplitMode(
487
489
  }
488
490
 
489
491
  const { generateWithRetry } = await import('./openai');
490
- const result = await generateWithRetry(groupDiff, config, group.files);
492
+ const result = await generateWithRetry(groupDiff, config, group.files, 3, commitlintRules);
491
493
 
492
494
  if (!result.success) {
493
495
  if (!args.silent) {