@nalvietnam/avatar-cli 1.2.2 → 1.2.3

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
@@ -4,7 +4,6 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/ai.ts
7
- import { spawnSync as spawnSync4 } from "child_process";
8
7
  import { promises as fs4 } from "fs";
9
8
  import { join as join5 } from "path";
10
9
  import { confirm } from "@inquirer/prompts";
@@ -654,6 +653,102 @@ async function runAiSetupPhase(args) {
654
653
  }
655
654
  }
656
655
 
656
+ // src/lib/test-ai-provider-by-detected-mode.ts
657
+ import { spawnSync as spawnSync4 } from "child_process";
658
+ var FETCH_TIMEOUT_MS2 = 1e4;
659
+ var CLAUDE_PRINT_TIMEOUT_MS = 3e4;
660
+ var TEST_CHAT_MAX_TOKENS = 5;
661
+ var TEST_CHAT_PROMPT = "say ok";
662
+ async function testLLMLiteProvider(baseUrl, token, model) {
663
+ log.info(`Testing LLMLite provider: ${baseUrl} (key: ${maskApiKey(token)})`);
664
+ const controller = new AbortController();
665
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
666
+ try {
667
+ const modelsRes = await fetch(`${baseUrl}/v1/models`, {
668
+ headers: { Authorization: `Bearer ${token}` },
669
+ signal: controller.signal
670
+ });
671
+ if (modelsRes.status === 401 || modelsRes.status === 403) {
672
+ throw new Error(`API key invalid (HTTP ${modelsRes.status}). Re-run: avatar ai setup`);
673
+ }
674
+ if (!modelsRes.ok) {
675
+ throw new Error(`Endpoint /v1/models l\u1ED7i (HTTP ${modelsRes.status}).`);
676
+ }
677
+ const modelsJson = await modelsRes.json();
678
+ const models = (modelsJson.data || []).map((m) => typeof m.id === "string" ? m.id : null).filter((id) => id !== null);
679
+ log.success(`Connectivity OK \xB7 ${models.length} models available`);
680
+ if (models.length > 0) {
681
+ const preview = models.slice(0, 5).join(", ");
682
+ const more = models.length > 5 ? ` ...+${models.length - 5} more` : "";
683
+ log.dim(` Models: ${preview}${more}`);
684
+ }
685
+ log.info(`Testing chat completion v\u1EDBi model "${model}"...`);
686
+ const chatRes = await fetch(`${baseUrl}/v1/chat/completions`, {
687
+ method: "POST",
688
+ headers: {
689
+ Authorization: `Bearer ${token}`,
690
+ "Content-Type": "application/json"
691
+ },
692
+ body: JSON.stringify({
693
+ model,
694
+ messages: [{ role: "user", content: TEST_CHAT_PROMPT }],
695
+ max_tokens: TEST_CHAT_MAX_TOKENS
696
+ }),
697
+ signal: controller.signal
698
+ });
699
+ if (!chatRes.ok) {
700
+ const errBody = (await chatRes.text()).slice(0, 200);
701
+ throw new Error(`Chat completion fail (HTTP ${chatRes.status}). ${errBody}`);
702
+ }
703
+ const chatJson = await chatRes.json();
704
+ const reply = typeof chatJson.choices?.[0]?.message?.content === "string" ? chatJson.choices[0].message.content : "(empty response)";
705
+ const tokens = chatJson.usage?.total_tokens ?? "?";
706
+ log.success(`Response: "${String(reply).trim().slice(0, 100)}"`);
707
+ log.dim(` Tokens used: ${tokens}`);
708
+ } catch (err) {
709
+ if (err.name === "AbortError") {
710
+ throw new Error(`Timeout ${FETCH_TIMEOUT_MS2 / 1e3}s. Check m\u1EA1ng / endpoint ${baseUrl}.`);
711
+ }
712
+ throw err;
713
+ } finally {
714
+ clearTimeout(timer);
715
+ }
716
+ }
717
+ function testSubscriptionProvider() {
718
+ log.info("Testing Subscription provider qua `claude --print`...");
719
+ const result = spawnSync4("claude", ["--print", TEST_CHAT_PROMPT], {
720
+ encoding: "utf8",
721
+ timeout: CLAUDE_PRINT_TIMEOUT_MS
722
+ });
723
+ if (result.signal === "SIGTERM") {
724
+ throw new Error(`Timeout ${CLAUDE_PRINT_TIMEOUT_MS / 1e3}s. Check m\u1EA1ng / endpoint.`);
725
+ }
726
+ if (result.status !== 0) {
727
+ const stderr = (result.stderr || "").toLowerCase();
728
+ if (stderr.includes("401") || stderr.includes("invalid authentication") || stderr.includes("unauthorized")) {
729
+ throw new Error(
730
+ "Token Claude Code stale (401). Fix: `claude auth logout && claude auth login`."
731
+ );
732
+ }
733
+ throw new Error(
734
+ `Test fail (exit ${result.status}). Stderr: ${(result.stderr || "").slice(0, 200)}`
735
+ );
736
+ }
737
+ log.success(`Response: "${(result.stdout || "").trim().slice(0, 100)}"`);
738
+ }
739
+ async function testAiProviderByDetectedMode(settings) {
740
+ const env = settings.env || {};
741
+ const baseUrl = typeof env.ANTHROPIC_BASE_URL === "string" ? env.ANTHROPIC_BASE_URL : void 0;
742
+ const token = typeof env.ANTHROPIC_AUTH_TOKEN === "string" ? env.ANTHROPIC_AUTH_TOKEN : void 0;
743
+ const model = typeof settings.model === "string" ? settings.model : "default";
744
+ if (baseUrl && token) {
745
+ await testLLMLiteProvider(baseUrl, token, model);
746
+ return { ok: true, provider: "llmlite", message: "LLMLite provider working" };
747
+ }
748
+ testSubscriptionProvider();
749
+ return { ok: true, provider: "subscription", message: "Subscription provider working" };
750
+ }
751
+
657
752
  // src/commands/ai.ts
