ghit 0.1.21 → 0.2.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.
Files changed (2) hide show
  1. package/bin/cli.mjs +4 -4
  2. package/package.json +3 -3
package/bin/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{createRequire as e}from"node:module";import{Logger as t}from"@h3ravel/shared";import{existsSync as n,mkdirSync as r,writeFileSync as i}from"node:fs";import{fileURLToPath as a}from"node:url";import o,{dirname as s,join as c}from"node:path";import l from"better-sqlite3";import{execSync as u}from"child_process";import d,{homedir as f,type as p}from"os";import m,{mkdirSync as h}from"fs";import g from"path";import{Octokit as _}from"@octokit/rest";import ee from"node:readline/promises";import{homedir as te}from"node:os";import{installPackage as ne}from"@antfu/install-pkg";import{Command as v,Kernel as re}from"@h3ravel/musket";import ie from"fast-diff";import ae from"cli-table3";import{createRequire as y}from"module";import oe from"dns/promises";import{createDeviceCode as se,exchangeDeviceCode as b}from"@octokit/oauth-methods";import x,{apps as S}from"open";import"dotenv/config";import C from"axios";String.prototype.toKebabCase=function(){return this.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()},String.prototype.toCamelCase=function(){return this.replace(/[-_ ]+([a-zA-Z0-9])/g,(e,t)=>t.toUpperCase()).replace(/^[A-Z]/,e=>e.toLowerCase())},String.prototype.toPascalCase=function(){return this.replace(/(^\w|[-_ ]+\w)/g,e=>e.replace(/[-_ ]+/,``).toUpperCase())},String.prototype.toSnakeCase=function(){return this.replace(/([a-z])([A-Z])/g,`$1_$2`).replace(/[\s-]+/g,`_`).toLowerCase()},String.prototype.toTitleCase=function(){return this.toLowerCase().replace(/(^|\s)\w/g,e=>e.toUpperCase())};function w(){let e=T();if(!e)return null;try{let t=u(`git credential fill`,{input:`url=${e.clone_url}\n\n`,encoding:`utf8`}),n=t.match(/^username=(.*)$/m),r=t.match(/^password=(.*)$/m);if(n&&r)return{username:n[1],password:r[1]}}catch{}return null}const T=()=>{try{let e=u(`git config --get remote.origin.url`,{encoding:`utf8`}).trim();if(!e)return null;let t=e.match(/[:/]([^/]+)\/([^/.]+)(?:\.git)?$/);if(!t)return null;let n=t[1],r=t[2],i=e.startsWith(`git@`)?e:`git@${e.replace(/^https?:\/\//,``)}.git`.replace(/\.git\.git$/,`.git`),a=e.startsWith(`http`)?e:`https://${e.replace(/^git@/,``).replace(`:`,`/`)}.git`.replace(/\.git\.git$/,`.git`);return{id:0,name:r,full_name:`${n}/${r}`,private:!1,ssh_url:i,clone_url:a}}catch{return null}};let E;function D(){return[()=>{if(!E)throw Error(`Commander instance has not been initialized`);return E},e=>{E=e}]}function O(){return[()=>R(`config`)||{debug:!1,apiBaseURL:`https://api.github.com`,timeoutDuration:3e3,useCurrentRepo:!0,ngrokAuthToken:void 0,skipLongCommandGeneration:!0},e=>(I(`config`,e),R(`config`))]}const k=new Set;function ce(){return[()=>Array.from(k).filter(e=>!!e),e=>e?k.has(e)?!1:(k.add(e),!0):(k.clear(),!1)]}const A=()=>{let e,[t]=O();if(t().useCurrentRepo){let t=w();t&&t.password&&(e=t.password)}if(e||=R(`token`),!e)throw Error(`No authentication token found. Please log in first.`);return new _({auth:e})};let j,M=g.join(f(),`.ghit`);h(M,{recursive:!0});const N=()=>[M,e=>{M=e}],[P,le]=[()=>j,e=>{j=new l(g.join(M,e));let[{journal_mode:t}]=j.pragma(`journal_mode`);t!==`wal`&&j.pragma(`journal_mode = WAL`)}];le(`app.db`);function F(){return P().exec(`
2
+ import{createRequire as e}from"node:module";import{Logger as t}from"@h3ravel/shared";import{existsSync as n,mkdirSync as r,writeFileSync as i}from"node:fs";import{fileURLToPath as a}from"node:url";import o,{dirname as s,join as c}from"node:path";import l from"better-sqlite3";import{execSync as u}from"child_process";import d,{homedir as f,type as p}from"os";import m,{mkdirSync as h}from"fs";import g from"path";import{Octokit as _}from"@octokit/rest";import ee from"node:readline/promises";import{homedir as te}from"node:os";import{installPackage as ne}from"@antfu/install-pkg";import{Command as v,Kernel as re}from"@h3ravel/musket";import ie from"fast-diff";import ae from"cli-table3";import{createRequire as y}from"module";import oe from"dns/promises";import{createDeviceCode as se,exchangeDeviceCode as b}from"@octokit/oauth-methods";import x,{apps as S}from"open";import"dotenv/config";import C from"axios";String.prototype.toKebabCase=function(){return this.replace(/([a-z])([A-Z])/g,`$1-$2`).replace(/[\s_]+/g,`-`).toLowerCase()},String.prototype.toCamelCase=function(){return this.replace(/[-_ ]+([a-zA-Z0-9])/g,(e,t)=>t.toUpperCase()).replace(/^[A-Z]/,e=>e.toLowerCase())},String.prototype.toPascalCase=function(){return this.replace(/(^\w|[-_ ]+\w)/g,e=>e.replace(/[-_ ]+/,``).toUpperCase())},String.prototype.toSnakeCase=function(){return this.replace(/([a-z])([A-Z])/g,`$1_$2`).replace(/[\s-]+/g,`_`).toLowerCase()},String.prototype.toTitleCase=function(){return this.toLowerCase().replace(/(^|\s)\w/g,e=>e.toUpperCase())};function w(){let e=T();if(!e)return null;try{let t=u(`git credential fill`,{input:`url=${e.clone_url}\n\n`,encoding:`utf8`}),n=t.match(/^username=(.*)$/m),r=t.match(/^password=(.*)$/m);if(n&&r)return{username:n[1],password:r[1]}}catch{}return null}const T=()=>{try{let e=u(`git config --get remote.origin.url`,{encoding:`utf8`}).trim();if(!e)return null;let t=e.match(/[:/]([^/]+)\/([^/.]+)(?:\.git)?$/);if(!t)return null;let n=t[1],r=t[2],i=e.startsWith(`git@`)?e:`git@${e.replace(/^https?:\/\//,``)}.git`.replace(/\.git\.git$/,`.git`),a=e.startsWith(`http`)?e:`https://${e.replace(/^git@/,``).replace(`:`,`/`)}.git`.replace(/\.git\.git$/,`.git`);return{id:0,name:r,full_name:`${n}/${r}`,private:!1,ssh_url:i,clone_url:a}}catch{return null}};let E;function D(){return[()=>{if(!E)throw Error(`Commander instance has not been initialized`);return E},e=>{E=e}]}function O(){return[()=>R(`config`)||{debug:!1,apiBaseURL:`https://api.github.com`,timeoutDuration:3e3,useCurrentRepo:!0,ngrokAuthToken:void 0,skipLongCommandGeneration:!0},e=>(I(`config`,e),R(`config`))]}const k=new Set;function ce(){return[()=>Array.from(k).filter(e=>!!e),e=>e?k.has(e)?!1:(k.add(e),!0):(k.clear(),!1)]}const A=()=>{let e,[t]=O();if(t().useCurrentRepo===!0){let t=w();t&&t.password&&(e=t.password)}if(e||=R(`token`),!e)throw Error(`No authentication token found. Please log in first.`);return new _({auth:e})};let j,M=g.join(f(),`.ghit`);h(M,{recursive:!0});const N=()=>[M,e=>{M=e}],[P,le]=[()=>j,e=>{j=new l(g.join(M,e));let[{journal_mode:t}]=j.pragma(`journal_mode`);t!==`wal`&&j.pragma(`journal_mode = WAL`)}];le(`app.db`);function F(){return P().exec(`
3
3
  CREATE TABLE IF NOT EXISTS json_store (
4
4
  id INTEGER PRIMARY KEY AUTOINCREMENT,
5
5
  key TEXT UNIQUE,
@@ -20,7 +20,7 @@ Updated at:`,[`white`,`bold`]],[new Date(e.updated_at).toLocaleString(),[`blue`]
20
20
  export const APIs = ${JSON.stringify(l,null,2).replace(/"([A-Za-z_][\w$]*)":/g,`$1:`).replace(/:\s*"((?:[^"\\]|\\.)*)"/g,(e,t)=>`: '${t.replace(/\\"/g,`"`).replace(/'/g,`\\'`)}'`)}\n\nexport default APIs\n`;r(o.dirname(u),{recursive:!0}),i(u,d,`utf8`),c.succeed(`Generated Extended APIs to: `+W(u,[`gray`,`italic`]))}};const he=(e,t)=>{let[n,r]=ce(),i=``;if((!e.required||e.default!==void 0||e.type===`Boolean`||e.options||e.flag===!0)&&e.paramType!==`path`&&e.arg!==!0){if(i+=`{--`,r(t+`:`+e.parameter.charAt(0).toLowerCase()))i+=`${e.parameter.charAt(0).toLowerCase()}|`;else{let n=e.parameter.split(/[_-\s]/);n.length>1&&r(t+`:`+n[1].charAt(0).toLowerCase())&&(i+=`${n[1].charAt(0).toLowerCase()}|`)}if(i+=`${e.parameter}`,e.type!==`Boolean`&&(i+=e.default?`=${e.default}`:`?`),e.description&&(i+=` : ${e.description}`),e.options){let t=e.options.join(`,`);i+=` : ${t}`}i+=`}`}else i+=`{${e.parameter}`,e.default&&(i+=`=${e.default}`),e.description&&(i+=` : ${e.description}`),i+=`}`;return i},ge=e=>{let n=(e,r=0)=>{let i=` `.repeat(r);for(let a in e){let o=e[a];if(typeof o==`object`&&o)console.log(`${i}${J(a)}:`),n(o,r+2);else{let e;switch(typeof o){case`string`:e=t.log(o,`green`,!1);break;case`number`:e=t.log(String(o),`yellow`,!1);break;case`boolean`:e=t.log(String(o),`blue`,!1);break;case`object`:e=o===null?t.log(`null`,`gray`,!1):t.log(JSON.stringify(o),`cyan`,!1);break;default:e=o}console.log(`${i}${J(a)}: ${e}`)}}};n(e)},J=e=>e.replace(/([a-z])([A-Z])/g,`$1 $2`).replace(/[_-]+/g,` `).replace(/\s+/g,` `).split(` `).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `).trim().replace(/^(\w{2})$/,(e,t)=>t.toUpperCase()),Y=(e,t)=>ie(t,e).map(e=>{let[t,n]=e;return t===0?n:t===-1?W(n,[`red`,`strikethrough`],!1):W(n,[`green`,`underline`],!1)}).join(``);var _e=()=>{let t=e(import.meta.url),r=[],i=me,a=process.argv.includes(`generate:apis`),o=q.getOutputPath(R(`generated_commands_type`,`local`));!a&&n(o)&&({APIs:i}=t(o));let s=Object.entries(i).reduce((e,[t,n])=>(n.forEach(n=>{let r=t===n.api?t:`${t}:${(n.alias??n.api).toKebabCase()}`;e[r]=n}),e),{});for(let[e,t]of Object.entries(s)){let n=t.params.map(t=>he(t,e)).join(`
21
21
  `),i=class extends v{signature=`${e} \n${n}`;description=t.description||`No description available.`;handle=async()=>{let n=e.split(`:`).shift(),r={...this.arguments()??{},...this.options()??{}},[i,a]=D();if(a(this),!n)return void this.error(`Unknown command entry.`).newLine();for(let e of t.params)if(e.required&&!this.argument(e.parameter))return void this.newLine().error(`Missing required argument: ${e.parameter}`).newLine();let o=R(`default_repo`)??{},s=R(`token`),c=([r.owner,r.repo].filter(Boolean).join(`/`)||o.full_name).split(`/`)??[``,``],l=t.params.some(e=>[`repo`,`user`].includes(e.parameter));if(l&&(!c[0]||!c[1]))return void this.error(`ERROR: No repository set. Please set a default repository using the [set-repo] command or provide one using the --repo option.`).newLine();if(!s)return void this.error(`ERROR: You're not signed in, please run the [login] command before you begin`).newLine();this.newLine();let u=this.spinner(`Loading...
22
22
  `).start();l&&(r.owner=c[0],r.repo=c[1]);let[d,f]=await V(H(n,t,r));if(d||!f)return void u.fail((d||`An error occurred`)+`
23
- `);u.succeed(f.message),this.newLine(),ge(f.data),this.newLine()}};r.push(i)}return r};const ve=e=>[{name:`Debug Mode`,value:`debug`,description:`Enable or disable debug mode (${e.debug?`Enabled`:`Disabled`})`},{name:`API Base URL`,value:`apiBaseURL`,description:`Set the base URL for the API (${e.apiBaseURL})`},{name:`Timeout Duration`,value:`timeoutDuration`,description:`Set the timeout duration for API requests (${e.timeoutDuration} ms)`},{name:`Use Current Repo for Commands`,value:`useCurrentRepo`,description:`Enable or disable automatic detection of the current git repository for commands that support it (${e.useCurrentRepo?`Enabled`:`Disabled`})`},{name:`Skip Long Command Generation`,value:`skipLongCommandGeneration`,description:`Enable or disable skipping of long command generation when calling ${W(`generate:apis`,[`grey`,`italic`])} (${e.skipLongCommandGeneration?`Enabled`:`Disabled`})`},{name:`Ngrok Auth Token`,value:`ngrokAuthToken`,description:`Set the Ngrok Auth Token - will default to environment variable if not set (${e.ngrokAuthToken?`************`:`Not Set`})`},{name:`Reset Configuration`,value:`reset`,description:`Reset all configurations to default values`}],ye=async e=>{let[t,n]=O(),[r]=D(),i=t();if(e===`debug`){let e=await r().choice(`Enable debug mode? (${i.debug?`Enabled`:`Disabled`})`,[{name:`Enable`,value:`1`},{name:`Disable`,value:`0`}],i.debug?0:1);i.debug=e===`1`}else if(e===`apiBaseURL`){let e=await r().ask(`Enter API Base URL`,i.apiBaseURL);i.apiBaseURL=e}else if(e===`ngrokAuthToken`){let e=await r().ask(`Enter Ngrok Auth Token`,i.ngrokAuthToken||``);i.ngrokAuthToken=e}else if(e===`timeoutDuration`){let e=await r().ask(`Enter Timeout Duration (in ms)`,i.timeoutDuration.toString());i.timeoutDuration=parseInt(e)}else if(e===`useCurrentRepo`){let e=await r().choice(`Enable automatic detection of the current git repository for commands that support it? (${i.useCurrentRepo?`Enabled`:`Disabled`})`,[{name:`Enable`,value:`1`},{name:`Disable`,value:`0`}],i.useCurrentRepo?0:1);i.useCurrentRepo=e===`1`}else if(e===`skipLongCommandGeneration`){let e=await r().choice(`Enable skipping of long command generation? (${i.skipLongCommandGeneration?`Enabled`:`Disabled`})`,[{name:`Enable`,value:`1`},{name:`Disable`,value:`0`}],i.skipLongCommandGeneration?0:1);i.skipLongCommandGeneration=e===`1`}else e===`reset`&&(i={debug:!1,apiBaseURL:`https://api.github.com`,timeoutDuration:3e3,useCurrentRepo:!0,ngrokAuthToken:void 0,skipLongCommandGeneration:!0});n(i)};var be=class extends v{signature=`config`;description=`Configure Ghit`;async handle(){let[e,t]=D();t(this);let[n,r]=O(),i=n();i||(i={debug:!1,apiBaseURL:`https://api.github.com`,timeoutDuration:3e3,useCurrentRepo:!0,ngrokAuthToken:void 0,skipLongCommandGeneration:!0},r(i)),await ye(await this.choice(`Select configuration to set`,ve(i))),this.info(`Configuration updated successfully!`).newLine()}},xe=class extends v{signature=`#generate:
23
+ `);u.succeed(f.message),this.newLine(),ge(f.data),this.newLine()}};r.push(i)}return r};const ve=e=>[{name:`Debug Mode`,value:`debug`,description:`Enable or disable debug mode (${e.debug?`Enabled`:`Disabled`})`},{name:`API Base URL`,value:`apiBaseURL`,description:`Set the base URL for the API (${e.apiBaseURL})`},{name:`Timeout Duration`,value:`timeoutDuration`,description:`Set the timeout duration for API requests (${e.timeoutDuration} ms)`},{name:`Use Current Repo for Commands`,value:`useCurrentRepo`,description:`Enable or disable automatic detection of the current git repository for commands that support it (${e.useCurrentRepo===!0?`Enabled`:e.useCurrentRepo===`repo`?`Repo Only`:`Disabled`})`},{name:`Skip Long Command Generation`,value:`skipLongCommandGeneration`,description:`Enable or disable skipping of long command generation when calling ${W(`generate:apis`,[`grey`,`italic`])} (${e.skipLongCommandGeneration?`Enabled`:`Disabled`})`},{name:`Ngrok Auth Token`,value:`ngrokAuthToken`,description:`Set the Ngrok Auth Token - will default to environment variable if not set (${e.ngrokAuthToken?`************`:`Not Set`})`},{name:`Reset Configuration`,value:`reset`,description:`Reset all configurations to default values`}],ye=async e=>{let[t,n]=O(),[r]=D(),i=t();if(e===`debug`){let e=await r().choice(`Enable debug mode? (${i.debug?`Enabled`:`Disabled`})`,[{name:`Enable`,value:`1`},{name:`Disable`,value:`0`}],i.debug?0:1);i.debug=e===`1`}else if(e===`apiBaseURL`){let e=await r().ask(`Enter API Base URL`,i.apiBaseURL);i.apiBaseURL=e}else if(e===`ngrokAuthToken`){let e=await r().ask(`Enter Ngrok Auth Token`,i.ngrokAuthToken||``);i.ngrokAuthToken=e}else if(e===`timeoutDuration`){let e=await r().ask(`Enter Timeout Duration (in ms)`,i.timeoutDuration.toString());i.timeoutDuration=parseInt(e)}else if(e===`useCurrentRepo`){let e=await r().choice(`Enable automatic detection of the current git repository for commands that support it? (${i.useCurrentRepo===!0?`Enabled`:i.useCurrentRepo===`repo`?`Repo Only`:`Disabled`})`,[{name:`Enable`,value:`1`},{name:`Repo Only`,value:`repo`},{name:`Disable`,value:`0`}],i.useCurrentRepo===!0?0:i.useCurrentRepo===`repo`?1:2);i.useCurrentRepo=e===`repo`?`repo`:e===`1`}else if(e===`skipLongCommandGeneration`){let e=await r().choice(`Enable skipping of long command generation? (${i.skipLongCommandGeneration?`Enabled`:`Disabled`})`,[{name:`Enable`,value:`1`},{name:`Disable`,value:`0`}],i.skipLongCommandGeneration?0:1);i.skipLongCommandGeneration=e===`1`}else e===`reset`&&(i={debug:!1,apiBaseURL:`https://api.github.com`,timeoutDuration:3e3,useCurrentRepo:!0,ngrokAuthToken:void 0,skipLongCommandGeneration:!0});n(i)};var be=class extends v{signature=`config`;description=`Configure Ghit`;async handle(){let[e,t]=D();t(this);let[n,r]=O(),i=n();i||(i={debug:!1,apiBaseURL:`https://api.github.com`,timeoutDuration:3e3,useCurrentRepo:!0,ngrokAuthToken:void 0,skipLongCommandGeneration:!0},r(i)),await ye(await this.choice(`Select configuration to set`,ve(i))),this.info(`Configuration updated successfully!`).newLine()}},xe=class extends v{signature=`#generate:
24
24
  {apis : Generate extended API definitions from the GitHub OpenAPI spec.}
25
25
  {reset : Remove all generated commands}
26
26
  `;description=`Generate extended API definitions from the GitHub OpenAPI spec`;async handle(){let[e,t]=D();t(this);let{name:n}=this.dictionary;if(n===`apis`)q.run();else if(n===`reset`){if(!await this.confirm(`Are you sure you want to remove all generated APIs? This action cannot be undone.`))return void this.newLine().info(`Operation cancelled.`).newLine();let{existsSync:e,unlinkSync:t}=await import(`node:fs`),n=this.spinner(`Removing generated APIs...`).start(),r=0;for(let i of[`local`,`global`]){let a=q.getOutputPath(i);e(a)&&(t(a),n.succeed(`Removed generated ${i} APIs successfully.`),r++)}r>0?n.succeed(`Removed all generated APIs successfully.`):n.fail(`No generated APIs found to remove.`)}}},Se=class extends v{signature=`info`;description=`Display application runtime information.`;async handle(){let e={version:`unknown`,dependencies:{}},n=R(`user`),r=de(),i=y(import.meta.url),a=R(`default_repo`,void 0,{bypassCurrentRepo:!0})?.full_name||null,o=T(),[s,c]=D(),[l]=N();c(this),F();let u=this.spinner(`Gathering application information...
@@ -121,7 +121,7 @@ ${e.body??``}`,r=`${t+1}-${e.title.replace(/[^a-z0-9]+/gi,`-`).replace(/^-+/,``)
121
121
  {--dry-run : Simulate the deletion without actually deleting issues.}
122
122
  {--m|match=file : Matching strategy for existing issues. "title" matches issues by title, while "file" matches issues based on the file path derived from the issue body. : [title,file]}
123
123
  `;description=`Seed the database with updated issues from a preset directory or markdown file. Issues will be matched based on the specified matching strategy and updated if they already exist.`;async handle(){let[e,r]=D();r(this);let i=c(process.cwd(),this.argument(`path`,`issues`)),a=this.option(`dryRun`,!1),o=R(`default_repo`);if(!o)return void this.error(`ERROR: No default repository set. Please set a default repository using the ${W(`set-repo`,[`grey`,`italic`])} command.`);let s=new X;try{let e=this.option(`repo`,o.full_name).split(`/`)??[``,``];if(await s.checkConnectivity(),await s.validateAccess(...e),!n(i)){this.error(`ERROR: Issues path not found: ${W(i,[`grey`,`italic`])}`);return}let r=[],c=await s.fetchExistingIssues(...e,`all`),l=this.option(`match`,`file`),u=new Set(c.map(e=>l===`file`?s.getFilePath(e.body??``):e.title));r=i.endsWith(`.md`)?s.processMultiIssueMarkdown(i):s.getIssueFiles(i).map(s.processIssueFile.bind(s)).filter(Boolean);let d=[],f=[];if(r.forEach(e=>{if(u.has(l===`file`?e.filePath:e.title)){let t=c.find(t=>l===`file`?s.getFilePath(t.body??``)===e.filePath:t.title===e.title);f.push({issue:e,existingIssue:t})}else d.push(e)}),d.length>0&&(this.newLine().info(`INFO: Issues to SKIP (not created):`),d.forEach((e,t)=>{W(`${t+1}. ${e.title}`,`white`,!0),W(` File: ${e.filePath} (${e.type})`,`white`,!0)})),f.length>0)this.newLine().info(`INFO: Issues to UPDATE:`).newLine(),f.forEach(({issue:e,existingIssue:t})=>{W(` > ${Y(e.title,t.title)}`,`white`,!0),W(` Existing: #${t.number} (${t.state})`,`white`,!0)}),this.newLine();else{this.newLine().success(`INFO: No issues to update. All issues are up to date`).newLine(),t.log([[`☑ Total files:`,`white`],[r.length.toString(),`blue`]],` `),t.log([[`> Skipped:`,`white`],[d.length.toString(),`blue`]],` `),t.log([[`± To update:`,`white`],[f.length.toString(),`blue`]],` `),this.newLine();return}if(t.log([[`⚠️ `,`white`],[` CONFIRM `,`bgYellow`],[`This will update`,`yellow`],[f.length.toString(),`blue`],[`existing issues on GitHub.`,`yellow`]],` `),d.length>0&&this.info(`(Skipping ${d.length} existing issues)`),await this.confirm(`Do you want to proceed?${a?` (Dry Run - No changes will be made)`:``}`)){this.newLine();let n=0,i=0,o=this.spinner(`Updating issues...`).start();for(let{issue:t,existingIssue:r}of f)try{if(o.start(`Updating: ${t.title}...`),a)o.info(`Dry run: Issue ${W(t.title,[`cyan`,`italic`])} would be updated.`);else{let n=await s.updateIssue(t,r,...e);o.succeed(`Updated #${n.number}: ${n.title}`),this.info(`URL: ${n.html_url}\n`)}n++,await U(1e3)}catch(e){this.error(`ERROR: Failed to update Issue: ${W(t.title,[`cyan`,`italic`])}`),this.error(`ERROR: ${e.message}\n`),i++}o.succeed(`All ${f.length} issues processed.`),t.log([[`=========================`,`white`],[`✔ Updated: ${n}`,`white`],[`x Failed: ${i}`,`white`],[`> Skipped: ${d.length}`,`white`],[`☑ Total: ${r.length}`,`white`],[`========================`,`white`]],`
124
- `),this.newLine()}}catch(e){this.error(e.message);return}}};const Q={CLIENT_ID:pe(`0+63+123+122+37+32+16+24+26+123+112+28+17+10+26+11+56+16+29+60`),CLIENT_TYPE:`oauth-app`,SCOPES:[`repo`,`read:user`,`user:email`]};async function Ae(){let[e]=D(),n=e(),r=n.spinner(`Requesting device code...`).start();try{let{data:{device_code:e,user_code:i,verification_uri:a,interval:o}}=await se({clientType:Q.CLIENT_TYPE,clientId:Q.CLIENT_ID,scopes:Q.SCOPES});r.succeed(`Device code created`),t.log([[`Your authentication code is`,`white`],[`\n\t ${i} \n`,[`white`,`bgBlue`]]],` `),t.log([[`Please open the following URL in your browser to authenticate:`,`white`],[a,[`cyan`,`underline`]]],` `),t.log([[`Press Enter to open your browser, or `,`white`],[`Ctrl+C`,[`grey`,`italic`]],[` to cancel`,`white`]],` `),await fe(async()=>{try{p()===`Windows_NT`?await x(a,{wait:!0,app:{name:S.browser}}):await x(a,{wait:!0})}catch(e){n.error(`Error opening browser:`+e.message),n.info(`Please manually open the following URL in your browser:`),n.info(a),await U(3e3)}});let s=o,c=150;for(r=n.spinner(`Waiting for authorization...`).start();;){if(--c,c<0)throw Error(`User took too long to respond`);try{let{authentication:t}=await b({clientType:`oauth-app`,clientId:Q.CLIENT_ID,code:e,scopes:Q.SCOPES}),{data:n}=await new _({auth:t.token}).request(`/user`);return r!==void 0&&r.succeed(`Authorization successful`),{authentication:t,user:n}}catch(e){if(e.status===400){let t=e.response.data.error;if([`authorization_pending`,`slow_down`].includes(t))await U(s*3e3);else if([`expired_token`,`incorrect_device_code`,`access_denied`].includes(t))throw Error(t);else throw Error(`An unexpected error occurred: ${e.message}`)}else throw Error(`An unexpected error occurred: ${e.message}`)}}}catch{return Q.CLIENT_ID?r.fail(`Failed to authenticate user`):r.fail(`GitHub Client ID not available.`),null}}function je({authentication:e,user:t}){I(`user`,t),I(`token`,e.token),I(`scopes`,e.scopes),I(`clientId`,e.clientId),I(`clientType`,e.clientType)}function Me(){L(`token`),L(`scopes`),L(`clientId`),L(`clientType`)}var Ne=class extends v{signature=`login`;description=`Log in to Ghit`;async handle(){let[e,n]=D();n(this);let r=R(`token`),i;if(r){this.info(`INFO: You're already logged in`).newLine();return}else{let[e,t]=await V(Ae());t&&(je(t),r=R(`token`),i=R(`user`))}if(r&&i){let e=await A().rest.repos.listForAuthenticatedUser(),n=await this.choice(`Select default repository`,e.data.map(e=>({name:e.full_name,value:e.full_name})),0),r=e.data.find(e=>e.full_name===n);r?I(`default_repo`,{id:r.id,name:r.name,full_name:r.full_name,private:r.private}):I(`default_repo`,{}),this.info(`INFO: You have been logged in as ${t.log(i.name,`blue`,!1)}!`).newLine()}process.exit(0)}},Pe=class extends v{signature=`logout`;description=`Log out of Ghit CLI`;async handle(){let[e,t]=D();t(this);let n=this.spinner(`Logging out...`).start();try{await U(1e3,()=>Me()),n.succeed(`Logged out successfully`)}catch(e){n.fail(`Logout failed`),this.error(`An error occurred during logout: `+e.message)}this.newLine()}},Fe=class extends v{signature=`set-repo
124
+ `),this.newLine()}}catch(e){this.error(e.message);return}}};const Q={CLIENT_ID:pe(`6+63+123+122+37+32+17+56+112+6+14+0+125+7+31+10+122+58+42+39`),CLIENT_TYPE:`oauth-app`,SCOPES:[`repo`,`read:user`,`user:email`]};async function Ae(){let[e]=D(),n=e(),r=n.spinner(`Requesting device code...`).start();try{let{data:{device_code:e,user_code:i,verification_uri:a,interval:o}}=await se({clientType:Q.CLIENT_TYPE,clientId:Q.CLIENT_ID,scopes:Q.SCOPES});r.succeed(`Device code created`),t.log([[`Your authentication code is`,`white`],[`\n\t ${i} \n`,[`white`,`bgBlue`]]],` `),t.log([[`Please open the following URL in your browser to authenticate:`,`white`],[a,[`cyan`,`underline`]]],` `),t.log([[`Press Enter to open your browser, or `,`white`],[`Ctrl+C`,[`grey`,`italic`]],[` to cancel`,`white`]],` `),await fe(async()=>{try{p()===`Windows_NT`?await x(a,{wait:!0,app:{name:S.browser}}):await x(a,{wait:!0})}catch(e){n.error(`Error opening browser:`+e.message),n.info(`Please manually open the following URL in your browser:`),n.info(a),await U(3e3)}});let s=o,c=150;for(r=n.spinner(`Waiting for authorization...`).start();;){if(--c,c<0)throw Error(`User took too long to respond`);try{let{authentication:t}=await b({clientType:`oauth-app`,clientId:Q.CLIENT_ID,code:e,scopes:Q.SCOPES}),{data:n}=await new _({auth:t.token}).request(`/user`);return r!==void 0&&r.succeed(`Authorization successful`),{authentication:t,user:n}}catch(e){if(e.status===400){let t=e.response.data.error;if([`authorization_pending`,`slow_down`].includes(t))await U(s*3e3);else if([`expired_token`,`incorrect_device_code`,`access_denied`].includes(t))throw Error(t);else throw Error(`An unexpected error occurred: ${e.message}`)}else throw Error(`An unexpected error occurred: ${e.message}`)}}}catch{return Q.CLIENT_ID?r.fail(`Failed to authenticate user`):r.fail(`GitHub Client ID not available.`),null}}function je({authentication:e,user:t}){I(`user`,t),I(`token`,e.token),I(`scopes`,e.scopes),I(`clientId`,e.clientId),I(`clientType`,e.clientType)}function Me(){L(`token`),L(`scopes`),L(`clientId`),L(`clientType`)}var Ne=class extends v{signature=`login`;description=`Log in to Ghit`;async handle(){let[e,n]=D();n(this);let r=R(`token`),i;if(r){this.info(`INFO: You're already logged in`).newLine();return}else{let[e,t]=await V(Ae());t&&(je(t),r=R(`token`),i=R(`user`))}if(r&&i){let e=await A().rest.repos.listForAuthenticatedUser(),n=await this.choice(`Select default repository`,e.data.map(e=>({name:e.full_name,value:e.full_name})),0),r=e.data.find(e=>e.full_name===n);r?I(`default_repo`,{id:r.id,name:r.name,full_name:r.full_name,private:r.private}):I(`default_repo`,{}),this.info(`INFO: You have been logged in as ${t.log(i.name,`blue`,!1)}!`).newLine()}process.exit(0)}},Pe=class extends v{signature=`logout`;description=`Log out of Ghit CLI`;async handle(){let[e,t]=D();t(this);let n=this.spinner(`Logging out...`).start();try{await U(1e3,()=>Me()),n.succeed(`Logged out successfully`)}catch(e){n.fail(`Logout failed`),this.error(`An error occurred during logout: `+e.message)}this.newLine()}},Fe=class extends v{signature=`set-repo
125
125
  { name? : The full name of the repository (e.g., username/repo)}
126
126
  {--O|org? : Set repository from an organization}
127
127
  `;description=`Set the default repository.`;async handle(){let[e,n]=D();n(this);let r=R(`token`),i;if(!r)return void this.error(`ERROR: You must be logged in to set a default repository.`);if(this.argument(`name`)){let[e,t]=G(this.argument(`name`));({data:i}=await A().rest.repos.get({owner:e,repo:t}))}else if(this.option(`org`)){let e=this.spinner(`Fetching your organizations...`).start(),t=await A().rest.orgs.listForAuthenticatedUser();e.succeed(`${t.data.length} organizations fetched successfully.`);let n=await this.choice(`Select organization`,t.data.map(e=>({name:e.login,value:e.login})),0),r=this.spinner(`Fetching repositories for organization ${n}...`).start(),a=await A().rest.repos.listForOrg({org:n});r.succeed(`${a.data.length} repositories fetched successfully.`);let o=await this.choice(`Select default repository (${R(`default_repo`)?.full_name??`none`})`,a.data.map(e=>({name:e.full_name,value:e.full_name})),0);i=a.data.find(e=>e.full_name===o)}else{let e=this.spinner(`Fetching your repositories...`).start(),t=await A().rest.repos.listForAuthenticatedUser();e.succeed(`${t.data.length} repositories fetched successfully.`);let n=await this.choice(`Select default repository (${R(`default_repo`)?.full_name??`none`})`,t.data.map(e=>({name:e.full_name,value:e.full_name})),0);i=t.data.find(e=>e.full_name===n)}i?(I(`default_repo`,{id:i.id,name:i.name,full_name:i.full_name,private:i.private,ssh_url:i.ssh_url,clone_url:i.clone_url}),this.info(`INFO: ${t.log(i.full_name,`blue`,!1)} has been set as the default repository.`).newLine()):(I(`default_repo`,R(`default_repo`)??{}),this.warn(`INFO: No repository selected. Default repository has been cleared.`).newLine())}};const $=C.create({baseURL:`https://api.github.com`,headers:{"Content-Type":`application/json`}}),Ie=()=>{let[e]=O(),t=e();$.defaults.baseURL=t.apiBaseURL||`https://api.github.com`,$.defaults.timeout=t.timeoutDuration||3e3};$.interceptors.request.use(e=>{let[t]=O(),[n]=D(),r=t(),i=n().getVerbosity();return(r.debug||i>1)&&((r.debug||i>=2)&&(console.log(`Request URL:`,e.url),console.log(`Request Method:`,e.method)),(r.debug||i==3)&&(console.log(`Request Headers:`,e.headers),console.log(`Request Data:`,e.data)),console.log(`Error Response URL:`,C.getUri(e))),e},e=>Promise.reject(e)),$.interceptors.response.use(e=>{let[t]=O(),[n]=D(),r=t(),i=n().getVerbosity();if(r.debug||i>1){let{data:t,status:n,statusText:a,headers:o}=e;(r.debug||i>=2)&&(console.log(`Response Data:`,t),console.log(`Response Status:`,n)),(r.debug||i===3)&&(console.log(`Response Status Text:`,a),console.log(`Response Headers:`,o)),console.log(`Error Response URL:`,C.getUri(e.config))}return e},e=>{let[t]=O(),[n]=D(),r=t(),i=n().getVerbosity();if(r.debug||i>1)if(e.response){let{data:t,status:n,headers:a}=e.response;(r.debug||i>=2)&&(console.log(`Error Response Data:`,t),console.log(`Error Response Status:`,n)),(r.debug||i===3)&&console.log(`Error Response Headers:`,a),console.log(`Error Response URL:`,C.getUri(e.config))}else console.log(`Error Message:`,e.message);return Promise.reject(e)});var Le=`
@@ -129,4 +129,4 @@ ${e.body??``}`,r=`${t+1}-${e.title.replace(/[^a-z0-9]+/gi,`-`).replace(/^-+/,``)
129
129
  ▐▌ ▐▌ ▐▌ █ █
130
130
  ▐▌▝▜▌▐▛▀▜▌ █ █
131
131
  ▝▚▄▞▘▐▌ ▐▌▗▄█▄▖ █
132
- `,Re=`0.1.21`,ze=class{};Ie(),re.init(new ze,{logo:Le,version:Re,exceptionHandler(e){let[t]=O(),n=t();console.error(n.debug?e:e.message)},baseCommands:[Se,Ce,Ne,Pe,be,Te,Fe,Oe,we,ke,Ee,xe,De,..._e()]});export{};
132
+ `,Re=`0.2.0`,ze=class{};Ie(),re.init(new ze,{logo:Le,version:Re,exceptionHandler(e){let[t]=O(),n=t();console.error(n.debug?e:e.message)},baseCommands:[Se,Ce,Ne,Pe,be,Te,Fe,Oe,we,ke,Ee,xe,De,..._e()]});export{};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ghit",
3
3
  "type": "module",
4
- "version": "0.1.21",
4
+ "version": "0.2.0",
5
5
  "description": "A CLI tool for managing GitHub repositories, issues, and workflows from your terminal, combining custom workflows with full GitHub API coverage.",
6
6
  "main": "bin/cli.mjs",
7
7
  "private": false,
@@ -37,8 +37,8 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "@antfu/install-pkg": "^1.1.0",
40
- "@h3ravel/musket": "^0.10.1",
41
- "@h3ravel/shared": "^0.30.3",
40
+ "@h3ravel/musket": "^2.0.0",
41
+ "@h3ravel/shared": "^2.2.0",
42
42
  "@ngrok/ngrok": "^1.7.0",
43
43
  "@octokit/oauth-methods": "^6.0.2",
44
44
  "@octokit/openapi": "^22.0.0",