@nalvietnam/avatar-cli 3.0.0-beta.5 → 3.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +60 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @nalvietnam/avatar-cli — built with tsup
|
|
2
|
-
var
|
|
3
|
-
`,n)}async function
|
|
2
|
+
var Go=Object.defineProperty;var _=(t,e)=>()=>(t&&(e=t(t=0)),e);var dt=(t,e)=>{for(var n in e)Go(t,n,{get:e[n],enumerable:!0})};var Hn={};dt(Hn,{computeFileSha256:()=>qo,copyDirRecursive:()=>Pe,downloadFile:()=>Ee,ensureDir:()=>v,extractTarballToDir:()=>$e,pathExists:()=>m,readJson:()=>y,readText:()=>N,relativeFromCwd:()=>Vo,removeRecursive:()=>Q,writeJsonAtomic:()=>A,writeTextAtomic:()=>X});import{spawn as Lo,spawnSync as jo}from"child_process";import{createHash as Uo}from"crypto";import{constants as Do,promises as I,createReadStream as Ho}from"fs";import{dirname as Dn,join as Un,relative as Ko}from"path";import{Readable as Fo}from"stream";async function m(t){try{return await I.access(t,Do.F_OK),!0}catch{return!1}}async function v(t){await I.mkdir(t,{recursive:!0})}async function N(t){return await I.readFile(t,"utf8")}async function y(t){return JSON.parse(await N(t))}async function X(t,e,n){await v(Dn(t));let r=`${t}.tmp-${process.pid}-${Date.now()}`;await I.writeFile(r,e,"utf8"),n!==void 0&&await I.chmod(r,n),await I.rename(r,t)}async function A(t,e,n){await X(t,`${JSON.stringify(e,null,2)}
|
|
3
|
+
`,n)}async function Pe(t,e,n=[]){await v(e);let r=await I.readdir(t,{withFileTypes:!0});for(let i of r){if(n.includes(i.name))continue;let s=Un(t,i.name),a=Un(e,i.name);if(i.isDirectory())await Pe(s,a,n);else if(i.isSymbolicLink()){let c=await I.readlink(s);await I.symlink(c,a)}else await I.copyFile(s,a)}}async function Q(t){await I.rm(t,{recursive:!0,force:!0})}function Vo(t){return Ko(process.cwd(),t)}async function Ee(t,e,n=Bo){let r=new AbortController,i=setTimeout(()=>r.abort(),n);await v(Dn(e));let{createWriteStream:s}=await import("fs");try{let a=await fetch(t,{signal:r.signal});if(!a.ok)throw new Error(`Download fail (${a.status}) t\u1EEB ${t.slice(0,80)}...`);if(!a.body)throw new Error("Response kh\xF4ng c\xF3 body \u0111\u1EC3 t\u1EA3i.");let c=s(e);await new Promise((l,u)=>{let d=Fo.fromWeb(a.body);d.on("error",u),c.on("error",u),c.on("finish",()=>l()),d.pipe(c)})}catch(a){throw await I.rm(e,{force:!0}).catch(()=>{}),a instanceof Error&&a.name==="AbortError"?new Error(`T\u1EA3i file qu\xE1 ${n/1e3}s (signed URL c\xF3 th\u1EC3 \u0111\xE3 h\u1EBFt h\u1EA1n).`):a}finally{clearTimeout(i)}}function Wo(){return Ht!==null||(Ht=jo("tar",["--version"],{stdio:"ignore"}).status===0),Ht}async function $e(t,e){if(!Wo())throw new Error("Kh\xF4ng t\xECm th\u1EA5y l\u1EC7nh `tar` tr\xEAn h\u1EC7 th\u1ED1ng. C\u1EA7n \u0111\u1EC3 gi\u1EA3i n\xE9n pack.\n Windows: c\xE0i qua WSL2, Git Bash, ho\u1EB7c MSYS2.\n macOS/Linux: tar th\u01B0\u1EDDng c\xF3 s\u1EB5n \u2014 ki\u1EC3m tra PATH.");await v(e),await new Promise((n,r)=>{let i=Lo("tar",["-xzf",t,"-C",e],{stdio:["ignore","ignore","pipe"]}),s="";i.stderr.on("data",a=>{s+=a.toString()}),i.on("error",r),i.on("close",a=>{a===0?n():r(new Error(`tar extract exit ${a}: ${s.trim()}`))})})}async function qo(t){return await new Promise((e,n)=>{let r=Uo("sha256"),i=Ho(t);i.on("data",s=>r.update(s)),i.on("end",()=>e(r.digest("hex"))),i.on("error",n)})}var Bo,Ht,k=_(()=>{"use strict";Bo=3e5;Ht=null});var Kn={};dt(Kn,{addSubmodule:()=>Xo,checkoutBranchHeadInSubmodule:()=>Zo,checkoutTagInSubmodule:()=>Qo,currentBranch:()=>Yo,currentCommitSha:()=>ns,git:()=>$,isGitRepo:()=>Jo,listTags:()=>ts,tagAtHead:()=>es,workingTreeIsDirty:()=>rs});import{join as Re}from"path";import{simpleGit as zo}from"simple-git";function $(t=process.cwd()){return zo({baseDir:t,binary:"git"})}async function Jo(t=process.cwd()){return await m(Re(t,".git"))}async function Yo(t=process.cwd()){return(await $(t).revparse(["--abbrev-ref","HEAD"])).trim()}async function Xo(t,e,n=process.cwd()){await $(n).subModule(["add",t,e])}async function Qo(t,e,n=process.cwd()){let r=Re(n,t);await $(r).fetch(["--tags"]),await $(r).checkout(e)}async function Zo(t,e,n=process.cwd()){let r=Re(n,t);await $(r).fetch(["origin"]),await $(r).checkout(["-B",e,`origin/${e}`])}async function ts(t=process.cwd()){return(await $(t).tags()).all}async function es(t=process.cwd()){try{return(await $(t).raw(["describe","--tags","--exact-match","HEAD"])).trim()||null}catch{return null}}async function ns(t=process.cwd()){return(await $(t).revparse(["HEAD"])).trim()}async function rs(t=process.cwd()){return!(await $(t).status()).isClean()}var _e=_(()=>{"use strict";k()});function Fn(t,e){return t.replace(is,(n,r)=>{let i=e[r];return i===void 0?n:String(i)})}var is,Vn=_(()=>{"use strict";is=/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g});import{existsSync as os}from"fs";import{dirname as Bn,join as At}from"path";import{fileURLToPath as ss}from"url";function us(t){let e=t;for(;;){if(os(At(e,"package.json")))return e;let n=Bn(e);if(n===e)throw new Error(`Cannot locate package root from ${t}`);e=n}}async function ps(t){return await N(At(cs,`${t}.tpl`))}async function Ie(t,e){let n=await ps(t);return Fn(n,e)}async function qn(t){return await N(At(ls,`${t}.sh.tpl`))}var as,Wn,cs,ls,zn=_(()=>{"use strict";k();Vn();as=Bn(ss(import.meta.url)),Wn=us(as),cs=At(Wn,"src","templates"),ls=At(Wn,"src","hooks")});var Qn={};dt(Qn,{AVATAR_MANAGED_PATHS:()=>Jn,backupIfExists:()=>Yn,createClaudeDirTree:()=>Ne,installGitHook:()=>je,writeClaudeGitignore:()=>Le,writeProjectKnowledgeFiles:()=>Oe,writeProjectSettings:()=>Ge,writeRootClaudeMd:()=>Me});import{promises as ms}from"fs";import{join as U}from"path";async function Yn(t){if(!await m(t))return null;let e=new Date().toISOString().replace(/[:.]/g,"-"),n=`${t}.avatar-backup-${e}`,r=n,i=1;for(;await m(r);)if(r=`${n}-${i}`,i++,i>5)throw new Error(`Could not find free backup name for ${t}`);return await ms.rename(t,r),r}async function Xn(t,e,n){let r=await Yn(t);return await X(t,e,n),r}async function Ne(t){let e=U(t,".claude");await v(e);for(let n of ds){let r=U(e,n);await v(r),await X(U(r,".gitkeep"),"")}}async function Oe(t,e){return[]}async function Me(t,e){let n=await Ie("CLAUDE.md",e);return await Xn(U(t,"CLAUDE.md"),n)}async function Ge(t,e){let n=await Ie("settings.json",e);return await Xn(U(t,".claude","settings.json"),n)}async function Le(t){let e=U(t,".claude",".gitignore");await X(e,gs)}async function je(t,e){let n=await qn(e),r=U(t,"hooks");await v(r);let i=U(r,e);await X(i,n,493)}var Jn,ds,gs,St=_(()=>{"use strict";k();zn();Jn=[".claude","CLAUDE.md"];ds=["state","_pending","_backup"];gs=`# Avatar \u2014 b\u1EA3o v\u1EC7 file nh\u1EA1y c\u1EA3m trong .claude/.
|
|
4
4
|
# Workspace root KH\xD4NG git; file n\xE0y ph\xF2ng tr\u01B0\u1EDDng h\u1EE3p .claude/ b\u1ECB track nh\u1EA7m.
|
|
5
5
|
settings.json
|
|
6
6
|
settings.json.backup-*
|
|
@@ -9,61 +9,62 @@ settings.json.backup-*
|
|
|
9
9
|
_pending/
|
|
10
10
|
_backup/
|
|
11
11
|
state/session-*.json
|
|
12
|
-
`});import{join as
|
|
13
|
-
`),success:t=>process.stdout.write(`${
|
|
14
|
-
`),warn:t=>process.stdout.write(`${
|
|
15
|
-
`),error:t=>process.stderr.write(`${
|
|
16
|
-
`),dim:t=>process.stdout.write(`${
|
|
12
|
+
`});import{join as fs}from"path";function Zn(t){return fs(t,hs)}async function tr(t){let e=Zn(t);if(!await m(e))return[];try{let n=await y(e);return Array.isArray(n.repos)?n.repos:[]}catch{return[]}}async function er(t,e){let r=(await tr(t)).filter(i=>i.name!==e.name);r.push(e),await A(Zn(t),{repos:r})}async function nr(t,e){return(await tr(t)).some(r=>r.name===e)}var hs,rr=_(()=>{"use strict";k();hs=".claude/repos.json"});var or={};dt(or,{cloneCodeRepoIntoSrc:()=>He,folderDirtyStatus:()=>Ke,inferRepoNameFromUrl:()=>De,readFolderRemoteUrl:()=>Fe,validateRepoName:()=>ir});import{join as Ue}from"path";function ir(t){return!t||t.trim().length===0?"T\xEAn repo kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng.":/[/\\]/.test(t)||t.includes("..")?"T\xEAn repo kh\xF4ng \u0111\u01B0\u1EE3c ch\u1EE9a '/', '\\', '..'.":t==="pack"?"T\xEAn 'pack' b\u1ECB gi\u1EEF cho team-ai-pack.":null}function De(t){return(t.trim().replace(/\/+$/,"").split(/[/:]/).pop()??"").replace(/\.git$/,"")||"repo"}async function He(t){let e=ir(t.name);if(e)throw new Error(e);if(await nr(t.workspaceRoot,t.name))throw new Error(`Repo "${t.name}" \u0111\xE3 c\xF3 trong workspace. Ch\u1ECDn t\xEAn kh\xE1c ho\u1EB7c g\u1EE1 tr\u01B0\u1EDBc.`);let n=Ue(t.workspaceRoot,"src");await v(n);let r=Ue(n,t.name);if(await m(r))throw new Error(`Th\u01B0 m\u1EE5c src/${t.name} \u0111\xE3 t\u1ED3n t\u1EA1i. Ch\u1ECDn t\xEAn kh\xE1c.`);await $(n).clone(t.url,t.name);let i=Ue(r,".git");return await je(i,"pre-push").catch(()=>{}),await er(t.workspaceRoot,{name:t.name,path:`src/${t.name}`,url:t.url,addedAt:new Date().toISOString()}),{name:t.name,path:r,url:t.url}}async function Ke(t){try{let e=await $(t).status();return e.isClean()?"":e.files.map(n=>`${n.path}`).join(", ")}catch{return""}}async function Fe(t){try{let n=(await $(t).getRemotes(!0)).find(r=>r.name==="origin");return n?.refs.fetch??n?.refs.push??null}catch{return null}}var Kt=_(()=>{"use strict";k();_e();St();rr()});import p from"chalk";import ws from"ora";function gt(t){return ws({text:t,spinner:"dots",isEnabled:process.stdout.isTTY??!1}).start()}function Tt(t){let e=Date.now(),n=gt(`${t} (0:00)`),r=()=>{let s=Math.floor((Date.now()-e)/1e3),a=Math.floor(s/60),c=s%60;return`${a}:${String(c).padStart(2,"0")}`},i=setInterval(()=>{n.text=`${t} (${r()})`},1e3);return{succeed:s=>{clearInterval(i),n.succeed(`${s} (${r()})`)},fail:s=>{clearInterval(i),n.fail(`${s} (${r()})`)},stop:()=>{clearInterval(i),n.stop()}}}var o,f=_(()=>{"use strict";o={info:t=>process.stdout.write(`${p.blue("\u2139")} ${t}
|
|
13
|
+
`),success:t=>process.stdout.write(`${p.green("\u2713")} ${t}
|
|
14
|
+
`),warn:t=>process.stdout.write(`${p.yellow("\u26A0")} ${t}
|
|
15
|
+
`),error:t=>process.stderr.write(`${p.red("\u2717")} ${t}
|
|
16
|
+
`),dim:t=>process.stdout.write(`${p.dim(t)}
|
|
17
17
|
`),plain:t=>process.stdout.write(`${t}
|
|
18
|
-
`)}});import{spawnSync as
|
|
19
|
-
Ki\u1EC3m tra: \u0111ang login \u0111\xFAng GitHub account ch\u01B0a? (gh auth status)`);break;case"network":
|
|
18
|
+
`)}});import{spawnSync as Ss}from"child_process";function Ft(){let t=Ss("gh",["api","user","--jq",".login"],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});if(t.status!==0)throw new Error(`Kh\xF4ng l\u1EA5y \u0111\u01B0\u1EE3c GitHub username: ${t.stderr?.trim()}`);return t.stdout.trim()}var We=_(()=>{"use strict"});import{spawnSync as Hs}from"child_process";function fr(t){let e=`${t.org}/${t.name}`,n=["repo","create",e,`--${t.visibility}`,"--source",t.folder,"--remote","origin","--push"],r=Hs("gh",n,{stdio:"inherit"});if(r.status!==0)throw r.status===1?new qe(e):new Error(`gh repo create th\u1EA5t b\u1EA1i (exit ${r.status})`);return{sshUrl:`git@github.com:${e}.git`,httpsUrl:`https://github.com/${e}.git`}}var qe,hr=_(()=>{"use strict";qe=class extends Error{constructor(e){super(`Repo "${e}" \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. \u0110\u1ED5i t\xEAn ho\u1EB7c x\xF3a repo c\u0169.`),this.name="RepoAlreadyExistsError"}}});function wr(t){if(!Ks.test(t))throw new ze(t)}function kr(t){if(t!=="private"&&t!=="public")throw new Error(`Visibility ph\u1EA3i l\xE0 "private" ho\u1EB7c "public", nh\u1EADn: "${t}"`)}var Ks,ze,yr=_(()=>{"use strict";Ks=/^[a-zA-Z0-9._-]{1,100}$/,ze=class extends Error{constructor(e){super(`T\xEAn repo "${e}" kh\xF4ng h\u1EE3p l\u1EC7. Ch\u1EC9 d\xF9ng ch\u1EEF/s\u1ED1/d\u1EA5u ch\u1EA5m/g\u1EA1ch/underscore, d\xE0i 1-100 k\xFD t\u1EF1.`),this.name="InvalidRepoNameError"}}});var br={};dt(br,{createGithubRemoteFromFolder:()=>Je});function Je(t){wr(t.name),kr(t.visibility);let e=t.org??Ft();o.info(`T\u1EA1o GitHub repo ${e}/${t.name} (${t.visibility})...`);let n=fr({folder:t.folder,org:e,name:t.name,visibility:t.visibility});return o.success(`\u0110\xE3 t\u1EA1o: ${n.sshUrl}`),n}var Ye=_(()=>{"use strict";hr();We();f();yr()});var ln={};dt(ln,{HOSTED_DOMAIN:()=>Jt,SCOPES:()=>Kr,buildUserConfig:()=>sn,buildVerificationUrl:()=>cn,decodeIdToken:()=>rn,pollForToken:()=>nn,refreshAccessToken:()=>La,requestDeviceCode:()=>en,revokeToken:()=>an,verifyHostedDomain:()=>on,verifyIdTokenClaims:()=>Vr});async function en(){let t=new URLSearchParams({client_id:Yt,scope:Kr.join(" ")}),e=await fetch(Na,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:t});if(!e.ok){let n=await e.text();throw new Error(`Device code request failed (${e.status}): ${n}`)}return await e.json()}async function nn(t){let e=new URLSearchParams({client_id:Yt,client_secret:Hr,device_code:t,grant_type:"urn:ietf:params:oauth:grant-type:device_code"}),n=await fetch(Fr,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:e});if(n.ok)return await n.json();let r="";try{r=(await n.json()).error??""}catch{r=""}if(r==="authorization_pending"||r==="slow_down")return null;throw r==="access_denied"?new Error("User t\u1EEB ch\u1ED1i quy\u1EC1n truy c\u1EADp"):r==="expired_token"?new Error("H\u1EBFt h\u1EA1n ch\u1EDD (5 ph\xFAt). Ch\u1EA1y l\u1EA1i 'avatar login'."):new Error(`OAuth token endpoint tr\u1EA3 l\u1ED7i: ${r||n.status}`)}function rn(t){let e=t.split(".");if(e.length!==3)throw new Error("id_token format kh\xF4ng h\u1EE3p l\u1EC7");let n=e[1];if(!n)throw new Error("id_token thi\u1EBFu payload");let r=n.replace(/-/g,"+").replace(/_/g,"/"),i=Buffer.from(r,"base64").toString("utf8");return JSON.parse(i)}function Vr(t){if(!Ma.has(t.iss))throw new Error(`id_token issuer kh\xF4ng h\u1EE3p l\u1EC7: ${t.iss} (expect: accounts.google.com)`);if(t.aud!==Yt)throw new Error("id_token audience kh\xF4ng kh\u1EDBp client ID Avatar. Token c\xF3 th\u1EC3 \u0111\u01B0\u1EE3c sign cho app kh\xE1c.");let e=Math.floor(Date.now()/1e3);if(t.exp+Ga<e){let n=e-t.exp;throw new Error(`id_token \u0111\xE3 h\u1EBFt h\u1EA1n ${n}s tr\u01B0\u1EDBc. Login l\u1EA1i \u0111\u1EC3 l\u1EA5y token m\u1EDBi.`)}if(t.hd!==Jt)throw new Error(`Email kh\xF4ng thu\u1ED9c workspace NAL (y\xEAu c\u1EA7u @${Jt}). Nh\u1EADn: ${t.email}`);if(!t.email_verified)throw new Error("Email ch\u01B0a \u0111\u01B0\u1EE3c Google verify")}function sn(t,e){let n=new Date(Date.now()+t.expires_in*1e3).toISOString();return{email:e.email,name:e.name??e.email,access_token:t.access_token,refresh_token:t.refresh_token,expires_at:n,id_token:t.id_token}}async function La(t){let e=new URLSearchParams({client_id:Yt,client_secret:Hr,refresh_token:t,grant_type:"refresh_token"}),n=await fetch(Fr,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:e});if(!n.ok){let r=await n.text();throw new Error(`Refresh token failed (${n.status}): ${r}`)}return await n.json()}async function an(t){let e=new URLSearchParams({token:t});await fetch(Oa,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:e}).catch(()=>{})}function cn(t){let e=new URL(t.verification_url);return e.searchParams.set("user_code",t.user_code),e.searchParams.set("hd",Jt),e.toString()}var Yt,Hr,Jt,Kr,Na,Fr,Oa,Ma,Ga,on,Xt=_(()=>{"use strict";Yt="1014766441755-i4jimivh5rd7vt8phuhmepmt45sovtph.apps.googleusercontent.com",Hr="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",Jt="nal.vn",Kr=["openid","email","profile"],Na="https://oauth2.googleapis.com/device/code",Fr="https://oauth2.googleapis.com/token",Oa="https://oauth2.googleapis.com/revoke";Ma=new Set(["https://accounts.google.com","accounts.google.com"]),Ga=60;on=Vr});import{Command as Lu}from"commander";Kt();import{resolve as Lr}from"path";import{confirm as zt,input as _t,select as tn}from"@inquirer/prompts";Kt();k();f();import{join as vs}from"path";import{confirm as xs,input as Be,select as Cs}from"@inquirer/prompts";f();import{spawnSync as ks}from"child_process";function ft(){o.info("Kh\u1EDFi \u0111\u1ED9ng \u0111\u0103ng nh\u1EADp GitHub qua gh CLI (browser s\u1EBD m\u1EDF)...");let t=ks("gh",["auth","login","--hostname","github.com","--web","--git-protocol","ssh"],{stdio:"inherit"});if(t.status!==0)throw new Error(`gh auth login th\u1EA5t b\u1EA1i (exit ${t.status}). Th\u1EED 'gh auth login' tay.`);o.success("\u0110\xE3 \u0111\u0103ng nh\u1EADp GitHub")}import{spawnSync as ys}from"child_process";var Ve=5e3;function bs(t){let e=t.toLowerCase();return e.includes("authentication")||e.includes("could not read username")||e.includes("permission denied")||e.includes("403")||e.includes("access denied")||e.includes("repository not found")?"no-access":e.includes("404")||e.includes("does not exist")?"not-found":e.includes("could not resolve host")||e.includes("network")||e.includes("connection refused")||e.includes("connection timed out")?"network":"unknown"}function ht(t){let e=ys("git",["ls-remote","--exit-code",t,"HEAD"],{encoding:"utf8",timeout:Ve,stdio:["ignore","pipe","pipe"]});if(e.error){let i=e.error;return i.code==="ENOENT"?{ok:!1,reason:"network",detail:"git binary kh\xF4ng t\xECm th\u1EA5y \u2014 c\xE0i git r\u1ED3i retry"}:i.code==="ETIMEDOUT"?{ok:!1,reason:"timeout",detail:`git ls-remote > ${Ve/1e3}s`}:{ok:!1,reason:"unknown",detail:i.message.slice(0,300)}}if(e.status===0)return{ok:!0};if(e.signal==="SIGTERM")return{ok:!1,reason:"timeout",detail:`git ls-remote > ${Ve/1e3}s`};let n=(e.stderr||"").trim();return{ok:!1,reason:bs(n),detail:n.slice(0,300)}}function As(t,e,n){switch(o.warn(`\u26D4 Kh\xF4ng clone \u0111\u01B0\u1EE3c ${t}`),e){case"not-found":o.dim(" \u2192 Repo kh\xF4ng t\u1ED3n t\u1EA1i (sai URL, ho\u1EB7c repo ch\u01B0a \u0111\u01B0\u1EE3c t\u1EA1o tr\xEAn GitHub).");break;case"no-access":o.dim(` \u2192 Kh\xF4ng c\xF3 quy\u1EC1n truy c\u1EADp (repo private + sai account), HO\u1EB6C repo kh\xF4ng t\u1ED3n t\u1EA1i.
|
|
19
|
+
Ki\u1EC3m tra: \u0111ang login \u0111\xFAng GitHub account ch\u01B0a? (gh auth status)`);break;case"network":o.dim(" \u2192 L\u1ED7i m\u1EA1ng (m\u1EA5t k\u1EBFt n\u1ED1i / DNS / timeout).");break;default:o.dim(` \u2192 L\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh${n?`: ${n}`:""}.`)}}function sr(){o.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");try{ft()}catch(t){o.warn(`${t instanceof Error?t.message:t}. C\xF3 th\u1EC3 ch\u1EA1y 'gh auth login' tay r\u1ED3i retry.`)}}async function ar(t){let e=[{name:"Th\u1EED l\u1EA1i (retry)",value:"retry"},{name:"Nh\u1EADp URL repo kh\xE1c",value:"new-url"}];return t==="no-access"&&e.push({name:"Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",value:"switch-account"}),e.push({name:"B\u1ECF qua repo n\xE0y",value:"skip"}),await Cs({message:"C\xE1ch x\u1EED l\xFD?",choices:e})}async function cr(t){let e=t.url,n=t.name;for(;;){let r=ht(e);if(!r.ok){As(e,r.reason??"unknown",r.detail);let i=await ar(r.reason??"unknown");if(i==="skip")return o.dim(`B\u1ECF qua repo ${n}.`),{cloned:null,skipped:!0};if(i==="switch-account"){sr();continue}if(i==="new-url"){if(e=await Be({message:"URL repo kh\xE1c:",validate:a=>a.trim().length>0?!0:"URL b\u1EAFt bu\u1ED9c"}),await xs({message:`\u0110\u1ED5i t\xEAn th\u01B0 m\u1EE5c trong src/ theo URL m\u1EDBi? (hi\u1EC7n: ${n})`,default:!1})){let{inferRepoNameFromUrl:a}=await Promise.resolve().then(()=>(Kt(),or));n=await Be({message:"T\xEAn th\u01B0 m\u1EE5c:",default:a(e)})}continue}continue}try{return{cloned:await He({workspaceRoot:t.workspaceRoot,url:e,name:n}),skipped:!1}}catch(i){let s=i instanceof Error?i.message:String(i);o.warn(`Clone fail: ${s}`),await Q(vs(t.workspaceRoot,"src",n)).catch(()=>{});let a=await ar("unknown");if(a==="skip")return{cloned:null,skipped:!0};if(a==="switch-account"){sr();continue}if(a==="new-url"){e=await Be({message:"URL repo kh\xE1c:",validate:c=>c.trim().length>0?!0:"URL b\u1EAFt bu\u1ED9c"});continue}}}}import{input as $s,select as Rs}from"@inquirer/prompts";We();f();import{spawnSync as Ts}from"child_process";var Pt=class extends Error{reason;constructor(e,n){super(n),this.name="RepoCreateError",this.reason=e}};function Ps(t){let e=t.toLowerCase();return e.includes("name already exists")||e.includes("already exists")?"name-exists":e.includes("403")||e.includes("permission")||e.includes("not have access")||e.includes("resource not accessible")?"no-permission":e.includes("could not resolve")||e.includes("network")||e.includes("timeout")?"network":"unknown"}async function lr(t,e,n){let i=`${n??Ft()}/${t}`;o.info(`T\u1EA1o GitHub repo r\u1ED7ng ${i} (${e})...`);let s=Ts("gh",["repo","create",i,`--${e}`,"--add-readme"],{stdio:["ignore","pipe","pipe"],encoding:"utf8"});if(s.status!==0){let a=(s.stderr||"").trim();throw a&&process.stderr.write(`${a}
|
|
20
|
+
`),new Pt(Ps(a),`T\u1EA1o repo ${i} th\u1EA5t b\u1EA1i.`)}return o.success(`\u0110\xE3 t\u1EA1o: git@github.com:${i}.git`),`git@github.com:${i}.git`}f();import{input as Gp,select as Es}from"@inquirer/prompts";var x=class extends Error{constructor(e){super(e),this.name="UserAbortedRecoveryError"}};async function O(t){o.warn(`${t.taskName} th\u1EA5t b\u1EA1i: ${t.reason}`),t.hint&&o.info(t.hint);let e=[{name:"Th\u1EED l\u1EA1i (Retry)",value:"retry"}];return t.allowSkip&&e.push({name:"B\u1ECF qua b\u01B0\u1EDBc n\xE0y v\xE0 ti\u1EBFp t\u1EE5c (Skip)",value:"skip"}),e.push({name:"T\u1EA1m ng\u01B0ng init \u2014 ch\u1EA1y l\u1EA1i sau (Abort)",value:"abort"}),await Es({message:"C\xE1ch x\u1EED l\xFD?",choices:e})}f();function _s(t){switch(t){case"name-exists":o.warn("\u26D4 Repo t\xEAn n\xE0y \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub."),o.dim(" \u2192 \u0110\u1ED5i t\xEAn kh\xE1c, ho\u1EB7c d\xF9ng account/org kh\xE1c.");break;case"no-permission":o.warn("\u26D4 Kh\xF4ng c\xF3 quy\u1EC1n t\u1EA1o repo (sai account / org)."),o.dim(" \u2192 Ki\u1EC3m tra: \u0111ang login \u0111\xFAng GitHub account ch\u01B0a? (gh auth status)");break;case"network":o.warn("\u26D4 L\u1ED7i m\u1EA1ng khi t\u1EA1o repo.");break;default:o.warn("\u26D4 T\u1EA1o repo th\u1EA5t b\u1EA1i (l\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh)."),o.dim(" \u2192 Ki\u1EC3m tra gh auth status + th\u1EED l\u1EA1i.")}}async function Is(t){let e=[];return t==="name-exists"&&e.push({name:"\u0110\u1ED5i t\xEAn repo kh\xE1c",value:"rename"}),(t==="no-permission"||t==="unknown")&&e.push({name:"Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",value:"switch-account"}),e.push({name:"Th\u1EED l\u1EA1i (retry)",value:"retry"}),e.push({name:"H\u1EE7y",value:"abort"}),await Rs({message:"C\xE1ch x\u1EED l\xFD?",choices:e})}async function ur(t,e){let n=t;for(;;)try{return await lr(n,e)}catch(r){let i=r instanceof Pt?r.reason:"unknown",s=await mr(i,n);if(s.action==="retry")continue;n=s.newName}}async function pr(t,e){let n=t;for(;;)try{return e(n)}catch(r){let i=r instanceof Error&&r.name==="RepoAlreadyExistsError"?"name-exists":"unknown",s=await mr(i,n);if(s.action==="retry")continue;n=s.newName}}async function mr(t,e){_s(t);let n=await Is(t);if(n==="abort")throw new x("User h\u1EE7y t\u1EA1i b\u01B0\u1EDBc t\u1EA1o repo GitHub.");return n==="switch-account"?(ft(),{action:"retry",newName:e}):n==="rename"?{action:"rename",newName:(await $s({message:"T\xEAn repo m\u1EDBi:",default:e,validate:i=>i.trim().length>0?!0:"T\xEAn b\u1EAFt bu\u1ED9c"})).trim()}:{action:"retry",newName:e}}import{spawnSync as dr}from"child_process";import{platform as Ns}from"os";function Z(){let t=Ns();return t==="darwin"||t==="linux"||t==="win32"?t:"unsupported"}var Os=5e3,Ms=/(\d+\.\d+\.\d+)/;function Gs(){let e=Z()==="win32"?"where":"which",n=dr(e,["gitnexus"],{encoding:"utf8"});if(n.error||n.status!==0)return null;let r=(n.stdout||"").trim();return r?r.split(/\r?\n/)[0].trim():null}function Ls(){let t=dr("gitnexus",["--version"],{encoding:"utf8",timeout:Os});if(t.error||t.status!==0)return null;let e=(t.stdout||"").trim();return Ms.exec(e)?.[1]??null}var tt=null;function et(){if(tt!==null)return tt;let t=Gs();return t?(tt={installed:!0,version:Ls(),path:t},tt):(tt={installed:!1,version:null,path:null},tt)}function Vt(){tt=null}import{spawnSync as js}from"child_process";function Bt(){let t=js("gh",["auth","status"],{stdio:"ignore"});return t.error&&t.error.code==="ENOENT"?"not-installed":t.status===0?"authenticated":"not-authenticated"}import{spawnSync as Us}from"child_process";function Ds(t){let e=Z();return Us(e==="win32"?"where":"command",e==="win32"?[t]:["-v",t],{shell:e!=="win32",stdio:"ignore"}).status===0}function gr(){let t=Z(),e=t==="darwin"?["brew"]:t==="win32"?["winget"]:t==="linux"?["apt","dnf","pacman"]:[];for(let n of e)if(Ds(n))return n;return null}import{spawnSync as Cr}from"child_process";import{input as Ys,select as Xs}from"@inquirer/prompts";Ye();k();f();import{spawnSync as Xe}from"child_process";import{promises as Fs}from"fs";import{basename as Vs,join as vr}from"path";import{confirm as Bs,select as Ws}from"@inquirer/prompts";function qs(){let t=new Date;return`${t.getFullYear().toString().slice(-2)}${String(t.getMonth()+1).padStart(2,"0")}${String(t.getDate()).padStart(2,"0")}-${String(t.getHours()).padStart(2,"0")}${String(t.getMinutes()).padStart(2,"0")}`}async function zs(t){let e=vr(t,".git");if(!await m(e))throw new Error(`.git kh\xF4ng t\u1ED3n t\u1EA1i \u1EDF ${t} \u2014 kh\xF4ng c\u1EA7n reset.`);let n=`.git.backup-${qs()}`,r=vr(t,n);return await Fs.rename(e,r),o.success(`Backup .git \u2192 ${n}`),r}function Js(t){let e=Xe("git",["-C",t,"init","-b","main"],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});if(e.status!==0)throw new Error(`git init th\u1EA5t b\u1EA1i: ${e.stderr||e.stdout}`);o.success("Git init m\u1EDBi (branch main)"),Xe("git",["-C",t,"add","-A"],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});let n=Xe("git",["-C",t,"commit","-m","chore: initial commit from existing folder"],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});n.status!==0?o.warn(`git commit th\u1EA5t b\u1EA1i (folder c\xF3 th\u1EC3 r\u1ED7ng): ${(n.stderr||"").slice(0,200)}`):o.success("Initial commit")}async function xr(t){let e=Vs(t.folderPath),n=t.repoName??e;if(!t.autoYes&&!await Bs({message:`Folder '${e}' s\u1EBD \u0111\u01B0\u1EE3c reset:
|
|
20
21
|
1. Backup .git \u2192 .git.backup-{timestamp}
|
|
21
22
|
2. Git init m\u1EDBi (branch main, initial commit)
|
|
22
23
|
3. T\u1EA1o GitHub repo m\u1EDBi '${n}' d\u01B0\u1EDBi account c\u1EE7a b\u1EA1n
|
|
23
|
-
Ti\u1EBFp t\u1EE5c?`,default:!0}))throw new Error("User abort reset folder.");let r=t.visibility??(t.autoYes?"private":await
|
|
24
|
+
Ti\u1EBFp t\u1EE5c?`,default:!0}))throw new Error("User abort reset folder.");let r=t.visibility??(t.autoYes?"private":await Ws({message:"Visibility cho repo m\u1EDBi?",choices:[{name:"private (m\u1EB7c \u0111\u1ECBnh)",value:"private"},{name:"public",value:"public"}]})),i=await zs(t.folderPath);return Js(t.folderPath),{newRemoteUrl:Je({folder:t.folderPath,name:n,visibility:r,org:t.org}).httpsUrl,backupPath:i}}f();var Et=class extends Error{constructor(e){super(e),this.name="RemoteAccessAbortedError"}};function Qs(){let t=Cr("gh",["api","user","--jq",".login"],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});return t.status!==0?null:t.stdout.trim()||null}function Zs(){o.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");let t=Cr("gh",["auth","login","--web"],{stdio:"inherit"});t.status!==0&&o.warn(`gh auth login exit ${t.status}. B\u1EA1n c\xF3 th\u1EC3 ch\u1EA1y tay r\u1ED3i quay l\u1EA1i retry.`)}function ta(t,e,n){switch(t){case"no-access":return n?`Repo c\xF3 th\u1EC3 private v\xE0 account '${n}' kh\xF4ng c\xF3 quy\u1EC1n access. Switch sang account c\xF3 quy\u1EC1n, ho\u1EB7c xin invite t\u1EEB owner repo.`:"Repo c\xF3 th\u1EC3 private v\xE0 gh CLI ch\u01B0a login. Login tr\u01B0\u1EDBc r\u1ED3i retry.";case"not-found":return`URL c\xF3 th\u1EC3 sai ch\xEDnh t\u1EA3, ho\u1EB7c repo \u0111\xE3 b\u1ECB x\xF3a/rename. Nh\u1EADp l\u1EA1i URL \u0111\xFAng. Check: ${e}`;case"network":return"Kh\xF4ng k\u1EBFt n\u1ED1i \u0111\u01B0\u1EE3c GitHub. Check m\u1EA1ng / VPN / firewall.";case"timeout":return"Network ch\u1EADm > 5s. Check m\u1EA1ng r\u1ED3i retry.";default:return"L\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh. URL c\xF3 th\u1EC3 sai \u2014 nh\u1EADp l\u1EA1i, ho\u1EB7c check gh auth status."}}function ea(t){let e=t.trim();return e?/^https?:\/\/[\w.@/-]+$/.test(e)||/^git@[\w.-]+:[\w./-]+\.git$/.test(e)||/^[\w.-]+\/[\w.-]+$/.test(e):!1}async function Ar(t){let e=t.url,n=t.initialReason,r=t.initialDetail;for(;;){let i=Qs();o.warn(`Kh\xF4ng truy c\u1EADp \u0111\u01B0\u1EE3c ${e}`),o.dim(` L\xFD do: ${n}${r?` \u2014 ${r.slice(0,150)}`:""}`),o.info(ta(n,e,i)),i&&o.dim(` gh CLI hi\u1EC7n \u0111ang login: ${i}`);let s=[{name:"Nh\u1EADp l\u1EA1i URL \u0111\xFAng (recommended khi URL sai ch\xEDnh t\u1EA3)",value:"re-input-url"},{name:"Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",value:"switch"},{name:"T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa permission) \u2014 retry verify",value:"retry"}];t.folderPath&&s.push({name:"Reset folder & t\u1EA1o repo M\u1EDAI d\u01B0\u1EDBi account c\u1EE7a t\xF4i (backup .git c\u0169)",value:"reset-folder"}),s.push({name:"T\u1EA1m ng\u01B0ng init \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau",value:"abort"});let a=await Xs({message:"C\xE1ch x\u1EED l\xFD?",choices:s});if(a==="abort")throw new Et(`User ch\u1ECDn t\u1EA1m ng\u01B0ng. Fix access ${e} r\u1ED3i ch\u1EA1y l\u1EA1i 'avatar init'.`);if(a==="reset-folder"){if(!t.folderPath){o.warn("Reset folder c\u1EA7n folderPath \u2014 internal error.");continue}try{let l=await xr({folderPath:t.folderPath,visibility:t.defaultVisibility});return o.success(`Folder \u0111\xE3 reset. Backup t\u1EA1i: ${l.backupPath}`),o.success(`Remote m\u1EDBi: ${l.newRemoteUrl}`),{resolvedUrl:l.newRemoteUrl}}catch(l){o.warn(`Reset folder th\u1EA5t b\u1EA1i: ${l.message}`);continue}}a==="re-input-url"&&(e=(await Ys({message:"URL git remote (https://github.com/owner/repo.git ho\u1EB7c git@github.com:owner/repo.git):",default:e,validate:u=>ea(u)||"URL kh\xF4ng \u0111\xFAng format git remote"})).trim()),a==="switch"&&Zs(),o.info(`Verify remote l\u1EA1i: ${e}...`);let c=ht(e);if(c.ok)return o.success(`Remote accessible: ${e}`),{resolvedUrl:e};n=c.reason??"unknown",r=c.detail}}f();import{spawnSync as na}from"child_process";var ra={brew:{cmd:"brew",args:["install","gh"]},apt:{cmd:"sudo",args:["apt-get","install","-y","gh"]},dnf:{cmd:"sudo",args:["dnf","install","-y","gh"]},pacman:{cmd:"sudo",args:["pacman","-S","--noconfirm","github-cli"]},winget:{cmd:"winget",args:["install","--id","GitHub.cli","-e","--silent"]}};function Sr(t){let e=ra[t];o.info(`\u0110ang c\xE0i gh CLI qua ${t}...`);let n=na(e.cmd,e.args,{stdio:"inherit"});if(n.status!==0)throw new Error(`C\xE0i gh CLI th\u1EA5t b\u1EA1i qua ${t} (exit ${n.status}). C\xE0i tay r\u1ED3i ch\u1EA1y l\u1EA1i.`);o.success("\u0110\xE3 c\xE0i gh CLI")}f();import{spawnSync as ia}from"child_process";function Tr(){if(ia("gh",["auth","setup-git"],{stdio:"ignore"}).status!==0){o.warn("gh auth setup-git fail (non-fatal). N\u1EBFu git clone l\u1ED7i 128 \u2192 ch\u1EA1y th\u1EE7 c\xF4ng.");return}o.dim("Git credential helper \u0111\xE3 link v\u1EDBi gh token.")}f();async function Pr(t){for(;Bt()==="not-installed";){o.warn("gh CLI ch\u01B0a c\xE0i. Avatar s\u1EBD t\u1EF1 c\xE0i.");let e=gr();if(!e){if(await O({taskName:"Ph\xE1t hi\u1EC7n package manager",reason:"Kh\xF4ng t\xECm th\u1EA5y brew/apt/dnf/pacman/winget tr\xEAn m\xE1y.",allowSkip:!1,hint:"C\xE0i gh CLI tay (https://cli.github.com) r\u1ED3i ch\u1ECDn Retry."})==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc c\xE0i gh CLI.");continue}try{Sr(e)}catch(n){if(await O({taskName:`C\xE0i gh CLI qua ${e}`,reason:n.message,allowSkip:!1,hint:"C\xE0i tay (https://cli.github.com) r\u1ED3i ch\u1ECDn Retry, ho\u1EB7c Abort."})==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc c\xE0i gh CLI.")}}for(;Bt()==="not-authenticated";){o.warn("Ch\u01B0a \u0111\u0103ng nh\u1EADp GitHub.");try{ft()}catch(e){if(await O({taskName:"\u0110\u0103ng nh\u1EADp GitHub qua gh",reason:e.message,allowSkip:!1,hint:"Th\u1EED l\u1EA1i \u2014 ch\u1ECDn c\xE1ch login kh\xE1c (browser vs token) khi gh prompt."})==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc gh auth login.");continue}if(Bt()!=="authenticated"&&await O({taskName:"Verify gh auth",reason:"Sau gh auth login v\u1EABn b\xE1o not-authenticated.",allowSkip:!1,hint:"C\xF3 th\u1EC3 user cancel browser. Th\u1EED l\u1EA1i ho\u1EB7c abort."})==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc verify gh auth.")}if(o.success("gh CLI s\u1EB5n s\xE0ng"),Tr(),t){let e=ht(t);return e.ok?(o.success(`Remote accessible: ${t}`),{resolvedRemoteUrl:t}):{resolvedRemoteUrl:(await Ar({url:t,initialReason:e.reason??"unknown",initialDetail:e.detail})).resolvedUrl}}return{}}import{existsSync as Qe}from"fs";import{dirname as oa,join as Ze}from"path";var sa=5;function aa(t){let e=Qe(Ze(t,".claude")),n=Qe(Ze(t,"CLAUDE.md")),r=Qe(Ze(t,"src"));return e&&n&&r}function wt(t){let e=t;for(let n=0;n<sa;n++){if(aa(e))return e;let r=oa(e);if(r===e)return null;e=r}return null}f();import{spawnSync as $r}from"child_process";import{existsSync as ca}from"fs";import{join as Er}from"path";var la=120*1e3,ua=300*1e3,D=class extends Error{operation;reason;exitCode;stderr;constructor(e,n,r,i=null,s){super(r),this.name="GitnexusOperationError",this.operation=e,this.reason=n,this.exitCode=i,this.stderr=s}};function Rr(t,e,n,r){if(n==="SIGTERM")return new D(t,"timeout",`gitnexus ${t} timeout. Check m\u1EA1ng / repo size.`,null,r);let i=r.toLowerCase();return i.includes("eacces")||i.includes("permission denied")?new D(t,"permission",`gitnexus ${t} fail (permission). Check write access ~/.claude ho\u1EB7c cwd.`,e,r):new D(t,"non-zero-exit",`gitnexus ${t} exit ${e??"null"}. Xem log ph\xEDa tr\xEAn.`,e,r)}function Wt(t,e){return t.split(`
|
|
24
25
|
`).slice(-e).join(`
|
|
25
|
-
`)}function
|
|
26
|
-
`):r&&process.stderr.write(`${
|
|
27
|
-
`),
|
|
28
|
-
`):
|
|
29
|
-
`),
|
|
26
|
+
`)}function _r(){let t=Tt("Setup GitNexus global skills (~/.claude/skills/gitnexus-*)"),e=$r("gitnexus",["setup"],{stdio:["ignore","pipe","pipe"],timeout:la,encoding:"utf8"});if(e.status!==0||e.signal==="SIGTERM"){t.fail("GitNexus setup failed");let n=(e.stderr||"").trim(),r=(e.stdout||"").trim();throw n?process.stderr.write(`${Wt(n,30)}
|
|
27
|
+
`):r&&process.stderr.write(`${Wt(r,30)}
|
|
28
|
+
`),Rr("setup",e.status,e.signal,n)}t.succeed("GitNexus setup OK (global skills installed)")}function kt(t){let e=Tt(`Analyze workspace ${t} (1-3 ph\xFAt)`),n=$r("gitnexus",["analyze","."],{cwd:t,stdio:["ignore","pipe","pipe"],timeout:ua,encoding:"utf8"});if(n.status!==0||n.signal==="SIGTERM"){e.fail("Analyze failed");let i=(n.stderr||"").trim(),s=(n.stdout||"").trim();throw i?process.stderr.write(`${Wt(i,30)}
|
|
29
|
+
`):s&&process.stderr.write(`${Wt(s,30)}
|
|
30
|
+
`),Rr("analyze",n.status,n.signal,i)}let r=Er(t,".gitnexus","meta.json");if(!ca(r))throw e.fail("Analyze exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y meta.json"),new D("analyze","missing-output",`gitnexus analyze xong nh\u01B0ng kh\xF4ng th\u1EA5y ${r}. Repo c\xF3 th\u1EC3 empty ho\u1EB7c gitnexus fail silent.`);e.succeed(`Analyze OK (index t\u1EA1i ${Er(t,".gitnexus")})`)}import{spawnSync as wa}from"child_process";import{existsSync as ka}from"fs";import{join as Gr}from"path";import{confirm as ya}from"@inquirer/prompts";var pa=[/^claude-(opus|sonnet)-4/i,/^claude-(opus|sonnet|haiku)-[5-9]/i,/^o1(-|$)/i,/^o3(-|$)/i,/^o4(-|$)/i,/nal-claude-(opus|sonnet)-?4/i];function Ir(t){return t?pa.some(e=>e.test(t)):!1}k();f();import{promises as $t}from"fs";import{homedir as ma}from"os";import{dirname as da,join as Nr}from"path";var ga=Nr(ma(),".gitnexus"),Rt=Nr(ga,"config.json");async function fa(){try{let t=await $t.readFile(Rt,"utf8"),e=JSON.parse(t);return typeof e=="object"&&e!==null?e:{}}catch{return{}}}async function ha(t){await $t.mkdir(da(Rt),{recursive:!0});let e=`${Rt}.tmp-${process.pid}`;await $t.writeFile(e,t,{mode:384}),await $t.rename(e,Rt)}async function Or(t){let n={...await fa(),apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,isReasoningModel:t.isReasoningModel??!1};await ha(JSON.stringify(n,null,2)),await $t.chmod(Rt,384).catch(()=>{})}var ba=900*1e3,va="nal-claude",xa="claude-sonnet-4-5";function Ca(t){let e=t.replace(/\/+$/,"");return e.endsWith("/v1")?`${e}/`:e.endsWith("/v1/")?e:`${e}/v1/`}async function Aa(t){let e=Gr(t,".claude","settings.json");if(!await m(e))return null;try{let n=await y(e),r=n.env||{},i=typeof r.ANTHROPIC_BASE_URL=="string"?r.ANTHROPIC_BASE_URL:null;if(!i)return null;let s=typeof n.model=="string"?n.model:"",a=typeof r.ANTHROPIC_MODEL=="string"?r.ANTHROPIC_MODEL:"",c=s.length>0?s:a,l=h=>{let C=process.env[h];if(typeof C=="string"&&C.length>0)return C;let S=r[h];return typeof S=="string"&&S.length>0?S:null},u=typeof n.avatarProvider=="string"?n.avatarProvider:null,d=!1;try{d=new URL(i).hostname==="api.anthropic.com"}catch{d=!1}if((u==="anthropic"?"anthropic":u==="llmlite"?"llmlite":d?"anthropic":"llmlite")==="anthropic"){let h=l("ANTHROPIC_API_KEY");if(h)return{provider:"anthropic",apiKey:h,baseUrl:Ca(i),model:c.length>0?c:xa}}else{let h=l("ANTHROPIC_AUTH_TOKEN");if(h)return{provider:"llmlite",apiKey:h,baseUrl:i,model:c.length>0?c:va}}return null}catch{return null}}async function Sa(t,e){return await ya({message:`Generate wiki cho workspace? (~$0.50 qua ${t} model=${e}, 2-5 ph\xFAt). Continue?`,default:!0})}function Mr(t,e){return t.split(`
|
|
30
31
|
`).slice(-e).join(`
|
|
31
|
-
`)}async function
|
|
32
|
+
`)}async function qt(t,e=t){let n=await Aa(t);if(!n)return o.warn(`Kh\xF4ng resolve \u0111\u01B0\u1EE3c API key cho wiki gen.
|
|
32
33
|
\u0110\xE3 ki\u1EC3m tra: process.env \u2192 .envrc Avatar block \u2192 settings.json.env
|
|
33
34
|
Nguy\xEAn nh\xE2n c\xF3 th\u1EC3:
|
|
34
35
|
\u2022 Subscription mode (OAuth, kh\xF4ng c\xF3 key)
|
|
35
36
|
\u2022 Key ch\u01B0a setup \u2014 ch\u1EA1y 'avatar ai setup' ho\u1EB7c 'avatar secrets set ANTHROPIC_API_KEY'
|
|
36
|
-
\u2022 settings.json.env.ANTHROPIC_BASE_URL b\u1ECB thi\u1EBFu (provider ch\u01B0a configured)`),
|
|
37
|
-
gitnexus wiki . --api-key <key> --base-url <url> --model <model>`),{ran:!1,skipped:!0,reason:"subscription-mode"};if(!await
|
|
38
|
-
`):g&&process.stderr.write(`${
|
|
39
|
-
`),{ran:!1,skipped:!0,reason:"fail",detail:`Wiki gen ${
|
|
40
|
-
C\u1EA7n .claude/ + CLAUDE.md + src/. Ch\u1EA1y 'avatar init' tr\u01B0\u1EDBc.`),process.exit(1)),t}async function
|
|
41
|
-
`;await
|
|
42
|
-
${r}`);return a==="unknown"&&(
|
|
43
|
-
Hint: check m\u1EA1ng / firewall / VPN, ho\u1EB7c base URL c\xF3 \u0111\xFAng kh\xF4ng.`;throw new Error(`Connect ${t} timeout sau ${Qr/1e3}s.${c}`)}let i=o.message||String(o);if(i.toLowerCase().includes("fetch failed")||i.includes("ENOTFOUND")){let c=t.includes("nal.vn")?" (ai.nal.vn c\u1EA7n VPN NAL \u2014 ki\u1EC3m tra VPN \u0111\xE3 b\u1EADt ch\u01B0a)":"";throw new Error(`Network error khi connect ${t}: ${i}${c}`)}throw o}finally{clearTimeout(r)}}async function cc(t){let e=t.filter(r=>r.toLowerCase().includes("claude"));if(e.length===1){let r=e[0];return s.info(`Auto-pick model: ${r} (ch\u1EC9 1 claude alias tr\xEAn endpoint)`),r}let n=e.length>0?e:t;return await rc({message:"Ch\u1ECDn model m\u1EB7c \u0111\u1ECBnh cho project:",choices:n.map(r=>({name:r,value:r}))})}async function Zr(){let t=await ic(),e=await sc();s.info(`Verify key (${K(t)}) qua ${e}/v1/models...`);let n=await ac(e,t);s.success(`Endpoint OK \u2014 ${n.length} models available`);let r=await cc(n);return{apiKey:t,baseUrl:e,model:r}}f();k();import{promises as lc}from"fs";import{join as uc}from"path";var wn=384;function pc(t){return uc(t,".claude","settings.json")}async function mc(t){if(!await m(t))return{};try{return await y(t)}catch(e){throw new Error(`Kh\xF4ng parse \u0111\u01B0\u1EE3c ${t} (JSON l\u1ED7i): ${e.message}. Backup file r\u1ED3i x\xF3a \u0111\u1EC3 Avatar t\u1EA1o l\u1EA1i.`)}}function dc(t,e){let{env:n,...r}=t,o={...r,model:e};if(n){let{ANTHROPIC_BASE_URL:i,ANTHROPIC_AUTH_TOKEN:a,ANTHROPIC_API_KEY:c,...l}=n;Object.keys(l).length>0&&(o.env=l)}return o}function to(t){if(!t)return t;let{ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_API_KEY:n,...r}=t;return r}function gc(t,e,n,r,o){let a={...to(t.env),ANTHROPIC_BASE_URL:n};return o||(a.ANTHROPIC_AUTH_TOKEN=e),{...t,env:a,model:r,avatarProvider:"llmlite"}}function fc(t,e,n,r,o){let a={...to(t.env),ANTHROPIC_BASE_URL:n};return o||(a.ANTHROPIC_API_KEY=e),{...t,env:a,model:r,avatarProvider:"anthropic"}}function hc(t,e){let n=e.env||{},r=typeof e.model=="string"?e.model:void 0;return{...t,env:{...t.env||{},...n},...r?{model:r}:{}}}async function at(t,e){let n=pc(t),r=await mc(n),o;switch(e.provider){case"subscription":o=dc(r,e.model);break;case"llmlite":o=gc(r,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"anthropic":o=fc(r,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"use-global":o=hc(r,e.sourceSettings);break}await C(n,o,wn);try{await lc.chmod(n,wn)}catch{}return{path:n,mode:wn}}var L="sonnet";async function Zt(t){try{s.info("Setup AI provider cho workspace...");let e=Nt();if(e.installed)s.success(`Claude Code \u0111\xE3 c\xF3${e.version?` v${e.version}`:""}`);else if(s.info("Ch\u01B0a c\xF3 Claude Code \u2014 s\u1EBD t\u1EF1 c\xE0i qua npm."),qr(),Xt(),e=Nt(),!e.installed)throw new Error("C\xE0i Claude Code xong nh\u01B0ng v\u1EABn kh\xF4ng detect \u0111\u01B0\u1EE3c binary.");let n=hn();switch(await Jr(n)){case"subscription":{let o=dn();if(o.state!=="authenticated"&&(gn(),o=dn()),o.state==="authenticated"&&o.subscriptionType)return await at(t.workspacePath,{provider:"subscription",model:L}),await b("ai_setup",`provider=subscription,result=ok,plan=${o.subscriptionType},probe=skipped`),s.success(`AI ready \xB7 Subscription (${o.subscriptionType}) \xB7 model=${L}`),{ok:!0,provider:"subscription",model:L};s.dim("Auth status kh\xF4ng tr\u1EA3 subscriptionType \u2014 verify quota (30-60s)...");let i=fn();if(!i.ok&&i.reason==="auth-expired"&&(s.warn("Token Claude Code \u0111\xE3 h\u1EBFt h\u1EA1n. T\u1EF1 \u0111\u1ED9ng re-login..."),gn(),i=fn()),!i.ok&&(i.reason==="timeout"||i.reason==="unknown"))return s.warn(`Probe verify ${i.reason} \u2014 accept trust auth status. Ti\u1EBFp t\u1EE5c.`),i.detail?.trim()&&s.warn(` Chi ti\u1EBFt: ${i.detail.slice(0,200)}`),await at(t.workspacePath,{provider:"subscription",model:L}),await b("ai_setup",`provider=subscription,result=ok,probe=${i.reason}-soft-pass`),s.success(`AI ready \xB7 Subscription (probe ${i.reason}, soft-pass) \xB7 model=${L}`),{ok:!0,provider:"subscription",model:L};if(!i.ok){let a=i.reason??"unknown";return await b("ai_setup",`provider=subscription,result=no-quota,reason=${a}`),s.warn(`Subscription verify th\u1EA5t b\u1EA1i (${a}).`),i.detail?.trim()&&s.warn(` Chi ti\u1EBFt: ${i.detail.slice(0,200)}`),s.warn(` \u2192 ${Fr(a)}`),{ok:!1,reason:`subscription-${a}`,phase:"quota"}}return await at(t.workspacePath,{provider:"subscription",model:L}),await b("ai_setup","provider=subscription,result=ok"),s.success(`AI ready \xB7 Subscription \xB7 model=${L}`),{ok:!0,provider:"subscription",model:L}}case"llmlite":{let o=await Zr();return await at(t.workspacePath,{provider:"llmlite",apiKey:o.apiKey,baseUrl:o.baseUrl,model:o.model,skipApiKey:!1}),await b("ai_setup",`provider=llmlite,result=ok,model=${o.model},base=${o.baseUrl},storage=settings.json`),s.success(`AI ready \xB7 LLMLite (NAL) \xB7 model=${o.model} \xB7 ${o.baseUrl}`),{ok:!0,provider:"llmlite",model:o.model}}case"anthropic":{let o=await Xr();return await at(t.workspacePath,{provider:"anthropic",apiKey:o.apiKey,baseUrl:o.baseUrl,model:o.model,skipApiKey:!1}),await b("ai_setup",`provider=anthropic,result=ok,model=${o.model},storage=settings.json`),s.success(`AI ready \xB7 Anthropic Direct \xB7 model=${o.model} \xB7 ${o.baseUrl}`),{ok:!0,provider:"anthropic",model:o.model}}case"use-global":{if(!n.rawSettings)throw new Error("use-global ch\u1ECDn nh\u01B0ng kh\xF4ng \u0111\u1ECDc \u0111\u01B0\u1EE3c global settings.");return await at(t.workspacePath,{provider:"use-global",sourceSettings:n.rawSettings}),await b("ai_setup","provider=use-global,result=ok"),s.success(`AI ready \xB7 Copy t\u1EEB global config (${n.baseUrl??"subscription"})`),{ok:!0,provider:"use-global",model:n.model}}}}catch(e){let n=e instanceof Error?e.message:String(e);return s.warn(`AI setup th\u1EA5t b\u1EA1i: ${n}`),s.dim("Workspace v\u1EABn s\u1EB5n s\xE0ng. Setup AI sau qua: avatar ai setup"),await b("ai_setup",`result=failed,error=${n.slice(0,200)}`),{ok:!1,reason:n}}}f();import{spawnSync as wc}from"child_process";f();var kn=1e4,eo=3e4,ro=5,yn="say ok",no="2023-06-01";async function kc(t,e,n){s.info(`Testing LLMLite provider: ${t} (key: ${K(e)})`);let r=new AbortController,o=setTimeout(()=>r.abort(),kn);try{let i=await fetch(`${t}/v1/models`,{headers:{Authorization:`Bearer ${e}`},signal:r.signal});if(i.status===401||i.status===403)throw new Error(`API key invalid (HTTP ${i.status}). Re-run: avatar ai setup`);if(!i.ok)throw new Error(`Endpoint /v1/models l\u1ED7i (HTTP ${i.status}).`);let c=((await i.json()).data||[]).map(h=>typeof h.id=="string"?h.id:null).filter(h=>h!==null);if(s.success(`Connectivity OK \xB7 ${c.length} models available`),c.length>0){let h=c.slice(0,5).join(", "),x=c.length>5?` ...+${c.length-5} more`:"";s.dim(` Models: ${h}${x}`)}s.info(`Testing chat completion v\u1EDBi model "${n}"...`);let l=await fetch(`${t}/v1/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({model:n,messages:[{role:"user",content:yn}],max_tokens:ro}),signal:r.signal});if(!l.ok){let h=(await l.text()).slice(0,200);throw new Error(`Chat completion fail (HTTP ${l.status}). ${h}`)}let p=await l.json(),d=typeof p.choices?.[0]?.message?.content=="string"?p.choices[0].message.content:"(empty response)",g=p.usage?.total_tokens??"?";s.success(`Response: "${String(d).trim().slice(0,100)}"`),s.dim(` Tokens used: ${g}`)}catch(i){throw i.name==="AbortError"?new Error(`Timeout ${kn/1e3}s. Check m\u1EA1ng / endpoint ${t}.`):i}finally{clearTimeout(o)}}function yc(){s.info("Testing Subscription provider qua `claude --print`...");let t=wc("claude",["--print",yn],{encoding:"utf8",timeout:eo});if(t.signal==="SIGTERM")throw new Error(`Timeout ${eo/1e3}s. Check m\u1EA1ng / endpoint.`);if(t.status!==0){let e=(t.stderr||"").toLowerCase();throw e.includes("401")||e.includes("invalid authentication")||e.includes("unauthorized")?new Error("Token Claude Code stale (401). Fix: `claude auth logout && claude auth login`."):new Error(`Test fail (exit ${t.status}). Stderr: ${(t.stderr||"").slice(0,200)}`)}s.success(`Response: "${(t.stdout||"").trim().slice(0,100)}"`)}async function bc(t,e,n){s.info(`Testing Anthropic Direct provider: ${t} (key: ${K(e)})`);let r=new AbortController,o=setTimeout(()=>r.abort(),kn);try{let i=await fetch(`${t}/v1/models`,{headers:{"x-api-key":e,"anthropic-version":no},signal:r.signal});if(i.status===401||i.status===403)throw new Error(`API key invalid (HTTP ${i.status}). Re-run: avatar ai setup`);if(!i.ok)throw new Error(`Endpoint /v1/models l\u1ED7i (HTTP ${i.status}).`);let c=((await i.json()).data||[]).map(g=>typeof g.id=="string"?g.id:null).filter(g=>g!==null);s.success(`Connectivity OK \xB7 ${c.length} models available`),s.info(`Testing message v\u1EDBi model "${n}"...`);let l=await fetch(`${t}/v1/messages`,{method:"POST",headers:{"x-api-key":e,"anthropic-version":no,"Content-Type":"application/json"},body:JSON.stringify({model:n,max_tokens:ro,messages:[{role:"user",content:yn}]}),signal:r.signal});if(!l.ok){let g=(await l.text()).slice(0,200);throw new Error(`Message endpoint fail (HTTP ${l.status}): ${g}`)}let d=((await l.json()).content||[]).map(g=>typeof g.text=="string"?g.text:"").join("").trim().slice(0,100);s.success(`Response: "${d}"`)}finally{clearTimeout(o)}}async function oo(t){let e=t.env||{},n=typeof e.ANTHROPIC_BASE_URL=="string"?e.ANTHROPIC_BASE_URL:void 0,r=typeof e.ANTHROPIC_AUTH_TOKEN=="string"?e.ANTHROPIC_AUTH_TOKEN:void 0,o=typeof e.ANTHROPIC_API_KEY=="string"?e.ANTHROPIC_API_KEY:void 0,i=typeof t.model=="string"?t.model:"default";return o&&n?(await bc(n,o,i),{ok:!0,provider:"anthropic",message:"Anthropic Direct provider working"}):n&&r?(await kc(n,r,i),{ok:!0,provider:"llmlite",message:"LLMLite provider working"}):(yc(),{ok:!0,provider:"subscription",message:"Subscription provider working"})}async function te(){let t=process.cwd(),e=ht(t);return e||(s.error(`Kh\xF4ng t\xECm th\u1EA5y Avatar workspace t\u1EEB th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i.
|
|
37
|
+
\u2022 settings.json.env.ANTHROPIC_BASE_URL b\u1ECB thi\u1EBFu (provider ch\u01B0a configured)`),o.dim(`\u0110\u1EC3 gen wiki sau khi \u0111\xE3 c\xF3 key, ch\u1EA1y manual:
|
|
38
|
+
gitnexus wiki . --api-key <key> --base-url <url> --model <model>`),{ran:!1,skipped:!0,reason:"subscription-mode"};if(!await Sa(n.baseUrl,n.model))return o.dim("User decline wiki gen \u2014 workspace OK kh\xF4ng c\xF3 wiki. Ch\u1EA1y `gitnexus wiki` manual sau khi c\u1EA7n."),{ran:!1,skipped:!0,reason:"user-declined"};let i=Ir(n.model);await Or({apiKey:n.apiKey,baseUrl:n.baseUrl,model:n.model,isReasoningModel:i});let s=["wiki",".","--base-url",n.baseUrl,"--model",n.model];i&&s.push("--reasoning-model");let a=Tt(`Generating wiki via ${n.baseUrl} (${n.provider}) model=${n.model}${i?" [reasoning]":""}`),c=wa("gitnexus",s,{cwd:e,stdio:["ignore","pipe","pipe"],timeout:ba,encoding:"utf8"});if(c.status!==0||c.signal==="SIGTERM"){let u=c.signal==="SIGTERM"?"timeout":"non-zero-exit";a.fail(`Wiki gen ${u} (exit ${c.status??"null"})`);let d=(c.stderr||"").trim(),g=(c.stdout||"").trim();return d?process.stderr.write(`${Mr(d,30)}
|
|
39
|
+
`):g&&process.stderr.write(`${Mr(g,30)}
|
|
40
|
+
`),{ran:!1,skipped:!0,reason:"fail",detail:`Wiki gen ${u} (exit ${c.status??"null"})`}}let l=Gr(e,".gitnexus","wiki","index.html");return ka(l)?(a.succeed(`Wiki ready: ${l}`),{ran:!0,skipped:!1,wikiPath:l}):(a.fail("Wiki exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y index.html"),{ran:!1,skipped:!0,reason:"fail",detail:`Wiki exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y ${l}`})}f();function jr(t){t.command("add").description("Th\xEAm t\xE0i nguy\xEAn v\xE0o workspace").command("repo").description("Clone 1 repo code v\xE0o src/ (repo c\xF3 s\u1EB5n / th\u01B0 m\u1EE5c / d\u1EF1 \xE1n m\u1EDBi)").option("--url <url>","URL git repo c\xF3 s\u1EB5n (b\u1ECF qua prompt ngu\u1ED3n)").option("--name <name>","T\xEAn th\u01B0 m\u1EE5c trong src/ (m\u1EB7c \u0111\u1ECBnh suy t\u1EEB URL)").option("--yes","Auto-confirm prompt").action(async n=>{try{await Pa(n)}catch(r){o.error(r instanceof Error?r.message:String(r)),process.exit(1)}})}function Ur(t){let e=t.trim();return e.length===0?"T\xEAn b\u1EAFt bu\u1ED9c":/^[A-Za-z0-9._-]+$/.test(e)?!0:"T\xEAn repo ch\u1EC9 g\u1ED3m ch\u1EEF/s\u1ED1/d\u1EA5u . - _ (kh\xF4ng space, kh\xF4ng '/')."}function Ta(){let t=wt(process.cwd());return t||(o.error(`Kh\xF4ng t\xECm th\u1EA5y Avatar workspace t\u1EEB th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i.
|
|
41
|
+
C\u1EA7n .claude/ + CLAUDE.md + src/. Ch\u1EA1y 'avatar init' tr\u01B0\u1EDBc.`),process.exit(1)),t}async function Pa(t){let e=Ta();await Pr();let n=!0,r=t.url,i=t.name;for(;n;){let s=await Ea(r),a=i??await Ra(s);o.info(`Clone ${s} \u2192 src/${a} ...`);let{cloned:c,skipped:l}=await cr({workspaceRoot:e,url:s,name:a});if(l||!c?o.dim(`B\u1ECF qua repo ${a}.`):(o.success(`\u2713 \u0110\xE3 clone src/${c.name}`),await _a(e,c.path,t.yes),o.success(`\u2713 Done repo: ${c.name}`)),r=void 0,i=void 0,t.yes)break;n=await zt({message:"Add repo kh\xE1c n\u1EEFa?",default:!1})}}async function Ea(t){if(t)return t;let e=await tn({message:"Ngu\u1ED3n repo?",choices:[{name:"1. Repo c\xF3 s\u1EB5n (\u0111i\u1EC1n URL git)",value:"url"},{name:"2. Th\u01B0 m\u1EE5c \u0111\xE3 c\xF3 tr\xEAn m\xE1y (clone t\u1EEB remote c\u1EE7a n\xF3)",value:"folder"},{name:"3. D\u1EF1 \xE1n m\u1EDBi ho\xE0n to\xE0n (t\u1EA1o repo GitHub)",value:"new"}]});if(e==="url")return await _t({message:"URL git repo:",validate:i=>i.trim().length>0?!0:"URL b\u1EAFt bu\u1ED9c"});if(e==="folder"){let{statSync:i}=await import("fs"),s=Lr(await _t({message:"\u0110\u01B0\u1EDDng d\u1EABn folder:",validate:l=>{let u=l.trim();if(u.length===0)return"Path b\u1EAFt bu\u1ED9c";try{return i(Lr(u)).isDirectory()?!0:`"${u}" kh\xF4ng ph\u1EA3i th\u01B0 m\u1EE5c (l\xE0 file?).`}catch{return`Th\u01B0 m\u1EE5c "${u}" kh\xF4ng t\u1ED3n t\u1EA1i. Ki\u1EC3m tra l\u1EA1i \u0111\u01B0\u1EDDng d\u1EABn.`}}})),a=await Ke(s);if(a&&(o.warn(`\u26A0 Folder c\xF3 thay \u0111\u1ED5i ch\u01B0a commit: ${a.slice(0,120)}`),o.warn(" Clone l\u1EA5y b\u1EA3n REMOTE \u2192 c\xE1c thay \u0111\u1ED5i local n\xE0y s\u1EBD KH\xD4NG c\xF3 trong src/<name>."),!await zt({message:"Ti\u1EBFp t\u1EE5c clone t\u1EEB remote (b\u1ECF thay \u0111\u1ED5i local ch\u01B0a push)?",default:!1})))throw new Error("H\u1EE7y. Commit + push thay \u0111\u1ED5i trong folder g\u1ED1c r\u1ED3i ch\u1EA1y l\u1EA1i 'avatar add repo'.");let c=await Fe(s);return c||await $a(s)}let n=await _t({message:"T\xEAn repo m\u1EDBi:",validate:Ur}),r=await tn({message:"Visibility?",choices:[{name:"private (m\u1EB7c \u0111\u1ECBnh)",value:"private"},{name:"public",value:"public"}]});return await ur(n.trim(),r)}async function $a(t){let{basename:e}=await import("path");if(o.warn(`Folder ${t} ch\u01B0a c\xF3 remote origin.`),!await zt({message:"T\u1EA1o GitHub repo cho folder n\xE0y + push code l\xEAn?",default:!0}))throw new Error("H\u1EE7y. C\u1EA7n remote \u0111\u1EC3 add repo. T\u1EF1 t\u1EA1o remote (gh repo create) r\u1ED3i ch\u1EA1y l\u1EA1i, ho\u1EB7c ch\u1ECDn ngu\u1ED3n kh\xE1c.");let{git:r}=await Promise.resolve().then(()=>(_e(),Kn)),{pathExists:i}=await Promise.resolve().then(()=>(k(),Hn)),s=r(t);await i(`${t}/.git`)||(o.info("Folder ch\u01B0a git init \u2014 \u0111ang init..."),await s.init()),o.info("Commit code hi\u1EC7n t\u1EA1i trong folder (baseline cho push)..."),await s.add("."),await s.commit("chore: initial commit (avatar add repo)").catch(()=>{});let a=await _t({message:"T\xEAn repo GitHub:",default:e(t),validate:Ur}),c=await tn({message:"Visibility?",choices:[{name:"private (m\u1EB7c \u0111\u1ECBnh)",value:"private"},{name:"public",value:"public"}]}),{createGithubRemoteFromFolder:l}=await Promise.resolve().then(()=>(Ye(),br)),u=await pr(a.trim(),d=>l({folder:t,name:d,visibility:c}).sshUrl);return o.success(`\u2713 \u0110\xE3 t\u1EA1o remote + push: ${u}`),u}async function Ra(t){let e=De(t);return await _t({message:"T\xEAn th\u01B0 m\u1EE5c trong src/:",default:e})}async function _a(t,e,n){if(!et().installed){o.dim("GitNexus ch\u01B0a c\xE0i \u2014 skip index. C\xE0i qua 'avatar gitnexus install'.");return}if(n||await zt({message:"Index repo n\xE0y b\u1EB1ng GitNexus?",default:!0})){try{kt(e),o.success(` \u2713 GitNexus indexed src/${e.split("/").pop()}`)}catch(s){o.warn(` ! GitNexus index fail (repo v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c): ${s instanceof Error?s.message:s}`);return}try{let s=await qt(t,e);s.ran?o.success(` \u2713 GitNexus wiki t\u1EA1o cho src/${e.split("/").pop()}`):s.reason==="subscription-mode"&&o.dim(" (Subscription mode \u2014 kh\xF4ng c\xF3 API key cho wiki, skip.)")}catch(s){o.warn(` ! Wiki fail (repo v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c): ${s instanceof Error?s.message:s}`)}}}k();import{promises as Ec}from"fs";import{join as ui}from"path";import{confirm as $c}from"@inquirer/prompts";import{promises as Wr}from"fs";import{homedir as ja}from"os";import{join as Nt}from"path";import{z as w}from"zod";var Dr=w.object({email:w.string().email(),name:w.string(),access_token:w.string().min(1),refresh_token:w.string().min(1),expires_at:w.string().datetime(),id_token:w.string().min(1)}),Ia=w.object({installed_tools:w.record(w.string(),w.object({version:w.string().optional(),installed_at:w.string().datetime(),install_method:w.string()})).default({}),tool_inputs:w.record(w.string(),w.unknown()).default({})}),bd=w.object({$schema:w.string().optional(),includeCoAuthoredBy:w.boolean().optional(),env:w.record(w.string(),w.string()).default({}),permissions:w.object({allow:w.array(w.string()).default([]),deny:w.array(w.string()).default([])}).partial().optional(),hooks:w.record(w.string(),w.array(w.unknown())).optional(),statusLine:w.object({type:w.string(),command:w.string(),padding:w.number().optional()}).optional()}),vd=w.enum(["internal","client","library"]);k();var Ot=Nt(ja(),".avatar"),nt=Nt(Ot,"config.json"),Pd=Nt(Ot,"state.json"),un=Nt(Ot,"audit.log"),Ed=Nt(Ot,"backups"),Ua=384;async function pn(){await v(Ot)}async function H(){if(!await m(nt))return null;let t=await y(nt),e=Dr.safeParse(t);return e.success?e.data:null}async function mn(t){await pn(),await A(nt,t,Ua)}async function Br(){if(await m(nt)){let{promises:t}=await import("fs");await t.unlink(nt)}}function rt(t){let e=Date.parse(t.expires_at);return Number.isNaN(e)||e-Date.now()<6e4}var It=class extends Error{constructor(e){super(e),this.name="NoValidTokenError"}};async function Da(t){let{decodeIdToken:e}=await Promise.resolve().then(()=>(Xt(),ln));try{let n=e(t),r=Math.floor(Date.now()/1e3);return n.exp-60<r}catch{return!0}}async function Qt(){let t=await H();if(!t)throw new It("Ch\u01B0a \u0111\u0103ng nh\u1EADp. Ch\u1EA1y 'avatar login' tr\u01B0\u1EDBc.");let{refreshAccessToken:e,decodeIdToken:n,verifyIdTokenClaims:r}=await Promise.resolve().then(()=>(Xt(),ln));if(!await Da(t.id_token))return r(n(t.id_token)),t.id_token;let i;try{i=await e(t.refresh_token)}catch(a){throw new It(`Token h\u1EBFt h\u1EA1n v\xE0 refresh th\u1EA5t b\u1EA1i (${a instanceof Error?a.message:a}). Ch\u1EA1y 'avatar login' l\u1EA1i.`)}if(!i.id_token)throw new It("Refresh kh\xF4ng tr\u1EA3 id_token m\u1EDBi (id_token c\u0169 \u0111\xE3 h\u1EBFt h\u1EA1n). Ch\u1EA1y 'avatar login' l\u1EA1i.");r(n(i.id_token));let s={...t,access_token:i.access_token,id_token:i.id_token,expires_at:new Date(Date.now()+i.expires_in*1e3).toISOString()};return await mn(s),i.id_token}async function Ha(){try{await Wr.chmod(un,384)}catch{}}async function b(t,e){await pn();let n={timestamp:new Date().toISOString(),action:t,...e?{detail:e}:{}},r=`${JSON.stringify(n)}
|
|
42
|
+
`;await Wr.appendFile(un,r,{encoding:"utf8",mode:384}),await Ha()}f();import{spawnSync as dn}from"child_process";var qr=6e4,Ka="ok";function gn(){let t=dn("claude",["auth","status"],{encoding:"utf8"});if(t.error||t.status!==0)return{state:"not-authenticated"};let e=(t.stdout||"").trim();if(!e.startsWith("{"))return{state:"authenticated"};try{let n=JSON.parse(e);return n.loggedIn!==!0?{state:"not-authenticated"}:{state:"authenticated",email:n.email,subscriptionType:n.subscriptionType,apiProvider:n.apiProvider}}catch{return{state:"authenticated"}}}function fn(){o.info("Kh\u1EDFi \u0111\u1ED9ng \u0111\u0103ng nh\u1EADp Claude Code (browser s\u1EBD m\u1EDF)...");let t=dn("claude",["auth","login"],{stdio:"inherit"});if(t.status!==0)throw new Error(`claude auth login th\u1EA5t b\u1EA1i (exit ${t.status}). Th\u1EED 'claude auth login' tay r\u1ED3i ch\u1EA1y l\u1EA1i.`);o.success("\u0110\xE3 \u0111\u0103ng nh\u1EADp Claude Code")}function Fa(t){let e=t.toLowerCase();return e.includes("credit_balance_too_low")||e.includes("credit balance too low")?"credit_balance_too_low":e.includes("insufficient_quota")||e.includes("insufficient quota")||e.includes("quota_exceeded")||e.includes("quota exceeded")||e.includes("usage limit")||e.includes("you've used all")?"insufficient_quota":e.includes("401")||e.includes("invalid authentication")||e.includes("authentication credentials")||e.includes("failed to authenticate")||e.includes("authentication failed")||e.includes("unauthorized")?"auth-expired":e.includes("invalid_api_key")||e.includes("invalid api key")?"invalid_api_key":e.includes("rate_limit")||e.includes("rate limit")||e.includes("429")?"rate_limit":"unknown"}function zr(t){switch(t){case"auth-expired":return"Token Claude Code \u0111\xE3 h\u1EBFt h\u1EA1n/b\u1ECB revoke. Ch\u1EA1y: `claude auth logout && claude auth login`.";case"credit_balance_too_low":case"insufficient_quota":return"H\u1EBFt quota subscription. Upgrade plan ho\u1EB7c d\xF9ng LLMLite (avatar ai setup \u2192 ch\u1ECDn LLMLite).";case"invalid_api_key":return"API key invalid. Re-login: `claude auth login`.";case"rate_limit":return"B\u1ECB rate limit t\u1EA1m th\u1EDDi. Ch\u1EDD v\xE0i ph\xFAt r\u1ED3i ch\u1EA1y `avatar ai setup`.";case"timeout":return"Timeout 60s: (1) m\u1EA1ng VN ch\u1EADm \u2014 th\u1EED VPN, (2) Anthropic API spike \u2014 retry v\xE0i ph\xFAt, (3) token v\u1EABn auth nh\u01B0ng quota h\u1EBFt \xE2m th\u1EA7m \u2014 check t\u1EA1i claude.ai/settings/usage.";default:return"L\u1ED7i ch\u01B0a bi\u1EBFt. Xem stderr \u1EDF tr\xEAn + ch\u1EA1y `claude --print ok` tay \u0111\u1EC3 debug."}}function hn(){let t=dn("claude",["--print",Ka],{encoding:"utf8",timeout:qr,stdio:["ignore","pipe","pipe"]});if(t.signal==="SIGTERM"||t.status===143||t.error?.code==="ETIMEDOUT")return{ok:!1,reason:"timeout",detail:`claude --print > ${qr/1e3}s (m\u1EA1ng ch\u1EADm / API rate limit / token revoked kh\xF4ng return error)`};let n=t.stderr||"",r=t.stdout||"";if(t.status===0)return{ok:!0};let i=r.trim(),s=n.toLowerCase();if(i.length>20&&!s.includes("error")&&!s.includes("limit")&&!s.includes("quota")&&!s.includes("401"))return o.warn(`claude --print exit=${t.status} nh\u01B0ng c\xF3 response (${i.length} chars). Accept v\u1EDBi caution.`),{ok:!0};let a=Fa(`${n}
|
|
43
|
+
${r}`);return a==="unknown"&&(o.warn(`[debug] claude --print exit=${t.status} signal=${t.signal??"none"}`),n.trim()&&o.warn(`[debug] stderr: ${n.slice(0,500)}`),r.trim()&&o.warn(`[debug] stdout: ${r.slice(0,300)}`)),{ok:!1,reason:a,detail:n.slice(0,500)||r.slice(0,500)}}import{spawnSync as Jr}from"child_process";var Va=5e3,Ba=/(\d+\.\d+\.\d+)/;function Wa(){let e=Z()==="win32"?"where":"which",n=Jr(e,["claude"],{encoding:"utf8"});if(n.error||n.status!==0)return null;let r=(n.stdout||"").trim();return r?r.split(/\r?\n/)[0].trim():null}function qa(){let t=Jr("claude",["--version"],{encoding:"utf8",timeout:Va});if(t.error||t.status!==0)return null;let e=(t.stdout||"").trim();return Ba.exec(e)?.[1]??null}var it=null;function Mt(){if(it!==null)return it;let t=Wa();return t?(it={installed:!0,version:qa(),path:t},it):(it={installed:!1,version:null,path:null},it)}function Zt(){it=null}import{spawnSync as za}from"child_process";f();var Yr=300*1e3,Xr="@anthropic-ai/claude-code",ot=class extends Error{reason;exitCode;constructor(e,n,r=null){super(n),this.name="InstallClaudeCodeError",this.reason=e,this.exitCode=r}};function Ja(t,e){let n=e.toLowerCase();return n.includes("eacces")||n.includes("permission denied")?new ot("permission-denied",`npm install -g c\u1EA7n quy\u1EC1n. Th\u1EED: sudo npm install -g ${Xr} ho\u1EB7c fix npm prefix (npm config set prefix ~/.npm-global).`,t):n.includes("enospc")||n.includes("no space")?new ot("disk-full","\u0110\u0129a \u0111\u1EA7y. Free disk space r\u1ED3i th\u1EED l\u1EA1i.",t):new ot("generic",`npm install th\u1EA5t b\u1EA1i (exit ${t??"null"}). Xem log npm ph\xEDa tr\xEAn.`,t)}function Qr(){o.info("\u0110ang c\xE0i Claude Code qua npm (c\xF3 th\u1EC3 m\u1EA5t 1-2 ph\xFAt)...");let t=za("npm",["install","-g",Xr],{stdio:["inherit","inherit","pipe"],timeout:Yr,encoding:"utf8"});if(t.signal==="SIGTERM")throw new ot("timeout",`npm install timeout sau ${Yr/1e3}s. Check m\u1EA1ng r\u1ED3i th\u1EED l\u1EA1i.`,null);if(t.status!==0)throw t.stderr&&process.stderr.write(t.stderr),Ja(t.status,t.stderr||"");Zt();let e=Mt();if(!e.installed||!e.path)throw new ot("binary-not-in-path","npm c\xE0i xong nh\u01B0ng `claude` kh\xF4ng trong PATH. Reload shell (source ~/.zshrc) ho\u1EB7c th\xEAm npm global bin v\xE0o PATH.",null);return o.success(`\u0110\xE3 c\xE0i Claude Code${e.version?` v${e.version}`:""} t\u1EA1i ${e.path}`),{version:e.version,path:e.path}}import{readFileSync as Ya}from"fs";import{homedir as Xa}from"os";import{join as Qa}from"path";import{select as Zr}from"@inquirer/prompts";function Za(){return Qa(Xa(),".claude","settings.json")}function wn(){let t=Za(),e;try{e=Ya(t,"utf8")}catch{return{exists:!1,hasBaseUrl:!1,hasToken:!1}}let n;try{n=JSON.parse(e)}catch{return{exists:!0,hasBaseUrl:!1,hasToken:!1}}let r=n.env||{},i=typeof r.ANTHROPIC_BASE_URL=="string"?r.ANTHROPIC_BASE_URL:void 0,s=typeof r.ANTHROPIC_AUTH_TOKEN=="string"&&r.ANTHROPIC_AUTH_TOKEN.length>0,a=typeof n.model=="string"?n.model:void 0;return{exists:!0,hasBaseUrl:!!i,baseUrl:i,hasToken:s,model:a,rawSettings:n}}async function ti(t=wn()){return t.exists&&t.hasBaseUrl&&t.hasToken&&await Zr({message:`Ph\xE1t hi\u1EC7n AI config global (base URL: ${t.baseUrl}). D\xF9ng cho project n\xE0y?`,choices:[{name:"a. Yes \u2014 copy config global v\xE0o .claude/settings.json (per-project)",value:"use-global"},{name:"b. No \u2014 setup ri\xEAng (ch\u1ECDn provider kh\xE1c)",value:"setup-fresh"}]})==="use-global"?"use-global":await Zr({message:"Ch\u1ECDn provider cho AI tools:",choices:[{name:"1. Claude Code Subscription (d\xF9ng quota c\xE1 nh\xE2n Anthropic, OAuth login)",value:"subscription"},{name:"2. LLM key NAL (ai.nal.vn \u2014 gateway n\u1ED9i b\u1ED9, key sk-...)",value:"llmlite"},{name:"3. Anthropic API key tr\u1EF1c ti\u1EBFp (console.anthropic.com, key sk-ant-...)",value:"anthropic"}]})}f();import{password as tc,select as ec}from"@inquirer/prompts";var te="https://api.anthropic.com",nc="2023-06-01",ei=1e4;function rc(t){return t.length<=12?"sk-ant-***":`${t.slice(0,7)}...${t.slice(-4)}`}function ic(t){let e=t.trim();return e.length===0?"API key b\u1EAFt bu\u1ED9c":e.startsWith("sk-ant-")?!0:"Anthropic API key th\u01B0\u1EDDng b\u1EAFt \u0111\u1EA7u b\u1EB1ng 'sk-ant-' (l\u1EA5y t\u1EEB console.anthropic.com)."}async function oc(){return await tc({message:"Anthropic API key (sk-ant-..., \u1EA9n input):",mask:"*",validate:ic})}async function sc(t){let e=new AbortController,n=setTimeout(()=>e.abort(),ei);try{let r=await fetch(`${te}/v1/models`,{method:"GET",headers:{"x-api-key":t,"anthropic-version":nc,Accept:"application/json"},signal:e.signal});if(r.status===401)throw new Error("API key invalid (HTTP 401). Check key tr\xEAn console.anthropic.com.");if(r.status===403)throw new Error("API key b\u1ECB reject (HTTP 403). Key c\xF3 th\u1EC3 \u0111\xE3 b\u1ECB revoke ho\u1EB7c thi\u1EBFu permission.");if(r.status===429)throw new Error("Rate limit (HTTP 429). Ch\u1EDD v\xE0i gi\xE2y r\u1ED3i th\u1EED l\u1EA1i.");if(!r.ok)throw new Error(`Fetch models th\u1EA5t b\u1EA1i (HTTP ${r.status}).`);let s=((await r.json()).data||[]).map(a=>typeof a.id=="string"?a.id:null).filter(a=>a!==null);if(s.length===0)throw new Error("Anthropic tr\u1EA3 v\u1EC1 list r\u1ED7ng. Li\xEAn h\u1EC7 support ho\u1EB7c check account.");return s}catch(r){throw r.name==="AbortError"?new Error(`Connect ${te} timeout sau ${ei/1e3}s.`):r}finally{clearTimeout(n)}}async function ac(t){if(t.length===1){let n=t[0];return o.info(`Auto-pick model: ${n} (ch\u1EC9 1 model available)`),n}let e=[...t].sort((n,r)=>{let i=s=>{let a=s.toLowerCase();return a.includes("sonnet")?0:a.includes("opus")?1:a.includes("haiku")?2:3};return i(n)-i(r)});return await ec({message:"Ch\u1ECDn model m\u1EB7c \u0111\u1ECBnh cho project:",choices:e.map(n=>({name:n,value:n}))})}async function ni(){let t=await oc();o.info(`Verify key (${rc(t)}) qua ${te}/v1/models...`);let e=await sc(t);o.success(`Endpoint OK \u2014 ${e.length} models available`);let n=await ac(e);return{apiKey:t,baseUrl:te,model:n}}f();import{input as cc,password as lc,select as uc}from"@inquirer/prompts";var pc="https://ai.nal.vn",ri=1e4;function K(t){return t.length<=8?"sk-***":`${t.slice(0,3)}...${t.slice(-4)}`}async function mc(){return await lc({message:"LLMLite API key (\u1EA9n input):",mask:"*",validate:t=>t.trim().length>0?!0:"API key b\u1EAFt bu\u1ED9c"})}async function dc(t=pc){return(await cc({message:"LLMLite base URL:",default:t,validate:n=>/^https?:\/\//.test(n)?!0:"Ph\u1EA3i l\xE0 URL h\u1EE3p l\u1EC7 (http/https)"})).replace(/\/+$/,"")}async function gc(t,e){let n=new AbortController,r=setTimeout(()=>n.abort(),ri);try{let i=await fetch(`${t}/v1/models`,{method:"GET",headers:{Authorization:`Bearer ${e}`,Accept:"application/json"},signal:n.signal});if(i.status===401||i.status===403)throw new Error(`API key invalid (HTTP ${i.status}).`);if(i.status===404)throw new Error(`Endpoint /v1/models kh\xF4ng t\u1ED3n t\u1EA1i tr\xEAn ${t}.`);if(!i.ok)throw new Error(`Fetch models th\u1EA5t b\u1EA1i (HTTP ${i.status}).`);let a=((await i.json()).data||[]).map(c=>typeof c.id=="string"?c.id:null).filter(c=>c!==null);if(a.length===0)throw new Error("LLMLite tr\u1EA3 v\u1EC1 list r\u1ED7ng. Li\xEAn h\u1EC7 admin NAL.");return a}catch(i){if(i.name==="AbortError"){let c=t.includes("nal.vn")||t.includes("nal-vn")?"\n Hint: ai.nal.vn l\xE0 internal endpoint NAL \u2014 ki\u1EC3m tra VPN NAL \u0111\xE3 b\u1EADt ch\u01B0a, ho\u1EB7c tcp test b\u1EB1ng `curl -v https://ai.nal.vn`.":`
|
|
44
|
+
Hint: check m\u1EA1ng / firewall / VPN, ho\u1EB7c base URL c\xF3 \u0111\xFAng kh\xF4ng.`;throw new Error(`Connect ${t} timeout sau ${ri/1e3}s.${c}`)}let s=i.message||String(i);if(s.toLowerCase().includes("fetch failed")||s.includes("ENOTFOUND")){let c=t.includes("nal.vn")?" (ai.nal.vn c\u1EA7n VPN NAL \u2014 ki\u1EC3m tra VPN \u0111\xE3 b\u1EADt ch\u01B0a)":"";throw new Error(`Network error khi connect ${t}: ${s}${c}`)}throw i}finally{clearTimeout(r)}}async function fc(t){let e=t.filter(r=>r.toLowerCase().includes("claude"));if(e.length===1){let r=e[0];return o.info(`Auto-pick model: ${r} (ch\u1EC9 1 claude alias tr\xEAn endpoint)`),r}let n=e.length>0?e:t;return await uc({message:"Ch\u1ECDn model m\u1EB7c \u0111\u1ECBnh cho project:",choices:n.map(r=>({name:r,value:r}))})}async function ii(){let t=await mc(),e=await dc();o.info(`Verify key (${K(t)}) qua ${e}/v1/models...`);let n=await gc(e,t);o.success(`Endpoint OK \u2014 ${n.length} models available`);let r=await fc(n);return{apiKey:t,baseUrl:e,model:r}}f();k();import{promises as hc}from"fs";import{join as wc}from"path";var kn=384;function kc(t){return wc(t,".claude","settings.json")}async function yc(t){if(!await m(t))return{};try{return await y(t)}catch(e){throw new Error(`Kh\xF4ng parse \u0111\u01B0\u1EE3c ${t} (JSON l\u1ED7i): ${e.message}. Backup file r\u1ED3i x\xF3a \u0111\u1EC3 Avatar t\u1EA1o l\u1EA1i.`)}}function bc(t,e){let{env:n,...r}=t,i={...r,model:e};if(n){let{ANTHROPIC_BASE_URL:s,ANTHROPIC_AUTH_TOKEN:a,ANTHROPIC_API_KEY:c,...l}=n;Object.keys(l).length>0&&(i.env=l)}return i}function oi(t){if(!t)return t;let{ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_API_KEY:n,...r}=t;return r}function vc(t,e,n,r,i){let a={...oi(t.env),ANTHROPIC_BASE_URL:n};return i||(a.ANTHROPIC_AUTH_TOKEN=e),{...t,env:a,model:r,avatarProvider:"llmlite"}}function xc(t,e,n,r,i){let a={...oi(t.env),ANTHROPIC_BASE_URL:n};return i||(a.ANTHROPIC_API_KEY=e),{...t,env:a,model:r,avatarProvider:"anthropic"}}function Cc(t,e){let n=e.env||{},r=typeof e.model=="string"?e.model:void 0;return{...t,env:{...t.env||{},...n},...r?{model:r}:{}}}async function st(t,e){let n=kc(t),r=await yc(n),i;switch(e.provider){case"subscription":i=bc(r,e.model);break;case"llmlite":i=vc(r,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"anthropic":i=xc(r,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"use-global":i=Cc(r,e.sourceSettings);break}await A(n,i,kn);try{await hc.chmod(n,kn)}catch{}return{path:n,mode:kn}}var G="sonnet";async function ee(t){try{o.info("Setup AI provider cho workspace...");let e=Mt();if(e.installed)o.success(`Claude Code \u0111\xE3 c\xF3${e.version?` v${e.version}`:""}`);else if(o.info("Ch\u01B0a c\xF3 Claude Code \u2014 s\u1EBD t\u1EF1 c\xE0i qua npm."),Qr(),Zt(),e=Mt(),!e.installed)throw new Error("C\xE0i Claude Code xong nh\u01B0ng v\u1EABn kh\xF4ng detect \u0111\u01B0\u1EE3c binary.");let n=wn();switch(await ti(n)){case"subscription":{let i=gn();if(i.state!=="authenticated"&&(fn(),i=gn()),i.state==="authenticated"&&i.subscriptionType)return await st(t.workspacePath,{provider:"subscription",model:G}),await b("ai_setup",`provider=subscription,result=ok,plan=${i.subscriptionType},probe=skipped`),o.success(`AI ready \xB7 Subscription (${i.subscriptionType}) \xB7 model=${G}`),{ok:!0,provider:"subscription",model:G};o.dim("Auth status kh\xF4ng tr\u1EA3 subscriptionType \u2014 verify quota (30-60s)...");let s=hn();if(!s.ok&&s.reason==="auth-expired"&&(o.warn("Token Claude Code \u0111\xE3 h\u1EBFt h\u1EA1n. T\u1EF1 \u0111\u1ED9ng re-login..."),fn(),s=hn()),!s.ok&&(s.reason==="timeout"||s.reason==="unknown"))return o.warn(`Probe verify ${s.reason} \u2014 accept trust auth status. Ti\u1EBFp t\u1EE5c.`),s.detail?.trim()&&o.warn(` Chi ti\u1EBFt: ${s.detail.slice(0,200)}`),await st(t.workspacePath,{provider:"subscription",model:G}),await b("ai_setup",`provider=subscription,result=ok,probe=${s.reason}-soft-pass`),o.success(`AI ready \xB7 Subscription (probe ${s.reason}, soft-pass) \xB7 model=${G}`),{ok:!0,provider:"subscription",model:G};if(!s.ok){let a=s.reason??"unknown";return await b("ai_setup",`provider=subscription,result=no-quota,reason=${a}`),o.warn(`Subscription verify th\u1EA5t b\u1EA1i (${a}).`),s.detail?.trim()&&o.warn(` Chi ti\u1EBFt: ${s.detail.slice(0,200)}`),o.warn(` \u2192 ${zr(a)}`),{ok:!1,reason:`subscription-${a}`,phase:"quota"}}return await st(t.workspacePath,{provider:"subscription",model:G}),await b("ai_setup","provider=subscription,result=ok"),o.success(`AI ready \xB7 Subscription \xB7 model=${G}`),{ok:!0,provider:"subscription",model:G}}case"llmlite":{let i=await ii();return await st(t.workspacePath,{provider:"llmlite",apiKey:i.apiKey,baseUrl:i.baseUrl,model:i.model,skipApiKey:!1}),await b("ai_setup",`provider=llmlite,result=ok,model=${i.model},base=${i.baseUrl},storage=settings.json`),o.success(`AI ready \xB7 LLMLite (NAL) \xB7 model=${i.model} \xB7 ${i.baseUrl}`),{ok:!0,provider:"llmlite",model:i.model}}case"anthropic":{let i=await ni();return await st(t.workspacePath,{provider:"anthropic",apiKey:i.apiKey,baseUrl:i.baseUrl,model:i.model,skipApiKey:!1}),await b("ai_setup",`provider=anthropic,result=ok,model=${i.model},storage=settings.json`),o.success(`AI ready \xB7 Anthropic Direct \xB7 model=${i.model} \xB7 ${i.baseUrl}`),{ok:!0,provider:"anthropic",model:i.model}}case"use-global":{if(!n.rawSettings)throw new Error("use-global ch\u1ECDn nh\u01B0ng kh\xF4ng \u0111\u1ECDc \u0111\u01B0\u1EE3c global settings.");return await st(t.workspacePath,{provider:"use-global",sourceSettings:n.rawSettings}),await b("ai_setup","provider=use-global,result=ok"),o.success(`AI ready \xB7 Copy t\u1EEB global config (${n.baseUrl??"subscription"})`),{ok:!0,provider:"use-global",model:n.model}}}}catch(e){let n=e instanceof Error?e.message:String(e);return o.warn(`AI setup th\u1EA5t b\u1EA1i: ${n}`),o.dim("Workspace v\u1EABn s\u1EB5n s\xE0ng. Setup AI sau qua: avatar ai setup"),await b("ai_setup",`result=failed,error=${n.slice(0,200)}`),{ok:!1,reason:n}}}f();import{spawnSync as Ac}from"child_process";f();var yn=1e4,si=3e4,ci=5,bn="say ok",ai="2023-06-01";async function Sc(t,e,n){o.info(`Testing LLMLite provider: ${t} (key: ${K(e)})`);let r=new AbortController,i=setTimeout(()=>r.abort(),yn);try{let s=await fetch(`${t}/v1/models`,{headers:{Authorization:`Bearer ${e}`},signal:r.signal});if(s.status===401||s.status===403)throw new Error(`API key invalid (HTTP ${s.status}). Re-run: avatar ai setup`);if(!s.ok)throw new Error(`Endpoint /v1/models l\u1ED7i (HTTP ${s.status}).`);let c=((await s.json()).data||[]).map(h=>typeof h.id=="string"?h.id:null).filter(h=>h!==null);if(o.success(`Connectivity OK \xB7 ${c.length} models available`),c.length>0){let h=c.slice(0,5).join(", "),C=c.length>5?` ...+${c.length-5} more`:"";o.dim(` Models: ${h}${C}`)}o.info(`Testing chat completion v\u1EDBi model "${n}"...`);let l=await fetch(`${t}/v1/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify({model:n,messages:[{role:"user",content:bn}],max_tokens:ci}),signal:r.signal});if(!l.ok){let h=(await l.text()).slice(0,200);throw new Error(`Chat completion fail (HTTP ${l.status}). ${h}`)}let u=await l.json(),d=typeof u.choices?.[0]?.message?.content=="string"?u.choices[0].message.content:"(empty response)",g=u.usage?.total_tokens??"?";o.success(`Response: "${String(d).trim().slice(0,100)}"`),o.dim(` Tokens used: ${g}`)}catch(s){throw s.name==="AbortError"?new Error(`Timeout ${yn/1e3}s. Check m\u1EA1ng / endpoint ${t}.`):s}finally{clearTimeout(i)}}function Tc(){o.info("Testing Subscription provider qua `claude --print`...");let t=Ac("claude",["--print",bn],{encoding:"utf8",timeout:si});if(t.signal==="SIGTERM")throw new Error(`Timeout ${si/1e3}s. Check m\u1EA1ng / endpoint.`);if(t.status!==0){let e=(t.stderr||"").toLowerCase();throw e.includes("401")||e.includes("invalid authentication")||e.includes("unauthorized")?new Error("Token Claude Code stale (401). Fix: `claude auth logout && claude auth login`."):new Error(`Test fail (exit ${t.status}). Stderr: ${(t.stderr||"").slice(0,200)}`)}o.success(`Response: "${(t.stdout||"").trim().slice(0,100)}"`)}async function Pc(t,e,n){o.info(`Testing Anthropic Direct provider: ${t} (key: ${K(e)})`);let r=new AbortController,i=setTimeout(()=>r.abort(),yn);try{let s=await fetch(`${t}/v1/models`,{headers:{"x-api-key":e,"anthropic-version":ai},signal:r.signal});if(s.status===401||s.status===403)throw new Error(`API key invalid (HTTP ${s.status}). Re-run: avatar ai setup`);if(!s.ok)throw new Error(`Endpoint /v1/models l\u1ED7i (HTTP ${s.status}).`);let c=((await s.json()).data||[]).map(g=>typeof g.id=="string"?g.id:null).filter(g=>g!==null);o.success(`Connectivity OK \xB7 ${c.length} models available`),o.info(`Testing message v\u1EDBi model "${n}"...`);let l=await fetch(`${t}/v1/messages`,{method:"POST",headers:{"x-api-key":e,"anthropic-version":ai,"Content-Type":"application/json"},body:JSON.stringify({model:n,max_tokens:ci,messages:[{role:"user",content:bn}]}),signal:r.signal});if(!l.ok){let g=(await l.text()).slice(0,200);throw new Error(`Message endpoint fail (HTTP ${l.status}): ${g}`)}let d=((await l.json()).content||[]).map(g=>typeof g.text=="string"?g.text:"").join("").trim().slice(0,100);o.success(`Response: "${d}"`)}finally{clearTimeout(i)}}async function li(t){let e=t.env||{},n=typeof e.ANTHROPIC_BASE_URL=="string"?e.ANTHROPIC_BASE_URL:void 0,r=typeof e.ANTHROPIC_AUTH_TOKEN=="string"?e.ANTHROPIC_AUTH_TOKEN:void 0,i=typeof e.ANTHROPIC_API_KEY=="string"?e.ANTHROPIC_API_KEY:void 0,s=typeof t.model=="string"?t.model:"default";return i&&n?(await Pc(n,i,s),{ok:!0,provider:"anthropic",message:"Anthropic Direct provider working"}):n&&r?(await Sc(n,r,s),{ok:!0,provider:"llmlite",message:"LLMLite provider working"}):(Tc(),{ok:!0,provider:"subscription",message:"Subscription provider working"})}async function ne(){let t=process.cwd(),e=wt(t);return e||(o.error(`Kh\xF4ng t\xECm th\u1EA5y Avatar workspace t\u1EEB th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i.
|
|
44
45
|
Avatar workspace c\u1EA7n c\xF3: .claude/ + CLAUDE.md + src/ (ho\u1EB7c .gitmodules).
|
|
45
46
|
B\u1EA1n \u0111ang \u1EDF: ${t}
|
|
46
|
-
Cd v\xE0o workspace dir (vd /path/to/<project>-workspace) r\u1ED3i ch\u1EA1y l\u1EA1i.`),process.exit(1)),e!==t&&s.dim(`Detected workspace root: ${e}`),e}async function bn(t){let e=io(t,".claude","settings.json");if(!await m(e))return{};try{return await y(e)}catch{return{}}}async function Cc(){let t=await te();await Zt({workspacePath:t})}async function Ac(){let t=await te(),e=await bn(t),n=e.env||{},r=typeof n.ANTHROPIC_BASE_URL=="string"?n.ANTHROPIC_BASE_URL:void 0,o=typeof n.ANTHROPIC_AUTH_TOKEN=="string"?n.ANTHROPIC_AUTH_TOKEN:void 0,i=typeof n.ANTHROPIC_API_KEY=="string"?n.ANTHROPIC_API_KEY:void 0,a=typeof e.model=="string"?e.model:void 0,c,l;i?(c=r?.includes("api.anthropic.com")||i.startsWith("sk-ant-")?"Anthropic Direct":"Custom (API key)",l=K(i)):r&&o?(c="LLMLite",l=K(o)):o?(c="Custom",l=K(o)):(c="Subscription (default)",l="(kh\xF4ng set \u2014 d\xF9ng subscription auth)"),s.info(`Project: ${t}`),s.info(`Provider: ${c}${r?` (${r})`:""}`),s.info(`Model: ${a??"(default \u2014 Claude Code ch\u1ECDn)"}`),s.info(`Token: ${l}`)}async function Sc(){let t=await te(),e=await bn(t);try{let n=await oo(e);s.success(`\u2713 ${n.message}`)}catch(n){s.error(`Test fail: ${n.message}`),process.exit(1)}}async function Tc(t){let e=await te(),n=io(e,".claude","settings.json"),r=await bn(e);if(!t.yes&&!await xc({message:"X\xF3a AI config (v\u1EC1 d\xF9ng Claude Code Subscription default)?",default:!1})){s.dim("\u0110\xE3 h\u1EE7y.");return}let{env:o,...i}=r,a={...i};if(o){let{ANTHROPIC_BASE_URL:c,ANTHROPIC_AUTH_TOKEN:l,ANTHROPIC_API_KEY:p,...d}=o;Object.keys(d).length>0&&(a.env=d)}Object.keys(a).length===0?(await vc.unlink(n).catch(()=>{}),s.success("\u0110\xE3 x\xF3a .claude/settings.json (clean state)")):(await C(n,a,384),s.success("\u0110\xE3 reset env block trong .claude/settings.json"))}function so(t){let e=t.command("ai").description("Qu\u1EA3n l\xFD AI provider config (M12)");e.command("setup").description("Wizard setup/re-config AI provider cho workspace hi\u1EC7n t\u1EA1i").action(async()=>{await Cc()}),e.command("status").description("Show AI config hi\u1EC7n t\u1EA1i (mask token)").action(async()=>{await Ac()}),e.command("test").description("Verify AI provider qua cheap prompt").action(async()=>{await Sc()}),e.command("reset").description("X\xF3a env.ANTHROPIC_* kh\u1ECFi settings.json (v\u1EC1 Subscription default)").option("--yes","Skip confirm").action(async n=>{await Tc(n)})}import{spawnSync as Cn}from"child_process";import{promises as bo}from"fs";import{join as Lt}from"path";import Hc from"boxen";import{join as Uc}from"path";k();import{promises as Ic}from"fs";import{join as po}from"path";k();f();import{promises as Pc}from"fs";import ao,{join as vn}from"path";async function Ec(t,e){let r=e.trim().match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);if(!r?.[2])return!0;let o=r[2];if(o.startsWith("/"))return s.warn(`Pack hook reject: absolute path "${o}" \u2014 ch\u1EC9 accept relative path t\u1EDBi workspace.`),!1;let i=vn(t,o),a=ao.resolve(t),c=ao.resolve(i);return!c.startsWith(`${a}/`)&&c!==a?(s.warn(`Pack hook reject: path "${o}" resolve ra ngo\xE0i workspace (path traversal).`),!1):await m(i)}function xn(t){let e=new Date,n=`${e.getFullYear().toString().slice(-2)+String(e.getMonth()+1).padStart(2,"0")+String(e.getDate()).padStart(2,"0")}-${String(e.getHours()).padStart(2,"0")}${String(e.getMinutes()).padStart(2,"0")}`;return`${t}.backup-${n}`}function Ot(t,e){let n=new Set,r=[];for(let o of[...t,...e]){let i=typeof o=="string"?o:JSON.stringify(o);n.has(i)||(n.add(i),r.push(o))}return r}function $c(t){return t.replace(/\$\{CLAUDE_PROJECT_DIR\}\//g,"").trim()}function Rc(t){if(typeof t!="object"||t===null)return[];let e=t,n=Array.isArray(e.hooks)?e.hooks:[],r=[];for(let o of n)if(typeof o=="object"&&o!==null){let i=o.command;typeof i=="string"&&r.push(i)}return r}function co(t){let e=t.map((i,a)=>{let c=Rc(i),l=c.some(p=>p.includes("${CLAUDE_PROJECT_DIR}"));return{index:a,entry:i,commands:c,hasVar:l}}),n=new Map;for(let i of e){let a=i.commands.map($c).sort().join("|");if(!a)continue;let c=n.get(a)??[];c.push(i),n.set(a,c)}let r=new Set;for(let[,i]of n)if(!(i.length<2||!i.some(c=>c.hasVar)))for(let c of i)c.hasVar||r.add(c.index);return r.size===0?{entries:t,droppedCount:0}:{entries:t.filter((i,a)=>!r.has(a)),droppedCount:r.size}}function _c(t,e){let n=[],r={...e},o=0;for(let i of Object.keys(r)){let a=r[i]||[],{entries:c,droppedCount:l}=co(a);l>0&&(r[i]=c,o+=l,n.includes(i)||n.push(i))}for(let[i,a]of Object.entries(t)){let c=r[i]||[],l=Ot(c,a),{entries:p,droppedCount:d}=co(l);d>0&&(o+=d),p.length!==c.length&&(n.includes(i)||n.push(i)),r[i]=p}return{merged:r,touchedEvents:n,migratedCount:o}}async function ee(t){let e=vn(t,".claude","pack","templates","settings.json.tpl"),n=vn(t,".claude","settings.json");if(!await m(e))return{action:"no-pack-template",changes:[]};let r;try{let p=await N(e);r=JSON.parse(p)}catch(p){throw new Error(`Pack settings template kh\xF4ng parse \u0111\u01B0\u1EE3c JSON: ${p.message}. Path: ${e}`)}let o={},i=!1;if(await m(n)){i=!0;try{o=await y(n)}catch(p){throw new Error(`Project settings.json kh\xF4ng parse \u0111\u01B0\u1EE3c: ${p.message}. Manual fix tr\u01B0\u1EDBc khi sync.`)}}let a=[],c={...o};if(r.statusLine&&!o.statusLine&&(await Ec(t,r.statusLine.command)?(c.statusLine=r.statusLine,a.push("statusLine added")):a.push(`statusLine SKIPPED (file ref '${r.statusLine.command}' kh\xF4ng t\u1ED3n t\u1EA1i)`)),typeof r.includeCoAuthoredBy=="boolean"&&typeof o.includeCoAuthoredBy!="boolean"&&(c.includeCoAuthoredBy=r.includeCoAuthoredBy,a.push("includeCoAuthoredBy added")),r.model&&!o.model&&(c.model=r.model,a.push("model added")),r.env||o.env){let p={...o.env||{}},d=!1;for(let[g,h]of Object.entries(p))typeof h=="string"&&h.includes("llm.nal.vn")&&(p[g]=h.replace(/llm\.nal\.vn/g,"ai.nal.vn"),d=!0);if(d&&a.push("rewrote legacy llm.nal.vn \u2192 ai.nal.vn in env vars"),r.env)for(let[g,h]of Object.entries(r.env))g in p||(p[g]=h,d=!0);d&&(c.env=p,a.some(g=>g.includes("env vars"))||a.push("env vars added from pack"))}if(r.permissions){let p=o.permissions?.allow||[],d=o.permissions?.deny||[],g=r.permissions.allow||[],h=r.permissions.deny||[],x=Ot(p,g),S=Ot(d,h);(x.length!==p.length||S.length!==d.length)&&(c.permissions={allow:x,deny:S},a.push(`permissions union (+${x.length-p.length} allow, +${S.length-d.length} deny)`))}if(r.hooks){let p=o.hooks||{},{merged:d,touchedEvents:g,migratedCount:h}=_c(r.hooks,p);(g.length>0||h>0)&&(c.hooks=d,g.length>0&&a.push(`hooks added for events: ${g.join(", ")}`),h>0&&a.push(`migrated ${h} stale relative-path hook entries to use \${CLAUDE_PROJECT_DIR} version (fixes hook failure when shell cwd changes)`))}if(a.length===0)return{action:"no-change",changes:[]};let l;return i&&(l=xn(n),await Pc.copyFile(n,l)),await C(n,c),{action:"merged",backupPath:l,changes:a}}function Nc(t,e){return po(t,".claude","pack","tools",e,"tool.json")}async function Mt(t,e){let n=Nc(t,e);return await m(n)?await y(n):null}var Oc=[".claude","settings.json"];function mo(t){return po(t,...Oc)}function ne(t){let e=(t.hooks??[]).map(n=>n.command??"").sort().join("\0");return`${t.matcher??""}${e}`}async function go(t){let e=mo(t);if(!await m(e))return{settings:{},existed:!1};try{return{settings:await y(e),existed:!0}}catch(n){throw new Error(`Project settings.json kh\xF4ng parse \u0111\u01B0\u1EE3c: ${n.message}. Manual fix tr\u01B0\u1EDBc khi enable/disable tool.`)}}async function fo(t,e,n){let r=mo(t),o;return n&&(o=xn(r),await Ic.copyFile(r,o)),await C(r,e),o}function lo(t){let e=(t.hooks??[]).map(n=>(n.command??"").replace(/\$\{CLAUDE_PROJECT_DIR\}\//g,"").trim()).sort().join("|");return`${t.matcher??""}::${e}`}function uo(t){return(t.hooks??[]).some(e=>(e.command??"").includes("${CLAUDE_PROJECT_DIR}"))}function Mc(t,e){let n=new Set;for(let i of e)uo(i)&&n.add(lo(i));if(n.size===0)return{migratedUser:t,droppedCount:0};let r=0;return{migratedUser:t.filter(i=>uo(i)?!0:n.has(lo(i))?(r++,!1):!0),droppedCount:r}}async function re(t,e){let{settings:n,existed:r}=await go(t),o={...n},i=[],a=e.settings.hooks??{};if(Object.keys(a).length>0){let p=n.hooks??{},d={...p},g=[],h=0;for(let[x,S]of Object.entries(a)){let M=p[x]??[],{migratedUser:mt,droppedCount:R}=Mc(M,S);R>0&&(h+=R);let E=new Set(mt.map(ne)),T=S.filter(J=>!E.has(ne(J)));(T.length>0||R>0)&&(d[x]=[...mt,...T],g.push(x))}g.length>0&&(o.hooks=d,i.push(`hooks added: ${g.join(", ")}`),h>0&&i.push(`migrated ${h} stale relative-path entries \u2192 \\{CLAUDE_PROJECT_DIR\\} version`))}let c=e.settings.permissions?.deny??[];if(c.length>0){let p=n.permissions?.deny??[],d=Ot(p,c);d.length!==p.length&&(o.permissions={...n.permissions,deny:d},i.push(`deny +${d.length-p.length}`))}return i.length===0?{action:"no-change",changes:[]}:{action:"enabled",backupPath:await fo(t,o,r),changes:i}}async function ho(t,e){let{settings:n,existed:r}=await go(t);if(!r)return{action:"no-change",changes:[]};let{hooks:o,permissions:i,...a}=n,c={...a},l=[],p=e.settings.hooks??{},d=n.hooks??{},g={},h=[];for(let[R,E]of Object.entries(d)){let T=p[R];if(!T){g[R]=E;continue}let J=new Set(T.map(ne)),dt=E.filter(Y=>!J.has(ne(Y)));dt.length!==E.length&&h.push(R),dt.length>0&&(g[R]=dt)}Object.keys(g).length>0&&(c.hooks=g),h.length>0&&l.push(`hooks removed: ${h.join(", ")}`);let x=new Set(e.settings.permissions?.deny??[]),S=n.permissions?.deny??[],M=x.size>0?S.filter(R=>!x.has(R)):S;if(n.permissions){let{deny:R,...E}=n.permissions,T={...E,...M.length>0?{deny:M}:{}};Object.keys(T).length>0&&(c.permissions=T)}return M.length!==S.length&&l.push(`deny -${S.length-M.length}`),l.length===0?{action:"no-change",changes:[]}:{action:"disabled",backupPath:await fo(t,c,r),changes:l}}k();k();import{join as Lc}from"path";var Gc=".claude/avatar-tools.json";function wo(){return{tools:{}}}function ko(t){return Lc(t,Gc)}async function kt(t){let e=ko(t);if(!await m(e))return wo();try{return{tools:(await y(e)).tools??{}}}catch{return wo()}}async function jc(t,e){await C(ko(t),e)}async function oe(t,e,n){let r=await kt(t);return r.tools[e]={enabled:n.enabled,version:n.version,appliedAt:new Date().toISOString()},await jc(t,r),r}async function yt(t){let e=await kt(t);return Object.entries(e.tools).filter(([,n])=>n.enabled).map(([n])=>n)}function Dc(t,e){let n=e.settings.hooks??{},r=t.hooks??{};for(let[o,i]of Object.entries(n)){let a=r[o]??[],c=new Set(a.flatMap(l=>(l.hooks??[]).map(p=>p.command??"")));for(let l of i)for(let p of l.hooks??[])if(p.command&&!c.has(p.command))return!1}return!0}async function yo(t){let e=await yt(t);if(e.length===0)return[];let n=Uc(t,".claude","settings.json"),r={};if(await m(n))try{r=await y(n)}catch{r={}}let o=[];for(let i of e){let a=await Mt(t,i);if(!a){o.push({name:`Tool: ${i}`,status:"warn",detail:"state=enabled nh\u01B0ng pack thi\u1EBFu manifest (version skew). Ch\u1EA1y 'avatar sync'.",fixable:!1});continue}Dc(r,a)?o.push({name:`Tool: ${i}`,status:"ok",detail:`enabled, hook active (v${a.version})`,fixable:!1}):o.push({name:`Tool: ${i}`,status:"fail",detail:"state=enabled nh\u01B0ng hook thi\u1EBFu trong settings.json \u2014 fixable (re-apply)",fixable:!0,fix:async()=>{await re(t,a)}})}return o}k();f();function vo(t){t.command("doctor").description("Ch\u1EA9n \u0111o\xE1n c\xE0i \u0111\u1EB7t Avatar: hooks, MCP, login, submodule, ...").option("--fix","T\u1EF1 \u0111\u1ED9ng fix c\xE1c issue c\xF3 th\u1EC3 fix t\u1EF1 \u0111\u1ED9ng").action(async e=>{try{let n=await Kc(process.cwd());Fc(n),e.fix&&await Vc(n)}catch(n){s.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function Kc(t){let e=[],n=process.versions.node,[r,o]=n.split(".").map(E=>Number.parseInt(E,10)),i=(r??0)>18||(r??0)===18&&(o??0)>=17;e.push({name:"Node.js version",status:i?"ok":"fail",detail:`v${n}${i?"":" (c\u1EA7n >= 18.17)"}`,fixable:!1});let a=await H();a?ot(a)?e.push({name:"Login status",status:"warn",detail:`Token h\u1EBFt h\u1EA1n (${a.email}) \u2014 ch\u1EA1y 'avatar login'`,fixable:!1}):e.push({name:"Login status",status:"ok",detail:`Logged in: ${a.email}`,fixable:!1}):e.push({name:"Login status",status:"fail",detail:"Ch\u01B0a \u0111\u0103ng nh\u1EADp \u2014 ch\u1EA1y 'avatar login'",fixable:!1});let c=Lt(t,".claude","pack"),l=Lt(t,"CLAUDE.md"),[p,d]=await Promise.all([m(c),m(l)]);if(e.push({name:"team-ai-pack installation",status:p?"ok":"warn",detail:p?c:"Avatar ch\u01B0a init \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),e.push({name:"CLAUDE.md",status:d?"ok":"warn",detail:d?"t\u1ED3n t\u1EA1i \u1EDF project root":"thi\u1EBFu \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),p){let E=Lt(t,".claude",".gitignore"),T=!1;await m(E)&&(T=(await bo.readFile(E,"utf8")).includes("settings.json")),e.push({name:"\u{1F512} settings.json gitignored (.claude/.gitignore)",status:T?"ok":"fail",detail:T?"an to\xE0n \u2014 settings.json kh\xF4ng commit nh\u1EA7m":"CRITICAL: settings.json ch\u1EE9a API key \u2014 ch\u1EA1y 'avatar doctor --fix' \u0111\u1EC3 b\u1EA3o v\u1EC7",fixable:!T,fix:T?void 0:async()=>{let{writeClaudeGitignore:J}=await Promise.resolve().then(()=>(At(),Xn));await J(t)}})}let g=Cn("which",["python"]),h=Cn("which",["python3"]),x=g.status===0,S=h.status===0;S&&!x?e.push({name:"Python binary alias",status:"warn",detail:`Ch\u1EC9 c\xF3 python3 (modern macOS). Pack scripts th\u01B0\u1EDDng ref 'python' \u2192 suggest: ln -s ${h.stdout.toString().trim()} ~/.local/bin/python`,fixable:!1}):x?e.push({name:"Python binary",status:"ok",detail:`python: ${g.stdout.toString().trim()}`,fixable:!1}):S&&e.push({name:"Python binary",status:"ok",detail:`python3: ${h.stdout.toString().trim()}`,fixable:!1});let M=Lt(t,".claude","settings.json");if(await m(M))try{let E=await bo.readFile(M,"utf8"),T=JSON.parse(E);if(T.statusLine?.command){let dt=T.statusLine.command.trim().match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);if(dt?.[2]){let Y=dt[2],Ri=Y.startsWith("/")?Y:Lt(t,Y),Gn=await m(Ri);e.push({name:"statusLine command",status:Gn?"ok":"fail",detail:Gn?`ref OK: ${Y}`:`BROKEN: settings.json ref '${Y}' nh\u01B0ng file kh\xF4ng t\u1ED3n t\u1EA1i. Strip field statusLine ho\u1EB7c fix path.`,fixable:!1})}}}catch{}let mt=Cn("which",["claude"]),R=mt.status===0;if(e.push({name:"Claude Code CLI",status:R?"ok":"warn",detail:R?mt.stdout.toString().trim():"kh\xF4ng t\xECm th\u1EA5y 'claude' tr\xEAn PATH",fixable:!1}),p){let E=await yo(t);e.push(...E)}return e}function Fc(t){let e=[u.bold("Avatar Doctor"),"\u2500".repeat(48)],n=0,r=0,o=0;for(let i of t){let a=i.status==="ok"?u.green("\u2713"):i.status==="warn"?u.yellow("\u26A0"):u.red("\u2717");e.push(`${a} ${i.name.padEnd(28)} ${u.dim(i.detail)}`),i.status==="ok"?n+=1:(r+=1,i.fixable&&(o+=1))}e.push("\u2500".repeat(48)),e.push(`${n} checks passed, ${r} issue${r===1?"":"s"}${o>0?` (${o} fixable \u2014 ch\u1EA1y 'avatar doctor --fix')`:""}`),process.stdout.write(`${Hc(e.join(`
|
|
47
|
+
Cd v\xE0o workspace dir (vd /path/to/<project>-workspace) r\u1ED3i ch\u1EA1y l\u1EA1i.`),process.exit(1)),e!==t&&o.dim(`Detected workspace root: ${e}`),e}async function vn(t){let e=ui(t,".claude","settings.json");if(!await m(e))return{};try{return await y(e)}catch{return{}}}async function Rc(){let t=await ne();await ee({workspacePath:t})}async function _c(){let t=await ne(),e=await vn(t),n=e.env||{},r=typeof n.ANTHROPIC_BASE_URL=="string"?n.ANTHROPIC_BASE_URL:void 0,i=typeof n.ANTHROPIC_AUTH_TOKEN=="string"?n.ANTHROPIC_AUTH_TOKEN:void 0,s=typeof n.ANTHROPIC_API_KEY=="string"?n.ANTHROPIC_API_KEY:void 0,a=typeof e.model=="string"?e.model:void 0,c,l;s?(c=r?.includes("api.anthropic.com")||s.startsWith("sk-ant-")?"Anthropic Direct":"Custom (API key)",l=K(s)):r&&i?(c="LLMLite",l=K(i)):i?(c="Custom",l=K(i)):(c="Subscription (default)",l="(kh\xF4ng set \u2014 d\xF9ng subscription auth)"),o.info(`Project: ${t}`),o.info(`Provider: ${c}${r?` (${r})`:""}`),o.info(`Model: ${a??"(default \u2014 Claude Code ch\u1ECDn)"}`),o.info(`Token: ${l}`)}async function Ic(){let t=await ne(),e=await vn(t);try{let n=await li(e);o.success(`\u2713 ${n.message}`)}catch(n){o.error(`Test fail: ${n.message}`),process.exit(1)}}async function Nc(t){let e=await ne(),n=ui(e,".claude","settings.json"),r=await vn(e);if(!t.yes&&!await $c({message:"X\xF3a AI config (v\u1EC1 d\xF9ng Claude Code Subscription default)?",default:!1})){o.dim("\u0110\xE3 h\u1EE7y.");return}let{env:i,...s}=r,a={...s};if(i){let{ANTHROPIC_BASE_URL:c,ANTHROPIC_AUTH_TOKEN:l,ANTHROPIC_API_KEY:u,...d}=i;Object.keys(d).length>0&&(a.env=d)}Object.keys(a).length===0?(await Ec.unlink(n).catch(()=>{}),o.success("\u0110\xE3 x\xF3a .claude/settings.json (clean state)")):(await A(n,a,384),o.success("\u0110\xE3 reset env block trong .claude/settings.json"))}function pi(t){let e=t.command("ai").description("Qu\u1EA3n l\xFD AI provider config (M12)");e.command("setup").description("Wizard setup/re-config AI provider cho workspace hi\u1EC7n t\u1EA1i").action(async()=>{await Rc()}),e.command("status").description("Show AI config hi\u1EC7n t\u1EA1i (mask token)").action(async()=>{await _c()}),e.command("test").description("Verify AI provider qua cheap prompt").action(async()=>{await Ic()}),e.command("reset").description("X\xF3a env.ANTHROPIC_* kh\u1ECFi settings.json (v\u1EC1 Subscription default)").option("--yes","Skip confirm").action(async n=>{await Nc(n)})}import{spawnSync as An}from"child_process";import{promises as Ai}from"fs";import{join as jt}from"path";import zc from"boxen";import{join as Wc}from"path";k();import{promises as Uc}from"fs";import{join as hi}from"path";k();f();import{promises as Oc}from"fs";import mi,{join as xn}from"path";async function Mc(t,e){let r=e.trim().match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);if(!r?.[2])return!0;let i=r[2];if(i.startsWith("/"))return o.warn(`Pack hook reject: absolute path "${i}" \u2014 ch\u1EC9 accept relative path t\u1EDBi workspace.`),!1;let s=xn(t,i),a=mi.resolve(t),c=mi.resolve(s);return!c.startsWith(`${a}/`)&&c!==a?(o.warn(`Pack hook reject: path "${i}" resolve ra ngo\xE0i workspace (path traversal).`),!1):await m(s)}function Cn(t){let e=new Date,n=`${e.getFullYear().toString().slice(-2)+String(e.getMonth()+1).padStart(2,"0")+String(e.getDate()).padStart(2,"0")}-${String(e.getHours()).padStart(2,"0")}${String(e.getMinutes()).padStart(2,"0")}`;return`${t}.backup-${n}`}function Gt(t,e){let n=new Set,r=[];for(let i of[...t,...e]){let s=typeof i=="string"?i:JSON.stringify(i);n.has(s)||(n.add(s),r.push(i))}return r}function Gc(t){return t.replace(/\$\{CLAUDE_PROJECT_DIR\}\//g,"").trim()}function Lc(t){if(typeof t!="object"||t===null)return[];let e=t,n=Array.isArray(e.hooks)?e.hooks:[],r=[];for(let i of n)if(typeof i=="object"&&i!==null){let s=i.command;typeof s=="string"&&r.push(s)}return r}function di(t){let e=t.map((s,a)=>{let c=Lc(s),l=c.some(u=>u.includes("${CLAUDE_PROJECT_DIR}"));return{index:a,entry:s,commands:c,hasVar:l}}),n=new Map;for(let s of e){let a=s.commands.map(Gc).sort().join("|");if(!a)continue;let c=n.get(a)??[];c.push(s),n.set(a,c)}let r=new Set;for(let[,s]of n)if(!(s.length<2||!s.some(c=>c.hasVar)))for(let c of s)c.hasVar||r.add(c.index);return r.size===0?{entries:t,droppedCount:0}:{entries:t.filter((s,a)=>!r.has(a)),droppedCount:r.size}}function jc(t,e){let n=[],r={...e},i=0;for(let s of Object.keys(r)){let a=r[s]||[],{entries:c,droppedCount:l}=di(a);l>0&&(r[s]=c,i+=l,n.includes(s)||n.push(s))}for(let[s,a]of Object.entries(t)){let c=r[s]||[],l=Gt(c,a),{entries:u,droppedCount:d}=di(l);d>0&&(i+=d),u.length!==c.length&&(n.includes(s)||n.push(s)),r[s]=u}return{merged:r,touchedEvents:n,migratedCount:i}}async function re(t){let e=xn(t,".claude","pack","templates","settings.json.tpl"),n=xn(t,".claude","settings.json");if(!await m(e))return{action:"no-pack-template",changes:[]};let r;try{let u=await N(e);r=JSON.parse(u)}catch(u){throw new Error(`Pack settings template kh\xF4ng parse \u0111\u01B0\u1EE3c JSON: ${u.message}. Path: ${e}`)}let i={},s=!1;if(await m(n)){s=!0;try{i=await y(n)}catch(u){throw new Error(`Project settings.json kh\xF4ng parse \u0111\u01B0\u1EE3c: ${u.message}. Manual fix tr\u01B0\u1EDBc khi sync.`)}}let a=[],c={...i};if(r.statusLine&&!i.statusLine&&(await Mc(t,r.statusLine.command)?(c.statusLine=r.statusLine,a.push("statusLine added")):a.push(`statusLine SKIPPED (file ref '${r.statusLine.command}' kh\xF4ng t\u1ED3n t\u1EA1i)`)),typeof r.includeCoAuthoredBy=="boolean"&&typeof i.includeCoAuthoredBy!="boolean"&&(c.includeCoAuthoredBy=r.includeCoAuthoredBy,a.push("includeCoAuthoredBy added")),r.model&&!i.model&&(c.model=r.model,a.push("model added")),r.env||i.env){let u={...i.env||{}},d=!1;for(let[g,h]of Object.entries(u))typeof h=="string"&&h.includes("llm.nal.vn")&&(u[g]=h.replace(/llm\.nal\.vn/g,"ai.nal.vn"),d=!0);if(d&&a.push("rewrote legacy llm.nal.vn \u2192 ai.nal.vn in env vars"),r.env)for(let[g,h]of Object.entries(r.env))g in u||(u[g]=h,d=!0);d&&(c.env=u,a.some(g=>g.includes("env vars"))||a.push("env vars added from pack"))}if(r.permissions){let u=i.permissions?.allow||[],d=i.permissions?.deny||[],g=r.permissions.allow||[],h=r.permissions.deny||[],C=Gt(u,g),S=Gt(d,h);(C.length!==u.length||S.length!==d.length)&&(c.permissions={allow:C,deny:S},a.push(`permissions union (+${C.length-u.length} allow, +${S.length-d.length} deny)`))}if(r.hooks){let u=i.hooks||{},{merged:d,touchedEvents:g,migratedCount:h}=jc(r.hooks,u);(g.length>0||h>0)&&(c.hooks=d,g.length>0&&a.push(`hooks added for events: ${g.join(", ")}`),h>0&&a.push(`migrated ${h} stale relative-path hook entries to use \${CLAUDE_PROJECT_DIR} version (fixes hook failure when shell cwd changes)`))}if(a.length===0)return{action:"no-change",changes:[]};let l;return s&&(l=Cn(n),await Oc.copyFile(n,l)),await A(n,c),{action:"merged",backupPath:l,changes:a}}function Dc(t,e){return hi(t,".claude","pack","tools",e,"tool.json")}async function Lt(t,e){let n=Dc(t,e);return await m(n)?await y(n):null}var Hc=[".claude","settings.json"];function wi(t){return hi(t,...Hc)}function ie(t){let e=(t.hooks??[]).map(n=>n.command??"").sort().join("\0");return`${t.matcher??""}${e}`}async function ki(t){let e=wi(t);if(!await m(e))return{settings:{},existed:!1};try{return{settings:await y(e),existed:!0}}catch(n){throw new Error(`Project settings.json kh\xF4ng parse \u0111\u01B0\u1EE3c: ${n.message}. Manual fix tr\u01B0\u1EDBc khi enable/disable tool.`)}}async function yi(t,e,n){let r=wi(t),i;return n&&(i=Cn(r),await Uc.copyFile(r,i)),await A(r,e),i}function gi(t){let e=(t.hooks??[]).map(n=>(n.command??"").replace(/\$\{CLAUDE_PROJECT_DIR\}\//g,"").trim()).sort().join("|");return`${t.matcher??""}::${e}`}function fi(t){return(t.hooks??[]).some(e=>(e.command??"").includes("${CLAUDE_PROJECT_DIR}"))}function Kc(t,e){let n=new Set;for(let s of e)fi(s)&&n.add(gi(s));if(n.size===0)return{migratedUser:t,droppedCount:0};let r=0;return{migratedUser:t.filter(s=>fi(s)?!0:n.has(gi(s))?(r++,!1):!0),droppedCount:r}}async function oe(t,e){let{settings:n,existed:r}=await ki(t),i={...n},s=[],a=e.settings.hooks??{};if(Object.keys(a).length>0){let u=n.hooks??{},d={...u},g=[],h=0;for(let[C,S]of Object.entries(a)){let M=u[C]??[],{migratedUser:pt,droppedCount:R}=Kc(M,S);R>0&&(h+=R);let E=new Set(pt.map(ie)),T=S.filter(J=>!E.has(ie(J)));(T.length>0||R>0)&&(d[C]=[...pt,...T],g.push(C))}g.length>0&&(i.hooks=d,s.push(`hooks added: ${g.join(", ")}`),h>0&&s.push(`migrated ${h} stale relative-path entries \u2192 \\{CLAUDE_PROJECT_DIR\\} version`))}let c=e.settings.permissions?.deny??[];if(c.length>0){let u=n.permissions?.deny??[],d=Gt(u,c);d.length!==u.length&&(i.permissions={...n.permissions,deny:d},s.push(`deny +${d.length-u.length}`))}return s.length===0?{action:"no-change",changes:[]}:{action:"enabled",backupPath:await yi(t,i,r),changes:s}}async function bi(t,e){let{settings:n,existed:r}=await ki(t);if(!r)return{action:"no-change",changes:[]};let{hooks:i,permissions:s,...a}=n,c={...a},l=[],u=e.settings.hooks??{},d=n.hooks??{},g={},h=[];for(let[R,E]of Object.entries(d)){let T=u[R];if(!T){g[R]=E;continue}let J=new Set(T.map(ie)),mt=E.filter(Y=>!J.has(ie(Y)));mt.length!==E.length&&h.push(R),mt.length>0&&(g[R]=mt)}Object.keys(g).length>0&&(c.hooks=g),h.length>0&&l.push(`hooks removed: ${h.join(", ")}`);let C=new Set(e.settings.permissions?.deny??[]),S=n.permissions?.deny??[],M=C.size>0?S.filter(R=>!C.has(R)):S;if(n.permissions){let{deny:R,...E}=n.permissions,T={...E,...M.length>0?{deny:M}:{}};Object.keys(T).length>0&&(c.permissions=T)}return M.length!==S.length&&l.push(`deny -${S.length-M.length}`),l.length===0?{action:"no-change",changes:[]}:{action:"disabled",backupPath:await yi(t,c,r),changes:l}}k();k();import{join as Fc}from"path";var Vc=".claude/avatar-tools.json";function vi(){return{tools:{}}}function xi(t){return Fc(t,Vc)}async function yt(t){let e=xi(t);if(!await m(e))return vi();try{return{tools:(await y(e)).tools??{}}}catch{return vi()}}async function Bc(t,e){await A(xi(t),e)}async function se(t,e,n){let r=await yt(t);return r.tools[e]={enabled:n.enabled,version:n.version,appliedAt:new Date().toISOString()},await Bc(t,r),r}async function bt(t){let e=await yt(t);return Object.entries(e.tools).filter(([,n])=>n.enabled).map(([n])=>n)}function qc(t,e){let n=e.settings.hooks??{},r=t.hooks??{};for(let[i,s]of Object.entries(n)){let a=r[i]??[],c=new Set(a.flatMap(l=>(l.hooks??[]).map(u=>u.command??"")));for(let l of s)for(let u of l.hooks??[])if(u.command&&!c.has(u.command))return!1}return!0}async function Ci(t){let e=await bt(t);if(e.length===0)return[];let n=Wc(t,".claude","settings.json"),r={};if(await m(n))try{r=await y(n)}catch{r={}}let i=[];for(let s of e){let a=await Lt(t,s);if(!a){i.push({name:`Tool: ${s}`,status:"warn",detail:"state=enabled nh\u01B0ng pack thi\u1EBFu manifest (version skew). Ch\u1EA1y 'avatar sync'.",fixable:!1});continue}qc(r,a)?i.push({name:`Tool: ${s}`,status:"ok",detail:`enabled, hook active (v${a.version})`,fixable:!1}):i.push({name:`Tool: ${s}`,status:"fail",detail:"state=enabled nh\u01B0ng hook thi\u1EBFu trong settings.json \u2014 fixable (re-apply)",fixable:!0,fix:async()=>{await oe(t,a)}})}return i}k();f();function Si(t){t.command("doctor").description("Ch\u1EA9n \u0111o\xE1n c\xE0i \u0111\u1EB7t Avatar: hooks, MCP, login, submodule, ...").option("--fix","T\u1EF1 \u0111\u1ED9ng fix c\xE1c issue c\xF3 th\u1EC3 fix t\u1EF1 \u0111\u1ED9ng").action(async e=>{try{let n=await Jc(process.cwd());Yc(n),e.fix&&await Xc(n)}catch(n){o.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function Jc(t){let e=[],n=process.versions.node,[r,i]=n.split(".").map(E=>Number.parseInt(E,10)),s=(r??0)>18||(r??0)===18&&(i??0)>=17;e.push({name:"Node.js version",status:s?"ok":"fail",detail:`v${n}${s?"":" (c\u1EA7n >= 18.17)"}`,fixable:!1});let a=await H();a?rt(a)?e.push({name:"Login status",status:"warn",detail:`Token h\u1EBFt h\u1EA1n (${a.email}) \u2014 ch\u1EA1y 'avatar login'`,fixable:!1}):e.push({name:"Login status",status:"ok",detail:`Logged in: ${a.email}`,fixable:!1}):e.push({name:"Login status",status:"fail",detail:"Ch\u01B0a \u0111\u0103ng nh\u1EADp \u2014 ch\u1EA1y 'avatar login'",fixable:!1});let c=jt(t,".claude","pack"),l=jt(t,"CLAUDE.md"),[u,d]=await Promise.all([m(c),m(l)]);if(e.push({name:"team-ai-pack installation",status:u?"ok":"warn",detail:u?c:"Avatar ch\u01B0a init \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),e.push({name:"CLAUDE.md",status:d?"ok":"warn",detail:d?"t\u1ED3n t\u1EA1i \u1EDF project root":"thi\u1EBFu \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),u){let E=jt(t,".claude",".gitignore"),T=!1;await m(E)&&(T=(await Ai.readFile(E,"utf8")).includes("settings.json")),e.push({name:"\u{1F512} settings.json gitignored (.claude/.gitignore)",status:T?"ok":"fail",detail:T?"an to\xE0n \u2014 settings.json kh\xF4ng commit nh\u1EA7m":"CRITICAL: settings.json ch\u1EE9a API key \u2014 ch\u1EA1y 'avatar doctor --fix' \u0111\u1EC3 b\u1EA3o v\u1EC7",fixable:!T,fix:T?void 0:async()=>{let{writeClaudeGitignore:J}=await Promise.resolve().then(()=>(St(),Qn));await J(t)}})}let g=An("which",["python"]),h=An("which",["python3"]),C=g.status===0,S=h.status===0;S&&!C?e.push({name:"Python binary alias",status:"warn",detail:`Ch\u1EC9 c\xF3 python3 (modern macOS). Pack scripts th\u01B0\u1EDDng ref 'python' \u2192 suggest: ln -s ${h.stdout.toString().trim()} ~/.local/bin/python`,fixable:!1}):C?e.push({name:"Python binary",status:"ok",detail:`python: ${g.stdout.toString().trim()}`,fixable:!1}):S&&e.push({name:"Python binary",status:"ok",detail:`python3: ${h.stdout.toString().trim()}`,fixable:!1});let M=jt(t,".claude","settings.json");if(await m(M))try{let E=await Ai.readFile(M,"utf8"),T=JSON.parse(E);if(T.statusLine?.command){let mt=T.statusLine.command.trim().match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);if(mt?.[2]){let Y=mt[2],Mo=Y.startsWith("/")?Y:jt(t,Y),jn=await m(Mo);e.push({name:"statusLine command",status:jn?"ok":"fail",detail:jn?`ref OK: ${Y}`:`BROKEN: settings.json ref '${Y}' nh\u01B0ng file kh\xF4ng t\u1ED3n t\u1EA1i. Strip field statusLine ho\u1EB7c fix path.`,fixable:!1})}}}catch{}let pt=An("which",["claude"]),R=pt.status===0;if(e.push({name:"Claude Code CLI",status:R?"ok":"warn",detail:R?pt.stdout.toString().trim():"kh\xF4ng t\xECm th\u1EA5y 'claude' tr\xEAn PATH",fixable:!1}),u){let E=await Ci(t);e.push(...E)}return e}function Yc(t){let e=[p.bold("Avatar Doctor"),"\u2500".repeat(48)],n=0,r=0,i=0;for(let s of t){let a=s.status==="ok"?p.green("\u2713"):s.status==="warn"?p.yellow("\u26A0"):p.red("\u2717");e.push(`${a} ${s.name.padEnd(28)} ${p.dim(s.detail)}`),s.status==="ok"?n+=1:(r+=1,s.fixable&&(i+=1))}e.push("\u2500".repeat(48)),e.push(`${n} checks passed, ${r} issue${r===1?"":"s"}${i>0?` (${i} fixable \u2014 ch\u1EA1y 'avatar doctor --fix')`:""}`),process.stdout.write(`${zc(e.join(`
|
|
47
48
|
`),{padding:1,borderStyle:"round"})}
|
|
48
|
-
`)}async function
|
|
49
|
+
`)}async function Xc(t){let e=0;for(let n of t)if(n.fixable&&n.fix)try{await n.fix(),o.success(`Fixed: ${n.name}`),e+=1}catch(r){o.error(`Failed to fix ${n.name}: ${r instanceof Error?r.message:String(r)}`)}e===0&&o.dim("Kh\xF4ng c\xF3 g\xEC \u0111\u1EC3 fix t\u1EF1 \u0111\u1ED9ng.")}k();import{spawnSync as ul}from"child_process";import{promises as pl}from"fs";import{join as Ni}from"path";import{confirm as ol}from"@inquirer/prompts";import sl from"boxen";import{spawnSync as Qc}from"child_process";f();var Ti=300*1e3,Pi="gitnexus",L=class extends Error{reason;exitCode;constructor(e,n,r=null){super(n),this.name="InstallGitnexusError",this.reason=e,this.exitCode=r}};function Zc(t,e){let n=e.toLowerCase();return n.includes("eacces")||n.includes("permission denied")?new L("permission-denied",`npm install -g c\u1EA7n quy\u1EC1n. Th\u1EED: sudo npm install -g ${Pi} ho\u1EB7c fix npm prefix (npm config set prefix ~/.npm-global).`,t):n.includes("enospc")||n.includes("no space")?new L("disk-full","\u0110\u0129a \u0111\u1EA7y. Free disk space r\u1ED3i th\u1EED l\u1EA1i.",t):new L("generic",`npm install th\u1EA5t b\u1EA1i (exit ${t??"null"}). Xem log npm ph\xEDa tr\xEAn.`,t)}function Ei(){o.info("\u0110ang c\xE0i GitNexus qua npm (c\xF3 th\u1EC3 m\u1EA5t 1-2 ph\xFAt)...");let t=Qc("npm",["install","-g",Pi],{stdio:["inherit","inherit","pipe"],timeout:Ti,encoding:"utf8"});if(t.signal==="SIGTERM")throw new L("timeout",`npm install timeout sau ${Ti/1e3}s. Check m\u1EA1ng r\u1ED3i th\u1EED l\u1EA1i.`,null);if(t.status!==0)throw t.stderr&&process.stderr.write(t.stderr),Zc(t.status,t.stderr||"");Vt();let e=et();if(!e.installed||!e.path)throw new L("binary-not-in-path","npm c\xE0i xong nh\u01B0ng `gitnexus` kh\xF4ng trong PATH. Reload shell (source ~/.zshrc) ho\u1EB7c th\xEAm npm global bin v\xE0o PATH.",null);return o.success(`\u0110\xE3 c\xE0i GitNexus${e.version?` v${e.version}`:""} t\u1EA1i ${e.path}`),{version:e.version,path:e.path}}k();f();import{promises as _i}from"fs";import{homedir as tl}from"os";import{join as el}from"path";var $i=384,Ri={command:"gitnexus",args:["mcp"]};function nl(){return el(tl(),".claude","mcp_servers.json")}function rl(t,e){return t===e?!0:typeof t!="object"||typeof e!="object"||t===null||e===null?!1:JSON.stringify(t)===JSON.stringify(e)}async function il(t){let e=new Date().toISOString().replace(/[:.]/g,"-"),n=`${t}.avatar-backup-${e}`;return await _i.copyFile(t,n),n}async function Ii(){let t=nl(),e={},n=!1;if(await m(t)){n=!0;try{e=await y(t)}catch(a){throw new Error(`MCP config corrupted (${t}): ${a.message}. Backup + x\xF3a file \u0111\u1EC3 Avatar t\u1EA1o l\u1EA1i.`)}}let r=e.mcp_servers?.gitnexus;if(r&&rl(r,Ri))return o.dim(`MCP entry gitnexus \u0111\xE3 \u0111\xFAng t\u1EA1i ${t} (no-op)`),{path:t,wasUpdated:!1};let i;n&&(i=await il(t),o.dim(`Backup ${t} \u2192 ${i}`));let s={...e,mcp_servers:{...e.mcp_servers||{},gitnexus:Ri}};await A(t,s,$i);try{await _i.chmod(t,$i)}catch{}return o.success(`Registered MCP server: gitnexus \u2192 ${t}`),{path:t,wasUpdated:!0,backup:i}}f();async function al(){let t=[p.bold("\u{1F9E0} GitNexus ch\u01B0a c\xE0i"),"","GitNexus = code intelligence layer cho Claude Code:"," \u2022 Architectural awareness (impact analysis)"," \u2022 Call chain debug + blast radius tr\u01B0\u1EDBc refactor"," \u2022 Wiki HTML t\u1EF1 gen m\xF4 t\u1EA3 codebase","",`S\u1EBD c\xE0i: ${p.cyan("npm install -g gitnexus")} (global)`];return process.stdout.write(`${sl(t.join(`
|
|
49
50
|
`),{padding:1,borderStyle:"round",borderColor:"cyan"})}
|
|
50
|
-
`),await
|
|
51
|
+
`),await ol({message:"C\xE0i GitNexus global?",default:!0})}async function cl(){for(;;)try{return Ei(),!0}catch(t){let e=t instanceof Error?t.message:String(t),n=t instanceof L&&t.reason==="permission-denied"?"Th\u1EED l\u1EA1i v\u1EDBi sudo, ho\u1EB7c fix npm prefix: npm config set prefix ~/.npm-global":"Check log npm ph\xEDa tr\xEAn + th\u1EED l\u1EA1i.",r=await O({taskName:"C\xE0i GitNexus qua npm",reason:e,allowSkip:!0,hint:n});if(r==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc c\xE0i GitNexus.");if(r==="skip")return!1}}async function ll(t){for(;;)try{return kt(t),!0}catch(e){let n=e instanceof Error?e.message:String(e),r=e instanceof D&&e.reason==="missing-output"?"Repo c\xF3 th\u1EC3 empty ho\u1EB7c gitnexus version mismatch. Check `gitnexus --version`.":"Network glitch? Retry th\u01B0\u1EDDng work.",i=await O({taskName:"GitNexus analyze workspace",reason:n,allowSkip:!0,hint:r});if(i==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc GitNexus analyze.");if(i==="skip")return!1}}async function ae(t){let e={ok:!1,installed:!1,analyzed:!1,wikiGenerated:!1,mcpRegistered:!1};try{o.info("=== Phase 10: GitNexus Setup ===");let n=et();if(!n.installed){if(!await al())return await b("gitnexus_setup","result=skipped,reason=user-declined"),o.dim("Skip GitNexus. C\xE0i sau qua `avatar gitnexus install`."),e.reason="user-declined",e;if(!await cl())return await b("gitnexus_setup","result=skipped,reason=install-skipped"),o.dim("Skip GitNexus install. Workspace OK kh\xF4ng c\xF3 codebase intelligence."),e.reason="install-skipped",e;if(Vt(),n=et(),!n.installed)throw new Error("C\xE0i xong nh\u01B0ng kh\xF4ng detect \u0111\u01B0\u1EE3c binary (PATH issue).")}e.installed=!0,o.success(`GitNexus available${n.version?` v${n.version}`:""}`);try{_r()}catch(r){o.warn(`gitnexus setup fail: ${r.message}`),o.dim("Skip global skills install. Workspace v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c.")}if(t.skipAnalyze)o.dim("GitNexus: c\xE0i + MCP xong. Index s\u1EBD ch\u1EA1y per-repo khi `avatar add repo`.");else{if(!await ll(t.workspacePath))return await b("gitnexus_setup","result=skipped,reason=analyze-skipped"),o.dim("Skip analyze. GitNexus installed nh\u01B0ng ch\u01B0a index."),e.reason="analyze-skipped",e;e.analyzed=!0;let i=await qt(t.workspacePath);e.wikiGenerated=i.ran,i.skipped&&i.reason==="fail"&&o.warn(`Wiki gen fail (workspace v\u1EABn OK): ${i.detail??"unknown"}`)}try{let r=await Ii();e.mcpRegistered=!0,r.wasUpdated||o.dim("MCP server gitnexus \u0111\xE3 registered tr\u01B0\u1EDBc \u0111\xF3.")}catch(r){o.warn(`MCP server register fail: ${r.message}`),o.dim("Workspace OK nh\u01B0ng Claude Code kh\xF4ng t\u1EF1 attach MCP server. Manual add v\xE0o ~/.claude/mcp_servers.json.")}return e.ok=!0,await b("gitnexus_setup",`result=ok,analyzed=${e.analyzed},wiki=${e.wikiGenerated},mcp=${e.mcpRegistered}`),o.success("GitNexus ready"),e}catch(n){if(n instanceof x)throw n;let r=n instanceof Error?n.message:String(n);return o.warn(`GitNexus setup th\u1EA5t b\u1EA1i: ${r}`),o.dim("Workspace v\u1EABn s\u1EB5n s\xE0ng. Setup sau qua `avatar gitnexus install`."),await b("gitnexus_setup",`result=failed,error=${r.slice(0,200)}`),e.reason=r,e}}f();function Sn(){let t=process.cwd(),e=wt(t);return e||(o.error(`Kh\xF4ng t\xECm th\u1EA5y Avatar workspace t\u1EEB th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i.
|
|
51
52
|
Avatar workspace c\u1EA7n c\xF3: .claude/ + CLAUDE.md + src/ (ho\u1EB7c .gitmodules).
|
|
52
53
|
B\u1EA1n \u0111ang \u1EDF: ${t}
|
|
53
|
-
Cd v\xE0o workspace dir r\u1ED3i ch\u1EA1y l\u1EA1i.`),process.exit(1)),e!==t&&
|
|
54
|
-
`);let r=
|
|
55
|
-
`)}function
|
|
56
|
-
${
|
|
54
|
+
Cd v\xE0o workspace dir r\u1ED3i ch\u1EA1y l\u1EA1i.`),process.exit(1)),e!==t&&o.dim(`Detected workspace root: ${e}`),e}async function ml(){let t=Sn(),e=await ae({workspacePath:t});e.ok?(o.success("GitNexus setup complete"),o.dim("Update CLAUDE.md \u0111\u1EC3 re-render section GitNexus: re-run avatar init ho\u1EB7c ch\u1EC9nh tay.")):o.warn(`Setup kh\xF4ng complete: ${e.reason??"unknown"}`)}async function dl(){let t=Sn(),e=Ni(t,".gitnexus","meta.json");if(!await m(e)){o.warn(`Ch\u01B0a c\xF3 ${e}. Ch\u1EA1y: avatar gitnexus install`);return}try{let n=await y(e);if(o.info(`Project: ${t}`),o.info(`Last commit: ${n.lastCommit?.slice(0,7)??"(unknown)"}`),o.info(`Indexed at: ${n.indexedAt??"(unknown)"}`),n.stats&&o.info(`Stats: ${n.stats.files??"?"} files \xB7 ${n.stats.nodes??"?"} nodes \xB7 ${n.stats.edges??"?"} edges`),n.lastCommit){let i=ul("git",["rev-parse","HEAD"],{cwd:t,encoding:"utf8"});i.status===0&&(i.stdout.trim()!==n.lastCommit?o.warn("\u26A0 Index stale \u2014 HEAD \u0111\xE3 ti\u1EBFn t\u1EEB lastCommit. Ch\u1EA1y: avatar gitnexus analyze"):o.dim("Index fresh (HEAD === lastCommit)"))}let r=Ni(t,".gitnexus","wiki","index.html");if(await m(r)){let i=await pl.stat(r);o.info(`Wiki: ${i.mtime.toISOString()}`)}else o.dim("Wiki: ch\u01B0a generate (ch\u1EA1y gitnexus wiki manual)")}catch(n){o.error(`Read meta.json fail: ${n.message}`),process.exit(1)}}async function gl(){let t=Sn();try{kt(t),o.success("Index refreshed. Ch\u1EA1y `avatar gitnexus status` xem chi ti\u1EBFt.")}catch(e){o.error(`Analyze fail: ${e.message}`),process.exit(1)}}function Oi(t){let e=t.command("gitnexus").description("Qu\u1EA3n l\xFD GitNexus code intelligence (M10)");e.command("install").description("C\xE0i + setup GitNexus cho workspace hi\u1EC7n t\u1EA1i").action(async()=>{await ml()}),e.command("status").description("Show index info + staleness warning").action(async()=>{await dl()}),e.command("analyze").description("Re-run analyze refresh index (no wiki)").action(async()=>{await gl()})}import{resolve as Ql}from"path";import{input as Zl}from"@inquirer/prompts";import Tn from"chalk";var ce=[" \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551","\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"],le=[[217,79,30],[200,70,80],[170,70,140],[125,88,217]];function Pn(t,e,n){return Math.round(t+(e-t)*n)}function fl(t){let n=Math.max(0,Math.min(1,t))*(le.length-1),r=Math.floor(n),i=Math.min(le.length-1,r+1),s=n-r,a=le[r],c=le[i];return[Pn(a[0],c[0],s),Pn(a[1],c[1],s),Pn(a[2],c[2],s)]}function En(t){if(!((process.stdout.isTTY??!1)&&Tn.level>0))return[...ce,...t?.tagline?["",t.tagline]:[]].join(`
|
|
55
|
+
`);let r=ce.map((i,s)=>{let a=ce.length===1?0:s/(ce.length-1),[c,l,u]=fl(a);return Tn.rgb(c,l,u).bold(i)});return t?.tagline&&(r.push(""),r.push(Tn.dim(t.tagline))),r.join(`
|
|
56
|
+
`)}function vt(t){process.stdout.write(`
|
|
57
|
+
${En(t)}
|
|
57
58
|
|
|
58
|
-
`)}import{readdirSync as
|
|
59
|
-
`);return
|
|
60
|
-
Y\xEAu c\u1EA7u: ch\u1EC9 ch\u1EEF/s\u1ED1/dash/underscore/dot, kh\xF4ng '/', '\\', '..'.`);let n=
|
|
59
|
+
`)}import{readdirSync as gh}from"fs";import{select as hh}from"@inquirer/prompts";import{simpleGit as kh}from"simple-git";import{existsSync as Mf,statSync as Gf}from"fs";import{join as jf}from"path";import{simpleGit as Hf}from"simple-git";import{existsSync as Vf}from"fs";import{join as Wf}from"path";import{readFileSync as Jf}from"fs";import{dirname as hl,join as ue}from"path";import{fileURLToPath as wl}from"url";var pe=hl(wl(import.meta.url)),Qf=[ue(pe,"templates","gitignore"),ue(pe,"..","templates","gitignore"),ue(pe,"..","..","src","templates","gitignore"),ue(pe,"..","src","templates","gitignore")],$n="# === avatar ===",me="# === /avatar ===";f();import{existsSync as eh,readFileSync as nh,writeFileSync as rh}from"fs";import{join as oh}from"path";var de=class extends Error{constructor(e){super(e),this.name="InitAbortedByUserError"}};import{join as Ui}from"path";k();import{join as Mi}from"path";var kl=".avatar-pack-manifest.json",yl=".pack-version";function Gi(t){return Mi(t,kl)}async function ge(t,e){await A(Gi(t),e)}async function bl(t){let e=Gi(t);if(!await m(e))return null;try{return await y(e)}catch{return null}}async function fe(t){let e=await bl(t);if(e?.version)return e.version;let n=Mi(t,yl);if(await m(n)){let r=(await N(n)).trim();if(r)return r}return null}k();import{promises as he}from"fs";import{join as Li}from"path";var vl="https://qpdbkewtpkkbcrptnlos.supabase.co/functions/v1/get-pack";function xl(){return process.env.AVATAR_GET_PACK_ENDPOINT??vl}var ct=class extends Error{constructor(e){super(e),this.name="InvalidIdTokenError"}},lt=class extends Error{constructor(e){super(e),this.name="PackNetworkError"}},at=class extends Error{constructor(e){super(e),this.name="PackParseError"}};async function we(t,e){let n;try{n=await fetch(xl(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({id_token:t,version:e})})}catch(i){throw new lt(`Kh\xF4ng k\u1EBFt n\u1ED1i \u0111\u01B0\u1EE3c Supabase get-pack: ${i instanceof Error?i.message:i}`)}if(n.status===401||n.status===403){let i=await ji(n);throw new ct(`Supabase t\u1EEB ch\u1ED1i (${n.status}): ${i}. Ch\u1EA1y 'avatar login' v\u1EDBi t\xE0i kho\u1EA3n @nal.vn.`)}if(n.status>=500)throw new lt(`Supabase get-pack l\u1ED7i server (${n.status}). Th\u1EED l\u1EA1i sau.`);if(!n.ok){let i=await ji(n);throw new at(`get-pack tr\u1EA3 ${n.status}: ${i}`)}let r;try{r=await n.json()}catch{throw new at("get-pack tr\u1EA3 response kh\xF4ng ph\u1EA3i JSON.")}if(r.error)throw new at(`get-pack b\xE1o l\u1ED7i: ${r.error}`);if(!r.url||!r.object)throw new at("get-pack thi\u1EBFu url/object trong response.");return{url:r.url,object:r.object,expiresIn:r.expires_in??0,requestedBy:r.requested_by??""}}async function ke(t,e){let n=Li(e,"..");await v(n);let r=Li(n,`.pack-download-${process.pid}.tar.gz`),i=`${e}.new-${process.pid}`;try{try{await Ee(t,r)}catch(c){throw new lt(`T\u1EA3i tarball th\u1EA5t b\u1EA1i: ${c instanceof Error?c.message:c}`)}await Q(i);try{await $e(r,i)}catch(c){throw new at(`Gi\u1EA3i n\xE9n tarball th\u1EA5t b\u1EA1i: ${c instanceof Error?c.message:c}`)}let s=`${e}.old-${process.pid}`,a=await m(e);a&&await he.rename(e,s);try{await he.rename(i,e)}catch(c){throw a&&await he.rename(s,e).catch(()=>{}),c}a&&await Q(s).catch(()=>{})}finally{await he.rm(r,{force:!0}).catch(()=>{}),await Q(i).catch(()=>{})}}async function ji(t){try{let e=await t.json();return e.error??JSON.stringify(e).slice(0,200)}catch{return(await t.text().catch(()=>"")).slice(0,200)}}var F=".claude/pack",xt=class extends Error{constructor(e){super(e),this.name="TeamPackAccessAbortedError"}};async function Di(t,e){let n=await Qt(),r=await we(n,e),i=Ui(t,F);await ke(r.url,i);let s=e??Cl(r.object);return await ge(i,{version:s,downloadedObject:r.object,extractedAt:new Date().toISOString()}),{pinnedTag:s}}function Cl(t){return t.replace(/^pack-/,"").replace(/\.tar\.gz$/,"")}async function ye(t){let e=Ui(t,F);return await fe(e)??"(unknown)"}f();k();import{join as ve,relative as Pl,resolve as Vi}from"path";import{input as Wi,select as El}from"@inquirer/prompts";import $l from"boxen";f();import Al from"boxen";var Hi=[{cmd:"/avatar:help",desc:"H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng Avatar \u2014 ch\u1EC9 c\u1EA7n g\xF5 t\u1EF1 nhi\xEAn"},{cmd:"/avatar:brainstorm",desc:"Brainstorm \xFD t\u01B0\u1EDFng cho m\u1ED9t tool"},{cmd:"/avatar:plan",desc:"T\u1EA1o plan th\xF4ng minh v\u1EDBi prompt enhancement"},{cmd:"/avatar:scout",desc:"T\xECm file li\xEAn quan trong codebase, ti\u1EBFt ki\u1EC7m token"},{cmd:"/avatar:implement",desc:"B\u1EAFt \u0111\u1EA7u code & test theo plan c\xF3 s\u1EB5n"},{cmd:"/avatar:build-full-flow",desc:"Implement m\u1ED9t tool t\u1EEBng b\u01B0\u1EDBc (end-to-end)"},{cmd:"/avatar:fix",desc:"Ph\xE2n t\xEDch v\xE0 fix v\u1EA5n \u0111\u1EC1 t\u1EF1 \u0111i\u1EC1u h\u01B0\u1EDBng"},{cmd:"/avatar:debug",desc:"Debug v\u1EA5n \u0111\u1EC1 k\u1EF9 thu\u1EADt + \u0111\u01B0a ra gi\u1EA3i ph\xE1p"},{cmd:"/avatar:test",desc:"Ch\u1EA1y test tr\xEAn m\xE1y + ph\xE2n t\xEDch b\xE1o c\xE1o t\u1ED5ng h\u1EE3p"},{cmd:"/avatar:design:good",desc:"T\u1EA1o thi\u1EBFt k\u1EBF ch\u1EC9n chu, s\u1ED1ng \u0111\u1ED9ng"},{cmd:"/avatar:docs:init",desc:"Ph\xE2n t\xEDch codebase + t\u1EA1o t\xE0i li\u1EC7u kh\u1EDFi \u0111\u1EA7u"},{cmd:"/avatar:status",desc:"Xem l\u1EA1i thay \u0111\u1ED5i g\u1EA7n \u0111\xE2y + t\u1ED5ng k\u1EBFt c\xF4ng vi\u1EC7c"},{cmd:"/avatar:journal",desc:"Ghi nh\u1EADt k\xFD session"}];function Ki(){let t=Math.max(...Hi.map(a=>a.cmd.length)),e=p.bold("\u{1F3AF} Slash commands t\u1EEB team-ai-pack"),n=p.dim("G\xF5 trong Claude Code session \u0111\u1EC3 g\u1ECDi capability c\u1EE7a pack:"),r=Hi.map(a=>` ${p.cyan(a.cmd.padEnd(t))} ${p.dim(a.desc)}`),i=p.dim("Catalog \u0111\u1EA7y \u0111\u1EE7 46 commands: cat .claude/pack/scripts/commands_data.yaml"),s=[e,n,"",...r,"",i].join(`
|
|
60
|
+
`);return Al(s,{padding:1,borderStyle:"round",borderColor:"cyan"})}f();k();St();import{readdir as Sl}from"fs/promises";import{join as Tl}from"path";async function be(t){if(!await m(t))return!0;try{return(await Sl(t)).filter(r=>!r.startsWith(".")&&r!=="Thumbs.db").length===0}catch{return!1}}async function Fi(t,e,n=10){for(let r=2;r<n;r++){let i=Tl(t,`${e}-${r}`);if(await be(i))return i}return null}function Bi(t,e){if(/[\/\\]/.test(e)||e==="."||e===".."||e.includes(".."))throw new Error(`T\xEAn workspace kh\xF4ng an to\xE0n (ch\u1EE9a path separator ho\u1EB7c '..'): "${e}".
|
|
61
|
+
Y\xEAu c\u1EA7u: ch\u1EC9 ch\u1EEF/s\u1ED1/dash/underscore/dot, kh\xF4ng '/', '\\', '..'.`);let n=Vi(ve(t,e)),r=Vi(t);if(!n.startsWith(`${r}/`)&&n!==r)throw new Error(`T\xEAn workspace "${e}" resolve ra ngo\xE0i parent dir (path traversal). Block.`)}async function qi(t,e,n){Bi(t,e);let r=ve(t,e);if(await be(r))return r;for(o.warn(`Workspace path "${r}" \u0111\xE3 c\xF3 n\u1ED9i dung.`);;){let i=await Fi(t,e);if(n&&i)return o.info(`--force: d\xF9ng ${i}`),i;let s=[];i&&s.push({name:`D\xF9ng "${i}" (suggest)`,value:"use-alt"}),s.push({name:"Nh\u1EADp t\xEAn workspace kh\xE1c (manual)",value:"manual"}),s.push({name:"T\u1EA1m ng\u01B0ng init",value:"abort"});let a=await El({message:"C\xE1ch x\u1EED l\xFD workspace path conflict?",choices:s});if(a==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc resolve workspace path. Ch\u1EA1y l\u1EA1i v\u1EDBi --workspace-name kh\xE1c.");if(a==="use-alt"&&i)return i;let c=await Wi({message:"T\xEAn workspace m\u1EDBi:",validate:u=>{let d=u.trim();return d.length===0?"T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng":/[\/\\]/.test(d)||d==="."||d===".."||d.includes("..")?"T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c ch\u1EE9a '/', '\\', '..' (path traversal)":!0}});Bi(t,c.trim());let l=ve(t,c.trim());if(await be(l))return l;o.warn(`"${l}" c\u0169ng \u0111\xE3 c\xF3 n\u1ED9i dung. Th\u1EED t\xEAn kh\xE1c.`)}}async function zi(t){return await Wi({message:"Team owner email:",default:t})}async function Ji(t){try{await v(t)}catch(e){let n=e;throw n.code==="EACCES"||n.code==="EPERM"?new Error(`Kh\xF4ng t\u1EA1o \u0111\u01B0\u1EE3c workspace "${t}": kh\xF4ng c\xF3 quy\u1EC1n ghi.`):n.code==="ENAMETOOLONG"?new Error(`\u0110\u01B0\u1EDDng d\u1EABn workspace qu\xE1 d\xE0i: "${t}".`):n.code==="ENOSPC"?new Error("H\u1EBFt dung l\u01B0\u1EE3ng \u0111\u0129a, kh\xF4ng t\u1EA1o \u0111\u01B0\u1EE3c workspace."):new Error(`Kh\xF4ng t\u1EA1o \u0111\u01B0\u1EE3c workspace "${t}": ${n.message}`)}}function Rl(t){if(t===null)return` ${p.yellow("AI:")} skipped \xB7 ${p.cyan("avatar ai setup")} \u0111\u1EC3 config sau`;if(t.ok){let e=t.model?` \xB7 model=${t.model}`:"";return` ${p.green("AI:")} ready \xB7 ${t.provider}${e}`}return` ${p.yellow("AI:")} failed (${t.reason.slice(0,60)}) \xB7 th\u1EED ${p.cyan("avatar ai setup")}`}function _l(t){if(t===null)return` ${p.yellow("GitNexus:")} skipped \xB7 ${p.cyan("avatar gitnexus install")} \u0111\u1EC3 setup sau`;if(t.ok){let e=["ready"];return t.analyzed&&e.push("indexed"),t.wikiGenerated&&e.push("wiki"),t.mcpRegistered&&e.push("mcp"),` ${p.green("GitNexus:")} ${e.join(" \xB7 ")}`}return` ${p.yellow("GitNexus:")} skipped (${(t.reason??"unknown").slice(0,40)}) \xB7 th\u1EED ${p.cyan("avatar gitnexus install")}`}async function Yi(t,e=null,n=null){let r=[`${p.green("\u2713")} Workspace s\u1EB5n s\xE0ng: ${Pl(process.cwd(),t)||t}`,Rl(e),_l(n),"",` ${p.cyan(`cd ${t}`)}`,` ${p.cyan("avatar add repo")} Th\xEAm repo code v\xE0o src/`,` ${p.cyan("claude")} M\u1EDF Claude Code \u1EDF workspace root`,"",` ${p.cyan("avatar sync")} C\u1EADp nh\u1EADt team-ai-pack m\u1EDBi`,` ${p.cyan("avatar uninstall")} G\u1EE1 Avatar (gi\u1EEF code)`];process.stdout.write(`${$l(r.join(`
|
|
61
62
|
`),{padding:1,borderStyle:"round"})}
|
|
62
|
-
`);let
|
|
63
|
-
${
|
|
64
|
-
`)}import
|
|
65
|
-
`);process.stdout.write(`${
|
|
66
|
-
`),
|
|
63
|
+
`);let i=ve(t,F);await m(i)&&process.stdout.write(`
|
|
64
|
+
${Ki()}
|
|
65
|
+
`)}import Il from"boxen";import Nl from"open";Xt();f();function Xi(t){t.command("login").description("\u0110\u0103ng nh\u1EADp Google SSO (workspace @nal.vn)").option("--reset","X\xF3a credential c\u0169 v\xE0 \u0111\u0103ng nh\u1EADp l\u1EA1i").action(async e=>{try{await Rn(e)}catch(n){o.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function Rn(t){if(vt({tagline:"\u0110\u0103ng nh\u1EADp Google SSO \xB7 workspace @nal.vn"}),t.reset)await Br(),await b("login_reset");else{let g=await H();if(g&&!rt(g)){o.success(`\u0110\xE3 \u0111\u0103ng nh\u1EADp: ${g.email}`);return}}let e=gt("\u0110ang y\xEAu c\u1EA7u device code t\u1EEB Google..."),n;try{n=await en(),e.succeed("Nh\u1EADn device code")}catch(g){throw e.fail("Kh\xF4ng k\u1EBFt n\u1ED1i \u0111\u01B0\u1EE3c Google"),g}let r=cn(n),i=[`1. Truy c\u1EADp: ${p.cyan(n.verification_url)}`,`2. Nh\u1EADp code: ${p.bold.yellow(n.user_code)}`,"",`Ho\u1EB7c Avatar t\u1EF1 m\u1EDF browser, click ${p.green("Allow")}...`].join(`
|
|
66
|
+
`);process.stdout.write(`${Il(i,{padding:1,borderStyle:"round"})}
|
|
67
|
+
`),Nl(r).catch(()=>{o.dim("(Kh\xF4ng m\u1EDF \u0111\u01B0\u1EE3c browser t\u1EF1 \u0111\u1ED9ng \u2014 copy URL \u1EDF tr\xEAn)")});let s=gt("\u0110ang ch\u1EDD x\xE1c nh\u1EADn trong browser..."),a=n.interval*1e3,c=Date.now()+n.expires_in*1e3,l=null;for(;Date.now()<c;){await Ol(a);try{if(l=await nn(n.device_code),l)break}catch(g){throw s.fail("X\xE1c th\u1EF1c th\u1EA5t b\u1EA1i"),g}}l||(s.fail("H\u1EBFt h\u1EA1n ch\u1EDD (5 ph\xFAt). Ch\u1EA1y l\u1EA1i 'avatar login'."),process.exit(1)),s.succeed("\u0110\xE3 nh\u1EADn token t\u1EEB Google");let u=rn(l.id_token);try{on(u)}catch(g){throw await an(l.access_token),g}let d=sn(l,u);await mn(d),await b("login",d.email),o.success(`X\xE1c th\u1EF1c th\xE0nh c\xF4ng: ${d.email}`),o.success(`Verify hosted domain: ${u.hd} \u2713`),o.success(`L\u01B0u credential v\xE0o ${nt} (chmod 600)`)}function Ol(t){return new Promise(e=>setTimeout(e,t))}import{join as Ae}from"path";f();var Qi=3;async function Zi(t,e,n,r=!1){let i=0;for(;;)try{return{pinnedTag:(await Di(t,e)).pinnedTag,skipped:!1}}catch(s){if(s instanceof xt)throw s;if(s instanceof ct){o.error(s.message);let l=await O({taskName:"T\u1EA3i team-ai-pack (x\xE1c th\u1EF1c NAL)",reason:s.message,allowSkip:!0,hint:"Ch\u1EA1y 'avatar login' v\u1EDBi t\xE0i kho\u1EA3n @nal.vn r\u1ED3i th\u1EED l\u1EA1i. Ho\u1EB7c skip + 'avatar sync' sau."});if(l==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc x\xE1c th\u1EF1c t\u1EA3i pack.");if(l==="skip")return o.warn("Skip team-ai-pack. Workspace d\xF9ng \u0111\u01B0\u1EE3c nh\u01B0ng ch\u01B0a c\xF3 pack. Pull sau qua `avatar sync`."),{pinnedTag:null,skipped:!0};continue}if(s instanceof lt&&i<Qi){i+=1,o.warn(`T\u1EA3i pack l\u1ED7i m\u1EA1ng (l\u1EA7n ${i}/${Qi}): ${s.message}`),await Ml(1e3*i);continue}let a=s instanceof Error?s.message:String(s),c=await O({taskName:"T\u1EA3i team-ai-pack tarball",reason:a,allowSkip:!0,hint:"Network glitch? Retry th\u01B0\u1EDDng work. Skip \u2192 d\xF9ng `avatar sync` sau."});if(c==="abort")throw new x("User abort t\u1EA1i b\u01B0\u1EDBc t\u1EA3i team-ai-pack.");if(c==="skip")return o.warn("Skip team-ai-pack. Workspace d\xF9ng \u0111\u01B0\u1EE3c nh\u01B0ng ch\u01B0a c\xF3 pack. Pull sau qua `avatar sync`."),{pinnedTag:null,skipped:!0};i=0}}function Ml(t){return new Promise(e=>setTimeout(e,t))}k();St();import{confirm as Dl}from"@inquirer/prompts";k();import{promises as Gl}from"fs";import{join as _n}from"path";var Ll=[".claude","pack","tools"];function to(t){return _n(t,...Ll)}async function In(t){let e=to(t);if(!await m(e))return[];let n=await Gl.readdir(e,{withFileTypes:!0}),r=[];for(let i of n)i.isDirectory()&&await m(_n(e,i.name,"tool.json"))&&r.push(i.name);return r.sort()}async function eo(t){let e=_n(to(t),"defaults.json");if(!await m(e))return[];try{return(await y(e)).defaultTools??[]}catch{return[]}}async function Ut(t){let[e,n]=await Promise.all([In(t),bt(t)]);return{available:e,enabled:n}}f();import{spawnSync as jl}from"child_process";f();function Ul(t){if(!t)return;let e=jl(t,["--version"],{stdio:"ignore"});(e.status!==0||e.error)&&o.warn(`Tool y\xEAu c\u1EA7u runtime '${t}' nh\u01B0ng kh\xF4ng t\xECm th\u1EA5y tr\xEAn PATH. Hook c\xF3 th\u1EC3 fail khi ch\u1EA1y. C\xE0i '${t}' r\u1ED3i th\u1EED l\u1EA1i.`)}function no(t,e){switch(e.action){case"enabled":o.success(`Tool '${t}' enabled (${e.changes.join("; ")}).`),e.backupPath&&o.dim(` Backup: ${e.backupPath}`);break;case"disabled":o.success(`Tool '${t}' disabled (${e.changes.join("; ")}).`),e.backupPath&&o.dim(` Backup: ${e.backupPath}`);break;case"no-change":o.dim(`Tool '${t}': settings.json \u0111\xE3 \u0111\xFAng tr\u1EA1ng th\xE1i, kh\xF4ng thay \u0111\u1ED5i.`);break;case"no-manifest":break}}async function ut(t,e,n={}){let r=await Lt(t,e);if(!r)return n.silent||o.warn(`Pack version hi\u1EC7n t\u1EA1i ch\u01B0a h\u1ED7 tr\u1EE3 tool '${e}' (thi\u1EBFu .claude/pack/tools/${e}/tool.json). Ch\u1EA1y 'avatar sync' \u0111\u1EC3 pull pack m\u1EDBi, ho\u1EB7c ki\u1EC3m t\xEAn tool.`),!1;Ul(r.requires?.runtime);let i=await oe(t,r);return no(e,i),await se(t,e,{enabled:!0,version:r.version}),!0}async function Nn(t,e){let n=await Lt(t,e);if(!n){o.warn(`Kh\xF4ng t\xECm th\u1EA5y manifest tool '${e}' \u0111\u1EC3 bi\u1EBFt entry n\xE0o c\u1EA7n r\xFAt. N\u1EBFu pack \u0111\xE3 \u0111\u1ED5i, state v\u1EABn \u0111\u01B0\u1EE3c set disabled nh\u01B0ng settings.json c\xF3 th\u1EC3 c\xF2n s\xF3t entry.`);let s=(await yt(t)).tools[e]?.version??"unknown";return await se(t,e,{enabled:!1,version:s}),!1}let r=await bi(t,n);return no(e,r),await se(t,e,{enabled:!1,version:n.version}),!0}var Hl={"prompt-scoring":"prompt scoring"};async function ro(t,e={}){let n=await eo(t);if(n.length!==0)for(let r of n){let i=Hl[r]??r,s;if(e.autoYes?s=!0:s=await Dl({message:`Do you want to setup ${i} (y/n)`,default:!0}),!s){o.dim(` Skip tool '${r}' (user opt-out).`);continue}await ut(t,r,{silent:!0})}}import{promises as xe}from"fs";import{dirname as On,join as oo,relative as Mn}from"path";import{promises as Kl}from"fs";function Fl(){let t=new Date,e=n=>n.toString().padStart(2,"0");return`${t.getFullYear()}-${e(t.getMonth()+1)}-${e(t.getDate())}-${e(t.getHours())}${e(t.getMinutes())}`}async function io(t){let e=`${t}.backup-${Fl()}`;return await Kl.rename(t,e),e}k();var Gn=["skills","agents","commands","hooks","workflows","scripts","knowledge"];async function Vl(t){try{return(await xe.lstat(t)).isSymbolicLink()}catch{return!1}}async function Bl(t,e,n){let r=Mn(On(e),e)||e;if(!await m(t))return{dir:r,action:"source-missing"};if(await m(e))if(await Vl(e))await xe.unlink(e);else if(n){let s=await io(e),a=Mn(On(e),t);return await xe.symlink(a,e),{dir:r,action:"backed-up-and-linked",backupPath:s}}else return{dir:r,action:"skipped-conflict"};let i=Mn(On(e),t);return await xe.symlink(i,e),{dir:r,action:"created"}}async function Ce(t,e,n){let r=[];for(let i of Gn){let s=oo(t,i),a=oo(e,i);r.push(await Bl(s,a,n))}return r}f();import{readFileSync as Wl}from"fs";import{dirname as ql,resolve as zl}from"path";import{fileURLToPath as Jl}from"url";var Ct=null;function j(){if(Ct!==null)return Ct;let t=ql(Jl(import.meta.url));for(let e=0;e<5;e++){let n=zl(t,...Array(e).fill(".."),"package.json");try{let r=Wl(n,"utf8"),i=JSON.parse(r);if(i.name==="@nalvietnam/avatar-cli"&&typeof i.version=="string")return Ct=i.version,Ct}catch{}}return Ct="unknown",Ct}function Yl(t){return t?`
|
|
67
68
|
### \u{1F9E0} CODEBASE INTELLIGENCE \u2014 GitNexus
|
|
68
69
|
|
|
69
70
|
Workspace c\xF3 GitNexus index t\u1EA1i \`.gitnexus/\` cung c\u1EA5p architectural awareness
|
|
@@ -91,31 +92,31 @@ Khi user c\u1EA7n regenerate wiki sau refactor l\u1EDBn \u2014 ch\u1EA1y:
|
|
|
91
92
|
\`\`\`bash
|
|
92
93
|
gitnexus wiki . --api-key <key> --base-url <url>
|
|
93
94
|
\`\`\`
|
|
94
|
-
`:""}function
|
|
95
|
-
`),e&&process.stdout.write(` D\u1EF1 ki\u1EBFn: ${
|
|
95
|
+
`:""}function so(t){return{projectName:t.projectName,projectDescription:t.projectDescription,teamOwner:t.teamOwner,avatarVersion:j(),packVersion:t.packVersion,lastScan:new Date().toISOString(),gitnexusSection:Yl(t.gitnexusReady??!1)}}async function ao(t){await Ji(t.workspacePath),await v(Ae(t.workspacePath,"src")),await v(Ae(t.workspacePath,"notes"));let e="(skipped)";if(t.skipTeamPack)o.dim("Skip team-ai-pack (--skip-team-pack).");else{gt("T\u1EA3i team-ai-pack t\u1EEB Supabase...").stop();let c=await Zi(t.workspacePath,t.packVersion);e=c.pinnedTag??"(skipped)",c.skipped||o.success(`team-ai-pack: ${e}`)}let n=so({projectName:t.workspaceName,projectDescription:t.description,teamOwner:t.teamOwner,packVersion:e});await Ne(t.workspacePath),await Oe(t.workspacePath,n),await Me(t.workspacePath,n),await Ge(t.workspacePath,n),await Le(t.workspacePath),await Xl(t.workspacePath,t.autoYes),await b("init",`workspace=${t.workspaceName}`);let r=null;t.aiSkip?o.dim("B\u1ECF qua AI setup (--ai-skip). Setup sau: avatar ai setup"):r=await ee({workspacePath:t.workspacePath});let i=null;t.aiSkip||t.gitnexusSkip?o.dim(t.gitnexusSkip?"B\u1ECF qua GitNexus (--gitnexus-skip). Setup sau: avatar gitnexus install":"B\u1ECF qua GitNexus (auto-skip do --ai-skip)."):i=await ae({workspacePath:t.workspacePath,skipAnalyze:!0}),await Yi(t.workspacePath,r,i)}async function Xl(t,e){let n=Ae(t,F);if(!await m(n)){o.dim("Pack ch\u01B0a c\xE0i (skip auto-sync). Ch\u1EA1y `avatar sync` sau.");return}let r=Ae(t,".claude");o.info("Auto-sync pack content v\xE0o .claude/ (symlinks + settings)...");try{let i=await Ce(n,r,!1),s=i.filter(l=>l.action==="created"||l.action==="updated").length,a=i.filter(l=>l.action==="source-missing").length;o.success(` \u2713 Symlinks: ${s} created${a>0?`, ${a} source-missing`:""}`);let c=await re(t);c.action==="merged"&&o.success(` \u2713 settings.json merged (${c.changes.join("; ")})`),await ro(t,{autoYes:e})}catch(i){o.warn(`Auto-sync pack fail: ${i instanceof Error?i.message:i}. Ch\u1EA1y \`avatar sync\` \u0111\u1EC3 retry.`)}}function co(t){t.command("init").description("Kh\u1EDFi t\u1EA1o Avatar workspace (m\xF4i tr\u01B0\u1EDDng) \u2014 repo c\xE0i sau b\u1EB1ng 'avatar add repo'").argument("[workspace-name]","T\xEAn workspace (b\u1EAFt bu\u1ED9c, h\u1ECFi n\u1EBFu thi\u1EBFu)").option("--workspace-parent <path>","Th\u01B0 m\u1EE5c cha t\u1EA1o workspace (m\u1EB7c \u0111\u1ECBnh . \u2014 CWD)").option("--pack-version <tag>","Pin team-ai-pack v\xE0o tag c\u1EE5 th\u1EC3 (vd v0.2.0-beta.0)").option("--team-owner <email>","Email team owner (b\u1ECF qua prompt)").option("--description <text>","M\xF4 t\u1EA3 1 d\xF2ng c\u1EE7a workspace").option("--skip-team-pack","B\u1ECF qua t\u1EA3i team-ai-pack (test mode)").option("--force","B\u1ECF qua prompt khi workspace path \u0111\xE3 t\u1ED3n t\u1EA1i").option("--yes","Auto-confirm t\u1EA5t c\u1EA3 prompt").option("--ai-skip","B\u1ECF qua phase AI setup (ch\u1EA1y 'avatar ai setup' sau)").option("--gitnexus-skip","B\u1ECF qua phase GitNexus setup").action(async(e,n)=>{try{await tu(e,n)}catch(r){(r instanceof de||r instanceof xt||r instanceof Et||r instanceof x)&&(o.dim(r.message),process.exit(0)),o.error(r instanceof Error?r.message:String(r)),process.exit(1)}})}async function tu(t,e){e.yes||vt({tagline:"Kh\u1EDFi t\u1EA1o Avatar workspace"});let n=await H();for(;!n||rt(n);){o.info("Ch\u01B0a \u0111\u0103ng nh\u1EADp (ho\u1EB7c token h\u1EBFt h\u1EA1n) \u2014 ch\u1EA1y login tr\u01B0\u1EDBc...");try{await Rn({})}catch(c){o.warn(`Login fail: ${c.message}`)}if(n=await H(),n&&!rt(n))break;throw new x("C\u1EA7n \u0111\u0103ng nh\u1EADp @nal.vn \u0111\u1EC3 t\u1EA3i team-ai-pack. Ch\u1EA1y 'avatar login' r\u1ED3i 'avatar init' l\u1EA1i.")}let r=t??await Zl({message:"T\xEAn workspace:",validate:c=>c.trim().length>0?!0:"T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"}),i=e.teamOwner??await zi(n.email),s=Ql(e.workspaceParent??"."),a=await qi(s,r.trim(),e.force);await ao({workspacePath:a,workspaceName:r.trim(),teamOwner:i,description:e.description??`Avatar workspace: ${r.trim()}`,packVersion:e.packVersion,autoYes:e.yes,skipTeamPack:e.skipTeamPack,aiSkip:e.aiSkip,gitnexusSkip:e.gitnexusSkip})}f();function V(t,e){return()=>{process.stdout.write(`${p.yellow("\u23F3")} ${p.bold(`avatar ${t}`)} \u2014 ch\u01B0a implement \u1EDF milestone hi\u1EC7n t\u1EA1i.
|
|
96
|
+
`),e&&process.stdout.write(` D\u1EF1 ki\u1EBFn: ${p.cyan(e)}
|
|
96
97
|
`),process.stdout.write(` Spec \u0111\xE3 c\xF3 trong avatar-cli-implementation_4.html.
|
|
97
|
-
`),process.exit(0)}}function
|
|
98
|
-
`):
|
|
98
|
+
`),process.exit(0)}}function lo(t){t.command("mcp-run <tool-id>",{hidden:!0}).description("[internal] Spawn MCP v\u1EDBi secrets injected (M09)").action(V("mcp-run","Milestone 09"))}k();import{join as eu}from"path";import uo from"boxen";f();var nu=".claude/pack";function po(t){t.command("pack").description("Qu\u1EA3n l\xFD team-ai-pack (status, ...)").command("status").description("Hi\u1EC3n th\u1ECB version pack \u0111ang c\xE0i").option("--json","Output JSON cho script").action(async n=>{try{let r=await ru(process.cwd());n.json?process.stdout.write(`${JSON.stringify(r,null,2)}
|
|
99
|
+
`):iu(r)}catch(r){o.error(r instanceof Error?r.message:String(r)),process.exit(1)}})}async function ru(t){let e=eu(t,nu);return await m(e)?{installed:!0,currentVersion:await ye(t).catch(()=>null)}:{installed:!1,currentVersion:null}}function iu(t){if(!t.installed){let r=[`${p.bold("team-ai-pack")} \xB7 ${p.yellow("ch\u01B0a c\xE0i")}`,"\u2500".repeat(48),p.dim("Ch\u1EA1y `avatar init` ho\u1EB7c `avatar sync` \u0111\u1EC3 c\xE0i pack.")];process.stdout.write(`${uo(r.join(`
|
|
99
100
|
`),{padding:1,borderStyle:"round"})}
|
|
100
|
-
`);return}let e=t.currentVersion??
|
|
101
|
+
`);return}let e=t.currentVersion??p.yellow("(unknown)"),n=[`${p.bold("team-ai-pack status")}`,"\u2500".repeat(48),`${p.dim("Version hi\u1EC7n t\u1EA1i:")} ${e}`,"",p.dim("C\u1EADp nh\u1EADt m\u1EDBi nh\u1EA5t: avatar sync")];process.stdout.write(`${uo(n.join(`
|
|
101
102
|
`),{padding:1,borderStyle:"round"})}
|
|
102
|
-
`)}function
|
|
103
|
-
`):
|
|
104
|
-
`).find(
|
|
103
|
+
`)}function mo(t){t.command("restore").description("Kh\xF4i ph\u1EE5c .claude/pack/ t\u1EEB backup (M08)").option("--backup <name>","T\xEAn backup folder trong .claude/_backup/").option("--list","Li\u1EC7t k\xEA c\xE1c backup hi\u1EC7n c\xF3").action(V("restore","Milestone 08"))}function go(t){t.command("review").description("Review pending proposals t\u1EEB avatar scan (M08)").option("--accept-all","Approve m\u1ECDi pending kh\xF4ng h\u1ECFi (CI mode)").option("--reject-all","X\xF3a m\u1ECDi pending kh\xF4ng h\u1ECFi").action(V("review","Milestone 08"))}function fo(t){t.command("scan").description("Ch\u1EA1y project scanner v\xE0 \u0111\u1EC1 xu\u1EA5t knowledge update (M06)").option("--incremental","Ch\u1EC9 scan c\xE1c file thay \u0111\u1ED5i t\u1EEB commit cu\u1ED1i").option("--full","Scan to\xE0n b\u1ED9 d\u1EF1 \xE1n (default)").option("--scanners <list>","tech-stack,conventions,architecture,domain,git-pattern").option("--quiet","Ch\u1EA1y ng\u1EA7m, \xEDt output (d\xF9ng cho git hook)").action(V("scan","Milestone 06"))}import{promises as cu}from"fs";import{join as Se}from"path";import lu from"boxen";k();k();import{promises as ou}from"fs";import{join as su}from"path";var au="_backup";async function ho(t){let e=su(t,".claude",au);return await m(e)?(await ou.readdir(e,{withFileTypes:!0})).filter(r=>r.isDirectory()).map(r=>r.name).sort().reverse():[]}f();function wo(t){t.command("status").description("Snapshot t\u1EE9c th\xEC: project, pack version, pending, backup").option("--json","Output JSON cho script").action(async e=>{try{let n=await uu(process.cwd());e.json?process.stdout.write(`${JSON.stringify(n,null,2)}
|
|
104
|
+
`):du(n)}catch(n){o.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function uu(t){let e=t.split("/").filter(Boolean).pop()??"unknown",n=Se(t,".claude");if(!await m(n))return{projectName:e,cliVersion:j(),packVersion:null,pendingCount:0,backupCount:0,techStackSummary:"(Avatar ch\u01B0a init)",hasAvatar:!1,toolsEnabled:[],toolsAvailableCount:0};let i=Se(n,"_pending"),[s,a,c,l,u]=await Promise.all([(async()=>await m(Se(n,"pack"))?ye(t).catch(()=>null):null)(),(async()=>await m(i)?(await cu.readdir(i)).filter(g=>g.endsWith(".diff.md")).length:0)(),ho(t).then(d=>d.length).catch(()=>0),pu(n).catch(()=>"(read error)"),Ut(t).catch(()=>({available:[],enabled:[]}))]);return{projectName:e,cliVersion:j(),packVersion:s,pendingCount:a,backupCount:c,techStackSummary:l,hasAvatar:!0,toolsEnabled:u.enabled,toolsAvailableCount:u.available.length}}async function pu(t){let e=Se(t,"project","tech-stack.md");return await m(e)?(await N(e)).split(`
|
|
105
|
+
`).find(i=>i.trim()&&!i.startsWith("#")&&!i.startsWith(">"))?.trim()??"(empty)":"(no tech-stack.md)"}function mu(t){return t.toolsEnabled.length>0?`${t.toolsEnabled.join(", ")} (${t.toolsEnabled.length}/${t.toolsAvailableCount} available)`:t.toolsAvailableCount>0?`none enabled (${t.toolsAvailableCount} available \u2014 avatar tool list)`:"none"}function du(t){let e=[`${p.bold("Avatar Status")} \xB7 ${p.cyan(t.projectName)}`,"\u2500".repeat(48),`${p.dim("CLI version:")} ${t.cliVersion}`,`${p.dim("Pack version:")} ${t.packVersion??p.yellow("not installed")}`,`${p.dim("Pending changes:")} ${t.pendingCount}${t.pendingCount>0?p.dim(" (avatar review)"):""}`,`${p.dim("Backups:")} ${t.backupCount}`,`${p.dim("Tech stack:")} ${t.techStackSummary}`,`${p.dim("Tools:")} ${mu(t)}`];process.stdout.write(`${lu(e.join(`
|
|
105
106
|
`),{padding:1,borderStyle:"round"})}
|
|
106
|
-
`)}k();import{join as
|
|
107
|
-
Ch\u1EA1y 'avatar init' \u0111\u1EC3 kh\u1EDFi t\u1EA1o.`),process.exit(1)),t.dryRun){let c=await
|
|
108
|
-
Mount dir statuses:`);for(let l of c.mountDirStatuses)console.log(` ${l.dir.padEnd(12)} ${l.status}`);
|
|
109
|
-
Dry-run done. Kh\xF4ng apply. B\u1ECF --dry-run \u0111\u1EC3 th\u1EF1c thi.`);return}let
|
|
110
|
-
${
|
|
111
|
-
`,"utf8")}async function
|
|
112
|
-
`),
|
|
113
|
-
`).trim();a.length===0?await z(t,{force:!0}):await
|
|
114
|
-
`,"utf8")}f();function
|
|
107
|
+
`)}k();import{join as vo}from"path";k();import{join as ko}from"path";async function gu(t,e,n){let r=ko(t,n),i=ko(e,n);if(!await m(r))return"source-missing";if(!await m(i))return"needs-creation";let{promises:s}=await import("fs");return(await s.lstat(i)).isSymbolicLink()?"already-linked":"conflict-real-dir"}async function yo(t,e,n){let r=await fe(t)??"(ch\u01B0a c\xE0i)",i=n??"stable m\u1EDBi nh\u1EA5t (server resolve)",s=[];for(let a of Gn)s.push({dir:a,status:await gu(t,e,a)});return{currentVersion:r,targetVersion:i,mountDirStatuses:s}}f();async function bo(t){let e=await bt(t);if(e.length!==0){o.info(`Re-apply ${e.length} tool(s) \u0111ang b\u1EADt v\xE0o settings.json...`);for(let n of e)await ut(t,n)}}f();function fu(t){return t.replace(/^pack-/,"").replace(/\.tar\.gz$/,"")}async function hu(t){let e=process.cwd(),n=vo(e,".claude"),r=vo(e,F);if(await m(n)||(o.error(`Kh\xF4ng th\u1EA5y .claude/ \u2014 kh\xF4ng ph\u1EA3i Avatar workspace.
|
|
108
|
+
Ch\u1EA1y 'avatar init' \u0111\u1EC3 kh\u1EDFi t\u1EA1o.`),process.exit(1)),t.dryRun){let c=await yo(r,n,t.version);o.info(`Pack version hi\u1EC7n t\u1EA1i: ${c.currentVersion}`),o.info(`Target version: ${c.targetVersion}`),o.info(`
|
|
109
|
+
Mount dir statuses:`);for(let l of c.mountDirStatuses)console.log(` ${l.dir.padEnd(12)} ${l.status}`);o.info(`
|
|
110
|
+
Dry-run done. Kh\xF4ng apply. B\u1ECF --dry-run \u0111\u1EC3 th\u1EF1c thi.`);return}let i;try{i=await Qt()}catch(c){o.error(c instanceof Error?c.message:String(c)),process.exit(1)}o.info(t.version?`T\u1EA3i team-ai-pack ${t.version} t\u1EEB Supabase...`:"T\u1EA3i team-ai-pack (stable m\u1EDBi nh\u1EA5t) t\u1EEB Supabase...");let s;try{let c=await we(i,t.version);await ke(c.url,r),s=t.version??fu(c.object),await ge(r,{version:s,downloadedObject:c.object,extractedAt:new Date().toISOString()}),o.success(`\u0110\xE3 c\xE0i pack ${s}`)}catch(c){c instanceof ct?o.error(`${c.message}`):o.error(`T\u1EA3i pack th\u1EA5t b\u1EA1i: ${c instanceof Error?c.message:c}`),process.exit(1)}o.info("T\u1EA1o symlink farm...");let a=await Ce(r,n,t.force===!0);wu(a,t.force===!0),o.info("Merge pack settings.json template v\xE0o project settings.json...");try{let c=await re(e);switch(c.action){case"merged":o.success(` \u2713 settings.json merged (${c.changes.join("; ")})`);break;case"no-change":o.info(" - settings.json \u0111\xE3 sync.");break;case"no-pack-template":o.dim(" - Pack kh\xF4ng c\xF3 templates/settings.json.tpl, skip.");break}}catch(c){o.warn(` ! Merge settings.json fail: ${c instanceof Error?c.message:c}.`)}try{await bo(e)}catch(c){o.warn(` ! Re-apply tools fail: ${c instanceof Error?c.message:c}.`)}o.success(`Synced team-ai-pack \u2192 ${s}.`)}function wu(t,e){for(let r of t)switch(r.action){case"created":o.info(` \u2713 ${r.dir} \u2192 symlinked (new)`);break;case"updated":o.info(` \u2713 ${r.dir} \u2192 symlink refreshed`);break;case"backed-up-and-linked":o.info(` \u2713 ${r.dir} \u2192 symlinked (backup: ${r.backupPath})`);break;case"source-missing":o.warn(` - ${r.dir} \u2192 pack kh\xF4ng c\xF3 dir n\xE0y, skip`);break;case"skipped-conflict":o.warn(` ! ${r.dir} \u2192 CONFLICT: existing real dir. D\xF9ng --force \u0111\u1EC3 backup + override.`);break}let n=t.filter(r=>r.action==="skipped-conflict").length;n>0&&!e&&o.warn(`${n} mount dir(s) skip do conflict. Ch\u1EA1y l\u1EA1i v\u1EDBi --force.`)}function xo(t){t.command("sync").description("T\u1EA3i team-ai-pack m\u1EDBi nh\u1EA5t (tarball Supabase) + t\u1EA1o symlink farm").option("--force","Override .claude/<dir>/ n\u1EBFu l\xE0 real dir (backup tr\u01B0\u1EDBc)").option("--version <tag>","Pin version c\u1EE5 th\u1EC3 (vd: v0.2.0 ho\u1EB7c v0.2.0-beta.0)").option("--dry-run","Hi\u1EC3n th\u1ECB preview, kh\xF4ng apply").action(hu)}import{resolve as ku}from"path";import{checkbox as yu,confirm as bu}from"@inquirer/prompts";f();function Co(t,e){let n=new Set(e);return t.map(r=>({name:n.has(r)?`${r} (installed)`:r,value:r,checked:n.has(r)}))}function Ao(t){return t.map(e=>({name:e,value:e,checked:!1}))}function So(t){return[...t]}function Dt(t){return ku(t.target??process.cwd())}async function To(t,e,n,r){if(n.all===!0||!process.stdout.isTTY)return So(t);let s=r==="add"?Co(t,e):Ao(t);return await yu({message:r==="add"?"Ch\u1ECDn tool \u0111\u1EC3 c\xE0i (space ch\u1ECDn, enter x\xE1c nh\u1EADn):":"Ch\u1ECDn tool \u0111\u1EC3 g\u1EE1 (space ch\u1ECDn, enter x\xE1c nh\u1EADn):",choices:s})}async function vu(t){let e=Dt(t),{available:n,enabled:r}=await Ut(e);if(n.length===0){o.dim("Kh\xF4ng c\xF3 tool available (pack ch\u01B0a c\xF3 tools/, ho\u1EB7c ch\u01B0a sync).");return}let i=await To(n,r,t,"add");if(i.length===0){o.dim("Kh\xF4ng ch\u1ECDn tool n\xE0o \u2014 h\u1EE7y, kh\xF4ng thay \u0111\u1ED5i.");return}for(let s of i)await ut(e,s)}async function xu(t){let e=Dt(t),{enabled:n}=await Ut(e);if(n.length===0){o.dim("Kh\xF4ng c\xF3 tool n\xE0o \u0111ang b\u1EADt \u2014 kh\xF4ng c\xF3 g\xEC \u0111\u1EC3 g\u1EE1.");return}let r=await To(n,[],t,"remove");if(r.length===0){o.dim("Kh\xF4ng ch\u1ECDn tool n\xE0o \u2014 h\u1EE7y, kh\xF4ng thay \u0111\u1ED5i.");return}if(t.yes!==!0&&t.all!==!0&&process.stdout.isTTY&&!await bu({message:`G\u1EE1 ${r.length} tool: ${r.join(", ")}?`,default:!0})){o.dim("H\u1EE7y \u2014 kh\xF4ng g\u1EE1 tool n\xE0o.");return}for(let s of r)await Nn(e,s)}async function Cu(t){let e=Dt(t),n=await In(e),r=await yt(e);if(n.length===0&&Object.keys(r.tools).length===0){o.dim("Kh\xF4ng c\xF3 tool n\xE0o (pack ch\u01B0a c\xF3 tools/, ho\u1EB7c ch\u01B0a sync). Ch\u1EA1y 'avatar sync'.");return}let i=[...new Set([...n,...Object.keys(r.tools)])].sort();o.info("Tools:"),console.log(` ${"NAME".padEnd(20)} ${"AVAILABLE".padEnd(10)} ${"ENABLED".padEnd(8)} VERSION`);for(let s of i){let a=n.includes(s)?"yes":"no",c=r.tools[s],l=c?.enabled?"yes":"no",u=c?.version??"-";console.log(` ${s.padEnd(20)} ${a.padEnd(10)} ${l.padEnd(8)} ${u}`)}}function Po(t){let e=t.command("tools").description("Qu\u1EA3n l\xFD tool toggle (prompt-scoring, ...) b\u1EADt/t\u1EAFt nguy\xEAn kh\u1ED1i");e.command("enable <name>").description("B\u1EADt tool: merge manifest v\xE0o settings.json + ghi state").option("--target <path>","Workspace root (default: cwd)").action(async(n,r)=>{let i=Dt(r);await ut(i,n)||process.exit(1)}),e.command("disable <name>").description("T\u1EAFt tool: r\xFAt \u0111\xFAng entry manifest ra kh\u1ECFi settings.json + ghi state").option("--target <path>","Workspace root (default: cwd)").action(async(n,r)=>{let i=Dt(r);await Nn(i,n)}),e.command("list").description("Li\u1EC7t k\xEA tool: available | enabled | version").option("--target <path>","Workspace root (default: cwd)").action(async n=>{await Cu(n)}),e.command("add").description("Ch\u1ECDn (multi-select) tool available \u0111\u1EC3 c\xE0i").option("--target <path>","Workspace root (default: cwd)").option("--all","C\xE0i h\u1EBFt tool available (b\u1ECF qua prompt; auto khi non-TTY)").option("--yes","B\u1ECF x\xE1c nh\u1EADn").action(async n=>{await vu(n)}),e.command("remove").description("Ch\u1ECDn (multi-select) tool \u0111ang b\u1EADt \u0111\u1EC3 g\u1EE1").option("--target <path>","Workspace root (default: cwd)").option("--all","G\u1EE1 h\u1EBFt tool \u0111ang b\u1EADt (b\u1ECF qua prompt; auto khi non-TTY)").option("--yes","B\u1ECF x\xE1c nh\u1EADn").action(async n=>{await xu(n)})}import{relative as _u}from"path";import{confirm as Iu}from"@inquirer/prompts";import Nu from"boxen";import{cp as Te,mkdir as Eo,writeFile as Au}from"fs/promises";import{homedir as Su}from"os";import{basename as Tu,join as B}from"path";var Pu=B(Su(),".avatar","uninstall-backups");async function $o(t,e,n){let r=Tu(t),i=new Date().toISOString().replace(/[:.]/g,"-"),s=B(Pu,`${r}-${i}`);if(await Eo(s,{recursive:!0,mode:448}),e.claudeDir&&await Te(e.claudeDir,B(s,".claude"),{recursive:!0}),e.claudeMd&&await Te(e.claudeMd,B(s,"CLAUDE.md")),e.postMergeHook||e.prePushHook){let c=B(s,"hooks");await Eo(c,{recursive:!0}),e.postMergeHook&&await Te(e.postMergeHook,B(c,"post-merge")),e.prePushHook&&await Te(e.prePushHook,B(c,"pre-push"))}let a={projectName:r,projectPath:t,timestamp:i,avatarVersion:n,artifacts:{claudeDir:!!e.claudeDir,claudeMd:!!e.claudeMd,postMergeHook:!!e.postMergeHook,prePushHook:!!e.prePushHook}};return await Au(B(s,"manifest.json"),JSON.stringify(a,null,2),"utf8"),s}import{existsSync as Eu}from"fs";import{join as W}from"path";function q(t){return Eu(t)?t:null}function Ro(t){let e=q(W(t,".claude")),n=q(W(t,"CLAUDE.md")),r=q(W(t,".git","hooks","post-merge")),i=q(W(t,".git","modules","src","hooks","pre-push")),s=q(W(t,".gitignore")),a=q(W(t,".gitmodules")),c=q(W(t,"notes")),l=q(W(t,"scripts"));return{hasAnyArtifact:!!(e||n||r||i),claudeDir:e,claudeMd:n,postMergeHook:r,prePushHook:i,gitignorePath:s,gitmodulesPath:a,notesDir:c,scriptsDir:l}}import{readFile as _o,rm as z,writeFile as Io}from"fs/promises";async function No(t,e){if(t.claudeDir)if(e.keepSubmodule){let{readdir:n}=await import("fs/promises"),{join:r}=await import("path"),i=await n(t.claudeDir);for(let s of i)s!=="pack"&&await z(r(t.claudeDir,s),{recursive:!0,force:!0})}else await z(t.claudeDir,{recursive:!0,force:!0});t.claudeMd&&await z(t.claudeMd,{force:!0}),e.keepHooks||(t.postMergeHook&&await z(t.postMergeHook,{force:!0}),t.prePushHook&&await z(t.prePushHook,{force:!0})),t.gitignorePath&&await $u(t.gitignorePath),t.gitmodulesPath&&!e.keepSubmodule&&await Ru(t.gitmodulesPath,".claude/pack");for(let n of[t.notesDir,t.scriptsDir]){if(!n)continue;let{readdir:r}=await import("fs/promises");(await r(n)).length===0&&await z(n,{recursive:!0,force:!0})}}async function $u(t){let e=await _o(t,"utf8"),n=e.indexOf($n),r=e.indexOf(me);if(n===-1||r===-1)return;let i=e.slice(0,n),s=e.slice(r+me.length),a=`${i.trimEnd()}
|
|
111
|
+
${s.trimStart()}`.trim();a.length===0?await z(t,{force:!0}):await Io(t,`${a}
|
|
112
|
+
`,"utf8")}async function Ru(t,e){let r=(await _o(t,"utf8")).split(`
|
|
113
|
+
`),i=[],s=!1;for(let c of r){if(c.trim().startsWith("[submodule")&&c.includes(e)){s=!0;continue}s&&c.trim().startsWith("[submodule")&&(s=!1),s||i.push(c)}let a=i.join(`
|
|
114
|
+
`).trim();a.length===0?await z(t,{force:!0}):await Io(t,`${a}
|
|
115
|
+
`,"utf8")}f();function Oo(t){t.command("uninstall").description("G\u1EE1 Avatar kh\u1ECFi project \u2014 backup t\u1EF1 \u0111\u1ED9ng (M11)").option("--yes","Skip confirm prompt").option("--no-backup","Kh\xF4ng t\u1EA1o backup tr\u01B0\u1EDBc khi x\xF3a (nguy hi\u1EC3m)").option("--keep-submodule","Gi\u1EEF submodule .claude/pack/").option("--keep-hooks","Gi\u1EEF git hooks post-merge, pre-push").option("--dry-run","Hi\u1EC3n th\u1ECB danh s\xE1ch s\u1EBD x\xF3a, kh\xF4ng th\u1EF1c thi").action(async e=>{try{await Ou(e)}catch(n){o.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function Ou(t){let e=process.cwd(),n=Ro(e);if(!n.hasAnyArtifact){o.info("Project ch\u01B0a c\xE0i Avatar \u2014 kh\xF4ng c\xF3 g\xEC \u0111\u1EC3 g\u1EE1.");return}if(Mu(e,n,t),t.dryRun){o.dim("--dry-run: k\u1EBFt th\xFAc, kh\xF4ng x\xF3a.");return}if(!t.yes&&!await Iu({message:"Ti\u1EBFp t\u1EE5c g\u1EE1 Avatar?",default:!1})){o.info("\u0110\xE3 h\u1EE7y.");return}let r=null;t.noBackup||(r=await $o(e,n,j()),o.success(`Backup t\u1EA1o t\u1EA1i: ${r}`)),await No(n,{keepSubmodule:t.keepSubmodule,keepHooks:t.keepHooks}),await b("uninstall",`project=${e},backup=${r??"skipped"}`),Gu(r)}function Mu(t,e,n){o.info(`Project: ${t}`),o.plain(""),o.plain("C\xE1c artifact s\u1EBD g\u1EE1:"),e.claudeDir&&o.plain(` ${p.red("\u2717")} ${_u(t,e.claudeDir)||".claude/"}`),e.claudeMd&&o.plain(` ${p.red("\u2717")} CLAUDE.md`),e.postMergeHook&&!n.keepHooks&&o.plain(` ${p.red("\u2717")} .git/hooks/post-merge`),e.prePushHook&&!n.keepHooks&&o.plain(` ${p.red("\u2717")} .git/modules/src/hooks/pre-push`),e.gitignorePath&&o.plain(` ${p.yellow("\u270E")} .gitignore (g\u1EE1 Avatar block)`),e.gitmodulesPath&&!n.keepSubmodule&&o.plain(` ${p.yellow("\u270E")} .gitmodules (g\u1EE1 entry .claude/pack)`),o.plain(""),o.plain("Kh\xF4ng \u0111\u1EE5ng:"),o.plain(` ${p.green("\u2713")} src/ (code kh\xE1ch)`),o.plain(` ${p.green("\u2713")} Git history`),o.plain(` ${p.green("\u2713")} ~/.avatar/config.json (token SSO)`),o.plain(` ${p.green("\u2713")} Secrets trong keychain`),o.plain("")}function Gu(t){let e=[`${p.green("\u2713")} Avatar \u0111\xE3 \u0111\u01B0\u1EE3c g\u1EE1 kh\u1ECFi project`];t&&(e.push(""),e.push(` ${p.dim("Backup:")} ${t}`),e.push(` ${p.dim("Restore:")} ${p.cyan(`cp -r "${t}"/* .`)}`)),process.stdout.write(`${Nu(e.join(`
|
|
115
116
|
`),{padding:1,borderStyle:"round"})}
|
|
116
|
-
`)}var Ln=j(),P=new
|
|
117
|
-
${
|
|
117
|
+
`)}var Ln=j(),P=new Lu;P.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(Ln,"-v, --version","Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI").addHelpText("beforeAll",()=>`
|
|
118
|
+
${En({tagline:`v${Ln} \xB7 AI harness CLI for NAL Vietnam`})}
|
|
118
119
|
|
|
119
|
-
`);var
|
|
120
|
+
`);var ju=process.argv.includes("-v")||process.argv.includes("--version");ju&&(vt({tagline:`v${Ln} \xB7 AI harness CLI for NAL Vietnam`}),process.exit(0));Xi(P);co(P);xo(P);fo(P);go(P);wo(P);Si(P);mo(P);Po(P);lo(P);pi(P);Oi(P);po(P);jr(P);Oo(P);P.parseAsync(process.argv).catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`\u2717 L\u1ED7i kh\xF4ng x\u1EED l\xFD \u0111\u01B0\u1EE3c: ${e}
|
|
120
121
|
`),process.exit(1)});
|
|
121
122
|
//# sourceMappingURL=index.js.map
|