658
753
  async function ensureWorkspaceCwd() {
659
754
  const cwd = process.cwd();
@@ -691,24 +786,15 @@ async function runAiStatus() {
691
786
  log.info(`Token: ${token ? maskApiKey(token) : "(kh\xF4ng set \u2014 d\xF9ng subscription auth)"}`);
692
787
  }
693
788
  async function runAiTest() {
694
- await ensureWorkspaceCwd();
695
- log.info("Calling provider v\u1EDBi prompt 'say ok'...");
696
- const result = spawnSync4("claude", ["--print", "say ok"], {
697
- encoding: "utf8",
698
- timeout: 3e4
699
- });
700
- if (result.signal === "SIGTERM") {
701
- log.error("Timeout sau 30s. Check m\u1EA1ng / endpoint.");
702
- process.exit(1);
703
- }
704
- if (result.status !== 0) {
705
- log.error(`Test th\u1EA5t b\u1EA1i (exit ${result.status}).`);
706
- if (result.stderr) process.stderr.write(`${result.stderr}
707
- `);
789
+ const workspacePath = await ensureWorkspaceCwd();
790
+ const settings = await readWorkspaceSettings(workspacePath);
791
+ try {
792
+ const result = await testAiProviderByDetectedMode(settings);
793
+ log.success(`\u2713 ${result.message}`);
794
+ } catch (err) {
795
+ log.error(`Test fail: ${err.message}`);
708
796
  process.exit(1);
709
797
  }
710
- log.success(`Response: ${(result.stdout || "").trim().slice(0, 200)}`);
711
- log.success("AI provider working");
712
798
  }
713
799
  async function runAiReset(opts) {
714
800
  const workspacePath = await ensureWorkspaceCwd();
@@ -1108,7 +1194,7 @@ async function applyFixes(checks) {
1108
1194
 
1109
1195
  // src/commands/init.ts
1110
1196
  import { basename, join as join16, relative as relative2, resolve } from "path";
1111
- import { confirm as confirm2, input as input2, select as select4 } from "@inquirer/prompts";
1197
+ import { confirm as confirm3, input as input2, select as select5 } from "@inquirer/prompts";
1112
1198
  import boxen3 from "boxen";
1113
1199
 
1114
1200
  // src/lib/avatar-ascii-banner.ts
@@ -1385,11 +1471,41 @@ async function ensureGitHubReady(remoteUrl) {
1385
1471
  }
1386
1472
 
1387
1473
  // src/lib/create-workspace-remote-via-gh.ts
1474
+ function canCreateInNamespace(org, ghUser) {
1475
+ if (org.toLowerCase() === ghUser.toLowerCase()) return { ok: true };
1476
+ const r = spawnSync14("gh", ["api", `orgs/${org}/members/${ghUser}`, "--silent"], {
1477
+ stdio: "ignore"
1478
+ });
1479
+ if (r.status === 0) return { ok: true };
1480
+ const orgCheck = spawnSync14("gh", ["api", `orgs/${org}`, "--silent"], { stdio: "ignore" });
1481
+ if (orgCheck.status !== 0) {
1482
+ const userCheck = spawnSync14("gh", ["api", `users/${org}`, "--silent"], { stdio: "ignore" });
1483
+ if (userCheck.status === 0) {
1484
+ return {
1485
+ ok: false,
1486
+ reason: `'${org}' l\xE0 personal account kh\xE1c (kh\xF4ng ph\u1EA3i org). B\u1EA1n (${ghUser}) kh\xF4ng th\u1EC3 t\u1EA1o repo d\u01B0\u1EDBi account c\u1EE7a user kh\xE1c. Pass --repo-org=${ghUser} ho\u1EB7c switch gh: gh auth login`
1487
+ };
1488
+ }
1489
+ return {
1490
+ ok: false,
1491
+ reason: `'${org}' kh\xF4ng t\u1ED3n t\u1EA1i tr\xEAn GitHub. Check ch\xEDnh t\u1EA3 ho\u1EB7c t\u1EA1o org tr\u01B0\u1EDBc.`
1492
+ };
1493
+ }
1494
+ return {
1495
+ ok: false,
1496
+ reason: `'${ghUser}' kh\xF4ng ph\u1EA3i member c\u1EE7a org '${org}'. Li\xEAn h\u1EC7 admin org \u0111\u1EC3 \u0111\u01B0\u1EE3c invite, ho\u1EB7c pass --repo-org=${ghUser} t\u1EA1o d\u01B0\u1EDBi personal account.`
1497
+ };
1498
+ }
1388
1499
  async function createWorkspaceRemoteViaGh(input3) {
1389
1500
  validateRepoName(input3.workspaceName);
1390
1501
  validateRepoVisibility(input3.visibility);
1391
1502
  await ensureGitHubReady();
1392
- const org = input3.org ?? resolveGithubUsernameDefault();
1503
+ const ghUser = resolveGithubUsernameDefault();
1504
+ const org = input3.org ?? ghUser;
1505
+ const namespaceCheck = canCreateInNamespace(org, ghUser);
1506
+ if (!namespaceCheck.ok) {
1507
+ throw new Error(`Kh\xF4ng th\u1EC3 t\u1EA1o repo d\u01B0\u1EDBi '${org}/': ${namespaceCheck.reason}`);
1508
+ }
1393
1509
  const fullName = `${org}/${input3.workspaceName}`;
1394
1510
  log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${input3.visibility})...`);
1395
1511
  const r = spawnSync14(
@@ -1405,9 +1521,12 @@ async function createWorkspaceRemoteViaGh(input3) {
1405
1521
  "origin",
1406
1522
  "--push"
1407
1523
  ],
1408
- { stdio: "inherit" }
1524
+ { stdio: ["inherit", "inherit", "pipe"], encoding: "utf8" }
1409
1525
  );
1410
1526
  if (r.status !== 0) {
1527
+ const stderr = (r.stderr || "").trim();
1528
+ if (stderr) process.stderr.write(`${stderr}
1529
+ `);
1411
1530
  throw new Error(
1412
1531
  `T\u1EA1o workspace remote th\u1EA5t b\u1EA1i (exit ${r.status}). Workspace v\u1EABn d\xF9ng \u0111\u01B0\u1EE3c local. Setup remote sau qua: gh repo create ${fullName} --${input3.visibility} --source=. --remote=origin --push`
1413
1532
  );
@@ -1687,25 +1806,110 @@ async function safeBootstrapGitInFolder(folderPath, opts = {}) {
1687
1806
  // src/lib/team-pack-submodule-manager.ts
1688
1807
  import { join as join14 } from "path";
1689
1808
 
1809
+ // src/lib/check-team-pack-access-with-retry-loop.ts
1810
+ import { spawnSync as spawnSync15 } from "child_process";
1811
+ import { confirm as confirm2, select as select4 } from "@inquirer/prompts";
1812
+ function parseRepoSlugFromGitUrl(url) {
1813
+ const httpsMatch = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
1814
+ if (httpsMatch) return httpsMatch[1];
1815
+ return null;
1816
+ }
1817
+ function checkRepoAccess(repoSlug) {
1818
+ const r = spawnSync15("gh", ["api", `repos/${repoSlug}`], { stdio: "ignore" });
1819
+ return r.status === 0;
1820
+ }
1821
+ function getCurrentGhUser() {
1822
+ const r = spawnSync15("gh", ["api", "user", "--jq", ".login"], {
1823
+ encoding: "utf8",
1824
+ stdio: ["ignore", "pipe", "pipe"]
1825
+ });
1826
+ if (r.status !== 0) return null;
1827
+ return r.stdout.trim() || null;
1828
+ }
1829
+ async function copyInfoToClipboardWithConsent(info) {
1830
+ const ok = await confirm2({
1831
+ message: "Copy th\xF4ng tin (GitHub username + email) v\xE0o clipboard \u0111\u1EC3 d\xE1n v\xE0o Slack/email?",
1832
+ default: true
1833
+ });
1834
+ if (!ok) return;
1835
+ try {
1836
+ const { default: clipboardy } = await import("clipboardy");
1837
+ await clipboardy.write(info);
1838
+ log.success("\u0110\xE3 copy v\xE0o clipboard");
1839
+ } catch (err) {
1840
+ log.dim(`Copy clipboard fail: ${err.message}`);
1841
+ }
1842
+ }
1843
+ function buildAccessRequestInfo(ghUser, ssoEmail) {
1844
+ const lines = [
1845
+ "Request access team-ai-pack (NAL)",
1846
+ "",
1847
+ `GitHub username: ${ghUser ?? "(ch\u01B0a gh auth \u2014 ch\u1EA1y: gh auth login)"}`,
1848
+ `NAL email: ${ssoEmail ?? "(ch\u01B0a avatar login \u2014 ch\u1EA1y: avatar login)"}`,
1849
+ `Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
1850
+ ];
1851
+ return lines.join("\n");
1852
+ }
1853
+ async function ensureTeamPackAccessWithRetry(args) {
1854
+ if (checkRepoAccess(args.repoSlug)) return true;
1855
+ const ghUser = getCurrentGhUser();
1856
+ const info = buildAccessRequestInfo(ghUser, args.ssoEmail ?? null);
1857
+ log.warn(`B\u1EA1n ch\u01B0a c\xF3 quy\u1EC1n access v\xE0o b\u1ED9 package ${args.repoSlug}.`);
1858
+ log.info("Li\xEAn h\u1EC7 admin (Luke @nal.vn) \u0111\u1EC3 \u0111\u01B0\u1EE3c add v\xE0o org nalvn.");
1859
+ log.plain("");
1860
+ log.plain(info);
1861
+ log.plain("");
1862
+ await copyInfoToClipboardWithConsent(info);
1863
+ while (true) {
1864
+ const action = await select4({
1865
+ message: "Ti\u1EBFp t\u1EE5c?",
1866
+ choices: [
1867
+ { name: "\u0110\xE3 \u0111\u01B0\u1EE3c add \u2014 ki\u1EC3m tra l\u1EA1i v\xE0 ti\u1EBFp t\u1EE5c", value: "retry" },
1868
+ { name: "T\u1EA1m ng\u01B0ng \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau", value: "abort" }
1869
+ ]
1870
+ });
1871
+ if (action === "abort") {
1872
+ log.dim("T\u1EA1m ng\u01B0ng. Ch\u1EA1y l\u1EA1i 'avatar init' sau khi \u0111\xE3 accept invite t\u1EEB GitHub.");
1873
+ return false;
1874
+ }
1875
+ log.info("Ki\u1EC3m tra access...");
1876
+ if (checkRepoAccess(args.repoSlug)) {
1877
+ log.success("\u0110\xE3 c\xF3 access \u2014 ti\u1EBFp t\u1EE5c.");
1878
+ return true;
1879
+ }
1880
+ log.warn("V\u1EABn ch\u01B0a c\xF3 access. \u0110\u1EA3m b\u1EA3o b\u1EA1n \u0111\xE3 accept email invite t\u1EEB GitHub (Inbox + Spam).");
1881
+ }
1882
+ }
1883
+
1690
1884
  // src/lib/resolve-team-pack-repo-url.ts
1691
- var LEGACY_FALLBACK = "https://github.com/LukeNALS/team-ai-pack.git";
1885
+ var ORG_DEFAULT = "https://github.com/nalvn/team-ai-pack.git";
1692
1886
  function resolveTeamPackRepoUrl() {
1693
1887
  if (process.env.AVATAR_TEAM_PACK_REPO_URL) {
1694
1888
  return process.env.AVATAR_TEAM_PACK_REPO_URL;
1695
1889
  }
1696
- try {
1697
- const ghUser = resolveGithubUsernameDefault();
1698
- if (ghUser) return `https://github.com/${ghUser}/team-ai-pack.git`;
1699
- } catch {
1700
- }
1701
- return LEGACY_FALLBACK;
1890
+ return ORG_DEFAULT;
1702
1891
  }
1703
1892
 
1704
1893
  // src/lib/team-pack-submodule-manager.ts
1705
1894
  var TEAM_PACK_REPO_URL = resolveTeamPackRepoUrl();
1706
1895
  var TEAM_PACK_RELATIVE_PATH = ".claude/pack";
1707
- async function addTeamPackSubmodule(projectRoot, tag) {
1896
+ var TeamPackAccessAbortedError = class extends Error {
1897
+ constructor(message) {
1898
+ super(message);
1899
+ this.name = "TeamPackAccessAbortedError";
1900
+ }
1901
+ };
1902
+ async function addTeamPackSubmodule(projectRoot, tag, ssoEmail) {
1708
1903
  const url = resolveTeamPackRepoUrl();
1904
+ const repoSlug = parseRepoSlugFromGitUrl(url);
1905
+ if (repoSlug) {
1906
+ const hasAccess = await ensureTeamPackAccessWithRetry({ repoSlug, ssoEmail });
1907
+ if (!hasAccess) {
1908
+ throw new TeamPackAccessAbortedError(
1909
+ "User ch\u1ECDn t\u1EA1m ng\u01B0ng. Ch\u1EA1y l\u1EA1i 'avatar init' sau khi \u0111\u01B0\u1EE3c add v\xE0o org."
1910
+ );
1911
+ }
1912
+ }
1709
1913
  try {
1710
1914
  await addSubmodule(url, TEAM_PACK_RELATIVE_PATH, projectRoot);
1711
1915
  } catch (err) {
@@ -1992,6 +2196,10 @@ function registerInitCommand(program2) {
1992
2196
  log.dim(err.message);
1993
2197
  process.exit(0);
1994
2198
  }
2199
+ if (err instanceof TeamPackAccessAbortedError) {
2200
+ log.dim(err.message);
2201
+ process.exit(0);
2202
+ }
1995
2203
  log.error(err instanceof Error ? err.message : String(err));
1996
2204
  process.exit(1);
1997
2205
  }
@@ -2026,7 +2234,7 @@ async function runInit(opts) {
2026
2234
  }
2027
2235
  }
2028
2236
  async function promptProjectStatus() {
2029
- return await select4({
2237
+ return await select5({
2030
2238
  message: "T\xECnh tr\u1EA1ng d\u1EF1 \xE1n c\u1EE7a b\u1EA1n?",
2031
2239
  choices: [
2032
2240
  { name: "1. \u0110\xE3 c\xF3 repo git remote (URL c\xF3 s\u1EB5n)", value: "existing-remote" },
@@ -2104,7 +2312,7 @@ async function runInitFromScratch(opts, ownerEmail) {
2104
2312
  message: "T\xEAn d\u1EF1 \xE1n:",
2105
2313
  validate: (v) => v.length > 0 ? true : "T\xEAn b\u1EAFt bu\u1ED9c"
2106
2314
  });
2107
- const visibility = opts.repoVisibility ?? await select4({
2315
+ const visibility = opts.repoVisibility ?? await select5({
2108
2316
  message: "Visibility?",
2109
2317
  choices: [
2110
2318
  { name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
@@ -2164,7 +2372,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
2164
2372
  log.success(`Folder \u0111\xE3 c\xF3 remote origin: ${origin.refs.push}`);
2165
2373
  return origin.refs.push;
2166
2374
  }
2167
- const shouldCreate = opts.createRemote ?? await confirm2({
2375
+ const shouldCreate = opts.createRemote ?? await confirm3({
2168
2376
  message: "Folder ch\u01B0a c\xF3 remote. T\u1EA1o GitHub repo ngay \u0111\u1EC3 share team?",
2169
2377
  default: true
2170
2378
  });
@@ -2173,7 +2381,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
2173
2381
  return void 0;
2174
2382
  }
2175
2383
  await ensureGitHubReady();
2176
- const visibility = opts.repoVisibility ?? await select4({
2384
+ const visibility = opts.repoVisibility ?? await select5({
2177
2385
  message: "Visibility?",
2178
2386
  choices: [
2179
2387
  { name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
@@ -2264,13 +2472,13 @@ async function maybeCreateWorkspaceRemote(args) {
2264
2472
  let shouldCreate = args.createWorkspaceRemote;
2265
2473
  if (shouldCreate === void 0) {
2266
2474
  if (args.autoYes) return;
2267
- shouldCreate = await confirm2({
2475
+ shouldCreate = await confirm3({
2268
2476
  message: "T\u1EA1o remote GitHub cho workspace \u0111\u1EC3 share team? (Avatar state)",
2269
2477
  default: false
2270
2478
  });
2271
2479
  }
2272
2480
  if (!shouldCreate) return;
2273
- const visibility = args.repoVisibility ?? (args.autoYes ? "private" : await select4({
2481
+ const visibility = args.repoVisibility ?? (args.autoYes ? "private" : await select5({
2274
2482
  message: "Workspace visibility?",
2275
2483
  choices: [
2276
2484
  { name: "private (m\u1EB7c \u0111\u1ECBnh, an to\xE0n)", value: "private" },
@@ -2301,7 +2509,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
2301
2509
  log.info(`--force: d\xF9ng ${alternative}`);
2302
2510
  return alternative;
2303
2511
  }
2304
- const useAlt = await confirm2({ message: `D\xF9ng "${alternative}" thay th\u1EBF?`, default: true });
2512
+ const useAlt = await confirm3({ message: `D\xF9ng "${alternative}" thay th\u1EBF?`, default: true });
2305
2513
  if (!useAlt) throw new Error("H\u1EE7y init. Ch\u1EA1y l\u1EA1i v\u1EDBi --workspace-name kh\xE1c.");
2306
2514
  return alternative;
2307
2515
  }
@@ -2476,7 +2684,7 @@ function registerToolsCommand(program2) {
2476
2684
 
2477
2685
  // src/commands/uninstall.ts
2478
2686
  import { relative as relative3 } from "path";
2479
- import { confirm as confirm3 } from "@inquirer/prompts";
2687
+ import { confirm as confirm4 } from "@inquirer/prompts";
2480
2688
  import boxen5 from "boxen";
2481
2689
 
2482
2690
  // src/lib/create-uninstall-backup-snapshot.ts
@@ -2631,7 +2839,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
2631
2839
  }
2632
2840
 
2633
2841
  // src/commands/uninstall.ts
2634
- var CLI_VERSION = "1.2.2";
2842
+ var CLI_VERSION = "1.2.3";
2635
2843
  function registerUninstallCommand(program2) {
2636
2844
  program2.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 (opts) => {
2637
2845
  try {
@@ -2655,7 +2863,7 @@ async function runUninstall(opts) {
2655
2863
  return;
2656
2864
  }
2657
2865
  if (!opts.yes) {
2658
- const ok = await confirm3({
2866
+ const ok = await confirm4({
2659
2867
  message: "Ti\u1EBFp t\u1EE5c g\u1EE1 Avatar?",
2660
2868
  default: false
2661
2869
  });
@@ -2713,7 +2921,7 @@ function printUninstallSuccessBox(backupPath) {
2713
2921
  }
2714
2922
 
2715
2923
  // src/index.ts
2716
- var CLI_VERSION2 = "1.2.2";
2924
+ var CLI_VERSION2 = "1.2.3";
2717
2925
  var program = new Command();
2718
2926
  program.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(CLI_VERSION2, "-v, --version", "Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI").addHelpText(
2719
2927
  "beforeAll",