@nalvietnam/avatar-cli 1.11.2 → 1.12.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
@@ -145,8 +145,8 @@ async function writeUserConfig(config) {
145
145
  }
146
146
  async function clearUserConfig() {
147
147
  if (await pathExists(USER_CONFIG_PATH)) {
148
- const { promises: fs14 } = await import("fs");
149
- await fs14.unlink(USER_CONFIG_PATH);
148
+ const { promises: fs15 } = await import("fs");
149
+ await fs15.unlink(USER_CONFIG_PATH);
150
150
  }
151
151
  }
152
152
  function isTokenExpired(config) {
@@ -223,7 +223,7 @@ function spinnerWithElapsed(prefix) {
223
223
  }
224
224
 
225
225
  // src/lib/check-claude-code-subscription-and-quota.ts
226
- var QUOTA_VERIFY_TIMEOUT_MS = 3e4;
226
+ var QUOTA_VERIFY_TIMEOUT_MS = 6e4;
227
227
  var QUOTA_VERIFY_PROMPT = "ok";
228
228
  function checkClaudeCodeSubscriptionAuth() {
229
229
  const result = spawnSync("claude", ["auth", "status"], { encoding: "utf8" });
@@ -283,7 +283,7 @@ function getQuotaErrorHint(reason) {
283
283
  case "rate_limit":
284
284
  return "B\u1ECB rate limit t\u1EA1m th\u1EDDi. Ch\u1EDD v\xE0i ph\xFAt r\u1ED3i ch\u1EA1y `avatar ai setup`.";
285
285
  case "timeout":
286
- return "Network ch\u1EADm ho\u1EB7c Anthropic API down. Check m\u1EA1ng r\u1ED3i ch\u1EA1y l\u1EA1i.";
286
+ return "Timeout 60s: (1) m\u1EA1ng VN ch\u1EADm \u2014 th\u1EED VPN, (2) Anthropic API spike \u2014 retry v\xE0i ph\xFAt, (3) token v\u1EABn auth nh\u01B0ng quota h\u1EBFt \xE2m th\u1EA7m \u2014 check t\u1EA1i claude.ai/settings/usage.";
287
287
  default:
288
288
  return "L\u1ED7i ch\u01B0a bi\u1EBFt. Xem stderr \u1EDF tr\xEAn + ch\u1EA1y `claude --print ok` tay \u0111\u1EC3 debug.";
289
289
  }
@@ -294,8 +294,13 @@ function verifyClaudeCodeQuota() {
294
294
  timeout: QUOTA_VERIFY_TIMEOUT_MS,
295
295
  stdio: ["ignore", "pipe", "pipe"]
296
296
  });
297
- if (result.signal === "SIGTERM") {
298
- return { ok: false, reason: "timeout", detail: "claude --print > 30s" };
297
+ const isTimeout = result.signal === "SIGTERM" || result.status === 143 || result.error?.code === "ETIMEDOUT";
298
+ if (isTimeout) {
299
+ return {
300
+ ok: false,
301
+ reason: "timeout",
302
+ detail: `claude --print > ${QUOTA_VERIFY_TIMEOUT_MS / 1e3}s (m\u1EA1ng ch\u1EADm / API rate limit / token revoked kh\xF4ng return error)`
303
+ };
299
304
  }
300
305
  const stderr = result.stderr || "";
301
306
  const stdout = result.stdout || "";
@@ -2329,7 +2334,7 @@ function registerGitnexusCommand(program2) {
2329
2334
  }
2330
2335
 
2331
2336
  // src/commands/init.ts
2332
- import { select as select12 } from "@inquirer/prompts";
2337
+ import { select as select13 } from "@inquirer/prompts";
2333
2338
 
2334
2339
  // src/lib/avatar-ascii-banner.ts
2335
2340
  import chalk2 from "chalk";
@@ -2393,11 +2398,178 @@ ${renderAvatarBanner(opts)}
2393
2398
  }
2394
2399
 
2395
2400
  // src/lib/handle-remote-access-failure-with-account-switch.ts
2401
+ import { spawnSync as spawnSync16 } from "child_process";
2402
+ import { input as input4, select as select6 } from "@inquirer/prompts";
2403
+
2404
+ // src/lib/reset-folder-git-and-create-new-remote-under-current-user.ts
2405
+ import { spawnSync as spawnSync14 } from "child_process";
2406
+ import { promises as fs9 } from "fs";
2407
+ import { basename, join as join16 } from "path";
2408
+ import { confirm as confirm4, select as select5 } from "@inquirer/prompts";
2409
+
2410
+ // src/lib/execute-gh-repo-create.ts
2411
+ import { spawnSync as spawnSync12 } from "child_process";
2412
+ var RepoAlreadyExistsError = class extends Error {
2413
+ constructor(fullName) {
2414
+ super(`Repo "${fullName}" \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. \u0110\u1ED5i t\xEAn ho\u1EB7c x\xF3a repo c\u0169.`);
2415
+ this.name = "RepoAlreadyExistsError";
2416
+ }
2417
+ };
2418
+ function executeGhRepoCreate(input8) {
2419
+ const fullName = `${input8.org}/${input8.name}`;
2420
+ const args = [
2421
+ "repo",
2422
+ "create",
2423
+ fullName,
2424
+ `--${input8.visibility}`,
2425
+ "--source",
2426
+ input8.folder,
2427
+ "--remote",
2428
+ "origin",
2429
+ "--push"
2430
+ ];
2431
+ const r = spawnSync12("gh", args, { stdio: "inherit" });
2432
+ if (r.status !== 0) {
2433
+ if (r.status === 1) {
2434
+ throw new RepoAlreadyExistsError(fullName);
2435
+ }
2436
+ throw new Error(`gh repo create th\u1EA5t b\u1EA1i (exit ${r.status})`);
2437
+ }
2438
+ return {
2439
+ sshUrl: `git@github.com:${fullName}.git`,
2440
+ httpsUrl: `https://github.com/${fullName}.git`
2441
+ };
2442
+ }
2443
+
2444
+ // src/lib/resolve-github-username-default.ts
2396
2445
  import { spawnSync as spawnSync13 } from "child_process";
2397
- import { input as input4, select as select5 } from "@inquirer/prompts";
2446
+ function resolveGithubUsernameDefault() {
2447
+ const r = spawnSync13("gh", ["api", "user", "--jq", ".login"], {
2448
+ encoding: "utf8",
2449
+ stdio: ["ignore", "pipe", "pipe"]
2450
+ });
2451
+ if (r.status !== 0) {
2452
+ throw new Error(`Kh\xF4ng l\u1EA5y \u0111\u01B0\u1EE3c GitHub username: ${r.stderr?.trim()}`);
2453
+ }
2454
+ return r.stdout.trim();
2455
+ }
2456
+
2457
+ // src/lib/validate-repo-name-and-visibility.ts
2458
+ var REPO_NAME_REGEX = /^[a-zA-Z0-9._-]{1,100}$/;
2459
+ var InvalidRepoNameError = class extends Error {
2460
+ constructor(name) {
2461
+ super(
2462
+ `T\xEAn repo "${name}" kh\xF4ng h\u1EE3p l\u1EC7. Ch\u1EC9 d\xF9ng ch\u1EEF/s\u1ED1/d\u1EA5u ch\u1EA5m/g\u1EA1ch/underscore, d\xE0i 1-100 k\xFD t\u1EF1.`
2463
+ );
2464
+ this.name = "InvalidRepoNameError";
2465
+ }
2466
+ };
2467
+ function validateRepoName(name) {
2468
+ if (!REPO_NAME_REGEX.test(name)) {
2469
+ throw new InvalidRepoNameError(name);
2470
+ }
2471
+ }
2472
+ function validateRepoVisibility(v) {
2473
+ if (v !== "private" && v !== "public") {
2474
+ throw new Error(`Visibility ph\u1EA3i l\xE0 "private" ho\u1EB7c "public", nh\u1EADn: "${v}"`);
2475
+ }
2476
+ }
2477
+
2478
+ // src/lib/create-github-remote-from-folder.ts
2479
+ function createGithubRemoteFromFolder(input8) {
2480
+ validateRepoName(input8.name);
2481
+ validateRepoVisibility(input8.visibility);
2482
+ const org = input8.org ?? resolveGithubUsernameDefault();
2483
+ log.info(`T\u1EA1o GitHub repo ${org}/${input8.name} (${input8.visibility})...`);
2484
+ const urls = executeGhRepoCreate({
2485
+ folder: input8.folder,
2486
+ org,
2487
+ name: input8.name,
2488
+ visibility: input8.visibility
2489
+ });
2490
+ log.success(`\u0110\xE3 t\u1EA1o: ${urls.sshUrl}`);
2491
+ return urls;
2492
+ }
2493
+
2494
+ // src/lib/reset-folder-git-and-create-new-remote-under-current-user.ts
2495
+ function backupTimestamp() {
2496
+ const d = /* @__PURE__ */ new Date();
2497
+ return `${d.getFullYear().toString().slice(-2)}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}-${String(d.getHours()).padStart(2, "0")}${String(d.getMinutes()).padStart(2, "0")}`;
2498
+ }
2499
+ async function backupExistingDotGit(folderPath) {
2500
+ const gitDir = join16(folderPath, ".git");
2501
+ if (!await pathExists(gitDir)) {
2502
+ throw new Error(`.git kh\xF4ng t\u1ED3n t\u1EA1i \u1EDF ${folderPath} \u2014 kh\xF4ng c\u1EA7n reset.`);
2503
+ }
2504
+ const backupName = `.git.backup-${backupTimestamp()}`;
2505
+ const backupPath = join16(folderPath, backupName);
2506
+ await fs9.rename(gitDir, backupPath);
2507
+ log.success(`Backup .git \u2192 ${backupName}`);
2508
+ return backupPath;
2509
+ }
2510
+ function reinitGitInFolder(folderPath) {
2511
+ const r1 = spawnSync14("git", ["-C", folderPath, "init", "-b", "main"], {
2512
+ encoding: "utf8",
2513
+ stdio: ["ignore", "pipe", "pipe"]
2514
+ });
2515
+ if (r1.status !== 0) {
2516
+ throw new Error(`git init th\u1EA5t b\u1EA1i: ${r1.stderr || r1.stdout}`);
2517
+ }
2518
+ log.success("Git init m\u1EDBi (branch main)");
2519
+ spawnSync14("git", ["-C", folderPath, "add", "-A"], {
2520
+ encoding: "utf8",
2521
+ stdio: ["ignore", "pipe", "pipe"]
2522
+ });
2523
+ const r3 = spawnSync14(
2524
+ "git",
2525
+ ["-C", folderPath, "commit", "-m", "chore: initial commit from existing folder"],
2526
+ { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
2527
+ );
2528
+ if (r3.status !== 0) {
2529
+ log.warn(`git commit th\u1EA5t b\u1EA1i (folder c\xF3 th\u1EC3 r\u1ED7ng): ${(r3.stderr || "").slice(0, 200)}`);
2530
+ } else {
2531
+ log.success("Initial commit");
2532
+ }
2533
+ }
2534
+ async function resetFolderGitAndCreateNewRemoteUnderCurrentUser(opts) {
2535
+ const folderName = basename(opts.folderPath);
2536
+ const repoName = opts.repoName ?? folderName;
2537
+ if (!opts.autoYes) {
2538
+ const confirmed = await confirm4({
2539
+ message: `Folder '${folderName}' s\u1EBD \u0111\u01B0\u1EE3c reset:
2540
+ 1. Backup .git \u2192 .git.backup-{timestamp}
2541
+ 2. Git init m\u1EDBi (branch main, initial commit)
2542
+ 3. T\u1EA1o GitHub repo m\u1EDBi '${repoName}' d\u01B0\u1EDBi account c\u1EE7a b\u1EA1n
2543
+ Ti\u1EBFp t\u1EE5c?`,
2544
+ default: true
2545
+ });
2546
+ if (!confirmed) {
2547
+ throw new Error("User abort reset folder.");
2548
+ }
2549
+ }
2550
+ const visibility = opts.visibility ?? (opts.autoYes ? "private" : await select5({
2551
+ message: "Visibility cho repo m\u1EDBi?",
2552
+ choices: [
2553
+ { name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
2554
+ { name: "public", value: "public" }
2555
+ ]
2556
+ }));
2557
+ const backupPath = await backupExistingDotGit(opts.folderPath);
2558
+ reinitGitInFolder(opts.folderPath);
2559
+ const urls = createGithubRemoteFromFolder({
2560
+ folder: opts.folderPath,
2561
+ name: repoName,
2562
+ visibility,
2563
+ org: opts.org
2564
+ });
2565
+ return {
2566
+ newRemoteUrl: urls.httpsUrl,
2567
+ backupPath
2568
+ };
2569
+ }
2398
2570
 
2399
2571
  // src/lib/verify-git-remote-accessible.ts
2400
- import { spawnSync as spawnSync12 } from "child_process";
2572
+ import { spawnSync as spawnSync15 } from "child_process";
2401
2573
  var TIMEOUT_MS = 5e3;
2402
2574
  function classifyRemoteError(stderr) {
2403
2575
  const text = stderr.toLowerCase();
@@ -2413,7 +2585,7 @@ function classifyRemoteError(stderr) {
2413
2585
  return "unknown";
2414
2586
  }
2415
2587
  function tryVerifyGitRemoteAccessible(url) {
2416
- const r = spawnSync12("git", ["ls-remote", "--exit-code", url, "HEAD"], {
2588
+ const r = spawnSync15("git", ["ls-remote", "--exit-code", url, "HEAD"], {
2417
2589
  encoding: "utf8",
2418
2590
  timeout: TIMEOUT_MS,
2419
2591
  stdio: ["ignore", "pipe", "pipe"]
@@ -2435,7 +2607,7 @@ var RemoteAccessAbortedError = class extends Error {
2435
2607
  }
2436
2608
  };
2437
2609
  function getCurrentGhUser() {
2438
- const r = spawnSync13("gh", ["api", "user", "--jq", ".login"], {
2610
+ const r = spawnSync16("gh", ["api", "user", "--jq", ".login"], {
2439
2611
  encoding: "utf8",
2440
2612
  stdio: ["ignore", "pipe", "pipe"]
2441
2613
  });
@@ -2444,7 +2616,7 @@ function getCurrentGhUser() {
2444
2616
  }
2445
2617
  function triggerGhAuthLoginInteractive() {
2446
2618
  log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
2447
- const r = spawnSync13("gh", ["auth", "login", "--web"], { stdio: "inherit" });
2619
+ const r = spawnSync16("gh", ["auth", "login", "--web"], { stdio: "inherit" });
2448
2620
  if (r.status !== 0) {
2449
2621
  log.warn(`gh auth login exit ${r.status}. B\u1EA1n c\xF3 th\u1EC3 ch\u1EA1y tay r\u1ED3i quay l\u1EA1i retry.`);
2450
2622
  }
@@ -2478,32 +2650,54 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
2478
2650
  log.dim(` L\xFD do: ${reason}${detail ? ` \u2014 ${detail.slice(0, 150)}` : ""}`);
2479
2651
  log.info(getReasonHint(reason, currentUrl, ghUser));
2480
2652
  if (ghUser) log.dim(` gh CLI hi\u1EC7n \u0111ang login: ${ghUser}`);
2481
- const action = await select5({
2482
- message: "C\xE1ch x\u1EED l\xFD?",
2483
- choices: [
2484
- {
2485
- name: "Nh\u1EADp l\u1EA1i URL \u0111\xFAng (recommended khi URL sai ch\xEDnh t\u1EA3)",
2486
- value: "re-input-url"
2487
- },
2488
- {
2489
- name: "Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",
2490
- value: "switch"
2491
- },
2492
- {
2493
- name: "T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa permission) \u2014 retry verify",
2494
- value: "retry"
2495
- },
2496
- {
2497
- name: "T\u1EA1m ng\u01B0ng init \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau",
2498
- value: "abort"
2499
- }
2500
- ]
2653
+ const choices = [
2654
+ {
2655
+ name: "Nh\u1EADp l\u1EA1i URL \u0111\xFAng (recommended khi URL sai ch\xEDnh t\u1EA3)",
2656
+ value: "re-input-url"
2657
+ },
2658
+ {
2659
+ name: "Switch GitHub account (gh auth login \u2014 m\u1EDF browser)",
2660
+ value: "switch"
2661
+ },
2662
+ {
2663
+ name: "T\xF4i v\u1EEBa fix (accept invite / s\u1EEDa permission) \u2014 retry verify",
2664
+ value: "retry"
2665
+ }
2666
+ ];
2667
+ if (args.folderPath) {
2668
+ choices.push({
2669
+ name: "Reset folder & t\u1EA1o repo M\u1EDAI d\u01B0\u1EDBi account c\u1EE7a t\xF4i (backup .git c\u0169)",
2670
+ value: "reset-folder"
2671
+ });
2672
+ }
2673
+ choices.push({
2674
+ name: "T\u1EA1m ng\u01B0ng init \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau",
2675
+ value: "abort"
2501
2676
  });
2677
+ const action = await select6({ message: "C\xE1ch x\u1EED l\xFD?", choices });
2502
2678
  if (action === "abort") {
2503
2679
  throw new RemoteAccessAbortedError(
2504
2680
  `User ch\u1ECDn t\u1EA1m ng\u01B0ng. Fix access ${currentUrl} r\u1ED3i ch\u1EA1y l\u1EA1i 'avatar init'.`
2505
2681
  );
2506
2682
  }
2683
+ if (action === "reset-folder") {
2684
+ if (!args.folderPath) {
2685
+ log.warn("Reset folder c\u1EA7n folderPath \u2014 internal error.");
2686
+ continue;
2687
+ }
2688
+ try {
2689
+ const reset = await resetFolderGitAndCreateNewRemoteUnderCurrentUser({
2690
+ folderPath: args.folderPath,
2691
+ visibility: args.defaultVisibility
2692
+ });
2693
+ log.success(`Folder \u0111\xE3 reset. Backup t\u1EA1i: ${reset.backupPath}`);
2694
+ log.success(`Remote m\u1EDBi: ${reset.newRemoteUrl}`);
2695
+ return { resolvedUrl: reset.newRemoteUrl };
2696
+ } catch (err) {
2697
+ log.warn(`Reset folder th\u1EA5t b\u1EA1i: ${err.message}`);
2698
+ continue;
2699
+ }
2700
+ }
2507
2701
  if (action === "re-input-url") {
2508
2702
  const newUrl = await input4({
2509
2703
  message: "URL git remote (https://github.com/owner/repo.git ho\u1EB7c git@github.com:owner/repo.git):",
@@ -2528,14 +2722,14 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
2528
2722
 
2529
2723
  // src/lib/safe-bootstrap-for-dirty-folder.ts
2530
2724
  import { readdirSync } from "fs";
2531
- import { select as select6 } from "@inquirer/prompts";
2725
+ import { select as select7 } from "@inquirer/prompts";
2532
2726
  import { simpleGit as simpleGit3 } from "simple-git";
2533
2727
 
2534
2728
  // src/lib/check-folder-has-git.ts
2535
2729
  import { existsSync as existsSync6, statSync } from "fs";
2536
- import { join as join16 } from "path";
2730
+ import { join as join17 } from "path";
2537
2731
  function checkFolderHasGit(folderPath) {
2538
- const gitPath = join16(folderPath, ".git");
2732
+ const gitPath = join17(folderPath, ".git");
2539
2733
  if (!existsSync6(gitPath)) return false;
2540
2734
  const stat = statSync(gitPath);
2541
2735
  return stat.isDirectory() || stat.isFile();
@@ -2567,7 +2761,7 @@ async function createInitialGitCommit(folderPath) {
2567
2761
 
2568
2762
  // src/lib/detect-folder-tech-stack.ts
2569
2763
  import { existsSync as existsSync7 } from "fs";
2570
- import { join as join17 } from "path";
2764
+ import { join as join18 } from "path";
2571
2765
  var SIGNATURES = {
2572
2766
  node: ["package.json"],
2573
2767
  python: ["pyproject.toml", "requirements.txt", "setup.py", "Pipfile"],
@@ -2579,7 +2773,7 @@ var SIGNATURES = {
2579
2773
  function detectFolderTechStack(folderPath) {
2580
2774
  const matched = [];
2581
2775
  for (const [stack, files] of Object.entries(SIGNATURES)) {
2582
- if (files.some((f) => existsSync7(join17(folderPath, f)))) {
2776
+ if (files.some((f) => existsSync7(join18(folderPath, f)))) {
2583
2777
  matched.push(stack);
2584
2778
  }
2585
2779
  }
@@ -2588,25 +2782,25 @@ function detectFolderTechStack(folderPath) {
2588
2782
 
2589
2783
  // src/lib/gitignore-template-loader.ts
2590
2784
  import { readFileSync as readFileSync3 } from "fs";
2591
- import { dirname as dirname4, join as join18 } from "path";
2785
+ import { dirname as dirname4, join as join19 } from "path";
2592
2786
  import { fileURLToPath as fileURLToPath2 } from "url";
2593
2787
  var __dirname = dirname4(fileURLToPath2(import.meta.url));
2594
2788
  var CANDIDATE_DIRS = [
2595
2789
  // Bundled production: dist/index.js → __dirname = .../dist/, sibling dist/templates
2596
- join18(__dirname, "templates", "gitignore"),
2790
+ join19(__dirname, "templates", "gitignore"),
2597
2791
  // Legacy bundled: nếu file là dist/lib/*.js (sub-bundle), templates ở dist/templates
2598
- join18(__dirname, "..", "templates", "gitignore"),
2792
+ join19(__dirname, "..", "templates", "gitignore"),
2599
2793
  // Dev mode (vitest/tsx run src/ trực tiếp): __dirname = src/lib/
2600
- join18(__dirname, "..", "..", "src", "templates", "gitignore"),
2794
+ join19(__dirname, "..", "..", "src", "templates", "gitignore"),
2601
2795
  // npm-installed alt: __dirname = .../dist/ → package_root/src/templates
2602
- join18(__dirname, "..", "src", "templates", "gitignore")
2796
+ join19(__dirname, "..", "src", "templates", "gitignore")
2603
2797
  ];
2604
2798
  var AVATAR_MARKER_START = "# === avatar ===";
2605
2799
  var AVATAR_MARKER_END = "# === /avatar ===";
2606
2800
  function readTemplate(stack) {
2607
2801
  for (const dir of CANDIDATE_DIRS) {
2608
2802
  try {
2609
- return readFileSync3(join18(dir, `${stack}.txt`), "utf8");
2803
+ return readFileSync3(join19(dir, `${stack}.txt`), "utf8");
2610
2804
  } catch {
2611
2805
  }
2612
2806
  }
@@ -2621,9 +2815,9 @@ ${readTemplate(s).trim()}`);
2621
2815
 
2622
2816
  // src/lib/write-or-merge-gitignore.ts
2623
2817
  import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync } from "fs";
2624
- import { join as join19 } from "path";
2818
+ import { join as join20 } from "path";
2625
2819
  function writeOrMergeGitignore(folderPath, avatarBlock) {
2626
- const path = join19(folderPath, ".gitignore");
2820
+ const path = join20(folderPath, ".gitignore");
2627
2821
  if (!existsSync8(path)) {
2628
2822
  writeFileSync(path, avatarBlock, "utf8");
2629
2823
  return;
@@ -2665,7 +2859,7 @@ async function promptBootstrapStrategy(state, opts) {
2665
2859
  if (opts.presetStrategy) return opts.presetStrategy;
2666
2860
  if (opts.autoYes) return "stash";
2667
2861
  if (state === "empty" || state === "clean") return "commit-all";
2668
- return await select6({
2862
+ return await select7({
2669
2863
  message: state === "dirty" ? "Folder c\xF3 changes ch\u01B0a commit. C\xE1ch x\u1EED l\xFD:" : "Folder c\xF3 file ch\u01B0a version. C\xE1ch x\u1EED l\xFD:",
2670
2864
  choices: [
2671
2865
  {
@@ -2799,22 +2993,22 @@ async function safeBootstrapGitInFolder(folderPath, opts = {}) {
2799
2993
  }
2800
2994
 
2801
2995
  // src/lib/team-pack-submodule-manager.ts
2802
- import { join as join20 } from "path";
2996
+ import { join as join21 } from "path";
2803
2997
 
2804
2998
  // src/lib/check-team-pack-access-with-retry-loop.ts
2805
- import { spawnSync as spawnSync14 } from "child_process";
2806
- import { confirm as confirm4, select as select7 } from "@inquirer/prompts";
2999
+ import { spawnSync as spawnSync17 } from "child_process";
3000
+ import { confirm as confirm5, select as select8 } from "@inquirer/prompts";
2807
3001
  import boxen3 from "boxen";
2808
3002
  function parseRepoSlugFromGitUrl(url) {
2809
3003
  const httpsMatch = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
2810
3004
  return httpsMatch?.[1] ?? null;
2811
3005
  }
2812
3006
  function checkRepoAccess(repoSlug) {
2813
- const r = spawnSync14("gh", ["api", `repos/${repoSlug}`], { stdio: "ignore" });
3007
+ const r = spawnSync17("gh", ["api", `repos/${repoSlug}`], { stdio: "ignore" });
2814
3008
  return r.status === 0;
2815
3009
  }
2816
3010
  function getCurrentGhUser2() {
2817
- const r = spawnSync14("gh", ["api", "user", "--jq", ".login"], {
3011
+ const r = spawnSync17("gh", ["api", "user", "--jq", ".login"], {
2818
3012
  encoding: "utf8",
2819
3013
  stdio: ["ignore", "pipe", "pipe"]
2820
3014
  });
@@ -2823,13 +3017,13 @@ function getCurrentGhUser2() {
2823
3017
  }
2824
3018
  function triggerGhAuthLoginInteractive2() {
2825
3019
  log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
2826
- const r = spawnSync14("gh", ["auth", "login", "--web"], { stdio: "inherit" });
3020
+ const r = spawnSync17("gh", ["auth", "login", "--web"], { stdio: "inherit" });
2827
3021
  if (r.status !== 0) {
2828
3022
  log.warn(`gh auth login exit ${r.status}. C\xF3 th\u1EC3 ch\u1EA1y tay r\u1ED3i quay l\u1EA1i retry.`);
2829
3023
  }
2830
3024
  }
2831
3025
  async function copyInfoToClipboardWithConsent(info) {
2832
- const ok = await confirm4({
3026
+ const ok = await confirm5({
2833
3027
  message: "Copy th\xF4ng tin (GitHub username + email) v\xE0o clipboard \u0111\u1EC3 d\xE1n v\xE0o Slack/email?",
2834
3028
  default: true
2835
3029
  });
@@ -2881,7 +3075,7 @@ async function ensureTeamPackAccessWithRetry(args) {
2881
3075
  while (true) {
2882
3076
  const ghUser = getCurrentGhUser2();
2883
3077
  const ghUserDisplay = ghUser ?? "(ch\u01B0a gh auth)";
2884
- const action = await select7({
3078
+ const action = await select8({
2885
3079
  message: "C\xE1ch x\u1EED l\xFD?",
2886
3080
  choices: [
2887
3081
  {
@@ -2995,7 +3189,7 @@ async function addTeamPackSubmodule(projectRoot, tag, ssoEmail, latest = false)
2995
3189
  await checkoutBranchHeadInSubmodule(TEAM_PACK_RELATIVE_PATH, DEFAULT_PACK_BRANCH, projectRoot);
2996
3190
  return { pinnedTag: `${DEFAULT_PACK_BRANCH} (HEAD)` };
2997
3191
  }
2998
- const submoduleDir = join20(projectRoot, TEAM_PACK_RELATIVE_PATH);
3192
+ const submoduleDir = join21(projectRoot, TEAM_PACK_RELATIVE_PATH);
2999
3193
  const allTags = await listTags(submoduleDir);
3000
3194
  const target = pickLatestStableSemVerTag(allTags);
3001
3195
  if (target) {
@@ -3004,7 +3198,7 @@ async function addTeamPackSubmodule(projectRoot, tag, ssoEmail, latest = false)
3004
3198
  return { pinnedTag: target };
3005
3199
  }
3006
3200
  async function readPinnedPackVersion(projectRoot) {
3007
- const submoduleRoot = join20(projectRoot, TEAM_PACK_RELATIVE_PATH);
3201
+ const submoduleRoot = join21(projectRoot, TEAM_PACK_RELATIVE_PATH);
3008
3202
  const tag = await tagAtHead(submoduleRoot);
3009
3203
  if (tag) return tag;
3010
3204
  const sha = await currentCommitSha(submoduleRoot);
@@ -3012,97 +3206,13 @@ async function readPinnedPackVersion(projectRoot) {
3012
3206
  }
3013
3207
 
3014
3208
  // src/commands/init-flow-handlers-for-each-project-status.ts
3015
- import { basename as basename2, join as join26, resolve as resolve2 } from "path";
3016
- import { input as input7, select as select11 } from "@inquirer/prompts";
3017
-
3018
- // src/lib/execute-gh-repo-create.ts
3019
- import { spawnSync as spawnSync15 } from "child_process";
3020
- var RepoAlreadyExistsError = class extends Error {
3021
- constructor(fullName) {
3022
- super(`Repo "${fullName}" \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. \u0110\u1ED5i t\xEAn ho\u1EB7c x\xF3a repo c\u0169.`);
3023
- this.name = "RepoAlreadyExistsError";
3024
- }
3025
- };
3026
- function executeGhRepoCreate(input8) {
3027
- const fullName = `${input8.org}/${input8.name}`;
3028
- const args = [
3029
- "repo",
3030
- "create",
3031
- fullName,
3032
- `--${input8.visibility}`,
3033
- "--source",
3034
- input8.folder,
3035
- "--remote",
3036
- "origin",
3037
- "--push"
3038
- ];
3039
- const r = spawnSync15("gh", args, { stdio: "inherit" });
3040
- if (r.status !== 0) {
3041
- if (r.status === 1) {
3042
- throw new RepoAlreadyExistsError(fullName);
3043
- }
3044
- throw new Error(`gh repo create th\u1EA5t b\u1EA1i (exit ${r.status})`);
3045
- }
3046
- return {
3047
- sshUrl: `git@github.com:${fullName}.git`,
3048
- httpsUrl: `https://github.com/${fullName}.git`
3049
- };
3050
- }
3051
-
3052
- // src/lib/resolve-github-username-default.ts
3053
- import { spawnSync as spawnSync16 } from "child_process";
3054
- function resolveGithubUsernameDefault() {
3055
- const r = spawnSync16("gh", ["api", "user", "--jq", ".login"], {
3056
- encoding: "utf8",
3057
- stdio: ["ignore", "pipe", "pipe"]
3058
- });
3059
- if (r.status !== 0) {
3060
- throw new Error(`Kh\xF4ng l\u1EA5y \u0111\u01B0\u1EE3c GitHub username: ${r.stderr?.trim()}`);
3061
- }
3062
- return r.stdout.trim();
3063
- }
3064
-
3065
- // src/lib/validate-repo-name-and-visibility.ts
3066
- var REPO_NAME_REGEX = /^[a-zA-Z0-9._-]{1,100}$/;
3067
- var InvalidRepoNameError = class extends Error {
3068
- constructor(name) {
3069
- super(
3070
- `T\xEAn repo "${name}" kh\xF4ng h\u1EE3p l\u1EC7. Ch\u1EC9 d\xF9ng ch\u1EEF/s\u1ED1/d\u1EA5u ch\u1EA5m/g\u1EA1ch/underscore, d\xE0i 1-100 k\xFD t\u1EF1.`
3071
- );
3072
- this.name = "InvalidRepoNameError";
3073
- }
3074
- };
3075
- function validateRepoName(name) {
3076
- if (!REPO_NAME_REGEX.test(name)) {
3077
- throw new InvalidRepoNameError(name);
3078
- }
3079
- }
3080
- function validateRepoVisibility(v) {
3081
- if (v !== "private" && v !== "public") {
3082
- throw new Error(`Visibility ph\u1EA3i l\xE0 "private" ho\u1EB7c "public", nh\u1EADn: "${v}"`);
3083
- }
3084
- }
3085
-
3086
- // src/lib/create-github-remote-from-folder.ts
3087
- function createGithubRemoteFromFolder(input8) {
3088
- validateRepoName(input8.name);
3089
- validateRepoVisibility(input8.visibility);
3090
- const org = input8.org ?? resolveGithubUsernameDefault();
3091
- log.info(`T\u1EA1o GitHub repo ${org}/${input8.name} (${input8.visibility})...`);
3092
- const urls = executeGhRepoCreate({
3093
- folder: input8.folder,
3094
- org,
3095
- name: input8.name,
3096
- visibility: input8.visibility
3097
- });
3098
- log.success(`\u0110\xE3 t\u1EA1o: ${urls.sshUrl}`);
3099
- return urls;
3100
- }
3209
+ import { basename as basename3, join as join27, resolve as resolve2 } from "path";
3210
+ import { input as input7, select as select12 } from "@inquirer/prompts";
3101
3211
 
3102
3212
  // src/lib/check-gh-cli-auth-status.ts
3103
- import { spawnSync as spawnSync17 } from "child_process";
3213
+ import { spawnSync as spawnSync18 } from "child_process";
3104
3214
  function checkGhCliAuthStatus() {
3105
- const r = spawnSync17("gh", ["auth", "status"], { stdio: "ignore" });
3215
+ const r = spawnSync18("gh", ["auth", "status"], { stdio: "ignore" });
3106
3216
  if (r.error && r.error.code === "ENOENT") {
3107
3217
  return "not-installed";
3108
3218
  }
@@ -3110,12 +3220,12 @@ function checkGhCliAuthStatus() {
3110
3220
  }
3111
3221
 
3112
3222
  // src/lib/detect-package-manager.ts
3113
- import { spawnSync as spawnSync18 } from "child_process";
3223
+ import { spawnSync as spawnSync19 } from "child_process";
3114
3224
  function hasBinary(name) {
3115
3225
  const platform2 = detectHostPlatform();
3116
3226
  const probe = platform2 === "win32" ? "where" : "command";
3117
3227
  const args = platform2 === "win32" ? [name] : ["-v", name];
3118
- const r = spawnSync18(probe, args, {
3228
+ const r = spawnSync19(probe, args, {
3119
3229
  shell: platform2 !== "win32",
3120
3230
  stdio: "ignore"
3121
3231
  });
@@ -3131,7 +3241,7 @@ function detectPackageManager() {
3131
3241
  }
3132
3242
 
3133
3243
  // src/lib/install-gh-cli-via-package-manager.ts
3134
- import { spawnSync as spawnSync19 } from "child_process";
3244
+ import { spawnSync as spawnSync20 } from "child_process";
3135
3245
  var INSTALL_COMMANDS = {
3136
3246
  brew: { cmd: "brew", args: ["install", "gh"] },
3137
3247
  apt: { cmd: "sudo", args: ["apt-get", "install", "-y", "gh"] },
@@ -3142,7 +3252,7 @@ var INSTALL_COMMANDS = {
3142
3252
  function installGhCliViaPackageManager(pm) {
3143
3253
  const spec = INSTALL_COMMANDS[pm];
3144
3254
  log.info(`\u0110ang c\xE0i gh CLI qua ${pm}...`);
3145
- const r = spawnSync19(spec.cmd, spec.args, { stdio: "inherit" });
3255
+ const r = spawnSync20(spec.cmd, spec.args, { stdio: "inherit" });
3146
3256
  if (r.status !== 0) {
3147
3257
  throw new Error(`C\xE0i gh CLI th\u1EA5t b\u1EA1i qua ${pm} (exit ${r.status}). C\xE0i tay r\u1ED3i ch\u1EA1y l\u1EA1i.`);
3148
3258
  }
@@ -3150,9 +3260,9 @@ function installGhCliViaPackageManager(pm) {
3150
3260
  }
3151
3261
 
3152
3262
  // src/lib/setup-git-credential-via-gh.ts
3153
- import { spawnSync as spawnSync20 } from "child_process";
3263
+ import { spawnSync as spawnSync21 } from "child_process";
3154
3264
  function setupGitCredentialViaGh() {
3155
- const r = spawnSync20("gh", ["auth", "setup-git"], { stdio: "ignore" });
3265
+ const r = spawnSync21("gh", ["auth", "setup-git"], { stdio: "ignore" });
3156
3266
  if (r.status !== 0) {
3157
3267
  log.warn("gh auth setup-git fail (non-fatal). N\u1EBFu git clone l\u1ED7i 128 \u2192 ch\u1EA1y th\u1EE7 c\xF4ng.");
3158
3268
  return;
@@ -3161,10 +3271,10 @@ function setupGitCredentialViaGh() {
3161
3271
  }
3162
3272
 
3163
3273
  // src/lib/trigger-gh-cli-auth-login.ts
3164
- import { spawnSync as spawnSync21 } from "child_process";
3274
+ import { spawnSync as spawnSync22 } from "child_process";
3165
3275
  function triggerGhCliAuthLogin() {
3166
3276
  log.info("Kh\u1EDFi \u0111\u1ED9ng \u0111\u0103ng nh\u1EADp GitHub qua gh CLI (browser s\u1EBD m\u1EDF)...");
3167
- const r = spawnSync21(
3277
+ const r = spawnSync22(
3168
3278
  "gh",
3169
3279
  ["auth", "login", "--hostname", "github.com", "--web", "--git-protocol", "ssh"],
3170
3280
  { stdio: "inherit" }
@@ -3253,28 +3363,28 @@ async function ensureGitHubReady(remoteUrl) {
3253
3363
  }
3254
3364
 
3255
3365
  // src/lib/add-team-pack-submodule-with-retry-on-network-fail.ts
3256
- import { spawnSync as spawnSync22 } from "child_process";
3257
- import { select as select8 } from "@inquirer/prompts";
3366
+ import { spawnSync as spawnSync23 } from "child_process";
3367
+ import { select as select9 } from "@inquirer/prompts";
3258
3368
  function isSshPermissionError(message) {
3259
3369
  const text = message.toLowerCase();
3260
3370
  return text.includes("permission denied (publickey)") || text.includes("publickey)") || text.includes("ssh: could not resolve") || text.includes("host key verification failed");
3261
3371
  }
3262
3372
  function triggerGhAuthLoginInteractive3() {
3263
3373
  log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
3264
- const r = spawnSync22("gh", ["auth", "login", "--web"], { stdio: "inherit" });
3374
+ const r = spawnSync23("gh", ["auth", "login", "--web"], { stdio: "inherit" });
3265
3375
  if (r.status !== 0) {
3266
3376
  log.warn(`gh auth login exit ${r.status}. C\xF3 th\u1EC3 ch\u1EA1y tay r\u1ED3i quay l\u1EA1i retry.`);
3267
3377
  }
3268
3378
  }
3269
3379
  function openGithubSshKeysPage() {
3270
3380
  log.info("M\u1EDF trang GitHub Settings \u2192 SSH Keys...");
3271
- const r = spawnSync22("open", ["https://github.com/settings/keys"], { stdio: "ignore" });
3381
+ const r = spawnSync23("open", ["https://github.com/settings/keys"], { stdio: "ignore" });
3272
3382
  if (r.status !== 0) {
3273
3383
  log.info("URL: https://github.com/settings/keys");
3274
3384
  }
3275
3385
  }
3276
3386
  async function handleSshPermissionError() {
3277
- return await select8({
3387
+ return await select9({
3278
3388
  message: "SSH permission denied. C\xE1ch x\u1EED l\xFD?",
3279
3389
  choices: [
3280
3390
  {
@@ -3448,12 +3558,12 @@ function parseBootstrapStrategyOpts(opts) {
3448
3558
  }
3449
3559
 
3450
3560
  // src/commands/workspace-scaffold-and-finalize-orchestrator.ts
3451
- import { join as join25 } from "path";
3452
- import { basename } from "path";
3453
- import { confirm as confirm5, input as input6, select as select10 } from "@inquirer/prompts";
3561
+ import { join as join26 } from "path";
3562
+ import { basename as basename2 } from "path";
3563
+ import { confirm as confirm6, input as input6, select as select11 } from "@inquirer/prompts";
3454
3564
 
3455
3565
  // src/lib/create-workspace-remote-via-gh.ts
3456
- import { spawnSync as spawnSync23 } from "child_process";
3566
+ import { spawnSync as spawnSync24 } from "child_process";
3457
3567
  var CreateWorkspaceRemoteError = class extends Error {
3458
3568
  reason;
3459
3569
  fullName;
@@ -3483,20 +3593,20 @@ function classifyGhCreateError(stderr) {
3483
3593
  return "unknown";
3484
3594
  }
3485
3595
  function repoExistsOnGitHub(fullName) {
3486
- const r = spawnSync23("gh", ["repo", "view", fullName, "--json", "name"], {
3596
+ const r = spawnSync24("gh", ["repo", "view", fullName, "--json", "name"], {
3487
3597
  stdio: "ignore"
3488
3598
  });
3489
3599
  return r.status === 0;
3490
3600
  }
3491
3601
  function canCreateInNamespace(org, ghUser) {
3492
3602
  if (org.toLowerCase() === ghUser.toLowerCase()) return { ok: true };
3493
- const r = spawnSync23("gh", ["api", `orgs/${org}/members/${ghUser}`, "--silent"], {
3603
+ const r = spawnSync24("gh", ["api", `orgs/${org}/members/${ghUser}`, "--silent"], {
3494
3604
  stdio: "ignore"
3495
3605
  });
3496
3606
  if (r.status === 0) return { ok: true };
3497
- const orgCheck = spawnSync23("gh", ["api", `orgs/${org}`, "--silent"], { stdio: "ignore" });
3607
+ const orgCheck = spawnSync24("gh", ["api", `orgs/${org}`, "--silent"], { stdio: "ignore" });
3498
3608
  if (orgCheck.status !== 0) {
3499
- const userCheck = spawnSync23("gh", ["api", `users/${org}`, "--silent"], { stdio: "ignore" });
3609
+ const userCheck = spawnSync24("gh", ["api", `users/${org}`, "--silent"], { stdio: "ignore" });
3500
3610
  if (userCheck.status === 0) {
3501
3611
  return {
3502
3612
  ok: false,
@@ -3532,7 +3642,7 @@ async function createWorkspaceRemoteViaGh(input8) {
3532
3642
  );
3533
3643
  }
3534
3644
  log.info(`T\u1EA1o GitHub repo cho workspace: ${fullName} (${input8.visibility})...`);
3535
- const r = spawnSync23(
3645
+ const r = spawnSync24(
3536
3646
  "gh",
3537
3647
  [
3538
3648
  "repo",
@@ -3573,7 +3683,7 @@ ${combined}
3573
3683
  function linkExistingRemoteToWorkspace(args) {
3574
3684
  const sshUrl = `git@github.com:${args.fullName}.git`;
3575
3685
  const httpsUrl = `https://github.com/${args.fullName}.git`;
3576
- const addResult = spawnSync23(
3686
+ const addResult = spawnSync24(
3577
3687
  "git",
3578
3688
  ["-C", args.workspacePath, "remote", "add", "origin", sshUrl],
3579
3689
  {
@@ -3582,7 +3692,7 @@ function linkExistingRemoteToWorkspace(args) {
3582
3692
  }
3583
3693
  );
3584
3694
  if (addResult.status !== 0) {
3585
- spawnSync23("git", ["-C", args.workspacePath, "remote", "set-url", "origin", sshUrl], {
3695
+ spawnSync24("git", ["-C", args.workspacePath, "remote", "set-url", "origin", sshUrl], {
3586
3696
  stdio: "ignore"
3587
3697
  });
3588
3698
  }
@@ -3591,8 +3701,8 @@ function linkExistingRemoteToWorkspace(args) {
3591
3701
  }
3592
3702
 
3593
3703
  // src/lib/merge-pack-settings-into-project-settings.ts
3594
- import { promises as fs9 } from "fs";
3595
- import { join as join21 } from "path";
3704
+ import { promises as fs10 } from "fs";
3705
+ import { join as join22 } from "path";
3596
3706
  async function isStatusLineCommandResolvable(workspacePath, command) {
3597
3707
  const trimmed = command.trim();
3598
3708
  const match = trimmed.match(/^(node|python|python3|bash|sh)\s+([^\s]+)/);
@@ -3600,7 +3710,7 @@ async function isStatusLineCommandResolvable(workspacePath, command) {
3600
3710
  return true;
3601
3711
  }
3602
3712
  const filePath = match[2];
3603
- const fullPath = filePath.startsWith("/") ? filePath : join21(workspacePath, filePath);
3713
+ const fullPath = filePath.startsWith("/") ? filePath : join22(workspacePath, filePath);
3604
3714
  return await pathExists(fullPath);
3605
3715
  }
3606
3716
  function backupFilename(originalPath) {
@@ -3634,8 +3744,8 @@ function mergeHooksPerEvent(packHooks, userHooks) {
3634
3744
  return { merged, touchedEvents: touched };
3635
3745
  }
3636
3746
  async function mergePackSettingsIntoProjectSettings(workspacePath) {
3637
- const packTemplatePath = join21(workspacePath, ".claude", "pack", "templates", "settings.json.tpl");
3638
- const projectSettingsPath = join21(workspacePath, ".claude", "settings.json");
3747
+ const packTemplatePath = join22(workspacePath, ".claude", "pack", "templates", "settings.json.tpl");
3748
+ const projectSettingsPath = join22(workspacePath, ".claude", "settings.json");
3639
3749
  if (!await pathExists(packTemplatePath)) {
3640
3750
  return { action: "no-pack-template", changes: [] };
3641
3751
  }
@@ -3729,18 +3839,18 @@ async function mergePackSettingsIntoProjectSettings(workspacePath) {
3729
3839
  let backupPath;
3730
3840
  if (projectHasSettings) {
3731
3841
  backupPath = backupFilename(projectSettingsPath);
3732
- await fs9.copyFile(projectSettingsPath, backupPath);
3842
+ await fs10.copyFile(projectSettingsPath, backupPath);
3733
3843
  }
3734
3844
  await writeJsonAtomic(projectSettingsPath, merged);
3735
3845
  return { action: "merged", backupPath, changes };
3736
3846
  }
3737
3847
 
3738
3848
  // src/lib/symlink-farm-for-team-pack-mount-dirs.ts
3739
- import { promises as fs11 } from "fs";
3740
- import { dirname as dirname6, join as join22, relative as relative2 } from "path";
3849
+ import { promises as fs12 } from "fs";
3850
+ import { dirname as dirname6, join as join23, relative as relative2 } from "path";
3741
3851
 
3742
3852
  // src/lib/backup-existing-dir-before-symlink-override.ts
3743
- import { promises as fs10 } from "fs";
3853
+ import { promises as fs11 } from "fs";
3744
3854
  function timestamp() {
3745
3855
  const d = /* @__PURE__ */ new Date();
3746
3856
  const pad = (n) => n.toString().padStart(2, "0");
@@ -3748,7 +3858,7 @@ function timestamp() {
3748
3858
  }
3749
3859
  async function backupDirBeforeReplace(targetPath) {
3750
3860
  const backupPath = `${targetPath}.backup-${timestamp()}`;
3751
- await fs10.rename(targetPath, backupPath);
3861
+ await fs11.rename(targetPath, backupPath);
3752
3862
  return backupPath;
3753
3863
  }
3754
3864
 
@@ -3764,7 +3874,7 @@ var TEAM_PACK_MOUNT_DIRS = [
3764
3874
  ];
3765
3875
  async function isSymbolicLink(path) {
3766
3876
  try {
3767
- const st = await fs11.lstat(path);
3877
+ const st = await fs12.lstat(path);
3768
3878
  return st.isSymbolicLink();
3769
3879
  } catch {
3770
3880
  return false;
@@ -3777,33 +3887,33 @@ async function syncMountedDir(source, dest, force) {
3777
3887
  }
3778
3888
  if (await pathExists(dest)) {
3779
3889
  if (await isSymbolicLink(dest)) {
3780
- await fs11.unlink(dest);
3890
+ await fs12.unlink(dest);
3781
3891
  } else if (force) {
3782
3892
  const backupPath = await backupDirBeforeReplace(dest);
3783
3893
  const relativeSource2 = relative2(dirname6(dest), source);
3784
- await fs11.symlink(relativeSource2, dest);
3894
+ await fs12.symlink(relativeSource2, dest);
3785
3895
  return { dir, action: "backed-up-and-linked", backupPath };
3786
3896
  } else {
3787
3897
  return { dir, action: "skipped-conflict" };
3788
3898
  }
3789
3899
  }
3790
3900
  const relativeSource = relative2(dirname6(dest), source);
3791
- await fs11.symlink(relativeSource, dest);
3901
+ await fs12.symlink(relativeSource, dest);
3792
3902
  return { dir, action: "created" };
3793
3903
  }
3794
3904
  async function syncAllMountDirs(packDir, claudeDir, force) {
3795
3905
  const results = [];
3796
3906
  for (const dir of TEAM_PACK_MOUNT_DIRS) {
3797
- const source = join22(packDir, dir);
3798
- const dest = join22(claudeDir, dir);
3907
+ const source = join23(packDir, dir);
3908
+ const dest = join23(claudeDir, dir);
3799
3909
  results.push(await syncMountedDir(source, dest, force));
3800
3910
  }
3801
3911
  return results;
3802
3912
  }
3803
3913
 
3804
3914
  // src/commands/init-success-rendering-and-helpers.ts
3805
- import { join as join24, relative as relative3 } from "path";
3806
- import { input as input5, select as select9 } from "@inquirer/prompts";
3915
+ import { join as join25, relative as relative3 } from "path";
3916
+ import { input as input5, select as select10 } from "@inquirer/prompts";
3807
3917
  import boxen5 from "boxen";
3808
3918
 
3809
3919
  // src/lib/format-pack-commands-cheatsheet-box.ts
@@ -3844,7 +3954,7 @@ function formatPackCommandsCheatsheetBox() {
3844
3954
 
3845
3955
  // src/commands/init-conflict-detection-helpers.ts
3846
3956
  import { readdir } from "fs/promises";
3847
- import { join as join23 } from "path";
3957
+ import { join as join24 } from "path";
3848
3958
  async function isEmptyOrMissing(path) {
3849
3959
  if (!await pathExists(path)) return true;
3850
3960
  try {
@@ -3857,7 +3967,7 @@ async function isEmptyOrMissing(path) {
3857
3967
  }
3858
3968
  async function findAlternativeWorkspaceName(parent, desiredName, maxAttempts = 10) {
3859
3969
  for (let i = 2; i < maxAttempts; i++) {
3860
- const candidate = join23(parent, `${desiredName}-${i}`);
3970
+ const candidate = join24(parent, `${desiredName}-${i}`);
3861
3971
  if (await isEmptyOrMissing(candidate)) return candidate;
3862
3972
  }
3863
3973
  return null;
@@ -3865,7 +3975,7 @@ async function findAlternativeWorkspaceName(parent, desiredName, maxAttempts = 1
3865
3975
 
3866
3976
  // src/commands/init-success-rendering-and-helpers.ts
3867
3977
  async function resolveWorkspacePath(parent, desiredName, force) {
3868
- const desired = join24(parent, desiredName);
3978
+ const desired = join25(parent, desiredName);
3869
3979
  if (await isEmptyOrMissing(desired)) return desired;
3870
3980
  log.warn(`Workspace path "${desired}" \u0111\xE3 c\xF3 n\u1ED9i dung.`);
3871
3981
  while (true) {
@@ -3880,7 +3990,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
3880
3990
  }
3881
3991
  choices.push({ name: "Nh\u1EADp t\xEAn workspace kh\xE1c (manual)", value: "manual" });
3882
3992
  choices.push({ name: "T\u1EA1m ng\u01B0ng init", value: "abort" });
3883
- const action = await select9({
3993
+ const action = await select10({
3884
3994
  message: "C\xE1ch x\u1EED l\xFD workspace path conflict?",
3885
3995
  choices
3886
3996
  });
@@ -3896,7 +4006,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
3896
4006
  message: "T\xEAn workspace m\u1EDBi:",
3897
4007
  validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
3898
4008
  });
3899
- const newPath = join24(parent, newName.trim());
4009
+ const newPath = join25(parent, newName.trim());
3900
4010
  if (await isEmptyOrMissing(newPath)) return newPath;
3901
4011
  log.warn(`"${newPath}" c\u0169ng \u0111\xE3 c\xF3 n\u1ED9i dung. Th\u1EED t\xEAn kh\xE1c.`);
3902
4012
  }
@@ -3953,7 +4063,7 @@ async function printInitSuccessBox(rootPath, flow, aiResult = null, gitnexusResu
3953
4063
  ];
3954
4064
  process.stdout.write(`${boxen5(lines.join("\n"), { padding: 1, borderStyle: "round" })}
3955
4065
  `);
3956
- const packDir = join24(rootPath, TEAM_PACK_RELATIVE_PATH);
4066
+ const packDir = join25(rootPath, TEAM_PACK_RELATIVE_PATH);
3957
4067
  if (await pathExists(packDir)) {
3958
4068
  process.stdout.write(`
3959
4069
  ${formatPackCommandsCheatsheetBox()}
@@ -3969,7 +4079,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
3969
4079
  log.success(`Folder \u0111\xE3 c\xF3 remote origin: ${origin.refs.push}`);
3970
4080
  return origin.refs.push;
3971
4081
  }
3972
- const shouldCreate = opts.createRemote ?? await confirm5({
4082
+ const shouldCreate = opts.createRemote ?? await confirm6({
3973
4083
  message: "Folder ch\u01B0a c\xF3 remote. T\u1EA1o GitHub repo ngay \u0111\u1EC3 share team?",
3974
4084
  default: true
3975
4085
  });
@@ -3978,7 +4088,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
3978
4088
  return void 0;
3979
4089
  }
3980
4090
  await ensureGitHubReady();
3981
- const visibility = opts.repoVisibility ?? await select10({
4091
+ const visibility = opts.repoVisibility ?? await select11({
3982
4092
  message: "Visibility?",
3983
4093
  choices: [
3984
4094
  { name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
@@ -3987,7 +4097,7 @@ async function getOrCreateOriginRemote(folderPath, opts) {
3987
4097
  });
3988
4098
  const repoName = await input6({
3989
4099
  message: "T\xEAn repo:",
3990
- default: basename(folderPath)
4100
+ default: basename2(folderPath)
3991
4101
  });
3992
4102
  const urls = createGithubRemoteFromFolder({
3993
4103
  folder: folderPath,
@@ -4053,10 +4163,10 @@ async function finalizeWorkspaceScaffold(args) {
4053
4163
  await writeRootClaudeMd(args.workspacePath, vars);
4054
4164
  await writeProjectSettings(args.workspacePath, vars);
4055
4165
  await appendGitignoreEntries(args.workspacePath);
4056
- await ensureDir(join25(args.workspacePath, "notes"));
4057
- await ensureDir(join25(args.workspacePath, "scripts"));
4058
- await installGitHook(join25(args.workspacePath, ".git"), "post-merge");
4059
- await installGitHook(join25(args.workspacePath, ".git", "modules", "src"), "pre-push");
4166
+ await ensureDir(join26(args.workspacePath, "notes"));
4167
+ await ensureDir(join26(args.workspacePath, "scripts"));
4168
+ await installGitHook(join26(args.workspacePath, ".git"), "post-merge");
4169
+ await installGitHook(join26(args.workspacePath, ".git", "modules", "src"), "pre-push");
4060
4170
  log.success("C\xE0i post-merge (workspace) + pre-push (src/)");
4061
4171
  await autoSyncPackOnInit(args.workspacePath);
4062
4172
  await appendAuditEntry("init", `flow=${args.flow},workspace=${args.workspaceName}`);
@@ -4094,12 +4204,12 @@ async function finalizeWorkspaceScaffold(args) {
4094
4204
  await printInitSuccessBox(args.workspacePath, args.flow, aiResult, gitnexusResult);
4095
4205
  }
4096
4206
  async function autoSyncPackOnInit(workspacePath) {
4097
- const packDir = join25(workspacePath, TEAM_PACK_RELATIVE_PATH);
4207
+ const packDir = join26(workspacePath, TEAM_PACK_RELATIVE_PATH);
4098
4208
  if (!await pathExists(packDir)) {
4099
4209
  log.dim("Pack submodule kh\xF4ng t\u1ED3n t\u1EA1i (skip auto-sync). C\xF3 th\u1EC3 ch\u1EA1y `avatar sync` sau.");
4100
4210
  return;
4101
4211
  }
4102
- const claudeDir = join25(workspacePath, ".claude");
4212
+ const claudeDir = join26(workspacePath, ".claude");
4103
4213
  log.info("Auto-sync pack content v\xE0o .claude/ (symlinks + settings merge)...");
4104
4214
  try {
4105
4215
  const results = await syncAllMountDirs(packDir, claudeDir, false);
@@ -4134,13 +4244,13 @@ async function maybeCreateWorkspaceRemote(args) {
4134
4244
  let shouldCreate = args.createWorkspaceRemote;
4135
4245
  if (shouldCreate === void 0) {
4136
4246
  if (args.autoYes) return;
4137
- shouldCreate = await confirm5({
4247
+ shouldCreate = await confirm6({
4138
4248
  message: "T\u1EA1o remote GitHub cho workspace \u0111\u1EC3 share team? (Avatar state)",
4139
4249
  default: false
4140
4250
  });
4141
4251
  }
4142
4252
  if (!shouldCreate) return;
4143
- const visibility = args.repoVisibility ?? (args.autoYes ? "private" : await select10({
4253
+ const visibility = args.repoVisibility ?? (args.autoYes ? "private" : await select11({
4144
4254
  message: "Workspace visibility?",
4145
4255
  choices: [
4146
4256
  { name: "private (m\u1EB7c \u0111\u1ECBnh, an to\xE0n)", value: "private" },
@@ -4159,7 +4269,7 @@ async function maybeCreateWorkspaceRemote(args) {
4159
4269
  } catch (err) {
4160
4270
  if (err instanceof CreateWorkspaceRemoteError && err.reason === "repo-exists") {
4161
4271
  const fullName = err.fullName;
4162
- const reuseAction = await select10({
4272
+ const reuseAction = await select11({
4163
4273
  message: `Repo '${fullName}' \u0111\xE3 t\u1ED3n t\u1EA1i tr\xEAn GitHub. C\xE1ch x\u1EED l\xFD?`,
4164
4274
  choices: [
4165
4275
  {
@@ -4257,9 +4367,24 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
4257
4367
  presetStrategy: parseBootstrapStrategyOpts(opts),
4258
4368
  autoYes: opts.yes
4259
4369
  });
4260
- const remoteUrl = await getOrCreateOriginRemote(folderPath, opts);
4370
+ let remoteUrl = await getOrCreateOriginRemote(folderPath, opts);
4371
+ if (remoteUrl) {
4372
+ const verify = tryVerifyGitRemoteAccessible(remoteUrl);
4373
+ if (!verify.ok) {
4374
+ log.warn(`Remote ${remoteUrl} kh\xF4ng accessible (${verify.reason ?? "unknown"}).`);
4375
+ const recovered = await handleRemoteAccessFailureWithAccountSwitch({
4376
+ url: remoteUrl,
4377
+ initialReason: verify.reason ?? "unknown",
4378
+ initialDetail: verify.detail,
4379
+ folderPath,
4380
+ // enable option "Reset folder & tạo repo mới"
4381
+ defaultVisibility: opts.repoVisibility
4382
+ });
4383
+ remoteUrl = recovered.resolvedUrl;
4384
+ }
4385
+ }
4261
4386
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
4262
- const inferredName = opts.workspaceName ?? `${basename2(folderPath)}-avatar-workspace`;
4387
+ const inferredName = opts.workspaceName ?? `${basename3(folderPath)}-avatar-workspace`;
4263
4388
  const workspaceName = opts.workspaceName ?? await input7({ message: "T\xEAn workspace:", default: inferredName });
4264
4389
  const workspaceParent = resolve2(opts.workspaceParent ?? ".");
4265
4390
  const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
@@ -4290,7 +4415,7 @@ async function runInitFromScratch(opts, ownerEmail) {
4290
4415
  message: "T\xEAn d\u1EF1 \xE1n:",
4291
4416
  validate: (v) => v.length > 0 ? true : "T\xEAn b\u1EAFt bu\u1ED9c"
4292
4417
  });
4293
- const visibility = opts.repoVisibility ?? await select11({
4418
+ const visibility = opts.repoVisibility ?? await select12({
4294
4419
  message: "Visibility?",
4295
4420
  choices: [
4296
4421
  { name: "private (m\u1EB7c \u0111\u1ECBnh)", value: "private" },
@@ -4300,7 +4425,7 @@ async function runInitFromScratch(opts, ownerEmail) {
4300
4425
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
4301
4426
  const workspaceParent = resolve2(opts.workspaceParent ?? ".");
4302
4427
  const workspacePath = await resolveWorkspacePath(workspaceParent, projectName, opts.force);
4303
- const srcPath = join26(workspacePath, "src");
4428
+ const srcPath = join27(workspacePath, "src");
4304
4429
  await ensureDir(workspacePath);
4305
4430
  await ensureDir(srcPath);
4306
4431
  await safeBootstrapGitInFolder(srcPath, { autoYes: true });
@@ -4617,7 +4742,7 @@ async function runInit(opts) {
4617
4742
  }
4618
4743
  }
4619
4744
  async function promptProjectStatus() {
4620
- return await select12({
4745
+ return await select13({
4621
4746
  message: "T\xECnh tr\u1EA1ng d\u1EF1 \xE1n c\u1EE7a b\u1EA1n?",
4622
4747
  choices: [
4623
4748
  { name: "1. \u0110\xE3 c\xF3 repo git remote (URL c\xF3 s\u1EB5n)", value: "existing-remote" },
@@ -4649,7 +4774,7 @@ function registerMcpRunCommand(program2) {
4649
4774
  }
4650
4775
 
4651
4776
  // src/commands/pack-status-and-version-check.ts
4652
- import { join as join27 } from "path";
4777
+ import { join as join28 } from "path";
4653
4778
  import boxen7 from "boxen";
4654
4779
  var PACK_RELATIVE_PATH = ".claude/pack";
4655
4780
  function registerPackCommand(program2) {
@@ -4670,7 +4795,7 @@ function registerPackCommand(program2) {
4670
4795
  });
4671
4796
  }
4672
4797
  async function gatherPackStatus(cwd, doFetch) {
4673
- const packDir = join27(cwd, PACK_RELATIVE_PATH);
4798
+ const packDir = join28(cwd, PACK_RELATIVE_PATH);
4674
4799
  if (!await pathExists(packDir) || !await isGitRepo(packDir)) {
4675
4800
  return {
4676
4801
  installed: false,
@@ -4759,18 +4884,18 @@ function registerSecretsCommand(program2) {
4759
4884
  }
4760
4885
 
4761
4886
  // src/commands/status.ts
4762
- import { promises as fs13 } from "fs";
4763
- import { join as join29 } from "path";
4887
+ import { promises as fs14 } from "fs";
4888
+ import { join as join30 } from "path";
4764
4889
  import boxen8 from "boxen";
4765
4890
 
4766
4891
  // src/lib/pack-backup-manager.ts
4767
- import { promises as fs12 } from "fs";
4768
- import { join as join28 } from "path";
4892
+ import { promises as fs13 } from "fs";
4893
+ import { join as join29 } from "path";
4769
4894
  var BACKUP_DIR_NAME = "_backup";
4770
4895
  async function listBackups(projectRoot) {
4771
- const dir = join28(projectRoot, ".claude", BACKUP_DIR_NAME);
4896
+ const dir = join29(projectRoot, ".claude", BACKUP_DIR_NAME);
4772
4897
  if (!await pathExists(dir)) return [];
4773
- const entries = await fs12.readdir(dir, { withFileTypes: true });
4898
+ const entries = await fs13.readdir(dir, { withFileTypes: true });
4774
4899
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
4775
4900
  }
4776
4901
 
@@ -4793,7 +4918,7 @@ function registerStatusCommand(program2) {
4793
4918
  }
4794
4919
  async function gatherStatus(cwd) {
4795
4920
  const projectName = cwd.split("/").filter(Boolean).pop() ?? "unknown";
4796
- const claudeRoot = join29(cwd, ".claude");
4921
+ const claudeRoot = join30(cwd, ".claude");
4797
4922
  const hasAvatar = await pathExists(claudeRoot);
4798
4923
  if (!hasAvatar) {
4799
4924
  return {
@@ -4806,9 +4931,9 @@ async function gatherStatus(cwd) {
4806
4931
  hasAvatar: false
4807
4932
  };
4808
4933
  }
4809
- const packVersion = await isGitRepo(join29(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
4810
- const pendingDir = join29(claudeRoot, "_pending");
4811
- const pendingCount = await pathExists(pendingDir) ? (await fs13.readdir(pendingDir)).filter((n) => n.endsWith(".diff.md")).length : 0;
4934
+ const packVersion = await isGitRepo(join30(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
4935
+ const pendingDir = join30(claudeRoot, "_pending");
4936
+ const pendingCount = await pathExists(pendingDir) ? (await fs14.readdir(pendingDir)).filter((n) => n.endsWith(".diff.md")).length : 0;
4812
4937
  const backupCount = (await listBackups(cwd)).length;
4813
4938
  const techStackSummary = await readTechStackFirstLine(claudeRoot);
4814
4939
  return {
@@ -4822,7 +4947,7 @@ async function gatherStatus(cwd) {
4822
4947
  };
4823
4948
  }
4824
4949
  async function readTechStackFirstLine(claudeRoot) {
4825
- const techStackPath = join29(claudeRoot, "project", "tech-stack.md");
4950
+ const techStackPath = join30(claudeRoot, "project", "tech-stack.md");
4826
4951
  if (!await pathExists(techStackPath)) return "(no tech-stack.md)";
4827
4952
  const content = await readText(techStackPath);
4828
4953
  const firstNonHeaderLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#") && !l.startsWith(">"));
@@ -4843,17 +4968,17 @@ function renderStatusBox(s) {
4843
4968
  }
4844
4969
 
4845
4970
  // src/commands/sync.ts
4846
- import { join as join31 } from "path";
4971
+ import { join as join32 } from "path";
4847
4972
 
4848
4973
  // src/lib/preview-team-pack-sync-changes-for-dry-run.ts
4849
- import { join as join30 } from "path";
4974
+ import { join as join31 } from "path";
4850
4975
  async function inspectMountDir(packDir, claudeDir, dir) {
4851
- const source = join30(packDir, dir);
4852
- const dest = join30(claudeDir, dir);
4976
+ const source = join31(packDir, dir);
4977
+ const dest = join31(claudeDir, dir);
4853
4978
  if (!await pathExists(source)) return "source-missing";
4854
4979
  if (!await pathExists(dest)) return "needs-creation";
4855
- const { promises: fs14 } = await import("fs");
4856
- const st = await fs14.lstat(dest);
4980
+ const { promises: fs15 } = await import("fs");
4981
+ const st = await fs15.lstat(dest);
4857
4982
  if (st.isSymbolicLink()) return "already-linked";
4858
4983
  return "conflict-real-dir";
4859
4984
  }
@@ -4891,8 +5016,8 @@ async function buildSyncPreview(packDir, claudeDir, targetVersion) {
4891
5016
  var DEFAULT_PACK_BRANCH2 = "main";
4892
5017
  async function syncAction(opts) {
4893
5018
  const projectRoot = process.cwd();
4894
- const claudeDir = join31(projectRoot, ".claude");
4895
- const packDir = join31(projectRoot, TEAM_PACK_RELATIVE_PATH);
5019
+ const claudeDir = join32(projectRoot, ".claude");
5020
+ const packDir = join32(projectRoot, TEAM_PACK_RELATIVE_PATH);
4896
5021
  if (!await pathExists(packDir)) {
4897
5022
  log.error(
4898
5023
  `team-ai-pack submodule ch\u01B0a \u0111\u01B0\u1EE3c kh\u1EDFi t\u1EA1o \u1EDF ${TEAM_PACK_RELATIVE_PATH}/.
@@ -5027,33 +5152,33 @@ function registerToolsCommand(program2) {
5027
5152
 
5028
5153
  // src/commands/uninstall.ts
5029
5154
  import { relative as relative4 } from "path";
5030
- import { confirm as confirm6 } from "@inquirer/prompts";
5155
+ import { confirm as confirm7 } from "@inquirer/prompts";
5031
5156
  import boxen9 from "boxen";
5032
5157
 
5033
5158
  // src/lib/create-uninstall-backup-snapshot.ts
5034
5159
  import { cp, mkdir, writeFile } from "fs/promises";
5035
5160
  import { homedir as homedir4 } from "os";
5036
- import { basename as basename3, join as join32 } from "path";
5037
- var UNINSTALL_BACKUPS_DIR = join32(homedir4(), ".avatar", "uninstall-backups");
5161
+ import { basename as basename4, join as join33 } from "path";
5162
+ var UNINSTALL_BACKUPS_DIR = join33(homedir4(), ".avatar", "uninstall-backups");
5038
5163
  async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersion) {
5039
- const projectName = basename3(projectRoot);
5164
+ const projectName = basename4(projectRoot);
5040
5165
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5041
- const backupDir = join32(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp2}`);
5166
+ const backupDir = join33(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp2}`);
5042
5167
  await mkdir(backupDir, { recursive: true, mode: 448 });
5043
5168
  if (artifacts.claudeDir) {
5044
- await cp(artifacts.claudeDir, join32(backupDir, ".claude"), { recursive: true });
5169
+ await cp(artifacts.claudeDir, join33(backupDir, ".claude"), { recursive: true });
5045
5170
  }
5046
5171
  if (artifacts.claudeMd) {
5047
- await cp(artifacts.claudeMd, join32(backupDir, "CLAUDE.md"));
5172
+ await cp(artifacts.claudeMd, join33(backupDir, "CLAUDE.md"));
5048
5173
  }
5049
5174
  if (artifacts.postMergeHook || artifacts.prePushHook) {
5050
- const hooksBackupDir = join32(backupDir, "hooks");
5175
+ const hooksBackupDir = join33(backupDir, "hooks");
5051
5176
  await mkdir(hooksBackupDir, { recursive: true });
5052
5177
  if (artifacts.postMergeHook) {
5053
- await cp(artifacts.postMergeHook, join32(hooksBackupDir, "post-merge"));
5178
+ await cp(artifacts.postMergeHook, join33(hooksBackupDir, "post-merge"));
5054
5179
  }
5055
5180
  if (artifacts.prePushHook) {
5056
- await cp(artifacts.prePushHook, join32(hooksBackupDir, "pre-push"));
5181
+ await cp(artifacts.prePushHook, join33(hooksBackupDir, "pre-push"));
5057
5182
  }
5058
5183
  }
5059
5184
  const manifest = {
@@ -5068,27 +5193,27 @@ async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersi
5068
5193
  prePushHook: !!artifacts.prePushHook
5069
5194
  }
5070
5195
  };
5071
- await writeFile(join32(backupDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
5196
+ await writeFile(join33(backupDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
5072
5197
  return backupDir;
5073
5198
  }
5074
5199
 
5075
5200
  // src/lib/detect-avatar-project-artifacts.ts
5076
5201
  import { existsSync as existsSync9 } from "fs";
5077
- import { join as join33 } from "path";
5202
+ import { join as join34 } from "path";
5078
5203
  function existsOrNull(path) {
5079
5204
  return existsSync9(path) ? path : null;
5080
5205
  }
5081
5206
  function detectAvatarProjectArtifacts(projectRoot) {
5082
- const claudeDir = existsOrNull(join33(projectRoot, ".claude"));
5083
- const claudeMd = existsOrNull(join33(projectRoot, "CLAUDE.md"));
5084
- const postMergeHook = existsOrNull(join33(projectRoot, ".git", "hooks", "post-merge"));
5207
+ const claudeDir = existsOrNull(join34(projectRoot, ".claude"));
5208
+ const claudeMd = existsOrNull(join34(projectRoot, "CLAUDE.md"));
5209
+ const postMergeHook = existsOrNull(join34(projectRoot, ".git", "hooks", "post-merge"));
5085
5210
  const prePushHook = existsOrNull(
5086
- join33(projectRoot, ".git", "modules", "src", "hooks", "pre-push")
5211
+ join34(projectRoot, ".git", "modules", "src", "hooks", "pre-push")
5087
5212
  );
5088
- const gitignorePath = existsOrNull(join33(projectRoot, ".gitignore"));
5089
- const gitmodulesPath = existsOrNull(join33(projectRoot, ".gitmodules"));
5090
- const notesDir = existsOrNull(join33(projectRoot, "notes"));
5091
- const scriptsDir = existsOrNull(join33(projectRoot, "scripts"));
5213
+ const gitignorePath = existsOrNull(join34(projectRoot, ".gitignore"));
5214
+ const gitmodulesPath = existsOrNull(join34(projectRoot, ".gitmodules"));
5215
+ const notesDir = existsOrNull(join34(projectRoot, "notes"));
5216
+ const scriptsDir = existsOrNull(join34(projectRoot, "scripts"));
5092
5217
  const hasAnyArtifact = !!(claudeDir || claudeMd || postMergeHook || prePushHook);
5093
5218
  return {
5094
5219
  hasAnyArtifact,
@@ -5109,11 +5234,11 @@ async function executeUninstallDeletion(artifacts, flags) {
5109
5234
  if (artifacts.claudeDir) {
5110
5235
  if (flags.keepSubmodule) {
5111
5236
  const { readdir: readdir2 } = await import("fs/promises");
5112
- const { join: join34 } = await import("path");
5237
+ const { join: join35 } = await import("path");
5113
5238
  const entries = await readdir2(artifacts.claudeDir);
5114
5239
  for (const entry of entries) {
5115
5240
  if (entry === "pack") continue;
5116
- await rm(join34(artifacts.claudeDir, entry), { recursive: true, force: true });
5241
+ await rm(join35(artifacts.claudeDir, entry), { recursive: true, force: true });
5117
5242
  }
5118
5243
  } else {
5119
5244
  await rm(artifacts.claudeDir, { recursive: true, force: true });
@@ -5206,7 +5331,7 @@ async function runUninstall(opts) {
5206
5331
  return;
5207
5332
  }
5208
5333
  if (!opts.yes) {
5209
- const ok = await confirm6({
5334
+ const ok = await confirm7({
5210
5335
  message: "Ti\u1EBFp t\u1EE5c g\u1EE1 Avatar?",
5211
5336
  default: false
5212
5337
  });