pushai 1.0.8 → 1.0.10

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.
Files changed (2) hide show
  1. package/dist/index.mjs +10 -9
  2. package/package.json +3 -2
package/dist/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import{Command as Re}from"commander";import P from"chalk";import{confirm as fe,intro as he,isCancel as ye,outro as M}from"@clack/prompts";import Z from"os";import B from"path";import y from"fs";import D from"keytar";var q="pushai",U="api-key";async function j(){let t=G(),o={};if(y.existsSync(t))try{o=JSON.parse(y.readFileSync(t,"utf-8"))}catch{}let r=await D.getPassword(q,U);return r&&(o.apiKey=r),o}async function X(t){let o=G(),{apiKey:r,...g}=t;r&&await D.setPassword(q,U,r);let a={};if(y.existsSync(o))try{a=JSON.parse(y.readFileSync(o,"utf-8"))}catch{}let c={...a,...g};y.writeFileSync(o,JSON.stringify(c,null,2),"utf-8")}async function ee(){try{let t=Z.homedir(),o=B.join(t,".config","pushai"),r=!1;return y.existsSync(o)&&(y.rmSync(o,{recursive:!0,force:!0}),r=!0),await D.deletePassword(q,U),r}catch(t){return console.error("Failed to reset config:",t),!1}}function G(){let t=B.join(Z.homedir(),".config","pushai");return y.existsSync(t)||y.mkdirSync(t,{recursive:!0}),B.join(t,"config.json")}import w from"chalk";var e={common:{operationCancelled:"Operation cancelled.",interrupted:"Request interrupted.",dryRun:{header:"[DRY RUN] No changes were committed or pushed.",proposed:t=>`Here's the generated commit message:
3
- ${t}`},noRemote:{header:"There's no Git remote repository configured.",instruction:"Try adding a remote repository (e.g. `git remote add origin <url>`) and run the command again."},success:{committed:"Changes committed successfully.",pushed:"Changes pushed successfully."}},commit:{intro:(t,o)=>`${w.cyan("\u25CF")} ${w.bgCyan.black.bold(` ${t.toUpperCase()} `)} ${w.dim("\u2022")} ${w.white(o)}
4
- `,outro:"Successfully synced with the remote repository.",noteTitle:"Commit message preview",gitRepoMissing:"There's no Git repository in this directory.",initConfirm:"Would you like to initialize a new Git repository?",initSuccess:"Git repository initialized successfully.",abortNoRepo:"A Git repository is required to continue.",noChanges:"No changes were found to commit.",generating:"Putting together a commit message..",generatingSlow10:"Please wait.. This might take a moment.",generatingSlow60:"This is taking longer than usual..",generated:"Commit message generated.",generationFailed:"Couldn't generate a commit message.",generationCancelled:"Commit message generation cancelled.",emptyMessageTitle:"Empty commit message",emptyMessagePrompt:"AI returned an empty commit message. What would you like to do?",emptyRegenerate:"Regenerate",emptyManual:"Enter manually",emptyCancel:"Cancel",stillEmptyWarning:"Still empty, try manual entry.",manualMessagePrompt:"Enter commit message manually:",manualMessageRequired:"Message cannot be empty",manualAccepted:"Manual message accepted.",actionPrompt:"What would you like to do next?",actions:{accept:"Commit and push changes",edit:"Edit commit message",regenerate:"Generate another message",cancel:"Cancel operation"},editPrompt:"Update the commit message:",regenerating:"Regenerating a new commit message..",regenerated:"New commit message ready.",regenerationFailed:t=>`Couldn't generate another message: ${t}`,processStopped:"Process stopped. No changes were committed or pushed.",committing:"Creating commit..",committingSlow10:"Almost there..",committingSlow60:"Still processing your request..",pushing:"Pushing changes to remote..",pushingSlow10:"Syncing changes..",pushingSlow60:"Still syncing changes.. Please wait.",operationFailed:"Something went wrong while completing the operation.",gitError:t=>`Git reported an error: ${t}`},config:{intro:"Welcome to PushAI. Let's get things set up.",outro:"Configuration complete. You're ready to go!",providerPrompt:"Choose from the available AI providers:",apiKeyPrompt:t=>`Enter your ${w.cyan.bold(t)} API key:`,apiKeyRequired:"You'll need an API key to continue. Simply copy and paste to proceed.",modelPrompt:"Which model would you like to use?",customModelSeparator:"Use a custom model ID",customModelPrompt:"Enter the custom model ID:",customModelRequired:"A model ID is required.",baseUrlPrompt:"Enter a base URL",baseUrlPromptPlaceholder:"Optional \u2014 press Enter to skip",saved:"Configuration saved successfully.",providerLabel:t=>`${w.white.bold("Provider:")} ${t}`,modelLabel:t=>`${w.white.bold("Model: ")} ${t}`,apiKeyLabel:t=>`${w.white.bold("API Key: ")} ****${t}`,configFile:t=>`
2
+ import Ie from"chalk";import{basename as Re}from"path";import v from"chalk";var e={common:{operationCancelled:"Operation cancelled.",interrupted:"Request interrupted.",dryRun:{header:"[DRY RUN] No changes were committed or pushed.",proposed:t=>`Here's the generated commit message:
3
+ ${t}`},noRemote:{header:"There's no Git remote repository configured.",instruction:"Try adding a remote repository (e.g. `git remote add origin <url>`) and run the command again."},success:{committed:"Changes committed successfully.",pushed:"Changes pushed successfully."}},commit:{intro:(t,o)=>`
4
+ ${v.cyan("\u25CF")} ${v.bgCyan.black.bold(` ${t.toUpperCase()} `)} ${v.dim("\u2022")} ${v.white(o)}
5
+ `,outro:"Successfully synced with the remote repository.",noteTitle:"Commit message preview",gitRepoMissing:"There's no Git repository in this directory.",initConfirm:"Would you like to initialize a new Git repository?",initSuccess:"Git repository initialized successfully.",abortNoRepo:"A Git repository is required to continue.",noChanges:"No changes were found to commit.",generating:"Putting together a commit message..",generatingSlow10:"Please wait.. This might take a moment.",generatingSlow60:"This is taking longer than usual..",generated:"Commit message generated.",generationFailed:"Couldn't generate a commit message.",generationCancelled:"Commit message generation cancelled.",emptyMessageTitle:"Empty commit message",emptyMessagePrompt:"AI returned an empty commit message. What would you like to do?",emptyRegenerate:"Regenerate",emptyManual:"Enter manually",emptyCancel:"Cancel",stillEmptyWarning:"Still empty, try manual entry.",manualMessagePrompt:"Enter commit message manually:",manualMessageRequired:"Message cannot be empty",manualAccepted:"Manual message accepted.",actionPrompt:"What would you like to do next?",actions:{accept:"Commit and push changes",edit:"Edit commit message",regenerate:"Generate another message",cancel:"Cancel operation"},editPrompt:"Update the commit message:",regenerating:"Regenerating a new commit message..",regenerated:"New commit message ready.",regenerationFailed:t=>`Couldn't generate another message: ${t}`,processStopped:"Process stopped. No changes were committed or pushed.",committing:"Creating commit..",committingSlow10:"Almost there..",committingSlow60:"Still processing your request..",pushing:"Pushing changes to remote..",pushingSlow10:"Syncing changes..",pushingSlow60:"Still syncing changes.. Please wait.",operationFailed:"Something went wrong while completing the operation.",gitError:t=>`Git reported an error: ${t}`},config:{intro:"Welcome to PushAI. Let's get things set up.",outro:"Configuration complete. You're ready to go!",providerPrompt:"Choose from the available AI providers:",apiKeyPrompt:t=>`Enter your ${v.cyan.bold(t)} API key:`,apiKeyRequired:"You'll need an API key to continue. Simply copy and paste to proceed.",modelPrompt:"Which model would you like to use?",customModelSeparator:"Use a custom model ID",customModelPrompt:"Enter the custom model ID:",customModelRequired:"A model ID is required.",baseUrlPrompt:"Enter a base URL",baseUrlPromptPlaceholder:"Optional \u2014 press Enter to skip",saved:"Configuration saved successfully.",providerLabel:t=>`${v.white.bold("Provider:")} ${t}`,modelLabel:t=>`${v.white.bold("Model: ")} ${t}`,apiKeyLabel:t=>`${v.white.bold("API Key: ")} ****${t}`,configFile:t=>`
5
6
  File: ${t}`,commandsHint:"Here are a few commands to try:",hintCommit:"Generate AI-powered commit messages",hintConfig:"Update provider or API settings",hintReset:"Clear saved configuration"},reset:{intro:"Resetting PushAI configuration",confirm:"Remove all PushAI configurations and API keys?",outro:"PushAI configuration removed successfully.",nothingToDelete:"There's no configuration data to remove."},errors:{authInvalid:"Authentication failed. The API key or token appears to be invalid.",authFix:"Try running `pai reset` to update your credentials.",unknown:t=>`
6
- ${t||"Something unexpected went wrong."}`}};async function te(){he(P.yellow.bold(e.reset.intro));try{let t=await fe({message:P.red(e.reset.confirm),initialValue:!1});if(ye(t)){M(P.red(e.common.operationCancelled));return}if(!t){M(P.red(e.common.operationCancelled));return}await ee()?M(P.green.bold(e.reset.outro)):M(P.dim(e.reset.nothingToDelete))}catch(t){if(t.name==="ExitPromptError"||t.name==="AbortError"){M(P.red(e.common.operationCancelled));return}throw t}}import d from"chalk";import{select as re,password as ve,isCancel as L,text as Ce,intro as Pe,note as ne,outro as T}from"@clack/prompts";var v=class{apiKey;model;constructor(o,r){this.apiKey=o,this.model=r}};var b=(t,o=!1)=>{let r=`You are a Senior Software Engineer. Your task is to write a concise, technical, and impactful commit message based on a git diff.
7
+ ${t||"Something unexpected went wrong."}`}};import{Command as Ae}from"commander";import P from"chalk";import{confirm as ue,intro as fe,isCancel as he,outro as M}from"@clack/prompts";import Q from"os";import _ from"path";import y from"fs";import B from"keytar";var D="pushai",q="api-key";async function U(){let t=G(),o={};if(y.existsSync(t))try{o=JSON.parse(y.readFileSync(t,"utf-8"))}catch{}let r=await B.getPassword(D,q);return r&&(o.apiKey=r),o}async function z(t){let o=G(),{apiKey:r,...g}=t;r&&await B.setPassword(D,q,r);let s={};if(y.existsSync(o))try{s=JSON.parse(y.readFileSync(o,"utf-8"))}catch{}let c={...s,...g};y.writeFileSync(o,JSON.stringify(c,null,2),"utf-8")}async function V(){try{let t=Q.homedir(),o=_.join(t,".config","pushai"),r=!1;return y.existsSync(o)&&(y.rmSync(o,{recursive:!0,force:!0}),r=!0),await B.deletePassword(D,q),r}catch(t){return console.error("Failed to reset config:",t),!1}}function G(){let t=_.join(Q.homedir(),".config","pushai");return y.existsSync(t)||y.mkdirSync(t,{recursive:!0}),_.join(t,"config.json")}async function Z(){fe(P.yellow.bold(e.reset.intro));try{let t=await ue({message:P.red(e.reset.confirm),initialValue:!1});if(he(t)){M(P.red(e.common.operationCancelled));return}if(!t){M(P.red(e.common.operationCancelled));return}await V()?M(P.green.bold(e.reset.outro)):M(P.dim(e.reset.nothingToDelete))}catch(t){if(t.name==="ExitPromptError"||t.name==="AbortError"){M(P.red(e.common.operationCancelled));return}throw t}}import d from"chalk";import{select as te,password as we,isCancel as L,text as ve,intro as Ce,note as oe,outro as T}from"@clack/prompts";var C=class{apiKey;model;constructor(o,r){this.apiKey=o,this.model=r}};var b=(t,o=!1)=>{let r=`You are a Senior Software Engineer. Your task is to write a concise, technical, and impactful commit message based on a git diff.
7
8
 
8
9
  ### CONSTRAINTS
9
10
  - Format: <type>(<scope>): <description>
@@ -22,9 +23,9 @@ ${t||"Something unexpected went wrong."}`}};async function te(){he(P.yellow.bold
22
23
  ${t}`;return o&&(r+=`
23
24
 
24
25
  ### ADDITIONAL INSTRUCTION FOR REGENERATION:
25
- You already generated a commit message for this diff. Now please provide a **different, alternative** commit message. It should still follow the same constraints but approach the change from a slightly different perspective or emphasize a different aspect of the changes.`),r};var N=class extends v{async generateCommitMessage(o,r,g){let a=g?.regenerate||!1,c=`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:generateContent?key=${this.apiKey}`,s={contents:[{parts:[{text:b(o,a)}]}],generationConfig:{temperature:a?.8:.2,maxOutputTokens:100}},l=await fetch(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s),signal:r});if(!l.ok){let n=await l.text(),h=`Gemini API error (${l.status})`;try{let S=JSON.parse(n);S.error?.message&&(h=S.error.message)}catch{h=n}throw new Error(h)}return((await l.json()).candidates?.[0]?.content?.parts?.[0]?.text||"").trim().replace(/['"]/g,"")}};import{InferenceClient as oe}from"@huggingface/inference";var O=class extends v{async generateCommitMessage(o,r,g){let a=g?.regenerate||!1;try{return((await new oe(this.apiKey,{endpointUrl:"https://router.huggingface.co/v1"}).chatCompletion({model:this.model,messages:[{role:"user",content:b(o,a)}],max_tokens:100,temperature:a?.8:.2},{signal:r})).choices[0]?.message.content||"").trim().replace(/['"]/g,"").replace(/^commit:\s*/i,"")}catch{try{return(await new oe(this.apiKey).textGeneration({model:this.model,inputs:b(o,a),parameters:{max_new_tokens:100,temperature:a?.8:.2,return_full_text:!1}},{signal:r})).generated_text.trim().replace(/['"]/g,"").replace(/^commit:\s*/i,"")}catch{throw new Error(`Model "${this.model}" does not support chat or text generation. For Hugging Face, please use a model that supports "text-generation" or "conversational", e.g., "mistralai/Mistral-7B-Instruct-v0.3" or "microsoft/Phi-3-mini-4k-instruct".`)}}}};import we from"openai";var $=class extends v{constructor(o,r){super(o,r)}async generateCommitMessage(o,r,g){let a=g?.regenerate||!1;return(await new we({apiKey:this.apiKey}).chat.completions.create({model:this.model,messages:[{role:"user",content:b(o,a)}],temperature:a?.8:.7,max_tokens:100},{signal:r})).choices[0]?.message.content?.trim().replace(/['"]/g,"")||""}};var F=[{name:"Google Gemini",value:"gemini",models:[{name:"Gemini 3.1 Flash Lite",value:"gemini-3.1-flash-lite",hint:"free \u2022 recommended"},{name:"Gemini Flash Lite Latest",value:"gemini-flash-lite-latest",hint:"free"},{name:"Gemini 2.0 Flash Lite",value:"gemini-2.0-flash-lite",hint:"free"},{name:"Gemini 2.5 Flash",value:"gemini-2.5-flash",hint:"free \u2022 fast"},{name:"Gemini 2.5 Pro",value:"gemini-2.5-pro",hint:"paid \u2022 best quality"},{name:"Gemini 1.5 Flash",value:"gemini-1.5-flash",hint:"free \u2022 stable"},{name:"Gemini 1.5 Pro",value:"gemini-1.5-pro",hint:"paid"}]},{name:"OpenAI",value:"openai",models:[{name:"GPT-4.1 Mini",value:"gpt-4.1-mini",hint:"paid \u2022 recommended"},{name:"GPT-4.1",value:"gpt-4.1",hint:"paid \u2022 best quality"},{name:"GPT-4.1 Nano",value:"gpt-4.1-nano",hint:"paid \u2022 lightweight"},{name:"GPT-4o",value:"gpt-4o",hint:"paid \u2022 multimodal"},{name:"GPT-4o Mini",value:"gpt-4o-mini",hint:"free \u2022 fast"},{name:"GPT-4 Turbo",value:"gpt-4-turbo",hint:"paid"}]},{name:"Hugging Face",value:"huggingface",models:[{name:"Llama 3 8B Instruct",value:"meta-llama/Meta-Llama-3-8B-Instruct",hint:"free \u2022 recommended"},{name:"Llama 3.1 8B Instruct",value:"meta-llama/Llama-3.1-8B-Instruct",hint:"free"},{name:"Llama 3.1 70B Instruct",value:"meta-llama/Llama-3.1-70B-Instruct",hint:"free \u2022 high quality"},{name:"Mistral 7B Instruct",value:"mistralai/Mistral-7B-Instruct-v0.3",hint:"free \u2022 lightweight"},{name:"Mixtral 8x7B Instruct",value:"mistralai/Mixtral-8x7B-Instruct-v0.1",hint:"free \u2022 powerful"},{name:"Qwen 2 7B Instruct",value:"Qwen/Qwen2-7B-Instruct",hint:"free"},{name:"Qwen 2.5 7B Instruct",value:"Qwen/Qwen2.5-7B-Instruct",hint:"free \u2022 improved"},{name:"DeepSeek Coder 33B",value:"deepseek-ai/deepseek-coder-33b-instruct",hint:"free \u2022 coding"},{name:"Phi-3 Mini 4K",value:"microsoft/Phi-3-mini-4k-instruct",hint:"free \u2022 compact"}]}];function ie(t){switch(t.provider){case"gemini":return Promise.resolve(new N(t.apiKey,t.model));case"huggingface":return Promise.resolve(new O(t.apiKey,t.model));case"openai":return Promise.resolve(new $(t.apiKey,t.model));default:throw new Error(`Provider ${t.provider} is not supported.`)}}async function K(){Pe(d.blue.bold(e.config.intro));try{let t=await re({message:e.config.providerPrompt,options:F.map(n=>({label:n.name,value:n.value}))});if(L(t)){T(d.red(e.common.operationCancelled));return}let o=t,r=F.find(n=>n.value===o),g=await ve({message:e.config.apiKeyPrompt(o),mask:"*",validate:n=>{if(!n||n.trim()==="")return e.config.apiKeyRequired}});if(L(g)){T(d.red(e.common.operationCancelled));return}let a=g,c=await re({message:e.config.modelPrompt,options:[...r?.models.map(n=>({label:n.name,value:n.value,hint:n.hint}))||[],{label:e.config.customModelSeparator,value:"custom_id"}]});if(L(c)){T(d.red(e.common.operationCancelled));return}let s=c;if(s==="custom_id"){let n=await Ce({message:e.config.customModelPrompt,placeholder:"...",validate:h=>{if(!h||h.trim()==="")return e.config.customModelRequired}});if(L(n)){T(d.red(e.common.operationCancelled));return}s=n}await X({provider:o,apiKey:a,model:s});let l=G(),u=`${e.config.providerLabel(F.find(n=>o===n.value)?.name||o)}
26
- ${e.config.modelLabel(s)}
27
- ${e.config.apiKeyLabel(a.slice(-4))}
28
- ${d.dim(e.config.configFile(l))}`;ne(u,d.cyan(e.config.saved));let m=`${d.white.bold("pai commit:")} ${d.white(e.config.hintCommit)}
26
+ You already generated a commit message for this diff. Now please provide a **different, alternative** commit message. It should still follow the same constraints but approach the change from a slightly different perspective or emphasize a different aspect of the changes.`),r};var N=class extends C{async generateCommitMessage(o,r,g){let s=g?.regenerate||!1,c=`https://generativelanguage.googleapis.com/v1beta/models/${this.model}:generateContent?key=${this.apiKey}`,n={contents:[{parts:[{text:b(o,s)}]}],generationConfig:{temperature:s?.8:.2,maxOutputTokens:100}},l=await fetch(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),signal:r});if(!l.ok){let a=await l.text(),h=`Gemini API error (${l.status})`;try{let S=JSON.parse(a);S.error?.message&&(h=S.error.message)}catch{h=a}throw new Error(h)}return((await l.json()).candidates?.[0]?.content?.parts?.[0]?.text||"").trim().replace(/['"]/g,"")}};import{InferenceClient as X}from"@huggingface/inference";var O=class extends C{async generateCommitMessage(o,r,g){let s=g?.regenerate||!1;try{return((await new X(this.apiKey,{endpointUrl:"https://router.huggingface.co/v1"}).chatCompletion({model:this.model,messages:[{role:"user",content:b(o,s)}],max_tokens:100,temperature:s?.8:.2},{signal:r})).choices[0]?.message.content||"").trim().replace(/['"]/g,"").replace(/^commit:\s*/i,"")}catch{try{return(await new X(this.apiKey).textGeneration({model:this.model,inputs:b(o,s),parameters:{max_new_tokens:100,temperature:s?.8:.2,return_full_text:!1}},{signal:r})).generated_text.trim().replace(/['"]/g,"").replace(/^commit:\s*/i,"")}catch(n){throw new Error(n instanceof Error?n.message:"Hugging Face API error")}}}};import ye from"openai";var $=class extends C{constructor(o,r){super(o,r)}async generateCommitMessage(o,r,g){let s=g?.regenerate||!1;return(await new ye({apiKey:this.apiKey}).chat.completions.create({model:this.model,messages:[{role:"user",content:b(o,s)}],temperature:s?.8:.7,max_tokens:100},{signal:r})).choices[0]?.message.content?.trim().replace(/['"]/g,"")||""}};var F=[{name:"Google Gemini",value:"gemini",models:[{name:"Gemini 3.1 Flash Lite",value:"gemini-3.1-flash-lite",hint:"free \u2022 recommended"},{name:"Gemini Flash Lite Latest",value:"gemini-flash-lite-latest",hint:"free"},{name:"Gemini 2.0 Flash Lite",value:"gemini-2.0-flash-lite",hint:"free"},{name:"Gemini 2.5 Flash",value:"gemini-2.5-flash",hint:"free \u2022 fast"},{name:"Gemini 2.5 Pro",value:"gemini-2.5-pro",hint:"paid \u2022 best quality"},{name:"Gemini 1.5 Flash",value:"gemini-1.5-flash",hint:"free \u2022 stable"},{name:"Gemini 1.5 Pro",value:"gemini-1.5-pro",hint:"paid"}]},{name:"OpenAI",value:"openai",models:[{name:"GPT-4.1 Mini",value:"gpt-4.1-mini",hint:"paid \u2022 recommended"},{name:"GPT-4.1",value:"gpt-4.1",hint:"paid \u2022 best quality"},{name:"GPT-4.1 Nano",value:"gpt-4.1-nano",hint:"paid \u2022 lightweight"},{name:"GPT-4o",value:"gpt-4o",hint:"paid \u2022 multimodal"},{name:"GPT-4o Mini",value:"gpt-4o-mini",hint:"free \u2022 fast"},{name:"GPT-4 Turbo",value:"gpt-4-turbo",hint:"paid"}]},{name:"Hugging Face",value:"huggingface",models:[{name:"Llama 3 8B Instruct",value:"meta-llama/Meta-Llama-3-8B-Instruct",hint:"free \u2022 recommended"},{name:"Llama 3.1 8B Instruct",value:"meta-llama/Llama-3.1-8B-Instruct",hint:"free"},{name:"Llama 3.1 70B Instruct",value:"meta-llama/Llama-3.1-70B-Instruct",hint:"free \u2022 high quality"},{name:"Mistral 7B Instruct",value:"mistralai/Mistral-7B-Instruct-v0.3",hint:"free \u2022 lightweight"},{name:"Mixtral 8x7B Instruct",value:"mistralai/Mixtral-8x7B-Instruct-v0.1",hint:"free \u2022 powerful"},{name:"Qwen 2 7B Instruct",value:"Qwen/Qwen2-7B-Instruct",hint:"free"},{name:"Qwen 2.5 7B Instruct",value:"Qwen/Qwen2.5-7B-Instruct",hint:"free \u2022 improved"},{name:"DeepSeek Coder 33B",value:"deepseek-ai/deepseek-coder-33b-instruct",hint:"free \u2022 coding"},{name:"Phi-3 Mini 4K",value:"microsoft/Phi-3-mini-4k-instruct",hint:"free \u2022 compact"}]}];function ee(t){switch(t.provider){case"gemini":return Promise.resolve(new N(t.apiKey,t.model));case"huggingface":return Promise.resolve(new O(t.apiKey,t.model));case"openai":return Promise.resolve(new $(t.apiKey,t.model));default:throw new Error(`Provider ${t.provider} is not supported.`)}}async function K(){Ce(d.blue.bold(e.config.intro));try{let t=await te({message:e.config.providerPrompt,options:F.map(a=>({label:a.name,value:a.value}))});if(L(t)){T(d.red(e.common.operationCancelled));return}let o=t,r=F.find(a=>a.value===o),g=await we({message:e.config.apiKeyPrompt(o),mask:"*",validate:a=>{if(!a||a.trim()==="")return e.config.apiKeyRequired}});if(L(g)){T(d.red(e.common.operationCancelled));return}let s=g,c=await te({message:e.config.modelPrompt,options:[...r?.models.map(a=>({label:a.name,value:a.value,hint:a.hint}))||[],{label:e.config.customModelSeparator,value:"custom_id"}]});if(L(c)){T(d.red(e.common.operationCancelled));return}let n=c;if(n==="custom_id"){let a=await ve({message:e.config.customModelPrompt,placeholder:"...",validate:h=>{if(!h||h.trim()==="")return e.config.customModelRequired}});if(L(a)){T(d.red(e.common.operationCancelled));return}n=a}await z({provider:o,apiKey:s,model:n});let l=G(),u=`${e.config.providerLabel(F.find(a=>o===a.value)?.name||o)}
27
+ ${e.config.modelLabel(n)}
28
+ ${e.config.apiKeyLabel(s.slice(-4))}
29
+ ${d.dim(e.config.configFile(l))}`;oe(u,d.cyan(e.config.saved));let m=`${d.white.bold("pai commit:")} ${d.white(e.config.hintCommit)}
29
30
  ${d.white.bold("pai config:")} ${d.white(e.config.hintConfig)}
30
- ${d.white.bold("pai reset:")} ${d.white(e.config.hintReset)}`;ne(m,d.cyan(e.config.commandsHint)),T(d.green.bold(e.config.outro))}catch(t){if(t.name==="ExitPromptError"){console.log(d.dim(e.common.operationCancelled));return}throw t}}import xe from"ora";import i from"chalk";import Se from"simple-git";import{confirm as Ie,select as ge,text as de,isCancel as k,note as ce,outro as p}from"@clack/prompts";import H from"chalk";function W(t){if(t.name==="ExitPromptError")return;t.message?.includes("API key not valid")||t.message?.includes("Authorization header")||[400,401,403].includes(t.status)?(console.log(H.red(e.errors.authInvalid)),console.log(H.yellow(e.errors.authFix))):console.log(H.red(e.errors.unknown(t.message)))}import{simpleGit as be}from"simple-git";var I=be();async function Y(){if(!await I.checkIsRepo())return{isRepo:!1};let o=await I.status();return(o.not_added.length>0||o.modified.length>0||o.deleted.length>0)&&await I.add("--all"),{isRepo:!0,diff:await I.diff(["--cached"])}}async function ae(){await I.init()}async function se(){try{return(await I.getRemotes()).length>0}catch{return!1}}import me from"chalk";function R(t,o,r){let g=setTimeout(()=>{t.isSpinning&&(t.color="yellow",t.text=me.yellow(o))},1e4),a=setTimeout(()=>{t.isSpinning&&(t.color="red",t.text=me.red(r))},6e4);return()=>{clearTimeout(g),clearTimeout(a),t.color="cyan"}}var le=Se();async function pe(t,o,r,g,a){let c=t;for(;!c||c.trim()==="";){a.fail(i.red(e.commit.emptyMessageTitle));let s=await ge({message:e.commit.emptyMessagePrompt,options:[{label:e.commit.emptyRegenerate,value:"regenerate"},{label:e.commit.emptyManual,value:"manual"},{label:e.commit.emptyCancel,value:"cancel"}]});if((k(s)||s==="cancel")&&(p(i.dim(e.commit.processStopped)),process.exit(0)),s==="regenerate"){a.start(i.blue(e.commit.regenerating));let l=R(a,e.commit.generatingSlow10,e.commit.generatingSlow60);try{if(c=await o.generateCommitMessage(r,g,{regenerate:!0}),l(),!c||c.trim()===""){a.warn(i.yellow(e.commit.stillEmptyWarning));continue}a.succeed(i.green(e.commit.regenerated))}catch(u){l(),a.fail(i.red(e.commit.regenerationFailed(u.message))),u.name==="AbortError"&&(p(i.yellow(e.commit.generationCancelled)),process.exit(0))}}else if(s==="manual"){let l=await de({message:e.commit.manualMessagePrompt,validate:u=>!u||u.trim()===""?e.commit.manualMessageRequired:void 0});k(l)&&(p(i.dim(e.commit.processStopped)),process.exit(0)),c=l,a.succeed(i.green(e.commit.manualAccepted))}}return c}async function _(t=!1,o){let r=new AbortController;process.stdin.setRawMode?.(!0),process.stdin.resume(),process.stdin.setEncoding("utf8");let g=()=>{r.abort(),p(i.red(e.common.operationCancelled)),process.exit(0)},a=s=>{s===""&&g()};process.stdin.on("data",a),o?.(r);let c=()=>{r.abort(),p(i.red(e.common.operationCancelled)),process.exit(0)};process.once("SIGINT",c);try{let s=await j();if(!s.apiKey||!s.provider||!s.model){await K(),Object.assign(s,await j());return}console.log(e.commit.intro(s.provider.toUpperCase(),s.model));let l=await Y();if(!l.isRepo){console.log(i.yellow(e.commit.gitRepoMissing));let f=await Ie({message:e.commit.initConfirm,initialValue:!0});k(f)&&(p(i.red(e.common.operationCancelled)),process.exit(0)),f?(await ae(),console.log(i.green(e.commit.initSuccess)),l=await Y()):(p(i.dim(e.commit.abortNoRepo)),process.exit(0))}let u=l.diff;u||(p(i.red(e.commit.noChanges)),process.exit(0));let m=xe({color:"cyan"}),n="",h;try{m.start(i.blue(e.commit.generating));let f=R(m,e.commit.generatingSlow10,e.commit.generatingSlow60);h=await ie(s),n=await h.generateCommitMessage(u,r.signal),f(),n=await pe(n,h,u,r.signal,m),m.succeed(i.green(e.commit.generated))}catch(f){m.fail(i.red.bold(e.commit.generationFailed)),f.name==="AbortError"&&(p(i.yellow(e.commit.generationCancelled)),process.exit(0)),W(f),process.exit(1)}let S=!1;for(;!S;){ce(i.bold(n),e.commit.noteTitle);let f=await ge({message:e.commit.actionPrompt,options:[{label:e.commit.actions.accept,value:"accept"},{label:e.commit.actions.edit,value:"edit"},{label:e.commit.actions.regenerate,value:"regenerate"},{label:e.commit.actions.cancel,value:"cancel"}]});k(f)&&(p(i.dim(e.commit.processStopped)),process.exit(0));let A=f;if(A==="accept")S=!0;else if(A==="edit"){let E=await de({message:e.commit.editPrompt,initialValue:n});k(E)&&(p(i.dim(e.commit.processStopped)),process.exit(0)),n=E,S=!0}else if(A==="regenerate"){m.start(i.blue(e.commit.regenerating));let E=R(m,e.commit.generatingSlow10,e.commit.generatingSlow60);try{n=await h.generateCommitMessage(u,r.signal,{regenerate:!0}),E(),n=await pe(n,h,u,r.signal,m),m.succeed(i.green(e.commit.regenerated))}catch(V){E(),m.fail(i.red(e.commit.regenerationFailed(V.message))),V.name==="AbortError"&&(p(i.yellow(e.commit.generationCancelled)),process.exit(0))}}else p(i.dim(e.commit.processStopped)),process.exit(0)}t&&(ce(i.dim(e.common.dryRun.proposed(n)),i.yellow(e.common.dryRun.header)),process.exit(0)),await se()||(m.fail(i.red.bold(e.common.noRemote.header)),p(i.yellow(e.common.noRemote.instruction)),process.exit(1));try{m.start(i.blue(e.commit.committing));let f=R(m,e.commit.committingSlow10,e.commit.committingSlow60);await le.commit(n),f(),m.succeed(i.green(e.common.success.committed)),m.start(i.blue(e.commit.pushing));let A=R(m,e.commit.pushingSlow10,e.commit.pushingSlow60);await le.push(),A(),m.succeed(i.green.bold(e.common.success.pushed)),p(i.green(e.commit.outro))}catch(f){m.fail(i.red.bold(e.commit.operationFailed)),p(i.red(e.commit.gitError(f.message))),process.exit(1)}}catch(s){(s.name==="ExitPromptError"||s.name==="AbortError")&&(p(i.red(e.common.operationCancelled)),process.exit(0)),W(s),process.exit(1)}finally{process.stdin.removeListener("data",a),process.stdin.setRawMode?.(!1),o?.(null)}}var Q="pushai",ue="1.0.7",z="Stop writing manual commit messages.";import Ae from"chalk";var C=null,x=new Re;x.name(Q).description(z).option("--dry-run","Generate commit message but do not commit or push").action(async t=>{await _(t.dryRun,o=>{C=o}),C=null});x.name(Q).description(z).version(ue,"-v, --version","output the version number");x.action(async()=>{await _(!1,t=>{C=t}),C=null});x.command("commit").description("Stage changes, generate a message, and push").option("--dry-run","Generate commit message but do not commit or push").action(async t=>{await _(t.dryRun,o=>{C=o}),C=null});x.command("config").description("Configure AI providers and API keys").action(()=>K());x.command("reset").description("Delete the local config.json file").action(()=>te());process.on("SIGINT",()=>{console.log(Ae.yellow(e.common.interrupted)),C?C.abort():process.exit(0)});x.parse();
31
+ ${d.white.bold("pai reset:")} ${d.white(e.config.hintReset)}`;oe(m,d.cyan(e.config.commandsHint)),T(d.green.bold(e.config.outro))}catch(t){if(t.name==="ExitPromptError"){console.log(d.dim(e.common.operationCancelled));return}throw t}}import be from"ora";import i from"chalk";import Se from"simple-git";import{confirm as xe,select as ce,text as le,isCancel as k,note as ae,outro as p}from"@clack/prompts";import j from"chalk";function H(t){if(t.name==="ExitPromptError")return;t.message?.includes("API key not valid")||t.message?.includes("Authorization header")||[400,401,403].includes(t.status)?(console.log(j.red(e.errors.authInvalid)),console.log(j.yellow(e.errors.authFix))):console.log(j.red(e.errors.unknown(t.message)))}import{simpleGit as Pe}from"simple-git";var x=Pe();async function W(){if(!await x.checkIsRepo())return{isRepo:!1};let o=await x.status();return(o.not_added.length>0||o.modified.length>0||o.deleted.length>0)&&await x.add("--all"),{isRepo:!0,diff:await x.diff(["--cached"])}}async function ie(){await x.init()}async function re(){try{return(await x.getRemotes()).length>0}catch{return!1}}import ne from"chalk";function I(t,o,r){let g=setTimeout(()=>{t.isSpinning&&(t.color="yellow",t.text=ne.yellow(o))},1e4),s=setTimeout(()=>{t.isSpinning&&(t.color="red",t.text=ne.red(r))},6e4);return()=>{clearTimeout(g),clearTimeout(s),t.color="cyan"}}var se=Se();async function me(t,o,r,g,s){let c=t;for(;!c||c.trim()==="";){s.fail(i.red(e.commit.emptyMessageTitle));let n=await ce({message:e.commit.emptyMessagePrompt,options:[{label:e.commit.emptyRegenerate,value:"regenerate"},{label:e.commit.emptyManual,value:"manual"},{label:e.commit.emptyCancel,value:"cancel"}]});if((k(n)||n==="cancel")&&(p(i.dim(e.commit.processStopped)),process.exit(0)),n==="regenerate"){s.start(i.blue(e.commit.regenerating));let l=I(s,e.commit.generatingSlow10,e.commit.generatingSlow60);try{if(c=await o.generateCommitMessage(r,g,{regenerate:!0}),l(),!c||c.trim()===""){s.warn(i.yellow(e.commit.stillEmptyWarning));continue}s.succeed(i.green(e.commit.regenerated))}catch(u){l(),s.fail(i.red(e.commit.regenerationFailed(u.message))),u.name==="AbortError"&&(p(i.yellow(e.commit.generationCancelled)),process.exit(0))}}else if(n==="manual"){let l=await le({message:e.commit.manualMessagePrompt,validate:u=>!u||u.trim()===""?e.commit.manualMessageRequired:void 0});k(l)&&(p(i.dim(e.commit.processStopped)),process.exit(0)),c=l,s.succeed(i.green(e.commit.manualAccepted))}}return c}async function Y(t=!1,o){let r=new AbortController;process.stdin.setRawMode?.(!0),process.stdin.resume(),process.stdin.setEncoding("utf8");let g=()=>{r.abort(),p(i.red(e.common.operationCancelled)),process.exit(0)},s=n=>{n===""&&g()};process.stdin.on("data",s),o?.(r);let c=()=>{r.abort(),p(i.red(e.common.operationCancelled)),process.exit(0)};process.once("SIGINT",c);try{let n=await U();if(!n.apiKey||!n.provider||!n.model){await K(),Object.assign(n,await U());return}console.log(e.commit.intro(n.provider.toUpperCase(),n.model));let l=await W();if(!l.isRepo){console.log(i.yellow(e.commit.gitRepoMissing));let f=await xe({message:e.commit.initConfirm,initialValue:!0});k(f)&&(p(i.red(e.common.operationCancelled)),process.exit(0)),f?(await ie(),console.log(i.green(e.commit.initSuccess)),l=await W()):(p(i.dim(e.commit.abortNoRepo)),process.exit(0))}let u=l.diff;u||(p(i.red(e.commit.noChanges)),process.exit(0));let m=be({color:"cyan"}),a="",h;try{m.start(i.blue(e.commit.generating));let f=I(m,e.commit.generatingSlow10,e.commit.generatingSlow60);h=await ee(n),a=await h.generateCommitMessage(u,r.signal),f(),a=await me(a,h,u,r.signal,m),m.succeed(i.green(e.commit.generated))}catch(f){m.fail(i.red.bold(e.commit.generationFailed)),f.name==="AbortError"&&(p(i.yellow(e.commit.generationCancelled)),process.exit(0)),H(f),process.exit(1)}let S=!1;for(;!S;){ae(i.bold(a),e.commit.noteTitle);let f=await ce({message:e.commit.actionPrompt,options:[{label:e.commit.actions.accept,value:"accept"},{label:e.commit.actions.edit,value:"edit"},{label:e.commit.actions.regenerate,value:"regenerate"},{label:e.commit.actions.cancel,value:"cancel"}]});k(f)&&(p(i.dim(e.commit.processStopped)),process.exit(0));let A=f;if(A==="accept")S=!0;else if(A==="edit"){let E=await le({message:e.commit.editPrompt,initialValue:a});k(E)&&(p(i.dim(e.commit.processStopped)),process.exit(0)),a=E,S=!0}else if(A==="regenerate"){m.start(i.blue(e.commit.regenerating));let E=I(m,e.commit.generatingSlow10,e.commit.generatingSlow60);try{a=await h.generateCommitMessage(u,r.signal,{regenerate:!0}),E(),a=await me(a,h,u,r.signal,m),m.succeed(i.green(e.commit.regenerated))}catch(J){E(),m.fail(i.red(e.commit.regenerationFailed(J.message))),J.name==="AbortError"&&(p(i.yellow(e.commit.generationCancelled)),process.exit(0))}}else p(i.dim(e.commit.processStopped)),process.exit(0)}t&&(ae(i.dim(e.common.dryRun.proposed(a)),i.yellow(e.common.dryRun.header)),process.exit(0)),await re()||(m.fail(i.red.bold(e.common.noRemote.header)),p(i.yellow(e.common.noRemote.instruction)),process.exit(1));try{m.start(i.blue(e.commit.committing));let f=I(m,e.commit.committingSlow10,e.commit.committingSlow60);await se.commit(a),f(),m.succeed(i.green(e.common.success.committed)),m.start(i.blue(e.commit.pushing));let A=I(m,e.commit.pushingSlow10,e.commit.pushingSlow60);await se.push(),A(),m.succeed(i.green.bold(e.common.success.pushed)),p(i.green(e.commit.outro))}catch(f){m.fail(i.red.bold(e.commit.operationFailed)),p(i.red(e.commit.gitError(f.message))),process.exit(1)}}catch(n){(n.name==="ExitPromptError"||n.name==="AbortError")&&(p(i.red(e.common.operationCancelled)),process.exit(0)),H(n),process.exit(1)}finally{process.stdin.removeListener("data",s),process.stdin.setRawMode?.(!1),o?.(null)}}var ge="1.0.9",de="Stop writing manual commit messages.";var R=null,w=new Ae,Ee=Re(process.argv[1]);w.name(Ee);w.description(de);w.version(ge,"-v, --version","output the version number");w.option("--dry-run","Generate commit message but do not commit or push");w.action(async t=>{await Y(t.dryRun,o=>{R=o}),R=null});w.command("commit").description("Stage changes, generate a message, and push").option("--dry-run","Generate commit message but do not commit or push").action(async t=>{await Y(t.dryRun,o=>{R=o}),R=null});w.command("config").description("Configure AI providers and API keys").action(K);w.command("reset").description("Delete the local config.json file").action(Z);process.on("SIGINT",()=>{console.log(Ie.yellow(e.common.interrupted)),R?R.abort():process.exit(0)});w.parse();
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "pushai",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Stop writing manual commit messages.",
5
5
  "bin": {
6
- "pai": "./dist/index.mjs"
6
+ "pai": "./dist/index.mjs",
7
+ "pushai": "./dist/index.mjs"
7
8
  },
8
9
  "scripts": {
9
10
  "dev": "tsup src/index.ts --format esm --watch --clean",