@nalvietnam/avatar-cli 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -546,19 +546,19 @@ function applyUseGlobal(existing, source) {
546
546
  ...sourceModel ? { model: sourceModel } : {}
547
547
  };
548
548
  }
549
- async function writeClaudeSettings(workspacePath, input4) {
549
+ async function writeClaudeSettings(workspacePath, input5) {
550
550
  const path = getClaudeSettingsPath(workspacePath);
551
551
  const existing = await readExistingSettings(path);
552
552
  let merged;
553
- switch (input4.provider) {
553
+ switch (input5.provider) {
554
554
  case "subscription":
555
- merged = applySubscription(existing, input4.model);
555
+ merged = applySubscription(existing, input5.model);
556
556
  break;
557
557
  case "llmlite":
558
- merged = applyLLMLite(existing, input4.apiKey, input4.baseUrl, input4.model);
558
+ merged = applyLLMLite(existing, input5.apiKey, input5.baseUrl, input5.model);
559
559
  break;
560
560
  case "use-global":
561
- merged = applyUseGlobal(existing, input4.sourceSettings);
561
+ merged = applyUseGlobal(existing, input5.sourceSettings);
562
562
  break;
563
563
  }
564
564
  await writeJsonAtomic(path, merged, SECRET_FILE_MODE2);
@@ -1194,7 +1194,7 @@ async function applyFixes(checks) {
1194
1194
 
1195
1195
  // src/commands/init.ts
1196
1196
  import { basename, join as join16, relative as relative2, resolve } from "path";
1197
- import { confirm as confirm3, input as input3, select as select7 } from "@inquirer/prompts";
1197
+ import { confirm as confirm3, input as input4, select as select7 } from "@inquirer/prompts";
1198
1198
  import boxen4 from "boxen";
1199
1199
 
1200
1200
  // src/lib/prompt-recovery-action-on-failure.ts
@@ -1498,15 +1498,15 @@ var RepoAlreadyExistsError = class extends Error {
1498
1498
  this.name = "RepoAlreadyExistsError";
1499
1499
  }
1500
1500
  };
1501
- function executeGhRepoCreate(input4) {
1502
- const fullName = `${input4.org}/${input4.name}`;
1501
+ function executeGhRepoCreate(input5) {
1502
+ const fullName = `${input5.org}/${input5.name}`;
1503
1503
  const args = [
1504
1504
  "repo",
1505
1505
  "create",
1506
1506
  fullName,
1507
- `--${input4.visibility}`,
1507
+ `--${input5.visibility}`,
1508
1508
  "--source",
1509
- input4.folder,
1509
+ input5.folder,
1510
1510
  "--remote",
1511
1511
  "origin",
1512
1512
  "--push"
@@ -1559,16 +1559,16 @@ function validateRepoVisibility(v) {
1559
1559
  }
1560
1560
 
1561
1561
  // src/lib/create-github-remote-from-folder.ts
1562
- function createGithubRemoteFromFolder(input4) {
1563
- validateRepoName(input4.name);
1564
- validateRepoVisibility(input4.visibility);
1565
- const org = input4.org ?? resolveGithubUsernameDefault();
1566
- log.info(`T\u1EA1o GitHub repo ${org}/${input4.name} (${input4.visibility})...`);
1562
+ function createGithubRemoteFromFolder(input5) {
1563
+ validateRepoName(input5.name);
1564
+ validateRepoVisibility(input5.visibility);
1565
+ const org = input5.org ?? resolveGithubUsernameDefault();
1566
+ log.info(`T\u1EA1o GitHub repo ${org}/${input5.name} (${input5.visibility})...`);
1567
1567
  const urls = executeGhRepoCreate({
1568
- folder: input4.folder,
1568
+ folder: input5.folder,
1569
1569
  org,
1570
- name: input4.name,
1571
- visibility: input4.visibility
1570
+ name: input5.name,
1571
+ visibility: input5.visibility
1572
1572
  });
1573
1573
  log.success(`\u0110\xE3 t\u1EA1o: ${urls.sshUrl}`);
1574
1574
  return urls;
@@ -1610,7 +1610,7 @@ function detectPackageManager() {
1610
1610
 
1611
1611
  // src/lib/handle-remote-access-failure-with-account-switch.ts
1612
1612
  import { spawnSync as spawnSync12 } from "child_process";
1613
- import { select as select5 } from "@inquirer/prompts";
1613
+ import { input as input3, select as select5 } from "@inquirer/prompts";
1614
1614
 
1615
1615
  // src/lib/verify-git-remote-accessible.ts
1616
1616
  import { spawnSync as spawnSync11 } from "child_process";
@@ -1670,33 +1670,43 @@ function getReasonHint(reason, url, ghUser) {
1670
1670
  case "no-access":
1671
1671
  return ghUser ? `Repo c\xF3 th\u1EC3 private v\xE0 account '${ghUser}' kh\xF4ng c\xF3 quy\u1EC1n access. Switch sang account c\xF3 quy\u1EC1n, ho\u1EB7c xin invite t\u1EEB owner repo.` : "Repo c\xF3 th\u1EC3 private v\xE0 gh CLI ch\u01B0a login. Login tr\u01B0\u1EDBc r\u1ED3i retry.";
1672
1672
  case "not-found":
1673
- return `URL c\xF3 th\u1EC3 sai ch\xEDnh t\u1EA3, ho\u1EB7c repo \u0111\xE3 b\u1ECB x\xF3a/rename. Check: ${url}`;
1673
+ return `URL c\xF3 th\u1EC3 sai ch\xEDnh t\u1EA3, ho\u1EB7c repo \u0111\xE3 b\u1ECB x\xF3a/rename. Nh\u1EADp l\u1EA1i URL \u0111\xFAng. Check: ${url}`;
1674
1674
  case "network":
1675
1675
  return "Kh\xF4ng k\u1EBFt n\u1ED1i \u0111\u01B0\u1EE3c GitHub. Check m\u1EA1ng / VPN / firewall.";
1676
1676
  case "timeout":
1677
1677
  return "Network ch\u1EADm > 5s. Check m\u1EA1ng r\u1ED3i retry.";
1678
1678
  default:
1679
- return "L\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh. Check URL + gh auth status.";
1679
+ return "L\u1ED7i kh\xF4ng x\xE1c \u0111\u1ECBnh. URL c\xF3 th\u1EC3 sai \u2014 nh\u1EADp l\u1EA1i, ho\u1EB7c check gh auth status.";
1680
1680
  }
1681
1681
  }
1682
+ function isValidGitUrl(url) {
1683
+ const trimmed = url.trim();
1684
+ if (!trimmed) return false;
1685
+ return /^https?:\/\/[\w.@/-]+$/.test(trimmed) || /^git@[\w.-]+:[\w./-]+\.git$/.test(trimmed) || /^[\w.-]+\/[\w.-]+$/.test(trimmed);
1686
+ }
1682
1687
  async function handleRemoteAccessFailureWithAccountSwitch(args) {
1688
+ let currentUrl = args.url;
1683
1689
  let reason = args.initialReason;
1684
1690
  let detail = args.initialDetail;
1685
1691
  while (true) {
1686
1692
  const ghUser = getCurrentGhUser2();
1687
- log.warn(`Kh\xF4ng truy c\u1EADp \u0111\u01B0\u1EE3c ${args.url}`);
1693
+ log.warn(`Kh\xF4ng truy c\u1EADp \u0111\u01B0\u1EE3c ${currentUrl}`);
1688
1694
  log.dim(` L\xFD do: ${reason}${detail ? ` \u2014 ${detail.slice(0, 150)}` : ""}`);
1689
- log.info(getReasonHint(reason, args.url, ghUser));
1695
+ log.info(getReasonHint(reason, currentUrl, ghUser));
1690
1696
  if (ghUser) log.dim(` gh CLI hi\u1EC7n \u0111ang login: ${ghUser}`);
1691
1697
  const action = await select5({
1692
1698
  message: "C\xE1ch x\u1EED l\xFD?",
1693
1699
  choices: [
1700
+ {
1701
+ name: "Nh\u1EADp l\u1EA1i URL \u0111\xFAng (recommended khi URL sai ch\xEDnh t\u1EA3)",
1702
+ value: "re-input-url"
1703
+ },
1694
1704
  {
1695
1705
  name: "Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",
1696
1706
  value: "switch"
1697
1707
  },
1698
1708
  {
1699
- name: "T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa URL) \u2014 retry verify",
1709
+ name: "T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa permission) \u2014 retry verify",
1700
1710
  value: "retry"
1701
1711
  },
1702
1712
  {
@@ -1707,17 +1717,25 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
1707
1717
  });
1708
1718
  if (action === "abort") {
1709
1719
  throw new RemoteAccessAbortedError(
1710
- `User ch\u1ECDn t\u1EA1m ng\u01B0ng. Fix access ${args.url} r\u1ED3i ch\u1EA1y l\u1EA1i 'avatar init'.`
1720
+ `User ch\u1ECDn t\u1EA1m ng\u01B0ng. Fix access ${currentUrl} r\u1ED3i ch\u1EA1y l\u1EA1i 'avatar init'.`
1711
1721
  );
1712
1722
  }
1723
+ if (action === "re-input-url") {
1724
+ const newUrl = await input3({
1725
+ message: "URL git remote (https://github.com/owner/repo.git ho\u1EB7c git@github.com:owner/repo.git):",
1726
+ default: currentUrl,
1727
+ validate: (v) => isValidGitUrl(v) || "URL kh\xF4ng \u0111\xFAng format git remote"
1728
+ });
1729
+ currentUrl = newUrl.trim();
1730
+ }
1713
1731
  if (action === "switch") {
1714
1732
  triggerGhAuthLoginInteractive2();
1715
1733
  }
1716
- log.info("Verify remote l\u1EA1i...");
1717
- const result = tryVerifyGitRemoteAccessible(args.url);
1734
+ log.info(`Verify remote l\u1EA1i: ${currentUrl}...`);
1735
+ const result = tryVerifyGitRemoteAccessible(currentUrl);
1718
1736
  if (result.ok) {
1719
- log.success(`Remote accessible: ${args.url}`);
1720
- return;
1737
+ log.success(`Remote accessible: ${currentUrl}`);
1738
+ return { resolvedUrl: currentUrl };
1721
1739
  }
1722
1740
  reason = result.reason ?? "unknown";
1723
1741
  detail = result.detail;
@@ -1834,14 +1852,16 @@ async function ensureGitHubReady(remoteUrl) {
1834
1852
  const result = tryVerifyGitRemoteAccessible(remoteUrl);
1835
1853
  if (result.ok) {
1836
1854
  log.success(`Remote accessible: ${remoteUrl}`);
1837
- } else {
1838
- await handleRemoteAccessFailureWithAccountSwitch({
1839
- url: remoteUrl,
1840
- initialReason: result.reason ?? "unknown",
1841
- initialDetail: result.detail
1842
- });
1855
+ return { resolvedRemoteUrl: remoteUrl };
1843
1856
  }
1857
+ const recovered = await handleRemoteAccessFailureWithAccountSwitch({
1858
+ url: remoteUrl,
1859
+ initialReason: result.reason ?? "unknown",
1860
+ initialDetail: result.detail
1861
+ });
1862
+ return { resolvedRemoteUrl: recovered.resolvedUrl };
1844
1863
  }
1864
+ return {};
1845
1865
  }
1846
1866
 
1847
1867
  // src/lib/create-workspace-remote-via-gh.ts
@@ -1870,27 +1890,27 @@ function canCreateInNamespace(org, ghUser) {
1870
1890
  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.`
1871
1891
  };
1872
1892
  }
1873
- async function createWorkspaceRemoteViaGh(input4) {
1874
- validateRepoName(input4.workspaceName);
1875
- validateRepoVisibility(input4.visibility);
1893
+ async function createWorkspaceRemoteViaGh(input5) {
1894
+ validateRepoName(input5.workspaceName);
1895
+ validateRepoVisibility(input5.visibility);
1876
1896
  await ensureGitHubReady();
1877
1897
  const ghUser = resolveGithubUsernameDefault();
1878
- const org = input4.org ?? ghUser;
1898
+ const org = input5.org ?? ghUser;
1879
1899
  const namespaceCheck = canCreateInNamespace(org, ghUser);
1880
1900
  if (!namespaceCheck.ok) {
1881
1901
  throw new Error(`Kh\xF4ng th\u1EC3 t\u1EA1o repo d\u01B0\u1EDBi '${org}/': ${namespaceCheck.reason}`);
1882
1902
  }
1883
- const fullName = `${org}/${input4.workspaceName}`;
1884
- log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${input4.visibility})...`);
1903
+ const fullName = `${org}/${input5.workspaceName}`;
1904
+ log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${input5.visibility})...`);
1885
1905
  const r = spawnSync16(
1886
1906
  "gh",
1887
1907
  [
1888
1908
  "repo",
1889
1909
  "create",
1890
1910
  fullName,
1891
- `--${input4.visibility}`,
1911
+ `--${input5.visibility}`,
1892
1912
  "--source",
1893
- input4.workspacePath,
1913
+ input5.workspacePath,
1894
1914
  "--remote",
1895
1915
  "origin",
1896
1916
  "--push"
@@ -1902,7 +1922,7 @@ async function createWorkspaceRemoteViaGh(input4) {
1902
1922
  if (stderr) process.stderr.write(`${stderr}
1903
1923
  `);
1904
1924
  throw new Error(
1905
- `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} --${input4.visibility} --source=. --remote=origin --push`
1925
+ `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} --${input5.visibility} --source=. --remote=origin --push`
1906
1926
  );
1907
1927
  }
1908
1928
  const sshUrl = `git@github.com:${fullName}.git`;
@@ -2500,14 +2520,15 @@ async function promptProjectStatus() {
2500
2520
  });
2501
2521
  }
2502
2522
  async function runInitFromExistingRemote(opts, ownerEmail) {
2503
- const remoteUrl = opts.clientRepo ?? await input3({
2523
+ const initialRemoteUrl = opts.clientRepo ?? await input4({
2504
2524
  message: "URL git c\u1EE7a repo:",
2505
2525
  validate: (v) => v.length > 0 ? true : "URL b\u1EAFt bu\u1ED9c"
2506
2526
  });
2507
- await ensureGitHubReady(remoteUrl);
2527
+ const { resolvedRemoteUrl } = await ensureGitHubReady(initialRemoteUrl);
2528
+ const remoteUrl = resolvedRemoteUrl ?? initialRemoteUrl;
2508
2529
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
2509
2530
  const inferredName = inferWorkspaceName(remoteUrl);
2510
- const workspaceName = opts.workspaceName ?? await input3({ message: "T\xEAn workspace:", default: inferredName });
2531
+ const workspaceName = opts.workspaceName ?? await input4({ message: "T\xEAn workspace:", default: inferredName });
2511
2532
  const workspaceParent = resolve(opts.workspaceParent ?? ".");
2512
2533
  const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
2513
2534
  await scaffoldWorkspaceWithSrcSubmodule({
@@ -2530,7 +2551,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
2530
2551
  }
2531
2552
  async function runInitFromExistingFolder(opts, ownerEmail) {
2532
2553
  const folderPath = resolve(
2533
- opts.folderPath ?? await input3({
2554
+ opts.folderPath ?? await input4({
2534
2555
  message: "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3:",
2535
2556
  validate: (v) => v.length > 0 ? true : "Path b\u1EAFt bu\u1ED9c"
2536
2557
  })
@@ -2542,7 +2563,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
2542
2563
  const remoteUrl = await getOrCreateOriginRemote(folderPath, opts);
2543
2564
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
2544
2565
  const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;
2545
- const workspaceName = opts.workspaceName ?? await input3({ message: "T\xEAn workspace:", default: inferredName });
2566
+ const workspaceName = opts.workspaceName ?? await input4({ message: "T\xEAn workspace:", default: inferredName });
2546
2567
  const workspaceParent = resolve(opts.workspaceParent ?? ".");
2547
2568
  const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
2548
2569
  await scaffoldWorkspaceWithSrcSubmodule({
@@ -2566,7 +2587,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
2566
2587
  }
2567
2588
  async function runInitFromScratch(opts, ownerEmail) {
2568
2589
  await ensureGitHubReady();
2569
- const projectName = opts.workspaceName ?? await input3({
2590
+ const projectName = opts.workspaceName ?? await input4({
2570
2591
  message: "T\xEAn d\u1EF1 \xE1n:",
2571
2592
  validate: (v) => v.length > 0 ? true : "T\xEAn b\u1EAFt bu\u1ED9c"
2572
2593
  });
@@ -2651,7 +2672,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
2651
2672
  { name: "public", value: "public" }
2652
2673
  ]
2653
2674
  });
2654
- const repoName = await input3({
2675
+ const repoName = await input4({
2655
2676
  message: "T\xEAn repo:",
2656
2677
  default: basename(folderPath)
2657
2678
  });
@@ -2808,7 +2829,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
2808
2829
  if (action === "use-alt" && alternative) {
2809
2830
  return alternative;
2810
2831
  }
2811
- const newName = await input3({
2832
+ const newName = await input4({
2812
2833
  message: "T\xEAn workspace m\u1EDBi:",
2813
2834
  validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
2814
2835
  });
@@ -2818,7 +2839,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
2818
2839
  }
2819
2840
  }
2820
2841
  async function promptTeamOwner(currentUserEmail) {
2821
- return await input3({ message: "Team owner email:", default: currentUserEmail });
2842
+ return await input4({ message: "Team owner email:", default: currentUserEmail });
2822
2843
  }
2823
2844
  async function maybeCommitWorkspace(workspacePath, skipCommit) {
2824
2845
  if (skipCommit) {
@@ -3143,7 +3164,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
3143
3164
  }
3144
3165
 
3145
3166
  // src/commands/uninstall.ts
3146
- var CLI_VERSION = "1.2.6";
3167
+ var CLI_VERSION = "1.2.7";
3147
3168
  function registerUninstallCommand(program2) {
3148
3169
  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) => {
3149
3170
  try {
@@ -3225,7 +3246,7 @@ function printUninstallSuccessBox(backupPath) {
3225
3246
  }
3226
3247
 
3227
3248
  // src/index.ts
3228
- var CLI_VERSION2 = "1.2.6";
3249
+ var CLI_VERSION2 = "1.2.7";
3229
3250
  var program = new Command();
3230
3251
  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(
3231
3252
  "beforeAll",