lazycommitt 1.0.6 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/cli.mjs +52 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<div>
|
|
3
3
|
<h1 align="center">lazycommit</h1>
|
|
4
|
+
<img width="2816" height="1536" alt="lazycommit" src="https://github.com/user-attachments/assets/ee0419ef-2461-4b45-8509-973f3bb0f55c" />
|
|
5
|
+
|
|
4
6
|
</div>
|
|
5
7
|
<p>A CLI that writes your git commit messages for you with AI using Groq. Never write a commit message again.</p>
|
|
6
8
|
<a href="https://www.npmjs.com/package/lazycommitz"><img src="https://img.shields.io/npm/v/lazycommitt" alt="Current version"></a>
|
|
@@ -20,6 +22,18 @@
|
|
|
20
22
|
npm install -g lazycommitt
|
|
21
23
|
```
|
|
22
24
|
|
|
25
|
+
### Install via Homebrew (macOS)
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
brew install lazycommit
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Upgrade:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
brew upgrade lazycommit
|
|
35
|
+
```
|
|
36
|
+
|
|
23
37
|
2. Retrieve your API key from [Groq Console](https://console.groq.com/keys)
|
|
24
38
|
|
|
25
39
|
> Note: If you haven't already, you'll have to create an account and get your API key.
|
package/dist/cli.mjs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var ke=Object.defineProperty;var
|
|
2
|
+
var ke=Object.defineProperty;var g=(e,t)=>ke(e,"name",{value:t,configurable:!0});import{command as X,cli as Ae}from"cleye";import{readFileSync as ve}from"fs";import{fileURLToPath as V,pathToFileURL as Ce}from"url";import z,{dirname as Pe,join as xe}from"path";import{execa as A}from"execa";import{dim as G,bgCyan as Z,black as L,green as _,red as T}from"kolorist";import{intro as ee,spinner as j,confirm as De,isCancel as te,outro as v,select as Me}from"@clack/prompts";import w from"fs/promises";import _e from"os";import se from"ini";import ne from"groq-sdk";var Te="1.0.9",Ie={version:Te};const q=class q extends Error{};g(q,"KnownError");let p=q;const Y=" ",I=g(e=>{e instanceof Error&&!(e instanceof p)&&(e.stack&&console.error(G(e.stack.split(`
|
|
3
3
|
`).slice(1).join(`
|
|
4
4
|
`))),console.error(`
|
|
5
|
-
${
|
|
6
|
-
${
|
|
7
|
-
`),diff:n}},"getStagedDiff"),
|
|
8
|
-
`);let o="",
|
|
9
|
-
`,
|
|
10
|
-
`,
|
|
11
|
-
`).filter(Boolean),
|
|
12
|
-
`);for(const
|
|
13
|
-
`):s+=
|
|
14
|
-
`;return s.trim().length>0&&t.push(s.trim()),t},"splitDiffByFile"),
|
|
15
|
-
`)},"buildCompactSummary"),
|
|
5
|
+
${Y}${G(`lazycommit v${Ie.version}`)}`),console.error(`
|
|
6
|
+
${Y}Please open a Bug report with the information above:`),console.error(`${Y}https://github.com/KartikLabhshetwar/lazycommit/issues/new/choose`))},"handleCliError"),oe=g(async()=>{const{stdout:e,failed:t}=await A("git",["rev-parse","--show-toplevel"],{reject:!1});if(t)throw new p("The current directory must be a Git repository!");return e},"assertGitRepo"),O=g(e=>`:(exclude)${e}`,"excludeFromDiff"),N=["package-lock.json","pnpm-lock.yaml","node_modules/**","dist/**","build/**",".next/**","coverage/**",".nyc_output/**","*.log","*.tmp","*.temp","*.cache",".DS_Store","Thumbs.db","*.min.js","*.min.css","*.bundle.js","*.bundle.css","*.lock"].map(O),ue=g(async e=>{const t=["diff","--cached","--diff-algorithm=minimal"],{stdout:s}=await A("git",[...t,"--name-only",...N,...e?e.map(O):[]]);if(!s)return;const{stdout:n}=await A("git",[...t,...N,...e?e.map(O):[]]);return{files:s.split(`
|
|
7
|
+
`),diff:n}},"getStagedDiff"),ae=g(e=>`Detected ${e.length.toLocaleString()} staged file${e.length>1?"s":""}`,"getDetectedMessage"),U=g(e=>Math.ceil(e.length/4),"estimateTokenCount"),ie=g((e,t=4e3)=>{if(U(e)<=t)return[e];const n=[],u=e.split(`
|
|
8
|
+
`);let o="",r=0;for(const l of u){const f=U(l);r+f>t&&o.length>0?(n.push(o.trim()),o=l+`
|
|
9
|
+
`,r=f):(o+=l+`
|
|
10
|
+
`,r+=f)}return o.trim().length>0&&n.push(o.trim()),n},"chunkDiff"),re=g(async e=>{const t=["diff","--cached","--diff-algorithm=minimal"],{stdout:s}=await A("git",[...t,"--name-only",...N,...e?e.map(O):[]]);if(!s)return null;const n=s.split(`
|
|
11
|
+
`).filter(Boolean),u=await Promise.all(n.map(async o=>{try{const{stdout:r}=await A("git",[...t,"--numstat","--",o]),[l,f]=r.split(" ").slice(0,2).map(Number);return{file:o,additions:l||0,deletions:f||0,changes:(l||0)+(f||0)}}catch{return{file:o,additions:0,deletions:0,changes:0}}}));return{files:n,fileStats:u,totalChanges:u.reduce((o,r)=>o+r.changes,0)}},"getDiffSummary"),ce=g(e=>{const t=[];let s="";const n=e.split(`
|
|
12
|
+
`);for(const u of n)u.startsWith("diff --git ")?(s.trim().length>0&&t.push(s.trim()),s=u+`
|
|
13
|
+
`):s+=u+`
|
|
14
|
+
`;return s.trim().length>0&&t.push(s.trim()),t},"splitDiffByFile"),Oe=g(async(e,t=20)=>{const s=await re(e);if(!s)return null;const{fileStats:n}=s,u=[...n].sort((h,i)=>i.changes-h.changes),o=u.slice(0,Math.max(1,t)),r=s.files.length,l=s.totalChanges,f=n.reduce((h,i)=>h+(i.additions||0),0),d=n.reduce((h,i)=>h+(i.deletions||0),0),a=[];a.push(`Files changed: ${r}`),a.push(`Additions: ${f}, Deletions: ${d}, Total changes: ${l}`),a.push("Top files by changes:");for(const h of o)a.push(`- ${h.file} (+${h.additions} / -${h.deletions}, ${h.changes} changes)`);return u.length>o.length&&a.push(`\u2026and ${u.length-o.length} more files`),a.join(`
|
|
15
|
+
`)},"buildCompactSummary"),le=g(e=>w.lstat(e).then(()=>!0,()=>!1),"fileExists"),Ee=["","conventional"],{hasOwnProperty:Fe}=Object.prototype,me=g((e,t)=>Fe.call(e,t),"hasOwn"),y=g((e,t,s)=>{if(!t)throw new p(`Invalid config property ${e}: ${s}`)},"parseAssert"),E={GROQ_API_KEY(e){if(!e)throw new p("Please set your Groq API key via `lazycommit config set GROQ_API_KEY=<your token>`");return y("GROQ_API_KEY",e.startsWith("gsk_"),'Must start with "gsk_"'),e},locale(e){return e?(y("locale",e,"Cannot be empty"),y("locale",/^[a-z-]+$/i.test(e),"Must be a valid locale (letters and dashes/underscores). You can consult the list of codes in: https://wikipedia.org/wiki/List_of_ISO_639-1_codes"),e):"en"},generate(e){if(!e)return 1;y("generate",/^\d+$/.test(e),"Must be an integer");const t=Number(e);return y("generate",t>0,"Must be greater than 0"),y("generate",t<=5,"Must be less or equal to 5"),t},type(e){return e?(y("type",Ee.includes(e),"Invalid commit type"),e):""},proxy(e){if(!(!e||e.length===0))return y("proxy",/^https?:\/\//.test(e),"Must be a valid URL"),e},model(e){return!e||e.length===0?"openai/gpt-oss-120b":e},timeout(e){if(!e)return 1e4;y("timeout",/^\d+$/.test(e),"Must be an integer");const t=Number(e);return y("timeout",t>=500,"Must be greater than 500ms"),t},"max-length"(e){if(!e)return 50;y("max-length",/^\d+$/.test(e),"Must be an integer");const t=Number(e);return y("max-length",t>=20,"Must be greater than 20 characters"),t},"chunk-size"(e){if(!e)return 4e3;y("chunk-size",/^\d+$/.test(e),"Must be an integer");const t=Number(e);return y("chunk-size",t>=1e3,"Must be at least 1000 tokens"),y("chunk-size",t<=8e3,"Must be at most 8000 tokens (Groq limit)"),t}},B=z.join(_e.homedir(),".lazycommit"),fe=g(async()=>{if(!await le(B))return Object.create(null);const t=await w.readFile(B,"utf8");return se.parse(t)},"readConfigFile"),H=g(async(e,t)=>{const s=await fe(),n={};for(const u of Object.keys(E)){const o=E[u],r=e?.[u]??s[u];if(t)try{n[u]=o(r)}catch{}else n[u]=o(r)}return n},"getConfig"),Se=g(async e=>{const t=await fe();for(const[s,n]of e){if(!me(E,s))throw new p(`Invalid config property: ${s}`);const u=E[s](n);t[s]=u}await w.writeFile(B,se.stringify(t),"utf8")},"setConfigs"),Re={"":"<commit message>",conventional:"<type>(<optional scope>): <commit message>"},ge={"":"",conventional:`Choose the most appropriate type from the following categories that best describes the git diff:
|
|
16
16
|
|
|
17
17
|
${JSON.stringify({feat:"A new feature for the user",fix:"A bug fix",docs:"Documentation only changes",style:"Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)",refactor:"A code change that neither fixes a bug nor adds a feature",perf:"A code change that improves performance",test:"Adding missing tests or correcting existing tests",build:"Changes that affect the build system or external dependencies",ci:"Changes to our CI configuration files and scripts",chore:"Other changes that don't modify src or test files",revert:"Reverts a previous commit"},null,2)}
|
|
18
18
|
|
|
19
|
-
IMPORTANT: Use the exact type name from the list above.`},
|
|
19
|
+
IMPORTANT: Use the exact type name from the list above.`},F=g((e,t,s)=>`You are an expert software engineer and git commit message writer. Your task is to analyze git diffs and generate clear, concise, and professional commit messages.
|
|
20
20
|
|
|
21
21
|
## Instructions:
|
|
22
22
|
1. Analyze the provided git diff carefully
|
|
@@ -37,11 +37,11 @@ IMPORTANT: Use the exact type name from the list above.`},R=h((e,t,s)=>`You are
|
|
|
37
37
|
|
|
38
38
|
## Language: ${e}
|
|
39
39
|
## Maximum length: ${t} characters
|
|
40
|
-
## Output format: ${
|
|
40
|
+
## Output format: ${Re[s]||"<commit message>"}
|
|
41
41
|
|
|
42
|
-
${
|
|
42
|
+
${ge[s]?`
|
|
43
43
|
## Commit Type Guidelines:
|
|
44
|
-
${
|
|
44
|
+
${ge[s]}`:""}
|
|
45
45
|
|
|
46
46
|
## Examples of good commit messages:
|
|
47
47
|
- "feat: Add user authentication with JWT tokens"
|
|
@@ -58,46 +58,55 @@ ${he[s]}`:""}
|
|
|
58
58
|
- "WIP"
|
|
59
59
|
- "Stuff"
|
|
60
60
|
|
|
61
|
-
Remember: Your response will be used directly as the git commit message. Make it professional and informative.`,"generatePrompt"),S=
|
|
61
|
+
Remember: Your response will be used directly as the git commit message. Make it professional and informative.`,"generatePrompt"),S=g(async(e,t,s,n,u,o,r,l,f,d,a)=>{const h=new ne({apiKey:e,timeout:d});try{return f>1?{choices:(await Promise.all(Array.from({length:f},()=>h.chat.completions.create({model:t,messages:s,temperature:n,top_p:u,frequency_penalty:o,presence_penalty:r,max_tokens:l,n:1})))).flatMap(c=>c.choices)}:await h.chat.completions.create({model:t,messages:s,temperature:n,top_p:u,frequency_penalty:o,presence_penalty:r,max_tokens:l,n:1})}catch(i){if(i instanceof ne.APIError){let m=`Groq API Error: ${i.status} - ${i.name}`;throw i.message&&(m+=`
|
|
62
62
|
|
|
63
|
-
${
|
|
63
|
+
${i.message}`),i.status===500&&(m+=`
|
|
64
64
|
|
|
65
|
-
Check the API status: https://console.groq.com/status`),(
|
|
65
|
+
Check the API status: https://console.groq.com/status`),(i.status===413||i.message&&i.message.includes("rate_limit_exceeded"))&&(m+=`
|
|
66
66
|
|
|
67
67
|
\u{1F4A1} Tip: Your diff is too large. Try:
|
|
68
68
|
1. Commit files in smaller batches
|
|
69
69
|
2. Exclude large files with --exclude
|
|
70
70
|
3. Use a different model with --model
|
|
71
|
-
4. Check if you have build artifacts staged (dist/, .next/, etc.)`),new p(
|
|
71
|
+
4. Check if you have build artifacts staged (dist/, .next/, etc.)`),new p(m)}throw i.code==="ENOTFOUND"?new p(`Error connecting to ${i.hostname} (${i.syscall}). Are you connected to the internet?`):i}},"createChatCompletion"),P=g(e=>e.trim().replace(/[\n\r]/g,"").replace(/(\w)\.$/,"$1"),"sanitizeMessage"),he=g(e=>Array.from(new Set(e)),"deduplicateMessages"),ze=["feat:","fix:","docs:","style:","refactor:","perf:","test:","build:","ci:","chore:","revert:"],Q=g((e,t)=>{const s=e.replace(/\s+/g," ").trim(),n=s.match(/\b(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)\b\s*:?\s+[^.\n]+/i);let u=n?n[0]:s.split(/[.!?]/)[0];const o=u.toLowerCase();for(const r of ze){const l=r.slice(0,-1);if(o.startsWith(l+" ")&&!o.startsWith(r)){u=l+": "+u.slice(l.length+1);break}}return u=P(u),u?(u.length>t&&(u=u.slice(0,t)),u):null},"deriveMessageFromReasoning"),W=g(async(e,t,s,n,u,o,r,l,f)=>{try{const d=await S(e,t,[{role:"system",content:F(s,o,r)},{role:"user",content:n}],.7,1,0,0,Math.max(200,o*8),u,l,f),a=d.choices.map(i=>i.message?.content||"").map(i=>P(i)).filter(Boolean);if(a.length>0)return he(a);const h=d.choices.map(i=>i.message?.reasoning||"").filter(Boolean);for(const i of h){const m=Q(i,o);if(m)return[m]}return[]}catch(d){const a=d;throw a.code==="ENOTFOUND"?new p(`Error connecting to ${a.hostname} (${a.syscall}). Are you connected to the internet?`):a}},"generateCommitMessage"),de=g(async(e,t,s,n,u,o,r,l,f,d=6e3)=>{const h=ce(n).flatMap(c=>ie(c,d)),i=h.length>0?h:ie(n,d);if(i.length===1)try{return await W(e,t,s,n,u,o,r,l,f)}catch(c){throw new p(`Failed to generate commit message: ${c instanceof Error?c.message:"Unknown error"}`)}const m=[];for(let c=0;c<i.length;c++){const $=i[c],b=U($)+1200;let k=Math.max(200,o*8);b+k>7500&&(k=Math.max(200,7500-b));const x=`Analyze this git diff and propose a concise commit message limited to ${o} characters. Focus on the most significant intent of the change.
|
|
72
72
|
|
|
73
|
-
${$}`;try{const
|
|
73
|
+
${$}`;try{const C=await S(e,t,[{role:"system",content:F(s,o,r)},{role:"user",content:x}],.7,1,0,0,k,1,l,f),J=(C.choices||[]).map(D=>D.message?.content).filter(Boolean);if(J.length>0)m.push(P(J[0]));else{const D=C.choices.map(M=>M.message?.reasoning||"").filter(Boolean);if(D.length>0){const M=Q(D[0],o);M&&m.push(M)}}}catch(C){console.warn(`Failed to process chunk ${c+1}:`,C instanceof Error?C.message:"Unknown error")}}if(m.length===0){const c=ce(n).map(b=>(b.split(`
|
|
74
74
|
`,1)[0]||"").split(" ")[2]?.replace("a/","")||"").filter(Boolean).slice(0,15),$=`Generate a single, concise commit message (<= ${o} chars) summarizing changes across these files:
|
|
75
|
-
${
|
|
76
|
-
`)}`;try{const k=((await S(e,t,[{role:"system",content:
|
|
75
|
+
${c.map(b=>`- ${b}`).join(`
|
|
76
|
+
`)}`;try{const k=((await S(e,t,[{role:"system",content:F(s,o,r)},{role:"user",content:$}],.7,1,0,0,Math.max(200,o*8),1,l,f)).choices||[]).map(x=>x.message?.content).filter(Boolean);if(k.length>0)return[P(k[0])]}catch{}throw new p("Failed to generate commit messages for any chunks")}if(m.length>1){const c=`I have ${m.length} commit messages for different parts of a large change:
|
|
77
77
|
|
|
78
|
-
${
|
|
78
|
+
${m.map(($,b)=>`${b+1}. ${$}`).join(`
|
|
79
79
|
`)}
|
|
80
80
|
|
|
81
81
|
Please generate a single, comprehensive commit message that captures the overall changes.
|
|
82
|
-
The message should be concise but cover the main aspects of all the changes.`;try{return await W(e,t,s,
|
|
83
|
-
|
|
84
|
-
${n}`,
|
|
85
|
-
|
|
82
|
+
The message should be concise but cover the main aspects of all the changes.`;try{return await W(e,t,s,c,u,o,r,l,f)}catch{return m}}return m},"generateCommitMessageFromChunks"),Ge=g(async(e,t,s,n,u,o,r,l,f)=>{const d=`This is a compact summary of staged changes. Generate a single, concise commit message within ${o} characters that reflects the overall intent.
|
|
83
|
+
|
|
84
|
+
${n}`,a=await S(e,t,[{role:"system",content:F(s,o,r)},{role:"user",content:d}],.7,1,0,0,Math.max(200,o*8),u,l),h=(a.choices||[]).map(m=>m.message?.content||"").map(m=>P(m)).filter(Boolean);if(h.length>0)return he(h);const i=a.choices.map(m=>m.message?.reasoning||"").filter(Boolean);for(const m of i){const c=Q(m,o);if(c)return[c]}return[]},"generateCommitMessageFromSummary"),je=`\u2554\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2557
|
|
85
|
+
\u2502 \u2502
|
|
86
|
+
\u2502 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2502
|
|
87
|
+
\u2502 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D \u2502
|
|
88
|
+
\u2502 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2502
|
|
89
|
+
\u2502 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2502
|
|
90
|
+
\u2502 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2502
|
|
91
|
+
\u2502 \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u2502
|
|
92
|
+
\u2502 \u2502
|
|
93
|
+
\u255A\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u255D`;var Ye=g(async(e,t,s,n,u)=>(async()=>{console.log(je),console.log(),ee(Z(L(" lazycommit "))),await oe();const o=j();s&&await A("git",["add","--update"]),o.start("Detecting staged files");const r=await ue(t);if(!r)throw o.stop("Detecting staged files"),new p("No staged changes found. Stage your changes manually, or automatically stage all changes with the `--all` flag.");const l=await re(t),f=r.diff.length>5e4;f&&l?o.stop(`${ae(r.files)} (${l.totalChanges.toLocaleString()} changes):
|
|
94
|
+
${r.files.map(c=>` ${c}`).join(`
|
|
86
95
|
`)}
|
|
87
96
|
|
|
88
|
-
\u26A0\uFE0F Large diff detected - using chunked processing`):o.stop(`${
|
|
89
|
-
${
|
|
90
|
-
`)}`);const{env:d}=process,
|
|
91
|
-
|
|
92
|
-
${
|
|
93
|
-
`});if(!
|
|
94
|
-
`),
|
|
95
|
-
`),
|
|
96
|
-
${
|
|
97
|
-
`)}`):(
|
|
98
|
-
`),
|
|
99
|
-
${
|
|
100
|
-
`),await w.appendFile(K,
|
|
97
|
+
\u26A0\uFE0F Large diff detected - using chunked processing`):o.stop(`${ae(r.files)}:
|
|
98
|
+
${r.files.map(c=>` ${c}`).join(`
|
|
99
|
+
`)}`);const{env:d}=process,a=await H({GROQ_API_KEY:d.GROQ_API_KEY,proxy:d.https_proxy||d.HTTPS_PROXY||d.http_proxy||d.HTTP_PROXY,generate:e?.toString(),type:n?.toString()}),h=j();h.start("The AI is analyzing your changes");let i;try{if(f){const c=await Oe(t,25);c?i=await Ge(a.GROQ_API_KEY,a.model,a.locale,c,a.generate,a["max-length"],a.type,a.timeout,a.proxy):i=await de(a.GROQ_API_KEY,a.model,a.locale,r.diff,a.generate,a["max-length"],a.type,a.timeout,a.proxy,a["chunk-size"])}else i=await de(a.GROQ_API_KEY,a.model,a.locale,r.diff,a.generate,a["max-length"],a.type,a.timeout,a.proxy,a["chunk-size"])}finally{h.stop("Changes analyzed")}if(i.length===0)throw new p("No commit messages were generated. Try again.");let m;if(i.length===1){[m]=i;const c=await De({message:`Use this commit message?
|
|
100
|
+
|
|
101
|
+
${m}
|
|
102
|
+
`});if(!c||te(c)){v("Commit cancelled");return}}else{const c=await Me({message:`Pick a commit message to use: ${G("(Ctrl+c to exit)")}`,options:i.map($=>({label:$,value:$}))});if(te(c)){v("Commit cancelled");return}m=c}await A("git",["commit","-m",m,...u]),v(`${_("\u2714")} Successfully committed!`)})().catch(o=>{v(`${T("\u2716")} ${o.message}`),I(o),process.exit(1)}),"lazycommit");const[K,Ne]=process.argv.slice(2);var Ue=g(()=>(async()=>{if(!K)throw new p('Commit message file path is missing. This file should be called from the "prepare-commit-msg" git hook');if(Ne)return;const e=await ue();if(!e)return;ee(Z(L(" lazycommit ")));const{env:t}=process,s=await H({GROQ_API_KEY:t.GROQ_API_KEY,proxy:t.https_proxy||t.HTTPS_PROXY||t.http_proxy||t.HTTP_PROXY}),n=j();n.start("The AI is analyzing your changes");let u;try{u=await W(s.GROQ_API_KEY,s.model,s.locale,e.diff,s.generate,s["max-length"],s.type,s.timeout,s.proxy)}finally{n.stop("Changes analyzed")}const r=await w.readFile(K,"utf8")!=="",l=u.length>1;let f="";r&&(f=`# \u{1F916} AI generated commit${l?"s":""}
|
|
103
|
+
`),l?(r&&(f+=`# Select one of the following messages by uncommeting:
|
|
104
|
+
`),f+=`
|
|
105
|
+
${u.map(d=>`# ${d}`).join(`
|
|
106
|
+
`)}`):(r&&(f+=`# Edit the message below and commit:
|
|
107
|
+
`),f+=`
|
|
108
|
+
${u[0]}
|
|
109
|
+
`),await w.appendFile(K,f),v(`${_("\u2714")} Saved commit message!`)})().catch(e=>{v(`${T("\u2716")} ${e.message}`),I(e),process.exit(1)}),"prepareCommitMessageHook"),Be=X({name:"config",parameters:["<mode>","<key=value...>"]},e=>{(async()=>{const{mode:t,keyValue:s}=e._;if(t==="get"){const n=await H({},!0);for(const u of s)me(n,u)&&console.log(`${u}=${n[u]}`);return}if(t==="set"){await Se(s.map(n=>n.split("=")));return}throw new p(`Invalid mode: ${t}`)})().catch(t=>{console.error(`${T("\u2716")} ${t.message}`),I(t),process.exit(1)})});const pe="prepare-commit-msg",ye=`.git/hooks/${pe}`,R=V(new URL("cli.mjs",import.meta.url)),He=process.argv[1].replace(/\\/g,"/").endsWith(`/${ye}`),we=process.platform==="win32",$e=`
|
|
101
110
|
#!/usr/bin/env node
|
|
102
|
-
import(${JSON.stringify(
|
|
103
|
-
`.trim();var
|
|
111
|
+
import(${JSON.stringify(Ce(R))})
|
|
112
|
+
`.trim();var Qe=X({name:"hook",parameters:["<install/uninstall>"]},e=>{(async()=>{const t=await oe(),{installUninstall:s}=e._,n=z.join(t,ye),u=await le(n);if(s==="install"){if(u){if(await w.realpath(n).catch(()=>{})===R){console.warn("The hook is already installed");return}throw new p(`A different ${pe} hook seems to be installed. Please remove it before installing lazycommit.`)}await w.mkdir(z.dirname(n),{recursive:!0}),we?await w.writeFile(n,$e):(await w.symlink(R,n,"file"),await w.chmod(n,493)),console.log(`${_("\u2714")} Hook installed`);return}if(s==="uninstall"){if(!u){console.warn("Hook is not installed");return}if(we){if(await w.readFile(n,"utf8")!==$e){console.warn("Hook is not installed");return}}else if(await w.realpath(n)!==R){console.warn("Hook is not installed");return}await w.rm(n),console.log(`${_("\u2714")} Hook uninstalled`);return}throw new p(`Invalid mode: ${s}`)})().catch(t=>{console.error(`${T("\u2716")} ${t.message}`),I(t),process.exit(1)})});const We=V(import.meta.url),Ke=Pe(We),qe=JSON.parse(ve(xe(Ke,"../package.json"),"utf8")),{description:Je,version:Xe}=qe,be=process.argv.slice(2);Ae({name:"lazycommit",version:Xe,flags:{generate:{type:Number,description:"Number of messages to generate (Warning: generating multiple costs more) (default: 1)",alias:"g"},exclude:{type:[String],description:"Files to exclude from AI analysis",alias:"x"},all:{type:Boolean,description:"Automatically stage changes in tracked files for the commit",alias:"a",default:!1},type:{type:String,description:"Type of commit message to generate",alias:"t"}},commands:[Be,Qe],help:{description:Je},ignoreArgv:g(e=>e==="unknown-flag"||e==="argument","ignoreArgv")},e=>{He?Ue():Ye(e.flags.generate,e.flags.exclude,e.flags.all,e.flags.type,be)},be);
|