git-agents 0.2.0 → 0.2.1
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 +11 -3
- package/dist/index.js +2 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -8,6 +8,12 @@ Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [41 more](#su
|
|
|
8
8
|
|
|
9
9
|
`ga` keeps your AI agent directories in sync with a remote git repository — so your custom skills follow you everywhere.
|
|
10
10
|
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bunx git-agents@latest
|
|
15
|
+
```
|
|
16
|
+
|
|
11
17
|
## Install
|
|
12
18
|
|
|
13
19
|
```bash
|
|
@@ -37,7 +43,9 @@ ga pull # pull remote agents to local
|
|
|
37
43
|
ga push # push local agents to remote
|
|
38
44
|
```
|
|
39
45
|
|
|
40
|
-
##
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
### Setup
|
|
41
49
|
|
|
42
50
|
On first launch, `ga` walks you through setup:
|
|
43
51
|
|
|
@@ -46,7 +54,7 @@ On first launch, `ga` walks you through setup:
|
|
|
46
54
|
3. **Custom git path** — provide any accessible git remote URL
|
|
47
55
|
4. Clones the repo to `~/.git-agents` and saves your config
|
|
48
56
|
|
|
49
|
-
|
|
57
|
+
### Pull / Push
|
|
50
58
|
|
|
51
59
|
Both operations show a comparison summary before doing anything:
|
|
52
60
|
|
|
@@ -64,7 +72,7 @@ Both operations show a comparison summary before doing anything:
|
|
|
64
72
|
|
|
65
73
|
**No is always the default** — nothing happens unless you explicitly confirm.
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
### Edit config
|
|
68
76
|
|
|
69
77
|
Select **Edit Config** from the main menu to reconfigure your remote at any time.
|
|
70
78
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{createCliRenderer as On}from"@opentui/core";import{createRoot as Qn}from"@opentui/react";import{join as z,relative as sn}from"path";import{homedir as H}from"os";import{existsSync as Pn,mkdirSync as Cn,readFileSync as wn,writeFileSync as vn}from"fs";var C=z(H(),".git-agents"),T=z(C,"config.json"),Gn=z(C,".agents"),$n=z(H(),".agents");function D(){try{if(!Pn(T))return null;let n=wn(T,"utf8");return JSON.parse(n)}catch{return null}}function _(n){Cn(C,{recursive:!0}),vn(T,JSON.stringify(n,null,2),"utf8")}function G(n){let e=sn(H(),n);return z(C,e)}import{useState as mn}from"react";import{useKeyboard as Bn}from"@opentui/react";import{useKeyboard as In}from"@opentui/react";import{jsxDEV as L}from"@opentui/react/jsx-dev-runtime";function j({onNavigate:n}){return In((e)=>{if(e.name==="escape"||e.ctrl&&e.name==="c")process.exit(0)}),L("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:[L("box",{flexDirection:"column",alignItems:"center",gap:1,children:[L("ascii-font",{font:"tiny",text:"git-agents"},void 0,!1,void 0,this),L("text",{children:"Sync your Claude agents directory with a remote git repo"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),L("box",{flexDirection:"column",width:40,marginTop:2,children:L("select",{focused:!0,options:[{name:"Pull",description:"Download agents from remote to local"},{name:"Push",description:"Upload local agents to remote"},{name:"Edit Config",description:"Change remote configuration"}],onSelect:(e)=>{if(e===0)n({id:"sync",mode:"pull"});else if(e===1)n({id:"sync",mode:"push"});else if(e===2)n({id:"setup"})},height:6},void 0,!1,void 0,this)},void 0,!1,void 0,this),L("box",{marginTop:1,children:L("text",{children:"\u2191\u2193 navigate Enter select Ctrl+C quit"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}import{useState as O,useEffect as Kn}from"react";import{useKeyboard as qn}from"@opentui/react";import{TextAttributes as q}from"@opentui/core";var{$:h}=globalThis.Bun;async function V(){try{let n=await h`gh --version`.quiet();return{ok:n.exitCode===0,output:n.stdout.toString()}}catch(n){return{ok:!1,error:n.message??"gh not found"}}}async function $(){try{return{ok:(await h`gh auth status`.quiet()).exitCode===0}}catch{return{ok:!1,error:"Not authenticated. Run: gh auth login"}}}async function E(n){try{return{ok:(await h`gh repo view ${n}`.quiet()).exitCode===0}}catch{return{ok:!1}}}async function nn(n){try{let e=await h`gh repo create ${n} --private`.quiet();return{ok:e.exitCode===0,output:e.stdout.toString()}}catch(e){return{ok:!1,error:e.stderr?.toString()??e.message}}}async function U(n){try{let t=(await h`gh repo view ${n} --json sshUrl --jq .sshUrl`.quiet()).stdout.toString().trim();return{ok:!!t,output:t}}catch(e){return{ok:!1,error:e.message}}}async function on(n){try{return{ok:(await h`git ls-remote ${n}`.quiet()).exitCode===0}}catch{return{ok:!1,error:"Cannot reach repository"}}}async function en(n,e){try{return{ok:(await h`git clone ${n} ${e}`.quiet()).exitCode===0}}catch(t){return{ok:!1,error:t.stderr?.toString()??t.message}}}async function tn(n){try{let e=await h`git -C ${n} pull`.quiet();return{ok:e.exitCode===0,output:e.stdout.toString()}}catch(e){let t=e.stderr?.toString()??e.message??"";if(t.includes("no such ref was fetched")||t.includes("couldn't find remote ref"))return{ok:!0,output:"Remote is empty"};return{ok:!1,error:t}}}async function an(n,e){try{if(await h`git -C ${n} add -A`.quiet(),!(await h`git -C ${n} status --porcelain`.quiet()).stdout.toString().trim())return{ok:!0,output:"Nothing to commit"};return await h`git -C ${n} commit -m ${e}`.quiet(),{ok:(await h`git -C ${n} push -u origin HEAD`.quiet()).exitCode===0}}catch(t){return{ok:!1,error:t.stderr?.toString()??t.message}}}import{existsSync as An,readdirSync as rn,mkdirSync as Fn,cpSync as Ln}from"fs";import{join as Sn}from"path";function B(n){if(!An(n))return[];try{return rn(n,{withFileTypes:!0}).filter((e)=>e.isDirectory()).map((e)=>{let t=Sn(n,e.name),r=0;try{r=rn(t).length}catch{}return{name:e.name,fileCount:r}}).sort((e,t)=>e.name.localeCompare(t.name))}catch{return[]}}function ln(n,e){let t=B(n),r=B(e),w=new Map(t.map((p)=>[p.name,p])),s=new Map(r.map((p)=>[p.name,p])),d=[],K=[],v=[],P=[];for(let p of t)if(!s.has(p.name))d.push(p);else{let J=s.get(p.name);if(p.fileCount!==J.fileCount)v.push(p);else P.push(p)}for(let p of r)if(!w.has(p.name))K.push(p);return{added:d,removed:K,modified:v,unchanged:P}}function cn(n,e){Fn(e,{recursive:!0}),Ln(n,e,{recursive:!0,force:!0})}import{join as a}from"path";import{homedir as Nn}from"os";var i=Nn(),fn=[{id:"amp",name:"Amp",globalPath:a(i,".config/agents")},{id:"kimi-cli",name:"Kimi Code CLI",globalPath:a(i,".config/agents")},{id:"replit",name:"Replit",globalPath:a(i,".config/agents")},{id:"universal",name:"Universal",globalPath:a(i,".config/agents")},{id:"antigravity",name:"Antigravity",globalPath:a(i,".gemini/antigravity")},{id:"augment",name:"Augment",globalPath:a(i,".augment")},{id:"bob",name:"IBM Bob",globalPath:a(i,".bob")},{id:"claude-code",name:"Claude Code",globalPath:a(i,".claude")},{id:"openclaw",name:"OpenClaw",globalPath:a(i,".openclaw")},{id:"cline",name:"Cline",globalPath:a(i,".agents")},{id:"warp",name:"Warp",globalPath:a(i,".agents")},{id:"codebuddy",name:"CodeBuddy",globalPath:a(i,".codebuddy")},{id:"codex",name:"Codex",globalPath:a(i,".codex")},{id:"command-code",name:"Command Code",globalPath:a(i,".commandcode")},{id:"continue",name:"Continue",globalPath:a(i,".continue")},{id:"cortex",name:"Cortex Code",globalPath:a(i,".snowflake/cortex")},{id:"crush",name:"Crush",globalPath:a(i,".config/crush")},{id:"cursor",name:"Cursor",globalPath:a(i,".cursor")},{id:"deepagents",name:"Deep Agents",globalPath:a(i,".deepagents/agent")},{id:"droid",name:"Droid",globalPath:a(i,".factory")},{id:"firebender",name:"Firebender",globalPath:a(i,".firebender")},{id:"gemini-cli",name:"Gemini CLI",globalPath:a(i,".gemini")},{id:"github-copilot",name:"GitHub Copilot",globalPath:a(i,".copilot")},{id:"goose",name:"Goose",globalPath:a(i,".config/goose")},{id:"junie",name:"Junie",globalPath:a(i,".junie")},{id:"iflow-cli",name:"iFlow CLI",globalPath:a(i,".iflow")},{id:"kilo",name:"Kilo Code",globalPath:a(i,".kilocode")},{id:"kiro-cli",name:"Kiro CLI",globalPath:a(i,".kiro")},{id:"kode",name:"Kode",globalPath:a(i,".kode")},{id:"mcpjam",name:"MCPJam",globalPath:a(i,".mcpjam")},{id:"mistral-vibe",name:"Mistral Vibe",globalPath:a(i,".vibe")},{id:"mux",name:"Mux",globalPath:a(i,".mux")},{id:"opencode",name:"OpenCode",globalPath:a(i,".config/opencode")},{id:"openhands",name:"OpenHands",globalPath:a(i,".openhands")},{id:"pi",name:"Pi",globalPath:a(i,".pi/agent")},{id:"qoder",name:"Qoder",globalPath:a(i,".qoder")},{id:"qwen-code",name:"Qwen Code",globalPath:a(i,".qwen")},{id:"roo",name:"Roo Code",globalPath:a(i,".roo")},{id:"trae",name:"Trae",globalPath:a(i,".trae")},{id:"trae-cn",name:"Trae CN",globalPath:a(i,".trae-cn")},{id:"windsurf",name:"Windsurf",globalPath:a(i,".codeium/windsurf")},{id:"zencoder",name:"Zencoder",globalPath:a(i,".zencoder")},{id:"neovate",name:"Neovate",globalPath:a(i,".neovate")},{id:"pochi",name:"Pochi",globalPath:a(i,".pochi")},{id:"adal",name:"AdaL",globalPath:a(i,".adal")}];import{jsxDEV as c}from"@opentui/react/jsx-dev-runtime";function gn({mode:n,onBack:e}){let[t,r]=O("loading"),[w,s]=O("Fetching remote..."),[d,K]=O([]),[v,P]=O(null),[p,J]=O("");qn((f)=>{if(f.name==="escape"){if(t==="done"||t==="review")e()}}),Kn(()=>{async function f(){s("Pulling remote changes...");let g=await tn(C);if(!g.ok){P(`Failed to pull remote: ${g.error??"unknown error"}`),r("done");return}s("Comparing agents...");let u=new Map;for(let l of fn){let m=u.get(l.globalPath)??[];u.set(l.globalPath,[...m,l])}let b=[];for(let[l,m]of u){let k=G(l),Y=n==="pull"?k:l,x=n==="pull"?l:k,hn=ln(Y,x),Z=B(Y),M=B(x);if(Z.length===0&&M.length===0)continue;for(let yn of m)b.push({def:yn,diff:hn,remoteCount:n==="pull"?Z.length:M.length,localCount:n==="pull"?M.length:Z.length})}K(b),r("review")}f()},[]);async function Q(f){if(!f){e();return}r("executing"),s(n==="pull"?"Copying agents from remote...":"Copying agents to remote...");let g=new Set;try{for(let u of d){if(g.has(u.def.globalPath))continue;g.add(u.def.globalPath);let b=G(u.def.globalPath),l=n==="pull"?b:u.def.globalPath,m=n==="pull"?u.def.globalPath:b;cn(l,m)}}catch(u){P(`Failed to copy agents: ${u.message}`),r("done");return}if(n==="push"){s("Committing and pushing to remote...");let u=await an(C,`sync: update agents from local (${new Date().toISOString().slice(0,10)})`);if(!u.ok){P(`Failed to push: ${u.error??"unknown error"}`),r("done");return}}J(n==="pull"?"Pull complete! Local agents updated.":"Push complete! Remote updated."),P(null),r("done")}let A=n==="pull"?"Pull":"Push";if(t==="loading"||t==="executing")return c("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:c("text",{children:w},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(t==="done")return c("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:1,children:[v?c("text",{fg:"#ff5555",children:v},void 0,!1,void 0,this):c("text",{fg:"#50fa7b",children:p},void 0,!1,void 0,this),c("text",{attributes:q.DIM,children:"Press Esc to go back"},void 0,!1,void 0,this)]},void 0,!0,void 0,this);let I=new Set,F=d.reduce((f,g)=>{if(I.has(g.def.globalPath))return f;return I.add(g.def.globalPath),f+g.remoteCount},0);I.clear();let W=d.reduce((f,g)=>{if(I.has(g.def.globalPath))return f;return I.add(g.def.globalPath),f+g.localCount},0),X=d.some((f)=>f.diff.added.length>0||f.diff.removed.length>0||f.diff.modified.length>0);return c("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:1,children:[c("ascii-font",{font:"tiny",text:A},void 0,!1,void 0,this),c("box",{flexDirection:"column",border:!0,borderStyle:"rounded",title:" Comparison ",paddingX:2,paddingY:1,width:60,gap:1,children:[c("box",{flexDirection:"row",justifyContent:"space-between",children:[c("text",{children:[c("span",{children:"Remote: "},void 0,!1,void 0,this),c("span",{fg:"#8be9fd",children:[F," skills"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),c("text",{children:[c("span",{children:"Local: "},void 0,!1,void 0,this),c("span",{fg:"#8be9fd",children:[W," skills"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),d.length===0&&c("text",{attributes:q.DIM,children:"No agents found"},void 0,!1,void 0,this),d.map((f)=>{let g=f.diff,u=g.added.length>0||g.removed.length>0||g.modified.length>0;return c("box",{flexDirection:"column",children:[c("box",{flexDirection:"row",justifyContent:"space-between",children:[c("text",{fg:"#bd93f9",children:f.def.name},void 0,!1,void 0,this),c("text",{attributes:q.DIM,children:[f.remoteCount,"\u2193 / ",f.localCount,"\u2191"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),g.added.map((b)=>c("text",{children:[c("span",{fg:"#50fa7b",children:" + "},void 0,!1,void 0,this),c("span",{children:b.name},void 0,!1,void 0,this)]},b.name,!0,void 0,this)),g.removed.map((b)=>c("text",{children:[c("span",{fg:"#ff5555",children:" - "},void 0,!1,void 0,this),c("span",{children:b.name},void 0,!1,void 0,this)]},b.name,!0,void 0,this)),g.modified.map((b)=>c("text",{children:[c("span",{fg:"#ffb86c",children:" ~ "},void 0,!1,void 0,this),c("span",{children:b.name},void 0,!1,void 0,this)]},b.name,!0,void 0,this)),!u&&g.unchanged.length>0&&c("text",{attributes:q.DIM,children:[" ",g.unchanged.length," skill",g.unchanged.length!==1?"s":""," unchanged"]},void 0,!0,void 0,this)]},f.def.id,!0,void 0,this)})]},void 0,!0,void 0,this),c("box",{flexDirection:"column",alignItems:"center",gap:1,children:[c("text",{children:[c("span",{children:["Confirm ",n,"? "]},void 0,!0,void 0,this),c("span",{attributes:q.DIM,children:"(No is default \u2014 press Enter to cancel)"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),c("select",{focused:!0,options:[{name:"No, cancel",description:"Go back to main menu"},{name:`Yes, ${n}`,description:X?"Apply changes":"No changes to apply"}],onSelect:(f)=>Q(f===1),width:30,height:4},void 0,!1,void 0,this)]},void 0,!0,void 0,this),c("text",{attributes:q.DIM,children:"Esc to cancel"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}import{useState as S,useEffect as Jn}from"react";import{useKeyboard as kn}from"@opentui/react";import{TextAttributes as y}from"@opentui/core";import{existsSync as Yn}from"fs";import{join as zn}from"path";import{jsxDEV as o}from"@opentui/react/jsx-dev-runtime";var N="git-agents-remote";function dn({existingConfig:n,onComplete:e}){let[t,r]=S("welcome"),[w,s]=S(n?.remote??"gh"),[d,K]=S(n?.repoUrl??""),[v,P]=S(""),[p,J]=S(!1),[Q,A]=S(""),[I,F]=S("");kn((l)=>{if(l.ctrl&&l.name==="c")process.exit(0);if(t==="welcome"&&l.name==="return")r("choose-remote");if(t==="gh-auth-needed"&&l.name==="return")W();if(t==="error"&&l.name==="return")r("choose-remote")});async function W(){if(r("gh-checking"),F("Checking gh auth..."),!(await $()).ok){r("gh-auth-needed");return}f()}async function X(){if(r("gh-checking"),F("Checking gh CLI..."),!(await V()).ok){A("GitHub CLI (gh) is not installed. Install it from https://cli.github.com and try again."),r("error");return}if(!(await $()).ok){r("gh-auth-needed");return}f()}async function f(){r("gh-checking"),F("Checking for git-agents repo...");let l=await E(N);if(J(l.ok),!l.ok){r("gh-repo-check");return}let m=await U(N);if(!m.ok||!m.output){A("Could not get repo URL from gh CLI."),r("error");return}P(m.output.trim()),r("gh-confirm")}async function g(){r("gh-checking"),F(`Creating private repo "${N}"...`);let l=await nn(N);if(!l.ok){A(`Failed to create repo: ${l.error??"unknown error"}`),r("error");return}let m=await U(N);if(!m.ok||!m.output){A("Could not get repo URL after creating it."),r("error");return}P(m.output.trim()),r("gh-confirm")}async function u(l,m){r("cloning"),F(`Cloning ${l}...`);let k={remote:m,repoUrl:m==="git"?l:void 0};if(Yn(zn(C,".git"))){_(k),r("done");return}let Y=await en(l,C);if(!Y.ok){A(`Failed to clone: ${Y.error??"unknown error"}`),r("error");return}_(k),r("done")}async function b(l){if(r("git-url-checking"),F("Validating repository..."),!(await on(l)).ok){A(`Cannot reach repository: ${l}
|
|
4
|
-
Make sure the URL is correct and you have access.`),
|
|
3
|
+
import{createCliRenderer as He}from"@opentui/core";import{createRoot as Ke}from"@opentui/react";import{join as L,relative as xe}from"path";import{homedir as B}from"os";import{existsSync as Pe,mkdirSync as we,readFileSync as Re,writeFileSync as ke}from"fs";var R=L(B(),".git-agents"),K=L(R,"config.json"),Qe=L(R,".agents"),Ze=L(B(),".agents");function z(){try{if(!Pe(K))return null;let e=Re(K,"utf8");return JSON.parse(e)}catch{return null}}function Q(e){we(R,{recursive:!0}),ke(K,JSON.stringify(e,null,2),"utf8")}function Y(e){let t=xe(B(),e);return L(R,t)}import{useState as de}from"react";import{useKeyboard as _e}from"@opentui/react";import{useKeyboard as Se}from"@opentui/react";import{jsxDEV as I}from"@opentui/react/jsx-dev-runtime";function Z({onNavigate:e}){return Se((t)=>{if(t.name==="escape"||t.ctrl&&t.name==="c")process.exit(0)}),I("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:[I("box",{flexDirection:"column",alignItems:"center",gap:1,children:[I("ascii-font",{font:"tiny",text:"git-agents"},void 0,!1,void 0,this),I("text",{children:"Sync your Claude agents directory with a remote git repo"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),I("box",{flexDirection:"column",width:40,marginTop:2,children:I("select",{focused:!0,options:[{name:"Pull",description:"Download agents from remote to local"},{name:"Push",description:"Upload local agents to remote"},{name:"Edit Config",description:"Change remote configuration"}],onSelect:(t)=>{if(t===0)e({id:"sync",mode:"pull"});else if(t===1)e({id:"sync",mode:"push"});else if(t===2)e({id:"setup"})},height:6},void 0,!1,void 0,this)},void 0,!1,void 0,this),I("box",{marginTop:1,children:I("text",{children:"\u2191\u2193 navigate Enter select Ctrl+C quit"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}import{useState as N,useEffect as Fe}from"react";import{useKeyboard as Le}from"@opentui/react";import{TextAttributes as F}from"@opentui/core";var{$:C}=globalThis.Bun;async function $(){try{let e=await C`gh --version`.quiet();return{ok:e.exitCode===0,output:e.stdout.toString()}}catch(e){return{ok:!1,error:e.message??"gh not found"}}}async function q(){try{return{ok:(await C`gh auth status`.quiet()).exitCode===0}}catch{return{ok:!1,error:"Not authenticated. Run: gh auth login"}}}async function D(e){try{return{ok:(await C`gh repo view ${e}`.quiet()).exitCode===0}}catch{return{ok:!1}}}async function E(e){try{let t=await C`gh repo create ${e} --private`.quiet();return{ok:t.exitCode===0,output:t.stdout.toString()}}catch(t){return{ok:!1,error:t.stderr?.toString()??t.message}}}async function j(e){try{let n=(await C`gh repo view ${e} --json sshUrl --jq .sshUrl`.quiet()).stdout.toString().trim();return{ok:!!n,output:n}}catch(t){return{ok:!1,error:t.message}}}async function V(e){try{return{ok:(await C`git ls-remote ${e}`.quiet()).exitCode===0}}catch{return{ok:!1,error:"Cannot reach repository"}}}async function J(e,t){try{return{ok:(await C`git clone ${e} ${t}`.quiet()).exitCode===0}}catch(n){return{ok:!1,error:n.stderr?.toString()??n.message}}}async function ee(e){try{let t=await C`git -C ${e} pull`.quiet();return{ok:t.exitCode===0,output:t.stdout.toString()}}catch(t){let n=t.stderr?.toString()??t.message??"";if(n.includes("no such ref was fetched")||n.includes("couldn't find remote ref"))return{ok:!0,output:"Remote is empty"};return{ok:!1,error:n}}}async function te(e,t){try{if(await C`git -C ${e} add -A`.quiet(),!(await C`git -C ${e} status --porcelain`.quiet()).stdout.toString().trim())return{ok:!0,output:"Nothing to commit"};return await C`git -C ${e} commit -m ${t}`.quiet(),{ok:(await C`git -C ${e} push -u origin HEAD`.quiet()).exitCode===0}}catch(n){return{ok:!1,error:n.stderr?.toString()??n.message}}}import{join as i}from"path";import{homedir as Ge}from"os";var a=Ge(),ne=[{id:"amp",name:"Amp",globalPath:i(a,".config/agents")},{id:"kimi-cli",name:"Kimi Code CLI",globalPath:i(a,".config/agents")},{id:"replit",name:"Replit",globalPath:i(a,".config/agents")},{id:"universal",name:"Universal",globalPath:i(a,".config/agents")},{id:"antigravity",name:"Antigravity",globalPath:i(a,".gemini/antigravity")},{id:"augment",name:"Augment",globalPath:i(a,".augment")},{id:"bob",name:"IBM Bob",globalPath:i(a,".bob")},{id:"claude-code",name:"Claude Code",globalPath:i(a,".claude")},{id:"openclaw",name:"OpenClaw",globalPath:i(a,".openclaw")},{id:"cline",name:"Cline",globalPath:i(a,".agents")},{id:"warp",name:"Warp",globalPath:i(a,".agents")},{id:"codebuddy",name:"CodeBuddy",globalPath:i(a,".codebuddy")},{id:"codex",name:"Codex",globalPath:i(a,".codex")},{id:"command-code",name:"Command Code",globalPath:i(a,".commandcode")},{id:"continue",name:"Continue",globalPath:i(a,".continue")},{id:"cortex",name:"Cortex Code",globalPath:i(a,".snowflake/cortex")},{id:"crush",name:"Crush",globalPath:i(a,".config/crush")},{id:"cursor",name:"Cursor",globalPath:i(a,".cursor")},{id:"deepagents",name:"Deep Agents",globalPath:i(a,".deepagents/agent")},{id:"droid",name:"Droid",globalPath:i(a,".factory")},{id:"firebender",name:"Firebender",globalPath:i(a,".firebender")},{id:"gemini-cli",name:"Gemini CLI",globalPath:i(a,".gemini")},{id:"github-copilot",name:"GitHub Copilot",globalPath:i(a,".copilot")},{id:"goose",name:"Goose",globalPath:i(a,".config/goose")},{id:"junie",name:"Junie",globalPath:i(a,".junie")},{id:"iflow-cli",name:"iFlow CLI",globalPath:i(a,".iflow")},{id:"kilo",name:"Kilo Code",globalPath:i(a,".kilocode")},{id:"kiro-cli",name:"Kiro CLI",globalPath:i(a,".kiro")},{id:"kode",name:"Kode",globalPath:i(a,".kode")},{id:"mcpjam",name:"MCPJam",globalPath:i(a,".mcpjam")},{id:"mistral-vibe",name:"Mistral Vibe",globalPath:i(a,".vibe")},{id:"mux",name:"Mux",globalPath:i(a,".mux")},{id:"opencode",name:"OpenCode",globalPath:i(a,".config/opencode")},{id:"openhands",name:"OpenHands",globalPath:i(a,".openhands")},{id:"pi",name:"Pi",globalPath:i(a,".pi/agent")},{id:"qoder",name:"Qoder",globalPath:i(a,".qoder")},{id:"qwen-code",name:"Qwen Code",globalPath:i(a,".qwen")},{id:"roo",name:"Roo Code",globalPath:i(a,".roo")},{id:"trae",name:"Trae",globalPath:i(a,".trae")},{id:"trae-cn",name:"Trae CN",globalPath:i(a,".trae-cn")},{id:"windsurf",name:"Windsurf",globalPath:i(a,".codeium/windsurf")},{id:"zencoder",name:"Zencoder",globalPath:i(a,".zencoder")},{id:"neovate",name:"Neovate",globalPath:i(a,".neovate")},{id:"pochi",name:"Pochi",globalPath:i(a,".pochi")},{id:"adal",name:"AdaL",globalPath:i(a,".adal")}];import{existsSync as Ie,readdirSync as oe,mkdirSync as Ae,cpSync as Me}from"fs";import{join as ve}from"path";function U(e){if(!Ie(e))return[];try{return oe(e,{withFileTypes:!0}).filter((t)=>t.isDirectory()).map((t)=>{let n=ve(e,t.name),o=0;try{o=oe(n).length}catch{}return{name:t.name,fileCount:o}}).sort((t,n)=>t.name.localeCompare(n.name))}catch{return[]}}function re(e,t){let n=U(e),o=U(t),m=new Map(n.map((f)=>[f.name,f])),u=new Map(o.map((f)=>[f.name,f])),c=[],d=[],h=[],b=[];for(let f of n)if(!u.has(f.name))c.push(f);else{let k=u.get(f.name);if(f.fileCount!==k.fileCount)h.push(f);else b.push(f)}for(let f of o)if(!m.has(f.name))d.push(f);return{added:c,removed:d,modified:h,unchanged:b}}function ie(e,t){Ae(t,{recursive:!0}),Me(e,t,{recursive:!0,force:!0})}async function ae(e){if(!(await e.checkGhInstalled()).ok)return{type:"gh-not-installed"};if(!(await e.checkGhAuth()).ok)return{type:"needs-auth"};return{type:"ok"}}async function se(e,t){if(!(await t.ghRepoExists(e)).ok)return{type:"not-found"};let o=await t.ghGetRepoCloneUrl(e);if(!o.ok||!o.output)return{type:"error",message:"Could not get repo URL from gh CLI."};return{type:"found",url:o.output.trim()}}async function le(e,t){let n=await t.ghCreateRepo(e);if(!n.ok)return{type:"error",message:`Failed to create repo: ${n.error??"unknown error"}`};let o=await t.ghGetRepoCloneUrl(e);if(!o.ok||!o.output)return{type:"error",message:"Could not get repo URL after creating it."};return{type:"ok",url:o.output.trim()}}async function ce(e,t,n){let o={remote:t,repoUrl:t==="git"?e:void 0};if(n.isAlreadyCloned())return n.writeConfig(o),{type:"ok",config:o};let m=await n.cloneRepo(e,"");if(!m.ok)return{type:"error",message:`Failed to clone: ${m.error??"unknown error"}`};return n.writeConfig(o),{type:"ok",config:o}}async function ge(e,t){if(!(await t.checkGitRepoExists(e)).ok)return{type:"error",message:`Cannot reach repository: ${e}`};return{type:"ok"}}async function pe(e,t,n,o){let m=await o.gitPull(n);if(!m.ok)return{type:"error",message:`Failed to pull remote: ${m.error??"unknown error"}`};let u=new Map;for(let d of t){let h=u.get(d.globalPath)??[];u.set(d.globalPath,[...h,d])}let c=[];for(let[d,h]of u){let b=Y(d),f=e==="pull"?b:d,k=e==="pull"?d:b,v=re(f,k),P=U(f),S=U(k);if(P.length===0&&S.length===0)continue;for(let y of h)c.push({def:y,diff:v,remoteCount:e==="pull"?P.length:S.length,localCount:e==="pull"?S.length:P.length})}return{type:"ok",agentDiffs:c}}async function ue(e,t,n,o){let m=new Set;try{for(let u of t){if(m.has(u.def.globalPath))continue;m.add(u.def.globalPath);let c=Y(u.def.globalPath),d=e==="pull"?c:u.def.globalPath,h=e==="pull"?u.def.globalPath:c;ie(d,h)}}catch(u){return{type:"error",message:`Failed to copy agents: ${u.message}`}}if(e==="push"){let u=await o.gitAddCommitPush(n,`sync: update agents from local (${new Date().toISOString().slice(0,10)})`);if(!u.ok)return{type:"error",message:`Failed to push: ${u.error??"unknown error"}`}}return{type:"ok",message:e==="pull"?"Pull complete! Local agents updated.":"Push complete! Remote updated."}}import{jsxDEV as s}from"@opentui/react/jsx-dev-runtime";function fe({mode:e,onBack:t}){let[n,o]=N("loading"),[m,u]=N("Fetching remote..."),[c,d]=N([]),[h,b]=N(null),[f,k]=N("");Le((p)=>{if(p.name==="escape"){if(n==="done"||n==="review")t()}});let v={gitPull:ee,gitAddCommitPush:te};Fe(()=>{async function p(){u("Pulling remote changes...");let g=await pe(e,ne,R,v);if(g.type==="error"){b(g.message),o("done");return}u("Comparing agents..."),d(g.agentDiffs),o("review")}p()},[]);async function P(p){if(!p){t();return}o("executing"),u(e==="pull"?"Copying agents from remote...":"Copying agents to remote...");let g=await ue(e,c,R,v);if(g.type==="error")b(g.message);else k(g.message),b(null);o("done")}let S=e==="pull"?"Pull":"Push";if(n==="loading"||n==="executing")return s("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:s("text",{children:m},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(n==="done")return s("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:1,children:[h?s("text",{fg:"#ff5555",children:h},void 0,!1,void 0,this):s("text",{fg:"#50fa7b",children:f},void 0,!1,void 0,this),s("text",{attributes:F.DIM,children:"Press Esc to go back"},void 0,!1,void 0,this)]},void 0,!0,void 0,this);let y=new Set,_=c.reduce((p,g)=>{if(y.has(g.def.globalPath))return p;return y.add(g.def.globalPath),p+g.remoteCount},0);y.clear();let A=c.reduce((p,g)=>{if(y.has(g.def.globalPath))return p;return y.add(g.def.globalPath),p+g.localCount},0),H=c.some((p)=>p.diff.added.length>0||p.diff.removed.length>0||p.diff.modified.length>0);return s("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:1,children:[s("ascii-font",{font:"tiny",text:S},void 0,!1,void 0,this),s("box",{flexDirection:"column",border:!0,borderStyle:"rounded",title:" Comparison ",paddingX:2,paddingY:1,width:60,gap:1,children:[s("box",{flexDirection:"row",justifyContent:"space-between",children:[s("text",{children:[s("span",{children:"Remote: "},void 0,!1,void 0,this),s("span",{fg:"#8be9fd",children:[_," skills"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),s("text",{children:[s("span",{children:"Local: "},void 0,!1,void 0,this),s("span",{fg:"#8be9fd",children:[A," skills"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),c.length===0&&s("text",{attributes:F.DIM,children:"No agents found"},void 0,!1,void 0,this),c.map((p)=>{let g=p.diff,O=g.added.length>0||g.removed.length>0||g.modified.length>0;return s("box",{flexDirection:"column",children:[s("box",{flexDirection:"row",justifyContent:"space-between",children:[s("text",{fg:"#bd93f9",children:p.def.name},void 0,!1,void 0,this),s("text",{attributes:F.DIM,children:[p.remoteCount,"\u2193 / ",p.localCount,"\u2191"]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),g.added.map((w)=>s("text",{children:[s("span",{fg:"#50fa7b",children:" + "},void 0,!1,void 0,this),s("span",{children:w.name},void 0,!1,void 0,this)]},w.name,!0,void 0,this)),g.removed.map((w)=>s("text",{children:[s("span",{fg:"#ff5555",children:" - "},void 0,!1,void 0,this),s("span",{children:w.name},void 0,!1,void 0,this)]},w.name,!0,void 0,this)),g.modified.map((w)=>s("text",{children:[s("span",{fg:"#ffb86c",children:" ~ "},void 0,!1,void 0,this),s("span",{children:w.name},void 0,!1,void 0,this)]},w.name,!0,void 0,this)),!O&&g.unchanged.length>0&&s("text",{attributes:F.DIM,children:[" ",g.unchanged.length," skill",g.unchanged.length!==1?"s":""," unchanged"]},void 0,!0,void 0,this)]},p.def.id,!0,void 0,this)})]},void 0,!0,void 0,this),s("box",{flexDirection:"column",alignItems:"center",gap:1,children:[s("text",{children:[s("span",{children:["Confirm ",e,"? "]},void 0,!0,void 0,this),s("span",{attributes:F.DIM,children:"(No is default \u2014 press Enter to cancel)"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),s("select",{focused:!0,options:[{name:"No, cancel",description:"Go back to main menu"},{name:`Yes, ${e}`,description:H?"Apply changes":"No changes to apply"}],onSelect:(p)=>P(p===1),width:30,height:4},void 0,!1,void 0,this)]},void 0,!0,void 0,this),s("text",{attributes:F.DIM,children:"Esc to cancel"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}import{useState as M,useEffect as Ue}from"react";import{useKeyboard as Ne}from"@opentui/react";import{TextAttributes as x}from"@opentui/core";import{existsSync as Te}from"fs";import{join as Oe}from"path";import{jsxDEV as r}from"@opentui/react/jsx-dev-runtime";var T="git-agents-remote";function me({existingConfig:e,onComplete:t}){let[n,o]=M("welcome"),[m,u]=M(e?.remote??"gh"),[c,d]=M(e?.repoUrl??""),[h,b]=M(""),[f,k]=M(!1),[v,P]=M(""),[S,y]=M("");Ne((l)=>{if(l.ctrl&&l.name==="c")process.exit(0);if(n==="welcome"&&l.name==="return")o("choose-remote");if(n==="gh-auth-needed"&&l.name==="return")_();if(n==="error"&&l.name==="return")o("choose-remote")});async function _(){if(o("gh-checking"),y("Checking gh auth..."),!(await q()).ok){o("gh-auth-needed");return}p()}let A={checkGhInstalled:$,checkGhAuth:q,ghRepoExists:D,ghCreateRepo:E,ghGetRepoCloneUrl:j,checkGitRepoExists:V,cloneRepo:(l,G)=>J(l,G),isAlreadyCloned:()=>Te(Oe(R,".git")),writeConfig:Q};async function H(){o("gh-checking"),y("Checking gh CLI...");let l=await ae(A);if(l.type==="gh-not-installed"){P("GitHub CLI (gh) is not installed. Install it from https://cli.github.com and try again."),o("error");return}if(l.type==="needs-auth"){o("gh-auth-needed");return}p()}async function p(){o("gh-checking"),y("Checking for git-agents repo...");let l=await se(T,A);if(l.type==="error"){P(l.message),o("error");return}if(l.type==="not-found"){k(!1),o("gh-repo-check");return}k(!0),b(l.url),o("gh-confirm")}async function g(){o("gh-checking"),y(`Creating private repo "${T}"...`);let l=await le(T,A);if(l.type==="error"){P(l.message),o("error");return}b(l.url),o("gh-confirm")}async function O(l,G){o("cloning"),y(`Cloning ${l}...`);let W=await ce(l,G,{...A,cloneRepo:(Ce)=>J(Ce,R)});if(W.type==="error"){P(W.message),o("error");return}o("done")}async function w(l){o("git-url-checking"),y("Validating repository...");let G=await ge(l,A);if(G.type==="error"){P(`${G.message}
|
|
4
|
+
Make sure the URL is correct and you have access.`),o("error");return}await O(l,"git")}if(Ue(()=>{if(n==="done")t({remote:m,repoUrl:m==="git"?h||c:void 0})},[n]),n==="welcome")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("ascii-font",{font:"tiny",text:"git-agents"},void 0,!1,void 0,this),r("box",{flexDirection:"column",alignItems:"center",gap:1,width:60,children:[r("text",{children:"Sync your Claude agents skills with a remote git repo."},void 0,!1,void 0,this),r("text",{attributes:x.DIM,children:"Keeps ~/.agents in sync across machines using git."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),r("text",{children:[r("span",{attributes:x.DIM,children:"Press "},void 0,!1,void 0,this),r("span",{children:"Enter"},void 0,!1,void 0,this),r("span",{attributes:x.DIM,children:" to start setup"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this);if(n==="choose-remote")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("text",{children:"Choose how to connect your remote:"},void 0,!1,void 0,this),r("select",{focused:!0,options:[{name:"GitHub CLI (gh)",description:"Auto-create and manage a private GitHub repo"},{name:"Custom Git Repo",description:"Use any existing remote git repository"}],selectedIndex:m==="gh"?0:1,onSelect:(l)=>{let G=l===0?"gh":"git";if(u(G),G==="gh")H();else o("git-url-input")},width:50,height:4},void 0,!1,void 0,this),r("text",{attributes:x.DIM,children:"\u2191\u2193 navigate Enter select"},void 0,!1,void 0,this)]},void 0,!0,void 0,this);if(n==="gh-checking")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:r("text",{children:S},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(n==="gh-auth-needed")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("text",{fg:"#ffb86c",children:"Not authenticated with GitHub CLI."},void 0,!1,void 0,this),r("box",{border:!0,borderStyle:"rounded",paddingX:2,paddingY:1,children:r("text",{children:[r("span",{attributes:x.DIM,children:"Run in another terminal: "},void 0,!1,void 0,this),r("span",{fg:"#50fa7b",children:"gh auth login"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),r("text",{children:[r("span",{attributes:x.DIM,children:"Press "},void 0,!1,void 0,this),r("span",{children:"Enter"},void 0,!1,void 0,this),r("span",{attributes:x.DIM,children:" once authenticated to continue"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this);if(n==="gh-repo-check")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("text",{children:["Repo ",r("span",{fg:"#8be9fd",children:['"',T,'"']},void 0,!0,void 0,this)," does not exist on your GitHub account."]},void 0,!0,void 0,this),r("select",{focused:!0,options:[{name:`Create private repo "${T}"`,description:"Recommended"},{name:"Cancel",description:"Go back to remote selection"}],onSelect:(l)=>{if(l===0)g();else o("choose-remote")},width:50,height:4},void 0,!1,void 0,this)]},void 0,!0,void 0,this);if(n==="gh-confirm")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("text",{fg:"#50fa7b",children:f?"Found existing repo!":"Repository ready!"},void 0,!1,void 0,this),r("box",{border:!0,borderStyle:"rounded",paddingX:2,paddingY:1,flexDirection:"column",gap:1,children:[r("text",{children:[r("span",{attributes:x.DIM,children:"Repo: "},void 0,!1,void 0,this),r("span",{fg:"#8be9fd",children:h},void 0,!1,void 0,this)]},void 0,!0,void 0,this),r("text",{children:[r("span",{attributes:x.DIM,children:"Will clone to: "},void 0,!1,void 0,this),r("span",{children:"~/.git-agents"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this),r("select",{focused:!0,options:[{name:"Continue",description:"Clone repo and save config"},{name:"Cancel",description:"Go back to remote selection"}],onSelect:(l)=>{if(l===0)O(h,"gh");else o("choose-remote")},width:40,height:4},void 0,!1,void 0,this)]},void 0,!0,void 0,this);if(n==="git-url-input")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("text",{children:"Enter the Git repository URL:"},void 0,!1,void 0,this),r("box",{border:!0,borderStyle:"rounded",paddingX:2,paddingY:1,width:60,children:r("input",{focused:!0,placeholder:"git@github.com:user/repo.git",value:c,onInput:d,onSubmit:()=>{if(c.trim())w(c.trim())},flexGrow:1},void 0,!1,void 0,this)},void 0,!1,void 0,this),r("text",{attributes:x.DIM,children:"Enter to confirm Esc to go back"},void 0,!1,void 0,this)]},void 0,!0,void 0,this);if(n==="git-url-checking")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:r("text",{children:S},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(n==="cloning")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,children:r("text",{children:S},void 0,!1,void 0,this)},void 0,!1,void 0,this);if(n==="error")return r("box",{flexDirection:"column",alignItems:"center",justifyContent:"center",flexGrow:1,gap:2,children:[r("text",{fg:"#ff5555",children:v},void 0,!1,void 0,this),r("text",{children:[r("span",{attributes:x.DIM,children:"Press "},void 0,!1,void 0,this),r("span",{children:"Enter"},void 0,!1,void 0,this),r("span",{attributes:x.DIM,children:" to try again"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this);return null}import{jsxDEV as X}from"@opentui/react/jsx-dev-runtime";function he({initialScreen:e,initialConfig:t}){let[n,o]=de(e),[m,u]=de(t);if(_e((c)=>{if(c.ctrl&&c.name==="c")process.exit(0)}),n.id==="setup")return X(me,{existingConfig:n.existingConfig??m,onComplete:(c)=>{u(c),o({id:"main"})}},void 0,!1,void 0,this);if(n.id==="sync")return X(fe,{mode:n.mode,onBack:()=>o({id:"main"})},void 0,!1,void 0,this);return X(Z,{onNavigate:(c)=>o(c)},void 0,!1,void 0,this)}import{jsxDEV as Je}from"@opentui/react/jsx-dev-runtime";var Be=process.argv.slice(2),ye=Be[0],be=z(),Ye=!be?{id:"setup"}:ye==="pull"?{id:"sync",mode:"pull"}:ye==="push"?{id:"sync",mode:"push"}:{id:"main"},qe=await He({exitOnCtrlC:!1});Ke(qe).render(Je(he,{initialScreen:Ye,initialConfig:be??void 0},void 0,!1,void 0,this));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-agents",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "TUI CLI to sync your AI agents and skills across machines using git",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"dev": "bun run --watch src/index.tsx",
|
|
35
35
|
"start": "bun src/index.tsx",
|
|
36
36
|
"build": "bun scripts/build.ts",
|
|
37
|
-
"prepublishOnly": "bun run build"
|
|
37
|
+
"prepublishOnly": "bun run build",
|
|
38
|
+
"test": "bun test"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@opentui/core": "^0.1.95",
|