@nalvietnam/avatar-cli 1.20.0 → 1.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // @nalvietnam/avatar-cli — built with tsup
2
2
  var ls=Object.defineProperty;var nn=(t,e)=>()=>(t&&(e=t(t=0)),e);var us=(t,e)=>{for(var n in e)ls(t,n,{get:e[n],enumerable:!0})};import{constants as ps,promises as kt}from"fs";import{dirname as ms,join as Am,relative as Sm}from"path";async function d(t){try{return await kt.access(t,ps.F_OK),!0}catch{return!1}}async function $(t){await kt.mkdir(t,{recursive:!0})}async function U(t){return await kt.readFile(t,"utf8")}async function y(t){return JSON.parse(await U(t))}async function wt(t,e,n){await $(ms(t));let i=`${t}.tmp-${process.pid}-${Date.now()}`;await kt.writeFile(i,e,"utf8"),n!==void 0&&await kt.chmod(i,n),await kt.rename(i,t)}async function S(t,e,n){await wt(t,`${JSON.stringify(e,null,2)}
3
3
  `,n)}var k=nn(()=>{"use strict"});import{spawnSync as xa}from"child_process";import{chmodSync as Aa,existsSync as Si,readFileSync as Ci,renameSync as Sa,writeFileSync as Ca}from"fs";import{join as Pa}from"path";function bt(t){return Pa(t,$a)}function $i(t){let e=t.indexOf(Pi);if(e===-1)return null;let n=t.indexOf(mn,e);return n===-1?null:t.slice(e,n+mn.length)}function Ei(t){let e=new Map,n=/^export\s+([A-Z][A-Z0-9_]*)="((?:[^"\\]|\\.)*)"$/gm,i;for(;(i=n.exec(t))!==null;){let r=i[1],s=i[2];if(!r||s===void 0)continue;let a=s.replace(/\\"/g,'"').replace(/\\\\/g,"\\");e.set(r,a)}return e}function Ra(t){return t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Ta(t){let e=[Ea],n=[...t.keys()].sort();for(let i of n){let r=t.get(i);r!==void 0&&e.push(`export ${i}="${Ra(r)}"`)}return e.push(mn),e.join(`
4
- `)}async function ot(t,e){let n=bt(t),i="";Si(n)&&(i=Ci(n,"utf8"));let r=$i(i),s=r?Ei(r):new Map,a=s.size;for(let[h,x]of Object.entries(e))x===null?s.delete(h):s.set(h,x);let c=Ta(s),l,u=!1;r?(l=i.replace(r,c),u=i.replace(r,"").trim().length>0):i.trim()?(u=!0,l=`${i.replace(/\s+$/,"")}
4
+ `)}async function ot(t,e){let n=bt(t),i="";Si(n)&&(i=Ci(n,"utf8"));let r=$i(i),s=r?Ei(r):new Map,a=s.size;for(let[f,x]of Object.entries(e))x===null?s.delete(f):s.set(f,x);let c=Ta(s),l,u=!1;r?(l=i.replace(r,c),u=i.replace(r,"").trim().length>0):i.trim()?(u=!0,l=`${i.replace(/\s+$/,"")}
5
5
 
6
6
  ${c}
7
7
  `):l=`${c}
@@ -10,7 +10,7 @@ ${c}
10
10
  # DO NOT COMMIT \u2014 must be gitignored (avatar enforces this on setup).
11
11
  # Edit via: avatar secrets set <NAME>
12
12
  # Remove via: avatar secrets rm <NAME>
13
- # View: avatar secrets list`});var bo={};us(bo,{MIGRATABLE_SECRET_KEYS:()=>vo,detectPlaintextSecretsInSettings:()=>_t,migrateSecretsFromSettingsToEnvrc:()=>Xe});import{promises as cp}from"fs";import{join as lp}from"path";function pp(t){return lp(t,...up)}function mp(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-secrets-migration-${n}`}async function _t(t){let e=pp(t);if(!await d(e))return{keys:[],settingsPath:e};let n;try{n=await y(e)}catch{return{keys:[],settingsPath:e}}let i=n.env??{},r=[];for(let s of vo)typeof i[s]=="string"&&i[s].length>0&&r.push(s);return{keys:r,settingsPath:e}}async function Xe(t){let{keys:e,settingsPath:n}=await _t(t);if(e.length===0)return{migratedKeys:[],settingsJsonBackupPath:null,noOp:!0};let i=await y(n),r=i.env??{},s={};for(let c of e){let l=r[c];typeof l=="string"&&l.length>0&&(s[c]=l)}await ot(t,s);let a=mp(n);await cp.copyFile(n,a);for(let c of e)delete r[c];return Object.keys(r).length===0?i.env=void 0:i.env=r,await S(n,i),{migratedKeys:e,settingsJsonBackupPath:a,noOp:!1}}var vo,up,Wn=nn(()=>{"use strict";k();ke();vo=["ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","OPENAI_API_KEY","GEMINI_API_KEY","DEEPSEEK_API_KEY","GROQ_API_KEY","PERPLEXITY_API_KEY","ANTHROPIC_VERTEX_PROJECT_ID","GOOGLE_APPLICATION_CREDENTIALS"],up=[".claude","settings.json"]});import{Command as wm}from"commander";k();import{promises as za}from"fs";import{join as Ni}from"path";import{confirm as qa}from"@inquirer/prompts";import{existsSync as oe,readFileSync as ds}from"fs";import{dirname as gs,join as se}from"path";var fs=5;function hs(t){let e=oe(se(t,".claude")),n=oe(se(t,"CLAUDE.md"));if(!e||!n)return!1;let i=oe(se(t,"src")),r=se(t,".gitmodules");if(i)return!0;if(oe(r))try{let s=ds(r,"utf8");return s.includes("path = src")||s.includes("path = ./src")}catch{return!1}return!1}function yt(t){let e=t;for(let n=0;n<fs;n++){if(hs(e))return e;let i=gs(e);if(i===e)return null;e=i}return null}import{promises as oi}from"fs";import{homedir as ws}from"os";import{join as jt}from"path";import{z as f}from"zod";var ni=f.object({email:f.string().email(),name:f.string(),access_token:f.string().min(1),refresh_token:f.string().min(1),expires_at:f.string().datetime(),id_token:f.string().min(1)}),ks=f.object({installed_tools:f.record(f.string(),f.object({version:f.string().optional(),installed_at:f.string().datetime(),install_method:f.string()})).default({}),tool_inputs:f.record(f.string(),f.unknown()).default({})}),Tm=f.object({$schema:f.string().optional(),includeCoAuthoredBy:f.boolean().optional(),env:f.record(f.string(),f.string()).default({}),permissions:f.object({allow:f.array(f.string()).default([]),deny:f.array(f.string()).default([])}).partial().optional(),hooks:f.record(f.string(),f.array(f.unknown())).optional(),statusLine:f.object({type:f.string(),command:f.string(),padding:f.number().optional()}).optional()}),Im=f.enum(["internal","client","library"]);k();var Gt=jt(ws(),".avatar"),Z=jt(Gt,"config.json"),Gm=jt(Gt,"state.json"),rn=jt(Gt,"audit.log"),Lm=jt(Gt,"backups"),ys=384;async function on(){await $(Gt)}async function Q(){if(!await d(Z))return null;let t=await y(Z),e=ni.safeParse(t);return e.success?e.data:null}async function ii(t){await on(),await S(Z,t,ys)}async function ri(){if(await d(Z)){let{promises:t}=await import("fs");await t.unlink(Z)}}function tt(t){let e=Date.parse(t.expires_at);return Number.isNaN(e)||e-Date.now()<6e4}async function vs(){try{await oi.chmod(rn,384)}catch{}}async function w(t,e){await on();let n={timestamp:new Date().toISOString(),action:t,...e?{detail:e}:{}},i=`${JSON.stringify(n)}
13
+ # View: avatar secrets list`});var bo={};us(bo,{MIGRATABLE_SECRET_KEYS:()=>vo,detectPlaintextSecretsInSettings:()=>_t,migrateSecretsFromSettingsToEnvrc:()=>Xe});import{promises as cp}from"fs";import{join as lp}from"path";function pp(t){return lp(t,...up)}function mp(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-secrets-migration-${n}`}async function _t(t){let e=pp(t);if(!await d(e))return{keys:[],settingsPath:e};let n;try{n=await y(e)}catch{return{keys:[],settingsPath:e}}let i=n.env??{},r=[];for(let s of vo)typeof i[s]=="string"&&i[s].length>0&&r.push(s);return{keys:r,settingsPath:e}}async function Xe(t){let{keys:e,settingsPath:n}=await _t(t);if(e.length===0)return{migratedKeys:[],settingsJsonBackupPath:null,noOp:!0};let i=await y(n),r=i.env??{},s={};for(let c of e){let l=r[c];typeof l=="string"&&l.length>0&&(s[c]=l)}await ot(t,s);let a=mp(n);await cp.copyFile(n,a);for(let c of e)delete r[c];return Object.keys(r).length===0?i.env=void 0:i.env=r,await S(n,i),{migratedKeys:e,settingsJsonBackupPath:a,noOp:!1}}var vo,up,Wn=nn(()=>{"use strict";k();ke();vo=["ANTHROPIC_API_KEY","ANTHROPIC_AUTH_TOKEN","OPENAI_API_KEY","GEMINI_API_KEY","DEEPSEEK_API_KEY","GROQ_API_KEY","PERPLEXITY_API_KEY","ANTHROPIC_VERTEX_PROJECT_ID","GOOGLE_APPLICATION_CREDENTIALS"],up=[".claude","settings.json"]});import{Command as wm}from"commander";k();import{promises as za}from"fs";import{join as Ni}from"path";import{confirm as qa}from"@inquirer/prompts";import{existsSync as oe,readFileSync as ds}from"fs";import{dirname as gs,join as se}from"path";var fs=5;function hs(t){let e=oe(se(t,".claude")),n=oe(se(t,"CLAUDE.md"));if(!e||!n)return!1;let i=oe(se(t,"src")),r=se(t,".gitmodules");if(i)return!0;if(oe(r))try{let s=ds(r,"utf8");return s.includes("path = src")||s.includes("path = ./src")}catch{return!1}return!1}function yt(t){let e=t;for(let n=0;n<fs;n++){if(hs(e))return e;let i=gs(e);if(i===e)return null;e=i}return null}import{promises as oi}from"fs";import{homedir as ws}from"os";import{join as jt}from"path";import{z as h}from"zod";var ni=h.object({email:h.string().email(),name:h.string(),access_token:h.string().min(1),refresh_token:h.string().min(1),expires_at:h.string().datetime(),id_token:h.string().min(1)}),ks=h.object({installed_tools:h.record(h.string(),h.object({version:h.string().optional(),installed_at:h.string().datetime(),install_method:h.string()})).default({}),tool_inputs:h.record(h.string(),h.unknown()).default({})}),Tm=h.object({$schema:h.string().optional(),includeCoAuthoredBy:h.boolean().optional(),env:h.record(h.string(),h.string()).default({}),permissions:h.object({allow:h.array(h.string()).default([]),deny:h.array(h.string()).default([])}).partial().optional(),hooks:h.record(h.string(),h.array(h.unknown())).optional(),statusLine:h.object({type:h.string(),command:h.string(),padding:h.number().optional()}).optional()}),Im=h.enum(["internal","client","library"]);k();var Gt=jt(ws(),".avatar"),Z=jt(Gt,"config.json"),Gm=jt(Gt,"state.json"),rn=jt(Gt,"audit.log"),Lm=jt(Gt,"backups"),ys=384;async function on(){await $(Gt)}async function Q(){if(!await d(Z))return null;let t=await y(Z),e=ni.safeParse(t);return e.success?e.data:null}async function ii(t){await on(),await S(Z,t,ys)}async function ri(){if(await d(Z)){let{promises:t}=await import("fs");await t.unlink(Z)}}function tt(t){let e=Date.parse(t.expires_at);return Number.isNaN(e)||e-Date.now()<6e4}async function vs(){try{await oi.chmod(rn,384)}catch{}}async function w(t,e){await on();let n={timestamp:new Date().toISOString(),action:t,...e?{detail:e}:{}},i=`${JSON.stringify(n)}
14
14
  `;await oi.appendFile(rn,i,{encoding:"utf8",mode:384}),await vs()}import{spawnSync as sn}from"child_process";import p from"chalk";import bs from"ora";var o={info:t=>process.stdout.write(`${p.blue("\u2139")} ${t}
15
15
  `),success:t=>process.stdout.write(`${p.green("\u2713")} ${t}
16
16
  `),warn:t=>process.stdout.write(`${p.yellow("\u26A0")} ${t}
@@ -26,17 +26,17 @@ ${e}
26
26
  `):[],s=new Set(Object.keys(e)),a=r.filter(l=>{let u=l.match(/^([A-Z][A-Z0-9_]*)=/);return!(u?.[1]&&s.has(u[1]))});for(let[l,u]of Object.entries(e)){let m=u.replace(/'/g,"'\\''");a.push(`${l}='${m}'`)}let c=a.filter((l,u,m)=>!(l===""&&m[u-1]==="")).join(`
27
27
  `);return await we.writeFile(n,c.endsWith(`
28
28
  `)?c:`${c}
29
- `,{encoding:"utf8",mode:384}),await we.chmod(n,384),n}async function Ut(t,e){let n=await vt(),i=!1,r=!1;if(!n.installed){o.info("\u0110ang setup direnv \u0111\u1EC3 b\u1EA3o v\u1EC7 API key (silent)...");let a=await fe();if(a.success)o.dim(` \u2713 direnv installed via ${a.method}`),i=!0,n=await vt();else{o.warn(` ! direnv install fail (${a.message}). Fallback sang .claude/.env (gitignored).`);let c=await _a(t,e);return o.warn(` \u26A0 Key \u0111\xE3 ghi v\xE0o ${c} (chmod 600) nh\u01B0ng KH\xD4NG auto-load \u2014 c\u1EA7n 'source ${c}' m\u1ED7i terminal m\u1EDBi.`),o.dim(` Manual install direnv sau: ${a.manualInstructionsUrl}`),{storageMethod:"claude-env-fallback",storagePath:c,autoLoad:!1,installedDirenvNow:!1,addedShellHook:!1}}}if(!n.shellHookInZshrc){let a=he();a.modified&&(o.dim(` \u2713 Added direnv hook to ${a.zshrcPath}`),o.dim(" Reload shell: 'source ~/.zshrc' ho\u1EB7c m\u1EDF terminal m\u1EDBi"),r=!0)}let s=await ot(t,e);return o.success(`\u2713 Key \u0111\xE3 l\u01B0u (${Object.keys(e).length} vars) v\xE0o ${s.envrcPath}`),s.direnvAllowOk===!1&&o.warn(" direnv allow fail \u2014 ch\u1EA1y 'direnv allow' trong workspace \u0111\u1EC3 activate."),{storageMethod:"envrc",storagePath:s.envrcPath,autoLoad:!0,installedDirenvNow:i,addedShellHook:r}}k();import{promises as Oa}from"fs";import{join as Na}from"path";var dn=384;function Ma(t){return Na(t,".claude","settings.json")}async function ja(t){if(!await d(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 Ga(t,e){let{env:n,...i}=t,r={...i,model:e};if(n){let{ANTHROPIC_BASE_URL:s,ANTHROPIC_AUTH_TOKEN:a,ANTHROPIC_API_KEY:c,...l}=n;Object.keys(l).length>0&&(r.env=l)}return r}function ye(t){if(!t)return t;let{ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_API_KEY:n,OPENAI_API_KEY:i,GEMINI_API_KEY:r,...s}=t;return s}function La(t,e,n,i,r){let a={...ye(t.env),ANTHROPIC_BASE_URL:n};return r||(a.ANTHROPIC_AUTH_TOKEN=e),{...t,env:a,model:i,avatarProvider:"llmlite"}}function Ha(t,e,n,i,r){let a={...ye(t.env),ANTHROPIC_BASE_URL:n};return r||(a.ANTHROPIC_API_KEY=e),{...t,env:a,model:i,avatarProvider:"anthropic"}}function Ua(t,e,n,i,r){let a={...ye(t.env),OPENAI_BASE_URL:n};return r||(a.OPENAI_API_KEY=e),{...t,env:a,model:i,avatarProvider:"codex"}}function Da(t,e,n,i,r){let a={...ye(t.env),GEMINI_BASE_URL:n};return r||(a.GEMINI_API_KEY=e),{...t,env:a,model:i,avatarProvider:"gemini"}}function Fa(t,e){let n=e.env||{},i=typeof e.model=="string"?e.model:void 0;return{...t,env:{...t.env||{},...n},...i?{model:i}:{}}}async function M(t,e){let n=Ma(t),i=await ja(n),r;switch(e.provider){case"subscription":r=Ga(i,e.model);break;case"llmlite":r=La(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"anthropic":r=Ha(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"codex":r=Ua(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"gemini":r=Da(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"use-global":r=Fa(i,e.sourceSettings);break}await S(n,r,dn);try{await Oa.chmod(n,dn)}catch{}return{path:n,mode:dn}}var j="sonnet";async function ve(t){try{o.info("Setup AI provider cho workspace...");let e=Ht();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."),pi(),ae(),e=Ht(),!e.installed)throw new Error("C\xE0i Claude Code xong nh\u01B0ng v\u1EABn kh\xF4ng detect \u0111\u01B0\u1EE3c binary.");let n=un();switch(await di(n)){case"subscription":{let r=an();if(r.state!=="authenticated"&&(cn(),r=an()),r.state==="authenticated"&&r.subscriptionType)return await M(t.workspacePath,{provider:"subscription",model:j}),await w("ai_setup",`provider=subscription,result=ok,plan=${r.subscriptionType},probe=skipped`),o.success(`AI ready \xB7 Subscription (${r.subscriptionType}) \xB7 model=${j}`),{ok:!0,provider:"subscription",model:j};o.dim("Auth status kh\xF4ng tr\u1EA3 subscriptionType \u2014 verify quota (30-60s)...");let s=ln();if(!s.ok&&s.reason==="auth-expired"&&(o.warn("Token Claude Code \u0111\xE3 h\u1EBFt h\u1EA1n. T\u1EF1 \u0111\u1ED9ng re-login..."),cn(),s=ln()),!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 M(t.workspacePath,{provider:"subscription",model:j}),await w("ai_setup",`provider=subscription,result=ok,probe=${s.reason}-soft-pass`),o.success(`AI ready \xB7 Subscription (probe ${s.reason}, soft-pass) \xB7 model=${j}`),{ok:!0,provider:"subscription",model:j};if(!s.ok){let a=s.reason??"unknown";return await w("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 ${ai(a)}`),{ok:!1,reason:`subscription-${a}`,phase:"quota"}}return await M(t.workspacePath,{provider:"subscription",model:j}),await w("ai_setup","provider=subscription,result=ok"),o.success(`AI ready \xB7 Subscription \xB7 model=${j}`),{ok:!0,provider:"subscription",model:j}}case"llmlite":{let r=await yi(),s=await Ut(t.workspacePath,{ANTHROPIC_AUTH_TOKEN:r.apiKey});return await M(t.workspacePath,{provider:"llmlite",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=llmlite,result=ok,model=${r.model},base=${r.baseUrl},storage=${s.storageMethod}`),o.success(`AI ready \xB7 LLMLite (NAL) \xB7 model=${r.model} \xB7 ${r.baseUrl}`),{ok:!0,provider:"llmlite",model:r.model}}case"anthropic":{let r=await fi(),s=await Ut(t.workspacePath,{ANTHROPIC_API_KEY:r.apiKey});return await M(t.workspacePath,{provider:"anthropic",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=anthropic,result=ok,model=${r.model},storage=${s.storageMethod}`),o.success(`AI ready \xB7 Anthropic Direct \xB7 model=${r.model} \xB7 ${r.baseUrl}`),{ok:!0,provider:"anthropic",model:r.model}}case"codex":{let r=await bi(),s=await Ut(t.workspacePath,{OPENAI_API_KEY:r.apiKey});return await M(t.workspacePath,{provider:"codex",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=codex,result=ok,model=${r.model},storage=${s.storageMethod}`),o.success(`AI ready \xB7 OpenAI/Codex Direct \xB7 model=${r.model} \xB7 ${r.baseUrl}`),o.dim(" Note: Claude Code native kh\xF4ng support OpenAI \u2014 c\u1EA7n proxy qua LiteLLM/ai.nal.vn \u0111\u1EC3 d\xF9ng l\xE0m main AI. Key \u0111\xE3 l\u01B0u \u0111\u1EC3 skills kh\xE1c d\xF9ng (vd ai-multimodal)."),{ok:!0,provider:"codex",model:r.model}}case"gemini":{let r=await ki(),s=await Ut(t.workspacePath,{GEMINI_API_KEY:r.apiKey});return await M(t.workspacePath,{provider:"gemini",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=gemini,result=ok,model=${r.model},storage=${s.storageMethod}`),o.success(`AI ready \xB7 Google Gemini Direct \xB7 model=${r.model} \xB7 ${r.baseUrl}`),o.dim(" Note: Claude Code native kh\xF4ng support Gemini \u2014 c\u1EA7n proxy qua LiteLLM/ai.nal.vn \u0111\u1EC3 d\xF9ng l\xE0m main AI. Key \u0111\xE3 l\u01B0u \u0111\u1EC3 skills kh\xE1c d\xF9ng."),{ok:!0,provider:"gemini",model:r.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 M(t.workspacePath,{provider:"use-global",sourceSettings:n.rawSettings}),await w("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 w("ai_setup",`result=failed,error=${n.slice(0,200)}`),{ok:!1,reason:n}}}import{spawnSync as Ka}from"child_process";var gn=1e4,Ti=3e4,_i=5,fn="say ok",Ii="2023-06-01";async function Va(t,e,n){o.info(`Testing LLMLite provider: ${t} (key: ${F(e)})`);let i=new AbortController,r=setTimeout(()=>i.abort(),gn);try{let s=await fetch(`${t}/v1/models`,{headers:{Authorization:`Bearer ${e}`},signal:i.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(", "),x=c.length>5?` ...+${c.length-5} more`:"";o.dim(` Models: ${h}${x}`)}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:fn}],max_tokens:_i}),signal:i.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(),m=typeof u.choices?.[0]?.message?.content=="string"?u.choices[0].message.content:"(empty response)",g=u.usage?.total_tokens??"?";o.success(`Response: "${String(m).trim().slice(0,100)}"`),o.dim(` Tokens used: ${g}`)}catch(s){throw s.name==="AbortError"?new Error(`Timeout ${gn/1e3}s. Check m\u1EA1ng / endpoint ${t}.`):s}finally{clearTimeout(r)}}function Ba(){o.info("Testing Subscription provider qua `claude --print`...");let t=Ka("claude",["--print",fn],{encoding:"utf8",timeout:Ti});if(t.signal==="SIGTERM")throw new Error(`Timeout ${Ti/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 Wa(t,e,n){o.info(`Testing Anthropic Direct provider: ${t} (key: ${F(e)})`);let i=new AbortController,r=setTimeout(()=>i.abort(),gn);try{let s=await fetch(`${t}/v1/models`,{headers:{"x-api-key":e,"anthropic-version":Ii},signal:i.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":Ii,"Content-Type":"application/json"},body:JSON.stringify({model:n,max_tokens:_i,messages:[{role:"user",content:fn}]}),signal:i.signal});if(!l.ok){let g=(await l.text()).slice(0,200);throw new Error(`Message endpoint fail (HTTP ${l.status}): ${g}`)}let m=((await l.json()).content||[]).map(g=>typeof g.text=="string"?g.text:"").join("").trim().slice(0,100);o.success(`Response: "${m}"`)}finally{clearTimeout(r)}}async function Oi(t){let e=t.env||{},n=typeof e.ANTHROPIC_BASE_URL=="string"?e.ANTHROPIC_BASE_URL:void 0,i=typeof e.ANTHROPIC_AUTH_TOKEN=="string"?e.ANTHROPIC_AUTH_TOKEN:void 0,r=typeof e.ANTHROPIC_API_KEY=="string"?e.ANTHROPIC_API_KEY:void 0,s=typeof t.model=="string"?t.model:"default";return r&&n?(await Wa(n,r,s),{ok:!0,provider:"anthropic",message:"Anthropic Direct provider working"}):n&&i?(await Va(n,i,s),{ok:!0,provider:"llmlite",message:"LLMLite provider working"}):(Ba(),{ok:!0,provider:"subscription",message:"Subscription provider working"})}async function be(){let t=process.cwd(),e=yt(t);return e||(o.error(`Kh\xF4ng t\xECm th\u1EA5y Avatar workspace t\u1EEB th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i.
29
+ `,{encoding:"utf8",mode:384}),await we.chmod(n,384),n}async function Ut(t,e){let n=await vt(),i=!1,r=!1;if(!n.installed){o.info("\u0110ang setup direnv \u0111\u1EC3 b\u1EA3o v\u1EC7 API key (silent)...");let a=await fe();if(a.success)o.dim(` \u2713 direnv installed via ${a.method}`),i=!0,n=await vt();else{o.warn(` ! direnv install fail (${a.message}). Fallback sang .claude/.env (gitignored).`);let c=await _a(t,e);return o.warn(` \u26A0 Key \u0111\xE3 ghi v\xE0o ${c} (chmod 600) nh\u01B0ng KH\xD4NG auto-load \u2014 c\u1EA7n 'source ${c}' m\u1ED7i terminal m\u1EDBi.`),o.dim(` Manual install direnv sau: ${a.manualInstructionsUrl}`),{storageMethod:"claude-env-fallback",storagePath:c,autoLoad:!1,installedDirenvNow:!1,addedShellHook:!1}}}if(!n.shellHookInZshrc){let a=he();a.modified&&(o.dim(` \u2713 Added direnv hook to ${a.zshrcPath}`),o.dim(" Reload shell: 'source ~/.zshrc' ho\u1EB7c m\u1EDF terminal m\u1EDBi"),r=!0)}let s=await ot(t,e);return o.success(`\u2713 Key \u0111\xE3 l\u01B0u (${Object.keys(e).length} vars) v\xE0o ${s.envrcPath}`),s.direnvAllowOk===!1&&o.warn(" direnv allow fail \u2014 ch\u1EA1y 'direnv allow' trong workspace \u0111\u1EC3 activate."),{storageMethod:"envrc",storagePath:s.envrcPath,autoLoad:!0,installedDirenvNow:i,addedShellHook:r}}k();import{promises as Oa}from"fs";import{join as Na}from"path";var dn=384;function Ma(t){return Na(t,".claude","settings.json")}async function ja(t){if(!await d(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 Ga(t,e){let{env:n,...i}=t,r={...i,model:e};if(n){let{ANTHROPIC_BASE_URL:s,ANTHROPIC_AUTH_TOKEN:a,ANTHROPIC_API_KEY:c,...l}=n;Object.keys(l).length>0&&(r.env=l)}return r}function ye(t){if(!t)return t;let{ANTHROPIC_AUTH_TOKEN:e,ANTHROPIC_API_KEY:n,OPENAI_API_KEY:i,GEMINI_API_KEY:r,...s}=t;return s}function La(t,e,n,i,r){let a={...ye(t.env),ANTHROPIC_BASE_URL:n};return r||(a.ANTHROPIC_AUTH_TOKEN=e),{...t,env:a,model:i,avatarProvider:"llmlite"}}function Ha(t,e,n,i,r){let a={...ye(t.env),ANTHROPIC_BASE_URL:n};return r||(a.ANTHROPIC_API_KEY=e),{...t,env:a,model:i,avatarProvider:"anthropic"}}function Ua(t,e,n,i,r){let a={...ye(t.env),OPENAI_BASE_URL:n};return r||(a.OPENAI_API_KEY=e),{...t,env:a,model:i,avatarProvider:"codex"}}function Da(t,e,n,i,r){let a={...ye(t.env),GEMINI_BASE_URL:n};return r||(a.GEMINI_API_KEY=e),{...t,env:a,model:i,avatarProvider:"gemini"}}function Fa(t,e){let n=e.env||{},i=typeof e.model=="string"?e.model:void 0;return{...t,env:{...t.env||{},...n},...i?{model:i}:{}}}async function M(t,e){let n=Ma(t),i=await ja(n),r;switch(e.provider){case"subscription":r=Ga(i,e.model);break;case"llmlite":r=La(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"anthropic":r=Ha(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"codex":r=Ua(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"gemini":r=Da(i,e.apiKey,e.baseUrl,e.model,e.skipApiKey===!0);break;case"use-global":r=Fa(i,e.sourceSettings);break}await S(n,r,dn);try{await Oa.chmod(n,dn)}catch{}return{path:n,mode:dn}}var j="sonnet";async function ve(t){try{o.info("Setup AI provider cho workspace...");let e=Ht();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."),pi(),ae(),e=Ht(),!e.installed)throw new Error("C\xE0i Claude Code xong nh\u01B0ng v\u1EABn kh\xF4ng detect \u0111\u01B0\u1EE3c binary.");let n=un();switch(await di(n)){case"subscription":{let r=an();if(r.state!=="authenticated"&&(cn(),r=an()),r.state==="authenticated"&&r.subscriptionType)return await M(t.workspacePath,{provider:"subscription",model:j}),await w("ai_setup",`provider=subscription,result=ok,plan=${r.subscriptionType},probe=skipped`),o.success(`AI ready \xB7 Subscription (${r.subscriptionType}) \xB7 model=${j}`),{ok:!0,provider:"subscription",model:j};o.dim("Auth status kh\xF4ng tr\u1EA3 subscriptionType \u2014 verify quota (30-60s)...");let s=ln();if(!s.ok&&s.reason==="auth-expired"&&(o.warn("Token Claude Code \u0111\xE3 h\u1EBFt h\u1EA1n. T\u1EF1 \u0111\u1ED9ng re-login..."),cn(),s=ln()),!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 M(t.workspacePath,{provider:"subscription",model:j}),await w("ai_setup",`provider=subscription,result=ok,probe=${s.reason}-soft-pass`),o.success(`AI ready \xB7 Subscription (probe ${s.reason}, soft-pass) \xB7 model=${j}`),{ok:!0,provider:"subscription",model:j};if(!s.ok){let a=s.reason??"unknown";return await w("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 ${ai(a)}`),{ok:!1,reason:`subscription-${a}`,phase:"quota"}}return await M(t.workspacePath,{provider:"subscription",model:j}),await w("ai_setup","provider=subscription,result=ok"),o.success(`AI ready \xB7 Subscription \xB7 model=${j}`),{ok:!0,provider:"subscription",model:j}}case"llmlite":{let r=await yi(),s=await Ut(t.workspacePath,{ANTHROPIC_AUTH_TOKEN:r.apiKey});return await M(t.workspacePath,{provider:"llmlite",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=llmlite,result=ok,model=${r.model},base=${r.baseUrl},storage=${s.storageMethod}`),o.success(`AI ready \xB7 LLMLite (NAL) \xB7 model=${r.model} \xB7 ${r.baseUrl}`),{ok:!0,provider:"llmlite",model:r.model}}case"anthropic":{let r=await fi(),s=await Ut(t.workspacePath,{ANTHROPIC_API_KEY:r.apiKey});return await M(t.workspacePath,{provider:"anthropic",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=anthropic,result=ok,model=${r.model},storage=${s.storageMethod}`),o.success(`AI ready \xB7 Anthropic Direct \xB7 model=${r.model} \xB7 ${r.baseUrl}`),{ok:!0,provider:"anthropic",model:r.model}}case"codex":{let r=await bi(),s=await Ut(t.workspacePath,{OPENAI_API_KEY:r.apiKey});return await M(t.workspacePath,{provider:"codex",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=codex,result=ok,model=${r.model},storage=${s.storageMethod}`),o.success(`AI ready \xB7 OpenAI/Codex Direct \xB7 model=${r.model} \xB7 ${r.baseUrl}`),o.dim(" Note: Claude Code native kh\xF4ng support OpenAI \u2014 c\u1EA7n proxy qua LiteLLM/ai.nal.vn \u0111\u1EC3 d\xF9ng l\xE0m main AI. Key \u0111\xE3 l\u01B0u \u0111\u1EC3 skills kh\xE1c d\xF9ng (vd ai-multimodal)."),{ok:!0,provider:"codex",model:r.model}}case"gemini":{let r=await ki(),s=await Ut(t.workspacePath,{GEMINI_API_KEY:r.apiKey});return await M(t.workspacePath,{provider:"gemini",apiKey:r.apiKey,baseUrl:r.baseUrl,model:r.model,skipApiKey:s.storageMethod==="envrc"}),await w("ai_setup",`provider=gemini,result=ok,model=${r.model},storage=${s.storageMethod}`),o.success(`AI ready \xB7 Google Gemini Direct \xB7 model=${r.model} \xB7 ${r.baseUrl}`),o.dim(" Note: Claude Code native kh\xF4ng support Gemini \u2014 c\u1EA7n proxy qua LiteLLM/ai.nal.vn \u0111\u1EC3 d\xF9ng l\xE0m main AI. Key \u0111\xE3 l\u01B0u \u0111\u1EC3 skills kh\xE1c d\xF9ng."),{ok:!0,provider:"gemini",model:r.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 M(t.workspacePath,{provider:"use-global",sourceSettings:n.rawSettings}),await w("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 w("ai_setup",`result=failed,error=${n.slice(0,200)}`),{ok:!1,reason:n}}}import{spawnSync as Ka}from"child_process";var gn=1e4,Ti=3e4,_i=5,fn="say ok",Ii="2023-06-01";async function Va(t,e,n){o.info(`Testing LLMLite provider: ${t} (key: ${F(e)})`);let i=new AbortController,r=setTimeout(()=>i.abort(),gn);try{let s=await fetch(`${t}/v1/models`,{headers:{Authorization:`Bearer ${e}`},signal:i.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(f=>typeof f.id=="string"?f.id:null).filter(f=>f!==null);if(o.success(`Connectivity OK \xB7 ${c.length} models available`),c.length>0){let f=c.slice(0,5).join(", "),x=c.length>5?` ...+${c.length-5} more`:"";o.dim(` Models: ${f}${x}`)}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:fn}],max_tokens:_i}),signal:i.signal});if(!l.ok){let f=(await l.text()).slice(0,200);throw new Error(`Chat completion fail (HTTP ${l.status}). ${f}`)}let u=await l.json(),m=typeof u.choices?.[0]?.message?.content=="string"?u.choices[0].message.content:"(empty response)",g=u.usage?.total_tokens??"?";o.success(`Response: "${String(m).trim().slice(0,100)}"`),o.dim(` Tokens used: ${g}`)}catch(s){throw s.name==="AbortError"?new Error(`Timeout ${gn/1e3}s. Check m\u1EA1ng / endpoint ${t}.`):s}finally{clearTimeout(r)}}function Ba(){o.info("Testing Subscription provider qua `claude --print`...");let t=Ka("claude",["--print",fn],{encoding:"utf8",timeout:Ti});if(t.signal==="SIGTERM")throw new Error(`Timeout ${Ti/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 Wa(t,e,n){o.info(`Testing Anthropic Direct provider: ${t} (key: ${F(e)})`);let i=new AbortController,r=setTimeout(()=>i.abort(),gn);try{let s=await fetch(`${t}/v1/models`,{headers:{"x-api-key":e,"anthropic-version":Ii},signal:i.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":Ii,"Content-Type":"application/json"},body:JSON.stringify({model:n,max_tokens:_i,messages:[{role:"user",content:fn}]}),signal:i.signal});if(!l.ok){let g=(await l.text()).slice(0,200);throw new Error(`Message endpoint fail (HTTP ${l.status}): ${g}`)}let m=((await l.json()).content||[]).map(g=>typeof g.text=="string"?g.text:"").join("").trim().slice(0,100);o.success(`Response: "${m}"`)}finally{clearTimeout(r)}}async function Oi(t){let e=t.env||{},n=typeof e.ANTHROPIC_BASE_URL=="string"?e.ANTHROPIC_BASE_URL:void 0,i=typeof e.ANTHROPIC_AUTH_TOKEN=="string"?e.ANTHROPIC_AUTH_TOKEN:void 0,r=typeof e.ANTHROPIC_API_KEY=="string"?e.ANTHROPIC_API_KEY:void 0,s=typeof t.model=="string"?t.model:"default";return r&&n?(await Wa(n,r,s),{ok:!0,provider:"anthropic",message:"Anthropic Direct provider working"}):n&&i?(await Va(n,i,s),{ok:!0,provider:"llmlite",message:"LLMLite provider working"}):(Ba(),{ok:!0,provider:"subscription",message:"Subscription provider working"})}async function be(){let t=process.cwd(),e=yt(t);return e||(o.error(`Kh\xF4ng t\xECm th\u1EA5y Avatar workspace t\u1EEB th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i.
30
30
  Avatar workspace c\u1EA7n c\xF3: .claude/ + CLAUDE.md + src/ (ho\u1EB7c .gitmodules).
31
31
  B\u1EA1n \u0111ang \u1EDF: ${t}
32
32
  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 hn(t){let e=Ni(t,".claude","settings.json");if(!await d(e))return{};try{return await y(e)}catch{return{}}}async function Ya(){let t=await be();await ve({workspacePath:t})}async function Ja(){let t=await be(),e=await hn(t),n=e.env||{},i=typeof n.ANTHROPIC_BASE_URL=="string"?n.ANTHROPIC_BASE_URL:void 0,r=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=i?.includes("api.anthropic.com")||s.startsWith("sk-ant-")?"Anthropic Direct":"Custom (API key)",l=F(s)):i&&r?(c="LLMLite",l=F(r)):r?(c="Custom",l=F(r)):(c="Subscription (default)",l="(kh\xF4ng set \u2014 d\xF9ng subscription auth)"),o.info(`Project: ${t}`),o.info(`Provider: ${c}${i?` (${i})`:""}`),o.info(`Model: ${a??"(default \u2014 Claude Code ch\u1ECDn)"}`),o.info(`Token: ${l}`)}async function Xa(){let t=await be(),e=await hn(t);try{let n=await Oi(e);o.success(`\u2713 ${n.message}`)}catch(n){o.error(`Test fail: ${n.message}`),process.exit(1)}}async function Za(t){let e=await be(),n=Ni(e,".claude","settings.json"),i=await hn(e);if(!t.yes&&!await qa({message:"X\xF3a AI config (v\u1EC1 d\xF9ng Claude Code Subscription default)?",default:!1})){o.dim("\u0110\xE3 h\u1EE7y.");return}let{env:r,...s}=i,a={...s};if(r){let{ANTHROPIC_BASE_URL:c,ANTHROPIC_AUTH_TOKEN:l,ANTHROPIC_API_KEY:u,...m}=r;Object.keys(m).length>0&&(a.env=m)}Object.keys(a).length===0?(await za.unlink(n).catch(()=>{}),o.success("\u0110\xE3 x\xF3a .claude/settings.json (clean state)")):(await S(n,a,384),o.success("\u0110\xE3 reset env block trong .claude/settings.json"))}function Mi(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 Ya()}),e.command("status").description("Show AI config hi\u1EC7n t\u1EA1i (mask token)").action(async()=>{await Ja()}),e.command("test").description("Verify AI provider qua cheap prompt").action(async()=>{await Xa()}),e.command("reset").description("X\xF3a env.ANTHROPIC_* kh\u1ECFi settings.json (v\u1EC1 Subscription default)").option("--yes","Skip confirm").action(async n=>{await Za(n)})}import{input as ic}from"@inquirer/prompts";import{spawnSync as Qa}from"child_process";import{existsSync as kn}from"fs";import{join as xe}from"path";function tc(t){let e=xe(t,"src",".git"),n=xe(t,".git"),i=xe(t,".claude");if(!kn(n))throw new Error(`Kh\xF4ng ph\u1EA3i workspace root: ${t}
33
33
  Ch\u1EA1y 'avatar commit' trong workspace dir (c\xF3 .git v\xE0 .claude/).`);if(!kn(i))throw new Error(`Kh\xF4ng th\u1EA5y .claude/ trong ${t}.
34
34
  Ch\u1EA1y 'avatar commit' trong Avatar workspace, kh\xF4ng ph\u1EA3i project b\xECnh th\u01B0\u1EDDng.`);if(!kn(e))throw new Error(`Kh\xF4ng th\u1EA5y src/.git trong ${t}.
35
35
  Workspace thi\u1EBFu submodule src/. Ch\u1EA1y 'avatar init' l\u1EA1i?`)}function G(t,e){let n=Qa("git",["-C",t,...e],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});if(n.status!==0){let i=(n.stderr||"").trim();throw new Error(`git ${e.join(" ")} (in ${t}) failed:
36
- ${i}`)}return(n.stdout||"").trim()}function ji(t){return G(t,["status","--porcelain"]).length>0}async function ec(t,e){let n=xe(t,"src");if(!ji(n))return o.dim("src/: nothing to commit (clean)"),{};o.info("Committing src/ ..."),G(n,["add","."]),G(n,["commit","-m",e.message]);let i=G(n,["rev-parse","HEAD"]);o.success(`src/ committed: ${i.slice(0,7)}`);let r=!1;return e.push&&(o.info("Pushing src/ ..."),G(n,["push"]),o.success("src/ pushed"),r=!0),{sha:i,pushed:r}}async function nc(t,e){if(!ji(t))return o.dim("workspace: nothing to commit (clean)"),{};o.info("Committing workspace root ..."),G(t,["add","."]),G(t,["commit","-m",e.message]);let n=G(t,["rev-parse","HEAD"]);o.success(`workspace committed: ${n.slice(0,7)}`);let i=!1;return e.push&&(o.info("Pushing workspace ..."),G(t,["push"]),o.success("workspace pushed"),i=!0),{sha:n,pushed:i}}async function Gi(t){tc(t.workspaceRoot);let e={target:t.target,skipped:[]};if(t.target==="src"||t.target==="all"){let n=await ec(t.workspaceRoot,t.options);e.srcCommitSha=n.sha,e.srcPushed=n.pushed,n.sha||e.skipped?.push("src")}if(t.target==="workspace"||t.target==="all"){let n=await nc(t.workspaceRoot,t.options);e.workspaceCommitSha=n.sha,e.workspacePushed=n.pushed,n.sha||e.skipped?.push("workspace")}return e}function Li(t){t.command("commit").description("Commit code kh\xE1ch trong src/ (Avatar state do admin sync) (M07)").command("src").description("Commit src/ \u2192 push l\xEAn client repo remote").option("-m, --message <msg>","Commit message").option("--push","Auto push sau commit (default: ch\u1EC9 commit)").action(async n=>{await rc(n)})}async function rc(t){try{let e=t.message??await ic({message:"Commit message:",validate:i=>i.trim().length>0?!0:"Message b\u1EAFt bu\u1ED9c"}),n=await Gi({workspaceRoot:process.cwd(),target:"src",options:{message:e,push:t.push}});n.srcCommitSha&&o.success(`src/: ${n.srcCommitSha.slice(0,7)}${n.srcPushed?" (pushed)":""}`),n.skipped&&n.skipped.length>0&&o.dim(`Skipped (nothing to commit): ${n.skipped.join(", ")}`)}catch(e){o.error(e instanceof Error?e.message:String(e)),process.exit(1)}}import{spawnSync as xn}from"child_process";import{promises as An}from"fs";import{join as st}from"path";import Tc from"boxen";import{join as kc}from"path";k();import{promises as uc}from"fs";import{join as Ki}from"path";k();import{promises as oc}from"fs";import Hi,{join as wn}from"path";async function sc(t,e){let i=e.trim().match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);if(!i?.[2])return!0;let r=i[2];if(r.startsWith("/"))return o.warn(`Pack hook reject: absolute path "${r}" \u2014 ch\u1EC9 accept relative path t\u1EDBi workspace.`),!1;let s=wn(t,r),a=Hi.resolve(t),c=Hi.resolve(s);return!c.startsWith(`${a}/`)&&c!==a?(o.warn(`Pack hook reject: path "${r}" resolve ra ngo\xE0i workspace (path traversal).`),!1):await d(s)}function yn(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 Dt(t,e){let n=new Set,i=[];for(let r of[...t,...e]){let s=typeof r=="string"?r:JSON.stringify(r);n.has(s)||(n.add(s),i.push(r))}return i}function ac(t){return t.replace(/\$\{CLAUDE_PROJECT_DIR\}\//g,"").trim()}function cc(t){if(typeof t!="object"||t===null)return[];let e=t,n=Array.isArray(e.hooks)?e.hooks:[],i=[];for(let r of n)if(typeof r=="object"&&r!==null){let s=r.command;typeof s=="string"&&i.push(s)}return i}function Ui(t){let e=t.map((s,a)=>{let c=cc(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(ac).sort().join("|");if(!a)continue;let c=n.get(a)??[];c.push(s),n.set(a,c)}let i=new Set;for(let[,s]of n)if(!(s.length<2||!s.some(c=>c.hasVar)))for(let c of s)c.hasVar||i.add(c.index);return i.size===0?{entries:t,droppedCount:0}:{entries:t.filter((s,a)=>!i.has(a)),droppedCount:i.size}}function lc(t,e){let n=[],i={...e},r=0;for(let s of Object.keys(i)){let a=i[s]||[],{entries:c,droppedCount:l}=Ui(a);l>0&&(i[s]=c,r+=l,n.includes(s)||n.push(s))}for(let[s,a]of Object.entries(t)){let c=i[s]||[],l=Dt(c,a),{entries:u,droppedCount:m}=Ui(l);m>0&&(r+=m),u.length!==c.length&&(n.includes(s)||n.push(s)),i[s]=u}return{merged:i,touchedEvents:n,migratedCount:r}}async function Ae(t){let e=wn(t,".claude","pack","templates","settings.json.tpl"),n=wn(t,".claude","settings.json");if(!await d(e))return{action:"no-pack-template",changes:[]};let i;try{let u=await U(e);i=JSON.parse(u)}catch(u){throw new Error(`Pack settings template kh\xF4ng parse \u0111\u01B0\u1EE3c JSON: ${u.message}. Path: ${e}`)}let r={},s=!1;if(await d(n)){s=!0;try{r=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={...r};if(i.statusLine&&!r.statusLine&&(await sc(t,i.statusLine.command)?(c.statusLine=i.statusLine,a.push("statusLine added")):a.push(`statusLine SKIPPED (file ref '${i.statusLine.command}' kh\xF4ng t\u1ED3n t\u1EA1i)`)),typeof i.includeCoAuthoredBy=="boolean"&&typeof r.includeCoAuthoredBy!="boolean"&&(c.includeCoAuthoredBy=i.includeCoAuthoredBy,a.push("includeCoAuthoredBy added")),i.model&&!r.model&&(c.model=i.model,a.push("model added")),i.env||r.env){let u={...r.env||{}},m=!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"),m=!0);if(m&&a.push("rewrote legacy llm.nal.vn \u2192 ai.nal.vn in env vars"),i.env)for(let[g,h]of Object.entries(i.env))g in u||(u[g]=h,m=!0);m&&(c.env=u,a.some(g=>g.includes("env vars"))||a.push("env vars added from pack"))}if(i.permissions){let u=r.permissions?.allow||[],m=r.permissions?.deny||[],g=i.permissions.allow||[],h=i.permissions.deny||[],x=Dt(u,g),C=Dt(m,h);(x.length!==u.length||C.length!==m.length)&&(c.permissions={allow:x,deny:C},a.push(`permissions union (+${x.length-u.length} allow, +${C.length-m.length} deny)`))}if(i.hooks){let u=r.hooks||{},{merged:m,touchedEvents:g,migratedCount:h}=lc(i.hooks,u);(g.length>0||h>0)&&(c.hooks=m,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=yn(n),await oc.copyFile(n,l)),await S(n,c),{action:"merged",backupPath:l,changes:a}}function pc(t,e){return Ki(t,".claude","pack","features",e,"feature.json")}async function Ft(t,e){let n=pc(t,e);return await d(n)?await y(n):null}var mc=[".claude","settings.json"];function Vi(t){return Ki(t,...mc)}function Se(t){let e=(t.hooks??[]).map(n=>n.command??"").sort().join("\0");return`${t.matcher??""}${e}`}async function Bi(t){let e=Vi(t);if(!await d(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 feature.`)}}async function Wi(t,e,n){let i=Vi(t),r;return n&&(r=yn(i),await uc.copyFile(i,r)),await S(i,e),r}function Di(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 dc(t,e){let n=new Set;for(let s of e)Fi(s)&&n.add(Di(s));if(n.size===0)return{migratedUser:t,droppedCount:0};let i=0;return{migratedUser:t.filter(s=>Fi(s)?!0:n.has(Di(s))?(i++,!1):!0),droppedCount:i}}async function Ce(t,e){let{settings:n,existed:i}=await Bi(t),r={...n},s=[],a=e.settings.hooks??{};if(Object.keys(a).length>0){let u=n.hooks??{},m={...u},g=[],h=0;for(let[x,C]of Object.entries(a)){let T=u[x]??[],{migratedUser:gt,droppedCount:P}=dc(T,C);P>0&&(h+=P);let O=new Set(gt.map(Se)),_=C.filter(ft=>!O.has(Se(ft)));(_.length>0||P>0)&&(m[x]=[...gt,..._],g.push(x))}g.length>0&&(r.hooks=m,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??[],m=Dt(u,c);m.length!==u.length&&(r.permissions={...n.permissions,deny:m},s.push(`deny +${m.length-u.length}`))}return s.length===0?{action:"no-change",changes:[]}:{action:"enabled",backupPath:await Wi(t,r,i),changes:s}}async function zi(t,e){let{settings:n,existed:i}=await Bi(t);if(!i)return{action:"no-change",changes:[]};let{hooks:r,permissions:s,...a}=n,c={...a},l=[],u=e.settings.hooks??{},m=n.hooks??{},g={},h=[];for(let[P,O]of Object.entries(m)){let _=u[P];if(!_){g[P]=O;continue}let ft=new Set(_.map(Se)),ht=O.filter(re=>!ft.has(Se(re)));ht.length!==O.length&&h.push(P),ht.length>0&&(g[P]=ht)}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??[]),C=n.permissions?.deny??[],T=x.size>0?C.filter(P=>!x.has(P)):C;if(n.permissions){let{deny:P,...O}=n.permissions,_={...O,...T.length>0?{deny:T}:{}};Object.keys(_).length>0&&(c.permissions=_)}return T.length!==C.length&&l.push(`deny -${C.length-T.length}`),l.length===0?{action:"no-change",changes:[]}:{action:"disabled",backupPath:await Wi(t,c,i),changes:l}}k();import{join as gc}from"path";var fc=".claude/avatar-features.json";function qi(){return{features:{}}}function Yi(t){return gc(t,fc)}async function At(t){let e=Yi(t);if(!await d(e))return qi();try{return{features:(await y(e)).features??{}}}catch{return qi()}}async function hc(t,e){await S(Yi(t),e)}async function Pe(t,e,n){let i=await At(t);return i.features[e]={enabled:n.enabled,version:n.version,appliedAt:new Date().toISOString()},await hc(t,i),i}async function St(t){let e=await At(t);return Object.entries(e.features).filter(([,n])=>n.enabled).map(([n])=>n)}k();function wc(t,e){let n=e.settings.hooks??{},i=t.hooks??{};for(let[r,s]of Object.entries(n)){let a=i[r]??[],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 Ji(t){let e=await St(t);if(e.length===0)return[];let n=kc(t,".claude","settings.json"),i={};if(await d(n))try{i=await y(n)}catch{i={}}let r=[];for(let s of e){let a=await Ft(t,s);if(!a){r.push({name:`Feature: ${s}`,status:"warn",detail:"state=enabled nh\u01B0ng pack thi\u1EBFu manifest (version skew). Ch\u1EA1y 'avatar sync'.",fixable:!1});continue}wc(i,a)?r.push({name:`Feature: ${s}`,status:"ok",detail:`enabled, hook active (v${a.version})`,fixable:!1}):r.push({name:`Feature: ${s}`,status:"fail",detail:"state=enabled nh\u01B0ng hook thi\u1EBFu trong settings.json \u2014 fixable (re-apply)",fixable:!0,fix:async()=>{await Ce(t,a)}})}return r}k();k();import{join as vn}from"path";import{simpleGit as yc}from"simple-git";function v(t=process.cwd()){return yc({baseDir:t,binary:"git"})}async function Ct(t=process.cwd()){return await d(vn(t,".git"))}async function Xi(t,e,n=process.cwd()){await v(n).subModule(["add",t,e])}async function Kt(t,e,n=process.cwd()){let i=vn(n,t);await v(i).fetch(["--tags"]),await v(i).checkout(e)}async function $e(t,e,n=process.cwd()){let i=vn(n,t);await v(i).fetch(["origin"]),await v(i).checkout(["-B",e,`origin/${e}`])}async function K(t=process.cwd()){return(await v(t).tags()).all}async function Ee(t=process.cwd()){try{return(await v(t).raw(["describe","--tags","--exact-match","HEAD"])).trim()||null}catch{return null}}async function Pt(t=process.cwd()){return(await v(t).revparse(["HEAD"])).trim()}k();import{promises as nr}from"fs";import{join as V}from"path";k();import{existsSync as bc}from"fs";import{dirname as Qi,join as Vt}from"path";import{fileURLToPath as xc}from"url";var vc=/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;function Zi(t,e){return t.replace(vc,(n,i)=>{let r=e[i];return r===void 0?n:String(r)})}var Ac=Qi(xc(import.meta.url)),tr=Pc(Ac),Sc=Vt(tr,"src","templates"),Cc=Vt(tr,"src","hooks");function Pc(t){let e=t;for(;;){if(bc(Vt(e,"package.json")))return e;let n=Qi(e);if(n===e)throw new Error(`Cannot locate package root from ${t}`);e=n}}async function $c(t){return await U(Vt(Sc,`${t}.tpl`))}async function Re(t,e){let n=await $c(t);return Zi(n,e)}async function er(t){return await U(Vt(Cc,`${t}.sh.tpl`))}async function Ec(t){if(!await d(t))return null;let e=new Date().toISOString().replace(/[:.]/g,"-"),n=`${t}.avatar-backup-${e}`,i=n,r=1;for(;await d(i);)if(i=`${n}-${r}`,r++,r>5)throw new Error(`Could not find free backup name for ${t}`);return await nr.rename(t,i),i}async function ir(t,e,n){let i=await Ec(t);return await wt(t,e,n),i}var Rc=["state","_pending","_backup"];async function rr(t){let e=V(t,".claude");await $(e);for(let n of Rc){let i=V(e,n);await $(i),await wt(V(i,".gitkeep"),"")}}async function or(t,e){return[]}async function bn(t,e){let n=await Re("CLAUDE.md",e);return await ir(V(t,"CLAUDE.md"),n)}async function sr(t,e){let n=await Re("settings.json",e);return await ir(V(t,".claude","settings.json"),n)}async function ar(t){let e=V(t,".gitignore"),n=await Re("gitignore",{}),i="# Avatar \u2014 git-ignored entries injected on `avatar init`",r="";if(await d(e)&&(r=await nr.readFile(e,"utf8"),r.includes(i)))return;let s=r.endsWith(`
36
+ ${i}`)}return(n.stdout||"").trim()}function ji(t){return G(t,["status","--porcelain"]).length>0}async function ec(t,e){let n=xe(t,"src");if(!ji(n))return o.dim("src/: nothing to commit (clean)"),{};o.info("Committing src/ ..."),G(n,["add","."]),G(n,["commit","-m",e.message]);let i=G(n,["rev-parse","HEAD"]);o.success(`src/ committed: ${i.slice(0,7)}`);let r=!1;return e.push&&(o.info("Pushing src/ ..."),G(n,["push"]),o.success("src/ pushed"),r=!0),{sha:i,pushed:r}}async function nc(t,e){if(!ji(t))return o.dim("workspace: nothing to commit (clean)"),{};o.info("Committing workspace root ..."),G(t,["add","."]),G(t,["commit","-m",e.message]);let n=G(t,["rev-parse","HEAD"]);o.success(`workspace committed: ${n.slice(0,7)}`);let i=!1;return e.push&&(o.info("Pushing workspace ..."),G(t,["push"]),o.success("workspace pushed"),i=!0),{sha:n,pushed:i}}async function Gi(t){tc(t.workspaceRoot);let e={target:t.target,skipped:[]};if(t.target==="src"||t.target==="all"){let n=await ec(t.workspaceRoot,t.options);e.srcCommitSha=n.sha,e.srcPushed=n.pushed,n.sha||e.skipped?.push("src")}if(t.target==="workspace"||t.target==="all"){let n=await nc(t.workspaceRoot,t.options);e.workspaceCommitSha=n.sha,e.workspacePushed=n.pushed,n.sha||e.skipped?.push("workspace")}return e}function Li(t){t.command("commit").description("Commit code kh\xE1ch trong src/ (Avatar state do admin sync) (M07)").command("src").description("Commit src/ \u2192 push l\xEAn client repo remote").option("-m, --message <msg>","Commit message").option("--push","Auto push sau commit (default: ch\u1EC9 commit)").action(async n=>{await rc(n)})}async function rc(t){try{let e=t.message??await ic({message:"Commit message:",validate:i=>i.trim().length>0?!0:"Message b\u1EAFt bu\u1ED9c"}),n=await Gi({workspaceRoot:process.cwd(),target:"src",options:{message:e,push:t.push}});n.srcCommitSha&&o.success(`src/: ${n.srcCommitSha.slice(0,7)}${n.srcPushed?" (pushed)":""}`),n.skipped&&n.skipped.length>0&&o.dim(`Skipped (nothing to commit): ${n.skipped.join(", ")}`)}catch(e){o.error(e instanceof Error?e.message:String(e)),process.exit(1)}}import{spawnSync as xn}from"child_process";import{promises as An}from"fs";import{join as st}from"path";import Tc from"boxen";import{join as kc}from"path";k();import{promises as uc}from"fs";import{join as Ki}from"path";k();import{promises as oc}from"fs";import Hi,{join as wn}from"path";async function sc(t,e){let i=e.trim().match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);if(!i?.[2])return!0;let r=i[2];if(r.startsWith("/"))return o.warn(`Pack hook reject: absolute path "${r}" \u2014 ch\u1EC9 accept relative path t\u1EDBi workspace.`),!1;let s=wn(t,r),a=Hi.resolve(t),c=Hi.resolve(s);return!c.startsWith(`${a}/`)&&c!==a?(o.warn(`Pack hook reject: path "${r}" resolve ra ngo\xE0i workspace (path traversal).`),!1):await d(s)}function yn(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 Dt(t,e){let n=new Set,i=[];for(let r of[...t,...e]){let s=typeof r=="string"?r:JSON.stringify(r);n.has(s)||(n.add(s),i.push(r))}return i}function ac(t){return t.replace(/\$\{CLAUDE_PROJECT_DIR\}\//g,"").trim()}function cc(t){if(typeof t!="object"||t===null)return[];let e=t,n=Array.isArray(e.hooks)?e.hooks:[],i=[];for(let r of n)if(typeof r=="object"&&r!==null){let s=r.command;typeof s=="string"&&i.push(s)}return i}function Ui(t){let e=t.map((s,a)=>{let c=cc(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(ac).sort().join("|");if(!a)continue;let c=n.get(a)??[];c.push(s),n.set(a,c)}let i=new Set;for(let[,s]of n)if(!(s.length<2||!s.some(c=>c.hasVar)))for(let c of s)c.hasVar||i.add(c.index);return i.size===0?{entries:t,droppedCount:0}:{entries:t.filter((s,a)=>!i.has(a)),droppedCount:i.size}}function lc(t,e){let n=[],i={...e},r=0;for(let s of Object.keys(i)){let a=i[s]||[],{entries:c,droppedCount:l}=Ui(a);l>0&&(i[s]=c,r+=l,n.includes(s)||n.push(s))}for(let[s,a]of Object.entries(t)){let c=i[s]||[],l=Dt(c,a),{entries:u,droppedCount:m}=Ui(l);m>0&&(r+=m),u.length!==c.length&&(n.includes(s)||n.push(s)),i[s]=u}return{merged:i,touchedEvents:n,migratedCount:r}}async function Ae(t){let e=wn(t,".claude","pack","templates","settings.json.tpl"),n=wn(t,".claude","settings.json");if(!await d(e))return{action:"no-pack-template",changes:[]};let i;try{let u=await U(e);i=JSON.parse(u)}catch(u){throw new Error(`Pack settings template kh\xF4ng parse \u0111\u01B0\u1EE3c JSON: ${u.message}. Path: ${e}`)}let r={},s=!1;if(await d(n)){s=!0;try{r=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={...r};if(i.statusLine&&!r.statusLine&&(await sc(t,i.statusLine.command)?(c.statusLine=i.statusLine,a.push("statusLine added")):a.push(`statusLine SKIPPED (file ref '${i.statusLine.command}' kh\xF4ng t\u1ED3n t\u1EA1i)`)),typeof i.includeCoAuthoredBy=="boolean"&&typeof r.includeCoAuthoredBy!="boolean"&&(c.includeCoAuthoredBy=i.includeCoAuthoredBy,a.push("includeCoAuthoredBy added")),i.model&&!r.model&&(c.model=i.model,a.push("model added")),i.env||r.env){let u={...r.env||{}},m=!1;for(let[g,f]of Object.entries(u))typeof f=="string"&&f.includes("llm.nal.vn")&&(u[g]=f.replace(/llm\.nal\.vn/g,"ai.nal.vn"),m=!0);if(m&&a.push("rewrote legacy llm.nal.vn \u2192 ai.nal.vn in env vars"),i.env)for(let[g,f]of Object.entries(i.env))g in u||(u[g]=f,m=!0);m&&(c.env=u,a.some(g=>g.includes("env vars"))||a.push("env vars added from pack"))}if(i.permissions){let u=r.permissions?.allow||[],m=r.permissions?.deny||[],g=i.permissions.allow||[],f=i.permissions.deny||[],x=Dt(u,g),C=Dt(m,f);(x.length!==u.length||C.length!==m.length)&&(c.permissions={allow:x,deny:C},a.push(`permissions union (+${x.length-u.length} allow, +${C.length-m.length} deny)`))}if(i.hooks){let u=r.hooks||{},{merged:m,touchedEvents:g,migratedCount:f}=lc(i.hooks,u);(g.length>0||f>0)&&(c.hooks=m,g.length>0&&a.push(`hooks added for events: ${g.join(", ")}`),f>0&&a.push(`migrated ${f} 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=yn(n),await oc.copyFile(n,l)),await S(n,c),{action:"merged",backupPath:l,changes:a}}function pc(t,e){return Ki(t,".claude","pack","features",e,"feature.json")}async function Ft(t,e){let n=pc(t,e);return await d(n)?await y(n):null}var mc=[".claude","settings.json"];function Vi(t){return Ki(t,...mc)}function Se(t){let e=(t.hooks??[]).map(n=>n.command??"").sort().join("\0");return`${t.matcher??""}${e}`}async function Bi(t){let e=Vi(t);if(!await d(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 feature.`)}}async function Wi(t,e,n){let i=Vi(t),r;return n&&(r=yn(i),await uc.copyFile(i,r)),await S(i,e),r}function Di(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 dc(t,e){let n=new Set;for(let s of e)Fi(s)&&n.add(Di(s));if(n.size===0)return{migratedUser:t,droppedCount:0};let i=0;return{migratedUser:t.filter(s=>Fi(s)?!0:n.has(Di(s))?(i++,!1):!0),droppedCount:i}}async function Ce(t,e){let{settings:n,existed:i}=await Bi(t),r={...n},s=[],a=e.settings.hooks??{};if(Object.keys(a).length>0){let u=n.hooks??{},m={...u},g=[],f=0;for(let[x,C]of Object.entries(a)){let T=u[x]??[],{migratedUser:gt,droppedCount:P}=dc(T,C);P>0&&(f+=P);let O=new Set(gt.map(Se)),_=C.filter(ft=>!O.has(Se(ft)));(_.length>0||P>0)&&(m[x]=[...gt,..._],g.push(x))}g.length>0&&(r.hooks=m,s.push(`hooks added: ${g.join(", ")}`),f>0&&s.push(`migrated ${f} stale relative-path entries \u2192 \\{CLAUDE_PROJECT_DIR\\} version`))}let c=e.settings.permissions?.deny??[];if(c.length>0){let u=n.permissions?.deny??[],m=Dt(u,c);m.length!==u.length&&(r.permissions={...n.permissions,deny:m},s.push(`deny +${m.length-u.length}`))}return s.length===0?{action:"no-change",changes:[]}:{action:"enabled",backupPath:await Wi(t,r,i),changes:s}}async function zi(t,e){let{settings:n,existed:i}=await Bi(t);if(!i)return{action:"no-change",changes:[]};let{hooks:r,permissions:s,...a}=n,c={...a},l=[],u=e.settings.hooks??{},m=n.hooks??{},g={},f=[];for(let[P,O]of Object.entries(m)){let _=u[P];if(!_){g[P]=O;continue}let ft=new Set(_.map(Se)),ht=O.filter(re=>!ft.has(Se(re)));ht.length!==O.length&&f.push(P),ht.length>0&&(g[P]=ht)}Object.keys(g).length>0&&(c.hooks=g),f.length>0&&l.push(`hooks removed: ${f.join(", ")}`);let x=new Set(e.settings.permissions?.deny??[]),C=n.permissions?.deny??[],T=x.size>0?C.filter(P=>!x.has(P)):C;if(n.permissions){let{deny:P,...O}=n.permissions,_={...O,...T.length>0?{deny:T}:{}};Object.keys(_).length>0&&(c.permissions=_)}return T.length!==C.length&&l.push(`deny -${C.length-T.length}`),l.length===0?{action:"no-change",changes:[]}:{action:"disabled",backupPath:await Wi(t,c,i),changes:l}}k();import{join as gc}from"path";var fc=".claude/avatar-features.json";function qi(){return{features:{}}}function Yi(t){return gc(t,fc)}async function At(t){let e=Yi(t);if(!await d(e))return qi();try{return{features:(await y(e)).features??{}}}catch{return qi()}}async function hc(t,e){await S(Yi(t),e)}async function Pe(t,e,n){let i=await At(t);return i.features[e]={enabled:n.enabled,version:n.version,appliedAt:new Date().toISOString()},await hc(t,i),i}async function St(t){let e=await At(t);return Object.entries(e.features).filter(([,n])=>n.enabled).map(([n])=>n)}k();function wc(t,e){let n=e.settings.hooks??{},i=t.hooks??{};for(let[r,s]of Object.entries(n)){let a=i[r]??[],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 Ji(t){let e=await St(t);if(e.length===0)return[];let n=kc(t,".claude","settings.json"),i={};if(await d(n))try{i=await y(n)}catch{i={}}let r=[];for(let s of e){let a=await Ft(t,s);if(!a){r.push({name:`Feature: ${s}`,status:"warn",detail:"state=enabled nh\u01B0ng pack thi\u1EBFu manifest (version skew). Ch\u1EA1y 'avatar sync'.",fixable:!1});continue}wc(i,a)?r.push({name:`Feature: ${s}`,status:"ok",detail:`enabled, hook active (v${a.version})`,fixable:!1}):r.push({name:`Feature: ${s}`,status:"fail",detail:"state=enabled nh\u01B0ng hook thi\u1EBFu trong settings.json \u2014 fixable (re-apply)",fixable:!0,fix:async()=>{await Ce(t,a)}})}return r}k();k();import{join as vn}from"path";import{simpleGit as yc}from"simple-git";function v(t=process.cwd()){return yc({baseDir:t,binary:"git"})}async function Ct(t=process.cwd()){return await d(vn(t,".git"))}async function Xi(t,e,n=process.cwd()){await v(n).subModule(["add",t,e])}async function Kt(t,e,n=process.cwd()){let i=vn(n,t);await v(i).fetch(["--tags"]),await v(i).checkout(e)}async function $e(t,e,n=process.cwd()){let i=vn(n,t);await v(i).fetch(["origin"]),await v(i).checkout(["-B",e,`origin/${e}`])}async function K(t=process.cwd()){return(await v(t).tags()).all}async function Ee(t=process.cwd()){try{return(await v(t).raw(["describe","--tags","--exact-match","HEAD"])).trim()||null}catch{return null}}async function Pt(t=process.cwd()){return(await v(t).revparse(["HEAD"])).trim()}k();import{promises as nr}from"fs";import{join as V}from"path";k();import{existsSync as bc}from"fs";import{dirname as Qi,join as Vt}from"path";import{fileURLToPath as xc}from"url";var vc=/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;function Zi(t,e){return t.replace(vc,(n,i)=>{let r=e[i];return r===void 0?n:String(r)})}var Ac=Qi(xc(import.meta.url)),tr=Pc(Ac),Sc=Vt(tr,"src","templates"),Cc=Vt(tr,"src","hooks");function Pc(t){let e=t;for(;;){if(bc(Vt(e,"package.json")))return e;let n=Qi(e);if(n===e)throw new Error(`Cannot locate package root from ${t}`);e=n}}async function $c(t){return await U(Vt(Sc,`${t}.tpl`))}async function Re(t,e){let n=await $c(t);return Zi(n,e)}async function er(t){return await U(Vt(Cc,`${t}.sh.tpl`))}async function Ec(t){if(!await d(t))return null;let e=new Date().toISOString().replace(/[:.]/g,"-"),n=`${t}.avatar-backup-${e}`,i=n,r=1;for(;await d(i);)if(i=`${n}-${r}`,r++,r>5)throw new Error(`Could not find free backup name for ${t}`);return await nr.rename(t,i),i}async function ir(t,e,n){let i=await Ec(t);return await wt(t,e,n),i}var Rc=["state","_pending","_backup"];async function rr(t){let e=V(t,".claude");await $(e);for(let n of Rc){let i=V(e,n);await $(i),await wt(V(i,".gitkeep"),"")}}async function or(t,e){return[]}async function bn(t,e){let n=await Re("CLAUDE.md",e);return await ir(V(t,"CLAUDE.md"),n)}async function sr(t,e){let n=await Re("settings.json",e);return await ir(V(t,".claude","settings.json"),n)}async function ar(t){let e=V(t,".gitignore"),n=await Re("gitignore",{}),i="# Avatar \u2014 git-ignored entries injected on `avatar init`",r="";if(await d(e)&&(r=await nr.readFile(e,"utf8"),r.includes(i)))return;let s=r.endsWith(`
37
37
  `)||r.length===0?"":`
38
38
  `;await wt(e,`${r}${s}
39
- ${n}`)}async function Bt(t,e){let n=await er(e),i=V(t,"hooks");await $(i);let r=V(i,e);await wt(r,n,493)}function cr(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 Ic(process.cwd());_c(n),e.fix&&await Oc(n)}catch(n){o.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function Ic(t){let e=[],n=process.versions.node,[i,r]=n.split(".").map(N=>Number.parseInt(N,10)),s=(i??0)>18||(i??0)===18&&(r??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 Q();a?tt(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=st(t,".claude","pack"),l=st(t,"CLAUDE.md"),u=st(t,".git","hooks","post-merge"),[m,g,h,x]=await Promise.all([Ct(t),d(c),d(l),d(u)]);e.push({name:"Git repository",status:m?"ok":"warn",detail:m?t:"Kh\xF4ng ph\u1EA3i git repo (c\u1EA7n cho 'avatar init')",fixable:!1}),e.push({name:"team-ai-pack submodule",status:g?"ok":"warn",detail:g?c:"Avatar ch\u01B0a init \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),e.push({name:"CLAUDE.md",status:h?"ok":"warn",detail:h?"t\u1ED3n t\u1EA1i \u1EDF project root":"thi\u1EBFu \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),m&&g&&e.push({name:"Git hook post-merge",status:x?"ok":"fail",detail:x?"installed":"missing \u2014 fixable",fixable:!x,fix:x?void 0:async()=>{await Bt(st(t,".git"),"post-merge")}});let C=st(t,".gitignore"),T="";if(m){let N=!1;await d(C)&&(T=await An.readFile(C,"utf8"),N=T.includes(".claude/_pending/")),e.push({name:".gitignore Avatar entries",status:N?"ok":g?"fail":"warn",detail:N?"c\xF3 .claude/_pending/, .claude/_backup/":"thi\u1EBFu entries",fixable:!1});let X=T.includes(".claude/settings.json")||T.includes("/.claude/settings.json")||T.includes(".claude/*.json");e.push({name:"\u{1F512} settings.json gitignored",status:X?"ok":"fail",detail:X?"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 add gitignore",fixable:!X,fix:X?void 0:async()=>{await An.appendFile(C,`
39
+ ${n}`)}async function Bt(t,e){let n=await er(e),i=V(t,"hooks");await $(i);let r=V(i,e);await wt(r,n,493)}function cr(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 Ic(process.cwd());_c(n),e.fix&&await Oc(n)}catch(n){o.error(n instanceof Error?n.message:String(n)),process.exit(1)}})}async function Ic(t){let e=[],n=process.versions.node,[i,r]=n.split(".").map(N=>Number.parseInt(N,10)),s=(i??0)>18||(i??0)===18&&(r??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 Q();a?tt(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=st(t,".claude","pack"),l=st(t,"CLAUDE.md"),u=st(t,".git","hooks","post-merge"),[m,g,f,x]=await Promise.all([Ct(t),d(c),d(l),d(u)]);e.push({name:"Git repository",status:m?"ok":"warn",detail:m?t:"Kh\xF4ng ph\u1EA3i git repo (c\u1EA7n cho 'avatar init')",fixable:!1}),e.push({name:"team-ai-pack submodule",status:g?"ok":"warn",detail:g?c:"Avatar ch\u01B0a init \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),e.push({name:"CLAUDE.md",status:f?"ok":"warn",detail:f?"t\u1ED3n t\u1EA1i \u1EDF project root":"thi\u1EBFu \u2014 ch\u1EA1y 'avatar init'",fixable:!1}),m&&g&&e.push({name:"Git hook post-merge",status:x?"ok":"fail",detail:x?"installed":"missing \u2014 fixable",fixable:!x,fix:x?void 0:async()=>{await Bt(st(t,".git"),"post-merge")}});let C=st(t,".gitignore"),T="";if(m){let N=!1;await d(C)&&(T=await An.readFile(C,"utf8"),N=T.includes(".claude/_pending/")),e.push({name:".gitignore Avatar entries",status:N?"ok":g?"fail":"warn",detail:N?"c\xF3 .claude/_pending/, .claude/_backup/":"thi\u1EBFu entries",fixable:!1});let X=T.includes(".claude/settings.json")||T.includes("/.claude/settings.json")||T.includes(".claude/*.json");e.push({name:"\u{1F512} settings.json gitignored",status:X?"ok":"fail",detail:X?"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 add gitignore",fixable:!X,fix:X?void 0:async()=>{await An.appendFile(C,`
40
40
  # Avatar v1.7.0 \u2014 Security: settings.json ch\u1EE9a raw API key, KH\xD4NG commit.
41
41
  .claude/settings.json
42
42
  .claude/settings.json.backup-*
@@ -48,9 +48,13 @@ ${n}`)}async function Bt(t,e){let n=await er(e),i=V(t,"hooks");await $(i);let r=
48
48
  `):i&&process.stderr.write(`${Te(i,30)}
49
49
  `),yr("setup",e.status,e.signal,n)}t.succeed("GitNexus setup OK (global skills installed)")}function Ie(t){let e=Lt(`Analyze workspace ${t} (1-3 ph\xFAt)`),n=wr("gitnexus",["analyze","."],{cwd:t,stdio:["ignore","pipe","pipe"],timeout:Wc,encoding:"utf8"});if(n.status!==0||n.signal==="SIGTERM"){e.fail("Analyze failed");let r=(n.stderr||"").trim(),s=(n.stdout||"").trim();throw r?process.stderr.write(`${Te(r,30)}
50
50
  `):s&&process.stderr.write(`${Te(s,30)}
51
- `),yr("analyze",n.status,n.signal,r)}let i=kr(t,".gitnexus","meta.json");if(!Vc(i))throw e.fail("Analyze exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y meta.json"),new B("analyze","missing-output",`gitnexus analyze xong nh\u01B0ng kh\xF4ng th\u1EA5y ${i}. Repo c\xF3 th\u1EC3 empty ho\u1EB7c gitnexus fail silent.`);e.succeed(`Analyze OK (index t\u1EA1i ${kr(t,".gitnexus")})`)}import{confirm as vl}from"@inquirer/prompts";import bl from"boxen";import{spawnSync as br}from"child_process";var zc=5e3,qc=/(\d+\.\d+\.\d+)/;function Yc(){let e=et()==="win32"?"where":"which",n=br(e,["gitnexus"],{encoding:"utf8"});if(n.error||n.status!==0)return null;let i=(n.stdout||"").trim();return i?i.split(/\r?\n/)[0].trim():null}function Jc(){let t=br("gitnexus",["--version"],{encoding:"utf8",timeout:zc});if(t.error||t.status!==0)return null;let e=(t.stdout||"").trim();return qc.exec(e)?.[1]??null}var ct=null;function qt(){if(ct!==null)return ct;let t=Yc();return t?(ct={installed:!0,version:Jc(),path:t},ct):(ct={installed:!1,version:null,path:null},ct)}function _e(){ct=null}import{spawnSync as Xc}from"child_process";var xr=300*1e3,Ar="gitnexus",L=class extends Error{reason;exitCode;constructor(e,n,i=null){super(n),this.name="InstallGitnexusError",this.reason=e,this.exitCode=i}};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 ${Ar} 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 Sr(){o.info("\u0110ang c\xE0i GitNexus qua npm (c\xF3 th\u1EC3 m\u1EA5t 1-2 ph\xFAt)...");let t=Xc("npm",["install","-g",Ar],{stdio:["inherit","inherit","pipe"],timeout:xr,encoding:"utf8"});if(t.signal==="SIGTERM")throw new L("timeout",`npm install timeout sau ${xr/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||"");_e();let e=qt();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}}import{input as Zf,select as Qc}from"@inquirer/prompts";var b=class extends Error{constructor(e){super(e),this.name="UserAbortedRecoveryError"}};async function R(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 Qc({message:"C\xE1ch x\u1EED l\xFD?",choices:e})}k();import{promises as $r}from"fs";import{homedir as tl}from"os";import{join as el}from"path";var Cr=384,Pr={command:"gitnexus",args:["mcp"]};function nl(){return el(tl(),".claude","mcp_servers.json")}function il(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 rl(t){let e=new Date().toISOString().replace(/[:.]/g,"-"),n=`${t}.avatar-backup-${e}`;return await $r.copyFile(t,n),n}async function Er(){let t=nl(),e={},n=!1;if(await d(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 i=e.mcp_servers?.gitnexus;if(i&&il(i,Pr))return o.dim(`MCP entry gitnexus \u0111\xE3 \u0111\xFAng t\u1EA1i ${t} (no-op)`),{path:t,wasUpdated:!1};let r;n&&(r=await rl(t),o.dim(`Backup ${t} \u2192 ${r}`));let s={...e,mcp_servers:{...e.mcp_servers||{},gitnexus:Pr}};await S(t,s,Cr);try{await $r.chmod(t,Cr)}catch{}return o.success(`Registered MCP server: gitnexus \u2192 ${t}`),{path:t,wasUpdated:!0,backup:r}}import{spawnSync as pl}from"child_process";import{existsSync as ml}from"fs";import{join as Or}from"path";import{confirm as dl}from"@inquirer/prompts";var ol=[/^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 Rr(t){return t?ol.some(e=>e.test(t)):!1}k();import{promises as Yt}from"fs";import{homedir as sl}from"os";import{dirname as al,join as Tr}from"path";var cl=Tr(sl(),".gitnexus"),Jt=Tr(cl,"config.json");async function ll(){try{let t=await Yt.readFile(Jt,"utf8"),e=JSON.parse(t);return typeof e=="object"&&e!==null?e:{}}catch{return{}}}async function ul(t){await Yt.mkdir(al(Jt),{recursive:!0});let e=`${Jt}.tmp-${process.pid}`;await Yt.writeFile(e,t,{mode:384}),await Yt.rename(e,Jt)}async function Ir(t){let n={...await ll(),apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,isReasoningModel:t.isReasoningModel??!1};await ul(JSON.stringify(n,null,2)),await Yt.chmod(Jt,384).catch(()=>{})}var gl=900*1e3,fl="nal-claude",hl="claude-sonnet-4-5";function kl(t){let e=t.replace(/\/+$/,"");return e.endsWith("/v1")?`${e}/`:e.endsWith("/v1/")?e:`${e}/v1/`}async function wl(t){let e=Or(t,".claude","settings.json");if(!await d(e))return null;try{let n=await y(e),i=n.env||{},r=typeof i.ANTHROPIC_BASE_URL=="string"?i.ANTHROPIC_BASE_URL:null;if(!r)return null;let s=typeof n.model=="string"?n.model:"",a=typeof i.ANTHROPIC_MODEL=="string"?i.ANTHROPIC_MODEL:"",c=s.length>0?s:a,l=typeof i.ANTHROPIC_API_KEY=="string"?i.ANTHROPIC_API_KEY:null;if(l)return{provider:"anthropic",apiKey:l,baseUrl:kl(r),model:c.length>0?c:hl};let u=typeof i.ANTHROPIC_AUTH_TOKEN=="string"?i.ANTHROPIC_AUTH_TOKEN:null;return u?{provider:"llmlite",apiKey:u,baseUrl:r,model:c.length>0?c:fl}:null}catch{return null}}async function yl(t,e){return await dl({message:`Generate wiki cho workspace? (~$0.50 qua ${t} model=${e}, 2-5 ph\xFAt). Continue?`,default:!0})}function _r(t,e){return t.split(`
51
+ `),yr("analyze",n.status,n.signal,r)}let i=kr(t,".gitnexus","meta.json");if(!Vc(i))throw e.fail("Analyze exit 0 nh\u01B0ng kh\xF4ng th\u1EA5y meta.json"),new B("analyze","missing-output",`gitnexus analyze xong nh\u01B0ng kh\xF4ng th\u1EA5y ${i}. Repo c\xF3 th\u1EC3 empty ho\u1EB7c gitnexus fail silent.`);e.succeed(`Analyze OK (index t\u1EA1i ${kr(t,".gitnexus")})`)}import{confirm as vl}from"@inquirer/prompts";import bl from"boxen";import{spawnSync as br}from"child_process";var zc=5e3,qc=/(\d+\.\d+\.\d+)/;function Yc(){let e=et()==="win32"?"where":"which",n=br(e,["gitnexus"],{encoding:"utf8"});if(n.error||n.status!==0)return null;let i=(n.stdout||"").trim();return i?i.split(/\r?\n/)[0].trim():null}function Jc(){let t=br("gitnexus",["--version"],{encoding:"utf8",timeout:zc});if(t.error||t.status!==0)return null;let e=(t.stdout||"").trim();return qc.exec(e)?.[1]??null}var ct=null;function qt(){if(ct!==null)return ct;let t=Yc();return t?(ct={installed:!0,version:Jc(),path:t},ct):(ct={installed:!1,version:null,path:null},ct)}function _e(){ct=null}import{spawnSync as Xc}from"child_process";var xr=300*1e3,Ar="gitnexus",L=class extends Error{reason;exitCode;constructor(e,n,i=null){super(n),this.name="InstallGitnexusError",this.reason=e,this.exitCode=i}};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 ${Ar} 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 Sr(){o.info("\u0110ang c\xE0i GitNexus qua npm (c\xF3 th\u1EC3 m\u1EA5t 1-2 ph\xFAt)...");let t=Xc("npm",["install","-g",Ar],{stdio:["inherit","inherit","pipe"],timeout:xr,encoding:"utf8"});if(t.signal==="SIGTERM")throw new L("timeout",`npm install timeout sau ${xr/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||"");_e();let e=qt();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}}import{input as Zf,select as Qc}from"@inquirer/prompts";var b=class extends Error{constructor(e){super(e),this.name="UserAbortedRecoveryError"}};async function R(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 Qc({message:"C\xE1ch x\u1EED l\xFD?",choices:e})}k();import{promises as $r}from"fs";import{homedir as tl}from"os";import{join as el}from"path";var Cr=384,Pr={command:"gitnexus",args:["mcp"]};function nl(){return el(tl(),".claude","mcp_servers.json")}function il(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 rl(t){let e=new Date().toISOString().replace(/[:.]/g,"-"),n=`${t}.avatar-backup-${e}`;return await $r.copyFile(t,n),n}async function Er(){let t=nl(),e={},n=!1;if(await d(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 i=e.mcp_servers?.gitnexus;if(i&&il(i,Pr))return o.dim(`MCP entry gitnexus \u0111\xE3 \u0111\xFAng t\u1EA1i ${t} (no-op)`),{path:t,wasUpdated:!1};let r;n&&(r=await rl(t),o.dim(`Backup ${t} \u2192 ${r}`));let s={...e,mcp_servers:{...e.mcp_servers||{},gitnexus:Pr}};await S(t,s,Cr);try{await $r.chmod(t,Cr)}catch{}return o.success(`Registered MCP server: gitnexus \u2192 ${t}`),{path:t,wasUpdated:!0,backup:r}}import{spawnSync as pl}from"child_process";import{existsSync as ml}from"fs";import{join as Or}from"path";import{confirm as dl}from"@inquirer/prompts";var ol=[/^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 Rr(t){return t?ol.some(e=>e.test(t)):!1}k();import{promises as Yt}from"fs";import{homedir as sl}from"os";import{dirname as al,join as Tr}from"path";var cl=Tr(sl(),".gitnexus"),Jt=Tr(cl,"config.json");async function ll(){try{let t=await Yt.readFile(Jt,"utf8"),e=JSON.parse(t);return typeof e=="object"&&e!==null?e:{}}catch{return{}}}async function ul(t){await Yt.mkdir(al(Jt),{recursive:!0});let e=`${Jt}.tmp-${process.pid}`;await Yt.writeFile(e,t,{mode:384}),await Yt.rename(e,Jt)}async function Ir(t){let n={...await ll(),apiKey:t.apiKey,baseUrl:t.baseUrl,model:t.model,isReasoningModel:t.isReasoningModel??!1};await ul(JSON.stringify(n,null,2)),await Yt.chmod(Jt,384).catch(()=>{})}var gl=900*1e3,fl="nal-claude",hl="claude-sonnet-4-5";function kl(t){let e=t.replace(/\/+$/,"");return e.endsWith("/v1")?`${e}/`:e.endsWith("/v1/")?e:`${e}/v1/`}async function wl(t){let e=Or(t,".claude","settings.json");if(!await d(e))return null;try{let n=await y(e),i=n.env||{},r=typeof i.ANTHROPIC_BASE_URL=="string"?i.ANTHROPIC_BASE_URL:null;if(!r)return null;let s=typeof n.model=="string"?n.model:"",a=typeof i.ANTHROPIC_MODEL=="string"?i.ANTHROPIC_MODEL:"",c=s.length>0?s:a,l=m=>{let g=process.env[m];if(typeof g=="string"&&g.length>0)return g;let f=i[m];return typeof f=="string"&&f.length>0?f:null},u=typeof n.avatarProvider=="string"?n.avatarProvider:null;if(u==="anthropic"||u===null){let m=l("ANTHROPIC_API_KEY");if(m)return{provider:"anthropic",apiKey:m,baseUrl:kl(r),model:c.length>0?c:hl}}if(u==="llmlite"||u===null){let m=l("ANTHROPIC_AUTH_TOKEN");if(m)return{provider:"llmlite",apiKey:m,baseUrl:r,model:c.length>0?c:fl}}return null}catch{return null}}async function yl(t,e){return await dl({message:`Generate wiki cho workspace? (~$0.50 qua ${t} model=${e}, 2-5 ph\xFAt). Continue?`,default:!0})}function _r(t,e){return t.split(`
52
52
  `).slice(-e).join(`
53
- `)}async function Nr(t){let e=await wl(t);if(!e)return o.warn("Subscription mode (OAuth, kh\xF4ng c\xF3 API key trong settings.json) \u2192 skip wiki gen."),o.dim(`\u0110\u1EC3 gen wiki sau, ch\u1EA1y manual:
53
+ `)}async function Nr(t){let e=await wl(t);if(!e)return o.warn(`Kh\xF4ng resolve \u0111\u01B0\u1EE3c API key cho wiki gen.
54
+ Nguy\xEAn nh\xE2n c\xF3 th\u1EC3:
55
+ \u2022 Subscription mode (OAuth, kh\xF4ng c\xF3 key)
56
+ \u2022 Direnv ch\u01B0a load .envrc \u2014 ch\u1EA1y 'direnv allow .' trong workspace + reload shell
57
+ \u2022 Key th\u1EADt s\u1EF1 ch\u01B0a setup \u2014 ch\u1EA1y 'avatar ai setup' ho\u1EB7c 'avatar secrets set ANTHROPIC_API_KEY'`),o.dim(`\u0110\u1EC3 gen wiki sau khi \u0111\xE3 c\xF3 key, ch\u1EA1y manual:
54
58
  gitnexus wiki . --api-key <key> --base-url <url> --model <model>`),{ran:!1,skipped:!0,reason:"subscription-mode"};if(!await yl(e.baseUrl,e.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=Rr(e.model);await Ir({apiKey:e.apiKey,baseUrl:e.baseUrl,model:e.model,isReasoningModel:i});let r=["wiki",".","--base-url",e.baseUrl,"--model",e.model];i&&r.push("--reasoning-model");let s=Lt(`Generating wiki via ${e.baseUrl} (${e.provider}) model=${e.model}${i?" [reasoning]":""}`),a=pl("gitnexus",r,{cwd:t,stdio:["ignore","pipe","pipe"],timeout:gl,encoding:"utf8"});if(a.status!==0||a.signal==="SIGTERM"){let l=a.signal==="SIGTERM"?"timeout":"non-zero-exit";s.fail(`Wiki gen ${l} (exit ${a.status??"null"})`);let u=(a.stderr||"").trim(),m=(a.stdout||"").trim();return u?process.stderr.write(`${_r(u,30)}
55
59
  `):m&&process.stderr.write(`${_r(m,30)}
56
60
  `),{ran:!1,skipped:!0,reason:"fail",detail:`Wiki gen ${l} (exit ${a.status??"null"})`}}let c=Or(t,".gitnexus","wiki","index.html");return ml(c)?(s.succeed(`Wiki ready: ${c}`),{ran:!0,skipped:!1,wikiPath:c}):(s.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 ${c}`})}async function xl(){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(`${bl(t.join(`