pushai 0.1.9 → 1.0.0
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/dist/index.mjs +29 -22
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
2
|
+
import{Command as ce}from"commander";import w from"chalk";import{confirm as V}from"@inquirer/prompts";import L from"os";import E from"path";import m from"fs";import R from"keytar";var G="pushai",M="api-key";async function $(){let e=v(),o={};if(m.existsSync(e))try{o=JSON.parse(m.readFileSync(e,"utf-8"))}catch{}let r=await R.getPassword(G,M);return r&&(o.apiKey=r),o}async function F(e){let o=v(),{apiKey:r,...n}=e;r&&await R.setPassword(G,M,r);let a={};if(m.existsSync(o))try{a=JSON.parse(m.readFileSync(o,"utf-8"))}catch{}let c={...a,...n};m.writeFileSync(o,JSON.stringify(c,null,2),"utf-8")}async function B(){try{let e=L.homedir(),o=E.join(e,".config","pushai"),r=!1;return m.existsSync(o)&&(m.rmSync(o,{recursive:!0,force:!0}),r=!0),await R.deletePassword(G,M),r}catch(e){return console.error("Failed to reset config:",e),!1}}function v(){let e=E.join(L.homedir(),".config","pushai");return m.existsSync(e)||m.mkdirSync(e,{recursive:!0}),E.join(e,"config.json")}async function j(){try{if(!await V({message:w.red("Delete all PushAI configurations and API keys?"),default:!1})){console.log(w.dim(`
|
|
3
3
|
Operation cancelled.
|
|
4
|
-
`));return}let o=await
|
|
4
|
+
`));return}let o=await B();console.log(o?w.green(`
|
|
5
5
|
\u2714 Successfully removed the PushAI directory.
|
|
6
|
-
`):
|
|
6
|
+
`):w.dim(`
|
|
7
7
|
No configuration directory found. Nothing to delete.
|
|
8
|
-
`))}catch(e){if(e.name==="ExitPromptError"){console.log(
|
|
8
|
+
`))}catch(e){if(e.name==="ExitPromptError"){console.log(w.dim(`
|
|
9
9
|
Operation cancelled.
|
|
10
|
-
`));return}throw e}}import s from"chalk";import{select as
|
|
10
|
+
`));return}throw e}}import s from"chalk";import{select as Y,input as z,password as oe,Separator as te}from"@inquirer/prompts";import{GoogleGenerativeAI as Z}from"@google/generative-ai";var p=class{apiKey;model;constructor(o,r){this.apiKey=o,this.model=r}};var f=e=>`You are a Senior Software Engineer. Your task is to write a concise, technical, and impactful commit message based on a git diff.
|
|
11
11
|
|
|
12
12
|
### CONSTRAINTS
|
|
13
13
|
- Format: <type>(<scope>): <description>
|
|
@@ -23,28 +23,35 @@ Operation cancelled.
|
|
|
23
23
|
- If multiple changes are present, focus on the most significant one.
|
|
24
24
|
|
|
25
25
|
### GIT DIFF TO ANALYZE:
|
|
26
|
-
${e}`;var
|
|
26
|
+
${e}`;var P=class extends p{async generateCommitMessage(o,r){return(await new Z(this.apiKey).getGenerativeModel({model:this.model}).generateContent(f(o),{signal:r})).response.text().trim().replace(/['"]/g,"")}};import{InferenceClient as X}from"@huggingface/inference";var C=class extends p{async generateCommitMessage(o,r){return((await new X(this.apiKey,{endpointUrl:"https://router.huggingface.co/v1"}).chatCompletion({model:this.model,messages:[{role:"user",content:f(o)}],max_tokens:100,temperature:.2},{signal:r})).choices[0]?.message.content||"").trim().replace(/['"]/g,"").replace(/^commit:\s*/i,"")}};import ee from"openai";var b=class extends p{baseUrl;constructor(o,r,n){super(o,r),this.baseUrl=n}async generateCommitMessage(o,r){return(await new ee({apiKey:this.apiKey,baseURL:this.baseUrl||void 0}).chat.completions.create({model:this.model,messages:[{role:"user",content:f(o)}],temperature:.7,max_tokens:100},{signal:r})).choices[0]?.message.content?.trim().replace(/['"]/g,"")||""}};var T=[{name:"Gemini (Google)",value:"gemini",models:[{name:"Gemini 1.5 Flash (Recommended)",value:"gemini-1.5-flash"},{name:"Gemini 1.5 Pro",value:"gemini-1.5-pro"}]},{name:"OpenAI (GPT-4)",value:"openai",models:[{name:"GPT-4o (Recommended)",value:"gpt-4o"},{name:"GPT-4o mini",value:"gpt-4o-mini"},{name:"GPT-4 Turbo",value:"gpt-4-turbo"}]},{name:"HuggingFace (Open Source)",value:"huggingface",models:[{name:"Llama 3 8B Instruct (Recommended)",value:"meta-llama/Meta-Llama-3-8B-Instruct"},{name:"Mistral 7B v0.3",value:"mistralai/Mistral-7B-Instruct-v0.3"},{name:"Qwen 2 7B",value:"Qwen/Qwen2-7B-Instruct"}]},{name:"Custom (Ollama/Local)",value:"custom",models:[{name:"Llama 3 (Ollama Default)",value:"llama3"},{name:"Mistral",value:"mistral"},{name:"Phi-3",value:"phi3"}]}];function q(e){switch(e.provider){case"gemini":return Promise.resolve(new P(e.apiKey,e.model));case"huggingface":return Promise.resolve(new C(e.apiKey,e.model));case"openai":case"custom":return Promise.resolve(new b(e.apiKey,e.model,e.baseUrl));default:throw new Error(`Provider ${e.provider} is not supported.`)}}async function I(){console.log(s.blue.bold(`
|
|
27
27
|
PushAI Configuration
|
|
28
|
-
`));try{let e=await
|
|
29
|
-
Configuration saved successfully!`)),console.log(s.dim("----------------------------------------")),console.log(`${s.bold("Provider:")} ${e}`),console.log(`${s.bold("Model: ")} ${
|
|
28
|
+
`));try{let e=await Y({message:"Select your AI provider:",choices:T.map(i=>({name:i.name,value:i.value}))}),o=T.find(i=>i.value===e),r=await oe({message:`Enter your ${e} API Key:`,validate:i=>i.length===0?"Key is required":!0}),n=await Y({message:"Select a model:",choices:[...o?.models||[],new te,{name:"Custom model ID",value:"custom_id"}]});n==="custom_id"&&(n=await z({message:"Enter the custom model ID:",validate:i=>i.length===0?"Model ID is required":!0}));let a=await z({message:"Base URL (Optional, press Enter to skip):",default:""});await F({provider:e,apiKey:r,model:n,baseUrl:a||void 0});let c=v();console.log(s.green(`
|
|
29
|
+
Configuration saved successfully!`)),console.log(s.dim("----------------------------------------")),console.log(`${s.bold("Provider:")} ${e}`),console.log(`${s.bold("Model: ")} ${n}`),console.log(`${s.bold("API Key: ")} ****${r.slice(-4)}`),console.log(s.dim("----------------------------------------")),console.log(s.dim(`File: ${c}
|
|
30
30
|
`)),console.log(s.cyan("Try running these commands:")),console.log(` ${s.white.bold("pai commit")} ${s.white("\u2192 Just the AI generation")}`),console.log(` ${s.white.bold("pai config")} ${s.white("\u2192 Change model or keys")}`),console.log(` ${s.white.bold("pai reset")} ${s.white("\u2192 Wipe all local settings")}
|
|
31
31
|
`)}catch(e){if(e.name==="ExitPromptError"){console.log(s.dim(`
|
|
32
32
|
Setup cancelled.
|
|
33
|
-
`));return}throw e}}import
|
|
34
|
-
It looks like your API key/Token is invalid.`)),console.log(
|
|
35
|
-
${e.message||"An unknown error occurred."}`))}import{simpleGit as
|
|
36
|
-
${t.cyan("\u25CF")} ${
|
|
37
|
-
`)}let
|
|
38
|
-
Command aborted. A Git repository is required to continue
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
`));return}throw e}}import ne from"ora";import t from"chalk";import ie from"simple-git";import{confirm as se,input as ae,select as le}from"@inquirer/prompts";import k from"chalk";function N(e){if(e.name==="ExitPromptError")return;e.message?.includes("API key not valid")||e.message?.includes("Authorization header")||[400,401,403].includes(e.status)?(console.log(k.red(`
|
|
34
|
+
It looks like your API key/Token is invalid.`)),console.log(k.yellow("\u{1F449} Run `pai reset` to update your credentials."))):console.log(k.red(`
|
|
35
|
+
${e.message||"An unknown error occurred."}`))}import{simpleGit as re}from"simple-git";var h=re();async function O(){if(!await h.checkIsRepo())return{isRepo:!1};let o=await h.status();return(o.not_added.length>0||o.modified.length>0||o.deleted.length>0)&&await h.add("."),{isRepo:!0,diff:await h.diff(["--cached"])}}async function J(){await h.init()}async function H(){try{return(await h.getRemotes()).length>0}catch{return!1}}var Q=ie();async function x(e=!1,o){let r=new AbortController;o?.(r);try{let n=await $();if(!n.apiKey||!n.provider||!n.model){await I(),Object.assign(n,await $());return}else{let l=n.provider.toUpperCase(),y=`${t.bgCyan.black.bold(` ${l} `)} ${t.dim("\u2022")} ${t.white(n.model)}`;console.log(`
|
|
36
|
+
${t.cyan("\u25CF")} ${y}
|
|
37
|
+
`)}let a=await O();a.isRepo||(console.log(t.yellow("Git repository not found in the current directory.")),await se({message:"Initialize a new Git repository here?",default:!0})?(await J(),console.log(t.green("Git repository initialized successfully.")),a=await O()):(console.log(t.dim(`
|
|
38
|
+
Command aborted. A Git repository is required to continue.
|
|
39
|
+
`)),process.exit(0)));let c=a.diff;c||(console.log(t.red("There are no staged or unstaged changes to commit.")),process.exit(0));let i=ne({color:"cyan"}),d="",A;try{i.start(t.blue("Generating commit message...")),A=await q(n),d=await A.generateCommitMessage(c,r.signal),i.succeed(t.green("Commit message generated."))}catch(l){i.fail(t.red.bold("Commit message generation failed.")),l.name==="AbortError"&&(console.log(t.yellow(`
|
|
40
|
+
Generation was cancelled by the user.
|
|
41
|
+
`)),process.exit(0)),N(l),process.exit(1)}let S=!1;for(;!S;){let l="\u2500".repeat(Math.max(d.length+2,20));console.log(`
|
|
42
|
+
${t.cyan("\u250C"+l+"\u2510")}`),console.log(`${t.cyan("\u2502")} ${t.bold(d)} ${t.cyan("\u2502")}`),console.log(`${t.cyan("\u2514"+l+"\u2518")}
|
|
43
|
+
`);let y=await le({message:"Select how you want to continue:",choices:[{name:"Continue with Commit & Push",value:"accept"},{name:"Modify Commit Message",value:"edit"},{name:"Generate a Different Message",value:"regenerate"},{name:"Exit Without Pushing",value:"cancel"}]});if(y==="accept")S=!0;else if(y==="edit")d=await ae({message:"Refine commit message:",default:d}),S=!0;else if(y==="regenerate"){i.start(t.blue("Generating new message..."));try{d=await A.generateCommitMessage(c,r.signal),i.succeed(t.green("New commit message generated."))}catch(U){i.fail(t.red(`Could not generate a new message: ${U.message}`)),U.name==="AbortError"&&(console.log(t.yellow(`
|
|
44
|
+
Generation was cancelled by the user.
|
|
45
|
+
`)),process.exit(0))}}else console.log(t.dim(`
|
|
41
46
|
Process stopped. No commits or pushes were made.
|
|
42
47
|
`)),process.exit(0)}if(e&&(console.log(t.yellow(`
|
|
43
48
|
[DRY RUN] No commits or pushes were made.
|
|
44
49
|
`)),console.log(t.dim(`Proposed commit message:
|
|
45
|
-
${
|
|
46
|
-
`)),process.exit(0)),!e){await
|
|
47
|
-
Git reported an error: ${
|
|
48
|
-
`)),process.exit(1)}}}catch(
|
|
49
|
-
|
|
50
|
-
`)),process.exit(0)),
|
|
50
|
+
${d}
|
|
51
|
+
`)),process.exit(0)),!e){await H()||(i.fail(t.red.bold("No Git remote found.")),console.log(t.yellow("Please add a remote (e.g., `git remote add origin <url>`) and try again.\n")),process.exit(1));try{i.start(t.blue("Pushing changes...")),await Q.commit(d),await Q.push(),i.succeed(t.green.bold("Repository updated and pushed successfully."))}catch(l){i.fail(t.red.bold("Push operation could not be completed.")),console.log(t.red(`
|
|
52
|
+
Git reported an error: ${l.message}
|
|
53
|
+
`)),process.exit(1)}}}catch(n){(n.name==="ExitPromptError"||n.name==="AbortError")&&(console.log(t.dim(`
|
|
54
|
+
Operation cancelled.
|
|
55
|
+
`)),process.exit(0)),N(n),process.exit(1)}finally{o?.(null)}}var D="pushai",W="0.1.10",_="Stop writing manual commit messages.";var g=null,u=new ce;u.name(D).description(_).option("--dry-run","Generate commit message but do not commit or push").action(async e=>{await x(e.dryRun,o=>{g=o}),g=null});u.name(D).description(_).version(W,"-v, --version","output the version number");u.action(async()=>{await x(!1,e=>{g=e}),g=null});u.command("commit").description("Stage changes, generate a message, and push").option("--dry-run","Generate commit message but do not commit or push").action(async e=>{await x(e.dryRun,o=>{g=o}),g=null});u.command("config").description("Configure AI providers and API keys").action(I);u.command("reset").description("Delete the local config.json file").action(j);process.on("SIGINT",()=>{console.log(`
|
|
56
|
+
Interrupted by user. Aborting request...
|
|
57
|
+
`),g?g.abort():process.exit(0)});u.parse();
|