@involvex/gitlurk-desktop 0.1.5 → 0.1.6

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 CHANGED
@@ -20,6 +20,10 @@ gitlurk gh run list # passthrough to GitHub CLI
20
20
  gitlurk runs # shortcut for gh run list
21
21
  gitlurk git config list --global
22
22
  gitlurk install-desktop # download Windows installer from GitHub Releases
23
+
24
+ # PowerShell tab completion
25
+ gitlurk completions powershell | Out-String | Invoke-Expression
26
+ # or: gitlurk completions powershell >> $PROFILE
23
27
  ```
24
28
 
25
29
  Requires [GitHub CLI](https://cli.github.com) for `gh` commands.
package/dist/index.js CHANGED
@@ -1,8 +1,92 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as y}from"node:child_process";import{platform as lt}from"node:os";var k=null,ut="GitHub CLI (gh) not found. Install from https://cli.github.com";async function gt(){let t=lt()==="win32"?"where":"which";return new Promise((e)=>{let r=y(t,["gh"],{shell:!0}),n="";r.stdout.on("data",(o)=>{n+=o.toString()}),r.on("close",(o)=>{if(o!==0){e(null);return}let i=n.split(/\r?\n/).find((s)=>s.trim());e(i?.trim()??null)}),r.on("error",()=>e(null))})}async function d(){if(k)return k;let t=await gt();if(!t)throw Error(ut);return k=t,t}async function ft(t,e={}){let r=await d(),n=e.cwd??process.cwd();if(e.inheritStdio)return{stdout:"",stderr:"",exitCode:await a(t,n)};return new Promise((o,i)=>{let s=y(r,t,{cwd:n,shell:!1}),g="",l="";s.stdout.on("data",(c)=>{g+=c.toString()}),s.stderr.on("data",(c)=>{l+=c.toString()}),s.on("error",i),s.on("close",(c)=>{o({stdout:g,stderr:l,exitCode:c??1})})})}async function a(t,e=process.cwd()){let r=await d();return new Promise((n,o)=>{let i=y(r,t,{cwd:e,stdio:"inherit",shell:!1});i.on("error",o),i.on("close",(s)=>n(s??1))})}async function L(){try{let t=await ft(["--version"]);if(t.exitCode!==0)return null;return t.stdout.trim().split(`
3
- `)[0]??null}catch{return null}}async function I(t=[],e){return a(["run","list",...t],e)}async function A(t=[],e){return a(["run","watch",...t],e)}async function T(t=[],e){return a(["repo","fork",...t],e)}async function O(t=[],e){return a(["repo","edit",...t],e)}async function _(t=[],e){return a(["release","create",...t],e)}async function U(t=[],e){return a(["alias",...t],e)}async function D(t=[],e){return a(["config",...t],e)}async function F(t=[],e){return a(["skill",...t],e)}import{spawn as H}from"node:child_process";import{existsSync as V,readFileSync as ht}from"node:fs";import{join as f,resolve as X}from"node:path";function j(t,e){let r=`gitlurk://openRepo/${t}`;return e?`${r}?branch=${encodeURIComponent(e)}`:r}import{existsSync as N,readFileSync as pt}from"node:fs";import{dirname as B,join as W}from"node:path";import{fileURLToPath as dt}from"node:url";function h(){let t=B(dt(import.meta.url));for(let e=0;e<12;e++){let r=W(t,"package.json");if(N(r))try{let o=JSON.parse(pt(r,"utf8")),i=W(t,"packages/app/package.json");if(o.workspaces&&N(i))return t}catch{}let n=B(t);if(n===t)break;t=n}return null}var p="gitlurk-desktop.exe";function mt(t){let e=f(t,".cargo/config.toml");if(!V(e))return null;try{return ht(e,"utf8").match(/^\s*target-dir\s*=\s*"([^"]+)"/m)?.[1]??null}catch{return null}}function M(t){let e=X(t);return[f(e,"release",p),f(e,"debug",p)]}function wt(t){let e=[],r=process.env.GITLURK_DESKTOP_EXE??process.env.MYGIT_DESKTOP_EXE;if(r)e.push(r);let n=process.env.CARGO_TARGET_DIR;if(n)e.push(...M(n));if(t){let o=mt(t);if(o)e.push(...M(o));e.push(f(t,"packages/app/src-tauri/target/release",p),f(t,"packages/app/src-tauri/target/debug",p))}return e}function xt(t){for(let e of wt(t))if(e&&V(e))return e;return null}function b(t){let e=h(),r=xt(e);if(r){console.log(`Launching ${r}`),console.log(` arg: ${t}`),H(r,[t],{detached:!0,stdio:"ignore",windowsHide:!1}).unref();return}if(!e)console.error("GitLurk Desktop executable not found and monorepo root could not be resolved.\nSet GITLURK_DESKTOP_EXE or CARGO_TARGET_DIR, or install with `gitlurk install-desktop`."),process.exit(1);console.log(`No ${p} found — launching via bun tauri from ${e}`),console.log(` arg: ${t}`),H("bun",["run","--filter","@gitlurk/app","tauri","dev","--release","--no-watch","--",t],{cwd:e,detached:!0,stdio:"ignore"}).unref()}function K(t){let e=X(t);b(`--open-local=${e}`)}function q(t){let e=t[0];if(!e||e===".")return K(e==="."?".":process.cwd()),!0;if(e==="open"&&t[1])return K(t[1]),!0;if(e==="clone"&&t[1])return b(`--clone=${t[1]}`),!0;if(e==="url"&&t[1])return b(j(t[1],t[2])),!0;return!1}async function J(){try{await d()}catch(t){console.error(t instanceof Error?t.message:String(t)),process.exit(1)}}async function z(t){await J();let e=t[0];if(e==="runs")process.exit(await I(t.slice(1)));if(e==="watch")process.exit(await A(t.slice(1)));if(e==="fork")process.exit(await T(t.slice(1)));if(e==="repo"&&t[1]==="edit")process.exit(await O(t.slice(2)));if(e==="release"&&t[1]==="create")process.exit(await _(t.slice(2)));if(e==="alias")process.exit(await U(t.slice(1)));if(e==="config")process.exit(await D(t.slice(1)));if(e==="skill")process.exit(await F(t.slice(1)))}async function Y(t){await J(),process.exit(await a(t))}function Q(t){if(!t)return!1;return t==="gh"||t==="runs"||t==="watch"||t==="fork"||t==="repo"||t==="release"||t==="alias"||t==="config"||t==="skill"}import{spawn as G}from"node:child_process";import kt from"node:fs";import P from"node:path";import{platform as C}from"node:os";var m=null;function yt(){let t=[P.join(process.cwd(),"resources","git","cmd","git.exe"),P.join(process.cwd(),"resources","git","bin","git.exe"),P.join(process.env.LOCALAPPDATA??"","GitLurk","git","cmd","git.exe")];for(let e of t)if(e&&kt.existsSync(e))return e;return null}async function bt(){if(m)return m;let t=await Pt();if(t)return m=t,t;let e=yt();if(e)return m=e,e;throw Error("Git not found. Install Git for Windows or bundle Portable Git in resources/git/")}async function Pt(){let t=C()==="win32"?"where":"which";return new Promise((e)=>{let r=G(t,["git"],{shell:!0}),n="";r.stdout.on("data",(o)=>{n+=o.toString()}),r.on("close",(o)=>{if(o!==0){e(null);return}let i=n.split(/\r?\n/).find((s)=>s.trim());e(i?.trim()??null)}),r.on("error",()=>e(null))})}async function w(t,e,r){let n=await bt();return new Promise((o,i)=>{let s=G(n,t,{cwd:e,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),g="",l="";s.stdout.on("data",(c)=>{let u=c.toString();g+=u,r?.(u.trim())}),s.stderr.on("data",(c)=>{let u=c.toString();l+=u,r?.(u.trim())}),s.on("error",i),s.on("close",(c)=>{o({stdout:g,stderr:l,exitCode:c??1})})})}function x(t){return`--${t}`}function Ct(t){let e=t.trim();if(!e)return null;let r=e.match(/^file:(.+?)\s+(.+)$/);if(r){let o=r[2]??"",i=o.indexOf("=");if(i===-1)return null;return{origin:r[1],key:o.slice(0,i),value:o.slice(i+1)}}let n=e.indexOf("=");if(n===-1)return null;return{key:e.slice(0,n),value:e.slice(n+1)}}async function Z(t){let e=t.cwd??process.cwd(),r=t.scope??"local",n=["config",x(r),"--list"];if(t.showOrigin??r==="local")n.push("--show-origin");let o=await w(n,e);if(o.exitCode!==0)throw Error(o.stderr||"Failed to list git config");return o.stdout.split(/\r?\n/).map(Ct).filter((i)=>i!==null)}async function tt(t){let e=t.cwd??process.cwd(),r=t.scope??"local",n=await w(["config",x(r),t.key],e);if(n.exitCode!==0)return null;return n.stdout.trim()}async function et(t){let e=t.cwd??process.cwd(),r=t.scope??"local",n=await w(["config",x(r),t.key,t.value],e);if(n.exitCode!==0)throw Error(n.stderr||"Failed to set git config")}async function Gt(t){let e=t.cwd??process.cwd(),r=await w(["config",x(t.scope),"--path"],e);if(r.exitCode!==0)return null;return r.stdout.trim()||null}async function rt(t){let e=t.cwd??process.cwd(),r=t.scope??"global",n=await Gt({scope:r,cwd:e});if(!n)throw Error(`Could not resolve ${r} git config file path`);let i=(process.env.GIT_EDITOR??process.env.VISUAL??process.env.EDITOR??(C()==="win32"?"notepad":"vi")).split(/\s+/).filter(Boolean),s=i[0];if(!s)throw Error("No editor configured");await new Promise((g,l)=>{let c=G(s,[...i.slice(1),n],{cwd:e,stdio:"inherit",shell:C()==="win32"});c.on("error",l),c.on("close",(u)=>{if(u!==0){l(Error(`Editor exited with code ${u}`));return}g()})})}function Rt(t){let e="local",r=[];for(let n of t)if(n==="--global")e="global";else if(n==="--local")e="local";else if(n==="--system")e="system";else r.push(n);return{scope:e,remaining:r}}async function nt(t){if(t[0]!=="git"||t[1]!=="config")return!1;let e=t[2],{scope:r,remaining:n}=Rt(t.slice(3)),o=process.cwd();if(e==="list"){let i=await Z({scope:r,cwd:o});for(let s of i)if(s.origin)console.log(`${s.origin} ${s.key}=${s.value}`);else console.log(`${s.key}=${s.value}`);return!0}if(e==="get"){let i=n[0];if(!i)console.error("Usage: gitlurk git config get <key> [--global|--local]"),process.exit(1);let s=await tt({key:i,scope:r,cwd:o});if(s===null)process.exit(1);return console.log(s),!0}if(e==="set"){let i=n[0],s=n[1];if(!i||s===void 0)console.error("Usage: gitlurk git config set <key> <value> [--global|--local]"),process.exit(1);return await et({key:i,value:s,scope:r,cwd:o}),!0}if(e==="edit")return await rt({scope:r,cwd:o}),!0;console.error("Unknown git config subcommand. Use list, get, set, or edit."),process.exit(1)}function ot(t){return t[0]==="git"&&t[1]==="config"}import{spawn as Et}from"node:child_process";import{createWriteStream as vt}from"node:fs";import{mkdir as St}from"node:fs/promises";import{homedir as $t,tmpdir as Lt}from"node:os";import{join as R}from"node:path";import{pipeline as It}from"node:stream/promises";import{Readable as At}from"node:stream";var E="involvex/gitlurk",Tt=`https://api.github.com/repos/${E}/releases/latest`;function Ot(t){let e=t.find((r)=>r.name.toLowerCase().endsWith(".msi"));if(e)return e;return t.find((r)=>/setup\.exe$/i.test(r.name)||/-setup\.exe$/i.test(r.name))??null}async function it(t,e){let r=await fetch(t,{headers:{"User-Agent":"gitlurk-desktop-cli"},redirect:"follow"});if(!r.ok||!r.body)throw Error(`Download failed: ${r.status} ${r.statusText}`);await It(At.fromWeb(r.body),vt(e))}function _t(t){if(console.log("Launching installer…"),process.platform==="win32"){Et("cmd",["/c","start","",t],{detached:!0,stdio:"ignore"}).unref();return}console.log(`Installer downloaded to:
2
+ import{spawn as C}from"node:child_process";import{platform as pt}from"node:os";var x=null,ft="GitHub CLI (gh) not found. Install from https://cli.github.com";async function dt(){let t=pt()==="win32"?"where":"which";return new Promise((e)=>{let r=C(t,["gh"],{shell:!0}),o="";r.stdout.on("data",(n)=>{o+=n.toString()}),r.on("close",(n)=>{if(n!==0){e(null);return}let i=o.split(/\r?\n/).find((s)=>s.trim());e(i?.trim()??null)}),r.on("error",()=>e(null))})}async function d(){if(x)return x;let t=await dt();if(!t)throw Error(ft);return x=t,t}async function mt(t,e={}){let r=await d(),o=e.cwd??process.cwd();if(e.inheritStdio)return{stdout:"",stderr:"",exitCode:await c(t,o)};return new Promise((n,i)=>{let s=C(r,t,{cwd:o,shell:!1}),g="",a="";s.stdout.on("data",(l)=>{g+=l.toString()}),s.stderr.on("data",(l)=>{a+=l.toString()}),s.on("error",i),s.on("close",(l)=>{n({stdout:g,stderr:a,exitCode:l??1})})})}async function c(t,e=process.cwd()){let r=await d();return new Promise((o,n)=>{let i=C(r,t,{cwd:e,stdio:"inherit",shell:!1});i.on("error",n),i.on("close",(s)=>o(s??1))})}async function O(){try{let t=await mt(["--version"]);if(t.exitCode!==0)return null;return t.stdout.trim().split(`
3
+ `)[0]??null}catch{return null}}async function I(t=[],e){return c(["run","list",...t],e)}async function L(t=[],e){return c(["run","watch",...t],e)}async function T(t=[],e){return c(["repo","fork",...t],e)}async function A(t=[],e){return c(["repo","edit",...t],e)}async function _(t=[],e){return c(["release","create",...t],e)}async function U(t=[],e){return c(["alias",...t],e)}async function D(t=[],e){return c(["config",...t],e)}async function j(t=[],e){return c(["skill",...t],e)}import{spawn as H}from"node:child_process";import{existsSync as V,readFileSync as kt}from"node:fs";import{join as p,resolve as K}from"node:path";function F(t,e){let r=`gitlurk://openRepo/${t}`;return e?`${r}?branch=${encodeURIComponent(e)}`:r}import{existsSync as N,readFileSync as ht}from"node:fs";import{dirname as q,join as B}from"node:path";import{fileURLToPath as wt}from"node:url";function m(){let t=q(wt(import.meta.url));for(let e=0;e<12;e++){let r=B(t,"package.json");if(N(r))try{let n=JSON.parse(ht(r,"utf8")),i=B(t,"packages/app/package.json");if(n.workspaces&&N(i))return t}catch{}let o=q(t);if(o===t)break;t=o}return null}var f="gitlurk-desktop.exe";function xt(t){let e=p(t,".cargo/config.toml");if(!V(e))return null;try{return kt(e,"utf8").match(/^\s*target-dir\s*=\s*"([^"]+)"/m)?.[1]??null}catch{return null}}function W(t){let e=K(t);return[p(e,"release",f),p(e,"debug",f)]}function Ct(t){let e=[],r=process.env.GITLURK_DESKTOP_EXE??process.env.MYGIT_DESKTOP_EXE;if(r)e.push(r);let o=process.env.CARGO_TARGET_DIR;if(o)e.push(...W(o));if(t){let n=xt(t);if(n)e.push(...W(n));e.push(p(t,"packages/app/src-tauri/target/release",f),p(t,"packages/app/src-tauri/target/debug",f))}return e}function yt(t){for(let e of Ct(t))if(e&&V(e))return e;return null}function y(t){let e=m(),r=yt(e);if(r){console.log(`Launching ${r}`),console.log(` arg: ${t}`),H(r,[t],{detached:!0,stdio:"ignore",windowsHide:!1}).unref();return}if(!e)console.error("GitLurk Desktop executable not found and monorepo root could not be resolved.\nSet GITLURK_DESKTOP_EXE or CARGO_TARGET_DIR, or install with `gitlurk install-desktop`."),process.exit(1);console.log(`No ${f} found — launching via bun tauri from ${e}`),console.log(` arg: ${t}`),H("bun",["run","--filter","@gitlurk/app","tauri","dev","--release","--no-watch","--",t],{cwd:e,detached:!0,stdio:"ignore"}).unref()}function M(t){let e=K(t);y(`--open-local=${e}`)}function X(t){let e=t[0];if(!e||e===".")return M(e==="."?".":process.cwd()),!0;if(e==="open"&&t[1])return M(t[1]),!0;if(e==="clone"&&t[1])return y(`--clone=${t[1]}`),!0;if(e==="url"&&t[1])return y(F(t[1],t[2])),!0;return!1}var J=["powershell"];function bt(t){return t!==void 0&&J.includes(t)}function Pt(){console.log(`Usage:
4
+ gitlurk completions <shell>
5
+
6
+ Shells:
7
+ powershell
8
+
9
+ Install (PowerShell profile):
10
+ gitlurk completions powershell | Out-String | Invoke-Expression
11
+
12
+ Or persist:
13
+ gitlurk completions powershell >> $PROFILE
14
+ `)}function Et(){return`# gitlurk PowerShell completions
15
+ # Add to your profile: gitlurk completions powershell | Out-String | Invoke-Expression
16
+
17
+ Register-ArgumentCompleter -Native -CommandName gitlurk -ScriptBlock {
18
+ param($wordToComplete, $commandAst, $cursorPosition)
19
+
20
+ $elements = @($commandAst.CommandElements | ForEach-Object { $_.Extent.Text })
21
+ if ($elements.Count -eq 0 -or $elements[0] -ne 'gitlurk') { return }
22
+
23
+ $tokens = @($elements | Select-Object -Skip 1)
24
+ if ($wordToComplete -and $tokens.Count -gt 0 -and $tokens[-1] -eq $wordToComplete) {
25
+ $tokens = @($tokens | Select-Object -SkipLast 1)
26
+ }
27
+
28
+ function Emit([string[]]$Items) {
29
+ $Items |
30
+ Where-Object { $_ -like "$wordToComplete*" } |
31
+ ForEach-Object {
32
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
33
+ }
34
+ }
35
+
36
+ if ($tokens.Count -eq 0) {
37
+ Emit @(
38
+ 'open', 'clone', 'url', 'gh', 'runs', 'watch', 'fork', 'repo', 'release',
39
+ 'alias', 'config', 'skill', 'git', 'install-desktop', 'completions', 'help',
40
+ '--help', '--version', '-h', '-V'
41
+ )
42
+ return
43
+ }
44
+
45
+ $cmd = $tokens[0]
46
+
47
+ switch -Regex ($cmd) {
48
+ '^(open|clone|url|watch|fork|alias|config|skill|install-desktop|help)$' {
49
+ return
50
+ }
51
+ '^gh$' {
52
+ # Defer to file/path completion for gh passthrough args
53
+ return
54
+ }
55
+ '^runs$' {
56
+ Emit @('--limit', '--json', '--workflow')
57
+ return
58
+ }
59
+ '^repo$' {
60
+ if ($tokens.Count -eq 1) { Emit @('edit'); return }
61
+ return
62
+ }
63
+ '^release$' {
64
+ if ($tokens.Count -eq 1) { Emit @('create'); return }
65
+ return
66
+ }
67
+ '^git$' {
68
+ if ($tokens.Count -eq 1) { Emit @('config'); return }
69
+ if ($tokens.Count -eq 2 -and $tokens[1] -eq 'config') {
70
+ Emit @('list', 'get', 'set', 'edit')
71
+ return
72
+ }
73
+ if ($tokens.Count -ge 3 -and $tokens[1] -eq 'config') {
74
+ Emit @('--global', '--local', '--system')
75
+ return
76
+ }
77
+ return
78
+ }
79
+ '^completions$' {
80
+ if ($tokens.Count -eq 1) { Emit @('powershell'); return }
81
+ return
82
+ }
83
+ default { return }
84
+ }
85
+ }
86
+ `}function z(t){let e=t[1];if(!e||e==="-h"||e==="--help"){Pt();return}if(!bt(e))console.error(`Unsupported shell: ${e}
87
+ Supported: ${J.join(", ")}`),process.exit(1);if(e==="powershell")process.stdout.write(Et())}function Y(t){return t==="completions"||t==="completion"}async function Q(){try{await d()}catch(t){console.error(t instanceof Error?t.message:String(t)),process.exit(1)}}async function Z(t){await Q();let e=t[0];if(e==="runs")process.exit(await I(t.slice(1)));if(e==="watch")process.exit(await L(t.slice(1)));if(e==="fork")process.exit(await T(t.slice(1)));if(e==="repo"&&t[1]==="edit")process.exit(await A(t.slice(2)));if(e==="release"&&t[1]==="create")process.exit(await _(t.slice(2)));if(e==="alias")process.exit(await U(t.slice(1)));if(e==="config")process.exit(await D(t.slice(1)));if(e==="skill")process.exit(await j(t.slice(1)))}async function tt(t){await Q(),process.exit(await c(t))}function et(t){if(!t)return!1;return t==="gh"||t==="runs"||t==="watch"||t==="fork"||t==="repo"||t==="release"||t==="alias"||t==="config"||t==="skill"}import{spawn as E}from"node:child_process";import Gt from"node:fs";import b from"node:path";import{platform as P}from"node:os";var h=null;function St(){let t=[b.join(process.cwd(),"resources","git","cmd","git.exe"),b.join(process.cwd(),"resources","git","bin","git.exe"),b.join(process.env.LOCALAPPDATA??"","GitLurk","git","cmd","git.exe")];for(let e of t)if(e&&Gt.existsSync(e))return e;return null}async function $t(){if(h)return h;let t=await Rt();if(t)return h=t,t;let e=St();if(e)return h=e,e;throw Error("Git not found. Install Git for Windows or bundle Portable Git in resources/git/")}async function Rt(){let t=P()==="win32"?"where":"which";return new Promise((e)=>{let r=E(t,["git"],{shell:!0}),o="";r.stdout.on("data",(n)=>{o+=n.toString()}),r.on("close",(n)=>{if(n!==0){e(null);return}let i=o.split(/\r?\n/).find((s)=>s.trim());e(i?.trim()??null)}),r.on("error",()=>e(null))})}async function w(t,e,r){let o=await $t();return new Promise((n,i)=>{let s=E(o,t,{cwd:e,env:{...process.env,GIT_TERMINAL_PROMPT:"0"}}),g="",a="";s.stdout.on("data",(l)=>{let u=l.toString();g+=u,r?.(u.trim())}),s.stderr.on("data",(l)=>{let u=l.toString();a+=u,r?.(u.trim())}),s.on("error",i),s.on("close",(l)=>{n({stdout:g,stderr:a,exitCode:l??1})})})}function k(t){return`--${t}`}function vt(t){let e=t.trim();if(!e)return null;let r=e.match(/^file:(.+?)\s+(.+)$/);if(r){let n=r[2]??"",i=n.indexOf("=");if(i===-1)return null;return{origin:r[1],key:n.slice(0,i),value:n.slice(i+1)}}let o=e.indexOf("=");if(o===-1)return null;return{key:e.slice(0,o),value:e.slice(o+1)}}async function rt(t){let e=t.cwd??process.cwd(),r=t.scope??"local",o=["config",k(r),"--list"];if(t.showOrigin??r==="local")o.push("--show-origin");let n=await w(o,e);if(n.exitCode!==0)throw Error(n.stderr||"Failed to list git config");return n.stdout.split(/\r?\n/).map(vt).filter((i)=>i!==null)}async function ot(t){let e=t.cwd??process.cwd(),r=t.scope??"local",o=await w(["config",k(r),t.key],e);if(o.exitCode!==0)return null;return o.stdout.trim()}async function nt(t){let e=t.cwd??process.cwd(),r=t.scope??"local",o=await w(["config",k(r),t.key,t.value],e);if(o.exitCode!==0)throw Error(o.stderr||"Failed to set git config")}async function Ot(t){let e=t.cwd??process.cwd(),r=await w(["config",k(t.scope),"--path"],e);if(r.exitCode!==0)return null;return r.stdout.trim()||null}async function it(t){let e=t.cwd??process.cwd(),r=t.scope??"global",o=await Ot({scope:r,cwd:e});if(!o)throw Error(`Could not resolve ${r} git config file path`);let i=(process.env.GIT_EDITOR??process.env.VISUAL??process.env.EDITOR??(P()==="win32"?"notepad":"vi")).split(/\s+/).filter(Boolean),s=i[0];if(!s)throw Error("No editor configured");await new Promise((g,a)=>{let l=E(s,[...i.slice(1),o],{cwd:e,stdio:"inherit",shell:P()==="win32"});l.on("error",a),l.on("close",(u)=>{if(u!==0){a(Error(`Editor exited with code ${u}`));return}g()})})}function It(t){let e="local",r=[];for(let o of t)if(o==="--global")e="global";else if(o==="--local")e="local";else if(o==="--system")e="system";else r.push(o);return{scope:e,remaining:r}}async function st(t){if(t[0]!=="git"||t[1]!=="config")return!1;let e=t[2],{scope:r,remaining:o}=It(t.slice(3)),n=process.cwd();if(e==="list"){let i=await rt({scope:r,cwd:n});for(let s of i)if(s.origin)console.log(`${s.origin} ${s.key}=${s.value}`);else console.log(`${s.key}=${s.value}`);return!0}if(e==="get"){let i=o[0];if(!i)console.error("Usage: gitlurk git config get <key> [--global|--local]"),process.exit(1);let s=await ot({key:i,scope:r,cwd:n});if(s===null)process.exit(1);return console.log(s),!0}if(e==="set"){let i=o[0],s=o[1];if(!i||s===void 0)console.error("Usage: gitlurk git config set <key> <value> [--global|--local]"),process.exit(1);return await nt({key:i,value:s,scope:r,cwd:n}),!0}if(e==="edit")return await it({scope:r,cwd:n}),!0;console.error("Unknown git config subcommand. Use list, get, set, or edit."),process.exit(1)}function lt(t){return t[0]==="git"&&t[1]==="config"}import{spawn as Lt}from"node:child_process";import{createWriteStream as Tt}from"node:fs";import{mkdir as At}from"node:fs/promises";import{homedir as _t,tmpdir as Ut}from"node:os";import{join as G}from"node:path";import{pipeline as Dt}from"node:stream/promises";import{Readable as jt}from"node:stream";var S="involvex/gitlurk",Ft=`https://api.github.com/repos/${S}/releases/latest`;function Nt(t){let e=t.find((r)=>r.name.toLowerCase().endsWith(".msi"));if(e)return e;return t.find((r)=>/setup\.exe$/i.test(r.name)||/-setup\.exe$/i.test(r.name))??null}async function ct(t,e){let r=await fetch(t,{headers:{"User-Agent":"gitlurk-desktop-cli"},redirect:"follow"});if(!r.ok||!r.body)throw Error(`Download failed: ${r.status} ${r.statusText}`);await Dt(jt.fromWeb(r.body),Tt(e))}function qt(t){if(console.log("Launching installer…"),process.platform==="win32"){Lt("cmd",["/c","start","",t],{detached:!0,stdio:"ignore"}).unref();return}console.log(`Installer downloaded to:
4
88
  ${t}
5
- Open it manually on this platform.`)}async function st(){console.log(`Fetching latest release from ${E}…`);let t=await fetch(Tt,{headers:{"User-Agent":"gitlurk-desktop-cli",Accept:"application/vnd.github+json"}});if(!t.ok)throw Error(`Failed to fetch releases: ${t.status} ${t.statusText}`);let e=await t.json(),r=Ot(e.assets);if(!r)throw Error(`No MSI/setup.exe found in ${e.tag_name}. See https://github.com/${E}/releases`);let n=R($t(),".gitlurk","downloads");await St(n,{recursive:!0});let o=R(n,r.name);console.log(`Downloading ${r.name} (${e.tag_name})…`);try{await it(r.browser_download_url,o)}catch{o=R(Lt(),r.name),await it(r.browser_download_url,o)}console.log(`Saved to ${o}`),_t(o)}function v(){console.log(`GitLurk CLI
89
+ Open it manually on this platform.`)}async function at(){console.log(`Fetching latest release from ${S}…`);let t=await fetch(Ft,{headers:{"User-Agent":"gitlurk-desktop-cli",Accept:"application/vnd.github+json"}});if(!t.ok)throw Error(`Failed to fetch releases: ${t.status} ${t.statusText}`);let e=await t.json(),r=Nt(e.assets);if(!r)throw Error(`No MSI/setup.exe found in ${e.tag_name}. See https://github.com/${S}/releases`);let o=G(_t(),".gitlurk","downloads");await At(o,{recursive:!0});let n=G(o,r.name);console.log(`Downloading ${r.name} (${e.tag_name})…`);try{await ct(r.browser_download_url,n)}catch{n=G(Ut(),r.name),await ct(r.browser_download_url,n)}console.log(`Saved to ${n}`),qt(n)}function R(){console.log(`GitLurk CLI
6
90
 
7
91
  Usage:
8
92
  gitlurk [--help] [--version]
@@ -32,6 +116,10 @@ Git config:
32
116
  Desktop installer:
33
117
  gitlurk install-desktop Download & launch latest Windows installer
34
118
 
119
+ Completions:
120
+ gitlurk completions powershell
121
+ # Install: gitlurk completions powershell | Out-String | Invoke-Expression
122
+
35
123
  Env:
36
124
  GITLURK_DESKTOP_EXE Path to gitlurk-desktop.exe (optional)
37
125
  CARGO_TARGET_DIR Cargo target dir; uses release/ then debug/
@@ -42,4 +130,4 @@ Install:
42
130
 
43
131
  If no exe is found, falls back to:
44
132
  bun run --filter @gitlurk/app tauri dev --release --no-watch -- <arg>
45
- `)}import{existsSync as Ut,readFileSync as Dt}from"node:fs";import{dirname as Ft,join as S}from"node:path";import{fileURLToPath as jt}from"node:url";function ct(t){if(!Ut(t))return null;try{return JSON.parse(Dt(t,"utf8")).version??null}catch{return null}}function at(){let t=h();if(t){let r=ct(S(t,"package.json"));if(r)return r}let e=Ft(jt(import.meta.url));for(let r of[S(e,"..","package.json"),S(e,"..","..","package.json")]){let n=ct(r);if(n)return n}return"0.0.0"}function Nt(t){let e=[],r=!1,n=!1;for(let o of t)if(o==="-h"||o==="--help")r=!0;else if(o==="-V"||o==="--version")n=!0;else e.push(o);return{args:e,help:r,version:n}}async function Bt(){let t=process.argv.slice(2),e=t[0]==="--"?t.slice(1):t,{args:r,help:n,version:o}=Nt(e);if(o){console.log(`gitlurk ${at()}`);let i=await L();if(i)console.log(i);return}if(n||r[0]==="help"){v();return}if(r[0]==="install-desktop"){try{await st()}catch(i){console.error(i instanceof Error?i.message:String(i)),process.exit(1)}return}if(ot(r)){await nt(r);return}if(r[0]==="gh"){await Y(r.slice(1));return}if(Q(r[0])){await z(r);return}if(q(r))return;v(),process.exit(1)}Bt();
133
+ `)}import{existsSync as Bt,readFileSync as Ht}from"node:fs";import{dirname as Wt,join as v}from"node:path";import{fileURLToPath as Mt}from"node:url";function ut(t){if(!Bt(t))return null;try{return JSON.parse(Ht(t,"utf8")).version??null}catch{return null}}function gt(){let t=m();if(t){let r=ut(v(t,"package.json"));if(r)return r}let e=Wt(Mt(import.meta.url));for(let r of[v(e,"..","package.json"),v(e,"..","..","package.json")]){let o=ut(r);if(o)return o}return"0.0.0"}function Vt(t){let e=[],r=!1,o=!1;for(let n of t)if(n==="-h"||n==="--help")r=!0;else if(n==="-V"||n==="--version")o=!0;else e.push(n);return{args:e,help:r,version:o}}async function Kt(){let t=process.argv.slice(2),e=t[0]==="--"?t.slice(1):t,{args:r,help:o,version:n}=Vt(e);if(n){console.log(`gitlurk ${gt()}`);let i=await O();if(i)console.log(i);return}if(o||r[0]==="help"){R();return}if(r[0]==="install-desktop"){try{await at()}catch(i){console.error(i instanceof Error?i.message:String(i)),process.exit(1)}return}if(Y(r[0])){z(r);return}if(lt(r)){await st(r);return}if(r[0]==="gh"){await tt(r.slice(1));return}if(et(r[0])){await Z(r);return}if(X(r))return;R(),process.exit(1)}Kt();
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@involvex/gitlurk-desktop",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "GitLurk Desktop CLI — open repos, wrap gh, manage git config",
5
5
  "type": "module",
6
6
  "bin": {
7
- "gitlurk": "./dist/index.js"
7
+ "gitlurk": "dist/index.js"
8
8
  },
9
9
  "files": [
10
10
  "dist",