@nalvietnam/avatar-cli 1.2.5 → 1.2.6

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
@@ -1195,7 +1195,7 @@ async function applyFixes(checks) {
1195
1195
  // src/commands/init.ts
1196
1196
  import { basename, join as join16, relative as relative2, resolve } from "path";
1197
1197
  import { confirm as confirm3, input as input3, select as select7 } from "@inquirer/prompts";
1198
- import boxen3 from "boxen";
1198
+ import boxen4 from "boxen";
1199
1199
 
1200
1200
  // src/lib/prompt-recovery-action-on-failure.ts
1201
1201
  import { input as input2, select as select3 } from "@inquirer/prompts";
@@ -1227,6 +1227,7 @@ import { join as join10 } from "path";
1227
1227
  // src/lib/check-team-pack-access-with-retry-loop.ts
1228
1228
  import { spawnSync as spawnSync6 } from "child_process";
1229
1229
  import { confirm as confirm2, select as select4 } from "@inquirer/prompts";
1230
+ import boxen2 from "boxen";
1230
1231
  function parseRepoSlugFromGitUrl(url) {
1231
1232
  const httpsMatch = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
1232
1233
  if (httpsMatch) return httpsMatch[1];
@@ -1244,6 +1245,13 @@ function getCurrentGhUser() {
1244
1245
  if (r.status !== 0) return null;
1245
1246
  return r.stdout.trim() || null;
1246
1247
  }
1248
+ function triggerGhAuthLoginInteractive() {
1249
+ log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
1250
+ const r = spawnSync6("gh", ["auth", "login", "--web"], { stdio: "inherit" });
1251
+ if (r.status !== 0) {
1252
+ log.warn(`gh auth login exit ${r.status}. C\xF3 th\u1EC3 ch\u1EA1y tay r\u1ED3i quay l\u1EA1i retry.`);
1253
+ }
1254
+ }
1247
1255
  async function copyInfoToClipboardWithConsent(info) {
1248
1256
  const ok = await confirm2({
1249
1257
  message: "Copy th\xF4ng tin (GitHub username + email) v\xE0o clipboard \u0111\u1EC3 d\xE1n v\xE0o Slack/email?",
@@ -1258,44 +1266,78 @@ async function copyInfoToClipboardWithConsent(info) {
1258
1266
  log.dim(`Copy clipboard fail: ${err.message}`);
1259
1267
  }
1260
1268
  }
1261
- function buildAccessRequestInfo(ghUser, ssoEmail) {
1269
+ function printAccessWarningBox(repoSlug, ghUser, ssoEmail) {
1262
1270
  const lines = [
1263
- "Request access team-ai-pack (NAL)",
1271
+ `${chalk.red("\u26D4 KH\xD4NG C\xD3 QUY\u1EC0N ACCESS")}`,
1272
+ "",
1273
+ `Repo: ${chalk.bold(repoSlug)}`,
1274
+ "",
1275
+ "B\u1EA1n c\u1EA7n \u0111\u01B0\u1EE3c admin add v\xE0o org \u0111\u1EC3 pull team-ai-pack.",
1276
+ "",
1277
+ `${chalk.dim("Th\xF4ng tin g\u1EEDi admin:")}`,
1278
+ ` GitHub username: ${chalk.cyan(ghUser ?? "(ch\u01B0a gh auth \u2014 ch\u1EA1y: gh auth login)")}`,
1279
+ ` NAL email: ${chalk.cyan(ssoEmail ?? "(ch\u01B0a avatar login \u2014 ch\u1EA1y: avatar login)")}`,
1280
+ ` Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
1281
+ "",
1282
+ `${chalk.dim("Li\xEAn h\u1EC7:")} luke@nal.vn (Slack #avatar-setup)`
1283
+ ];
1284
+ process.stdout.write(
1285
+ `${boxen2(lines.join("\n"), { padding: 1, borderColor: "red", borderStyle: "round" })}
1286
+ `
1287
+ );
1288
+ }
1289
+ function buildAccessRequestInfo(repoSlug, ghUser, ssoEmail) {
1290
+ return [
1291
+ `Request access ${repoSlug} (NAL)`,
1264
1292
  "",
1265
1293
  `GitHub username: ${ghUser ?? "(ch\u01B0a gh auth \u2014 ch\u1EA1y: gh auth login)"}`,
1266
1294
  `NAL email: ${ssoEmail ?? "(ch\u01B0a avatar login \u2014 ch\u1EA1y: avatar login)"}`,
1267
1295
  `Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`
1268
- ];
1269
- return lines.join("\n");
1296
+ ].join("\n");
1270
1297
  }
1271
1298
  async function ensureTeamPackAccessWithRetry(args) {
1272
1299
  if (checkRepoAccess(args.repoSlug)) return true;
1273
- const ghUser = getCurrentGhUser();
1274
- const info = buildAccessRequestInfo(ghUser, args.ssoEmail ?? null);
1275
- log.warn(`B\u1EA1n ch\u01B0a c\xF3 quy\u1EC1n access v\xE0o b\u1ED9 package ${args.repoSlug}.`);
1276
- log.info("Li\xEAn h\u1EC7 admin (Luke @nal.vn) \u0111\u1EC3 \u0111\u01B0\u1EE3c add v\xE0o org nalvn.");
1277
- log.plain("");
1278
- log.plain(info);
1279
- log.plain("");
1280
- await copyInfoToClipboardWithConsent(info);
1300
+ const initialGhUser = getCurrentGhUser();
1301
+ printAccessWarningBox(args.repoSlug, initialGhUser, args.ssoEmail ?? null);
1302
+ await copyInfoToClipboardWithConsent(
1303
+ buildAccessRequestInfo(args.repoSlug, initialGhUser, args.ssoEmail ?? null)
1304
+ );
1281
1305
  while (true) {
1306
+ const ghUser = getCurrentGhUser();
1307
+ const ghUserDisplay = ghUser ?? "(ch\u01B0a gh auth)";
1282
1308
  const action = await select4({
1283
- message: "Ti\u1EBFp t\u1EE5c?",
1309
+ message: "C\xE1ch x\u1EED l\xFD?",
1284
1310
  choices: [
1285
- { name: "\u0110\xE3 \u0111\u01B0\u1EE3c add \u2014 ki\u1EC3m tra l\u1EA1i v\xE0 ti\u1EBFp t\u1EE5c", value: "retry" },
1286
- { name: "T\u1EA1m ng\u01B0ng \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau", value: "abort" }
1311
+ {
1312
+ name: `\u0110\xE3 \u0111\u01B0\u1EE3c grant access v\u1EDBi GitHub username '${ghUserDisplay}' \u2014 ki\u1EC3m tra l\u1EA1i`,
1313
+ value: "retry-same"
1314
+ },
1315
+ {
1316
+ name: "\u0110\xE3 grant v\u1EDBi GitHub account kh\xE1c \u2014 switch gh (m\u1EDF browser)",
1317
+ value: "switch-account"
1318
+ },
1319
+ {
1320
+ name: "T\u1EA1m ng\u01B0ng \u2014 ch\u1EA1y l\u1EA1i 'avatar init' sau",
1321
+ value: "abort"
1322
+ }
1287
1323
  ]
1288
1324
  });
1289
1325
  if (action === "abort") {
1290
1326
  log.dim("T\u1EA1m ng\u01B0ng. Ch\u1EA1y l\u1EA1i 'avatar init' sau khi \u0111\xE3 accept invite t\u1EEB GitHub.");
1291
1327
  return false;
1292
1328
  }
1329
+ if (action === "switch-account") {
1330
+ triggerGhAuthLoginInteractive();
1331
+ }
1293
1332
  log.info("Ki\u1EC3m tra access...");
1294
1333
  if (checkRepoAccess(args.repoSlug)) {
1295
- log.success("\u0110\xE3 c\xF3 access \u2014 ti\u1EBFp t\u1EE5c.");
1334
+ const finalUser = getCurrentGhUser();
1335
+ log.success(`\u0110\xE3 c\xF3 access v\u1EDBi '${finalUser ?? "(unknown)"}' \u2014 ti\u1EBFp t\u1EE5c.`);
1296
1336
  return true;
1297
1337
  }
1298
- 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).");
1338
+ log.warn(
1339
+ `V\u1EABn ch\u01B0a c\xF3 access v\u1EDBi account '${ghUser ?? "(unknown)"}'. \u0110\u1EA3m b\u1EA3o \u0111\xE3 accept email invite ho\u1EB7c switch \u0111\xFAng account.`
1340
+ );
1299
1341
  }
1300
1342
  }
1301
1343
 
@@ -1361,10 +1403,10 @@ async function readPinnedPackVersion(projectRoot) {
1361
1403
  }
1362
1404
 
1363
1405
  // src/lib/add-team-pack-submodule-with-retry-on-network-fail.ts
1364
- async function addTeamPackSubmoduleWithRetryOnNetworkFail(projectRoot, tag) {
1406
+ async function addTeamPackSubmoduleWithRetryOnNetworkFail(projectRoot, tag, ssoEmail) {
1365
1407
  while (true) {
1366
1408
  try {
1367
- const result = await addTeamPackSubmodule(projectRoot, tag);
1409
+ const result = await addTeamPackSubmodule(projectRoot, tag, ssoEmail);
1368
1410
  return { pinnedTag: result.pinnedTag, skipped: false };
1369
1411
  } catch (err) {
1370
1412
  if (err instanceof TeamPackAccessAbortedError) throw err;
@@ -1616,7 +1658,7 @@ function getCurrentGhUser2() {
1616
1658
  if (r.status !== 0) return null;
1617
1659
  return r.stdout.trim() || null;
1618
1660
  }
1619
- function triggerGhAuthLoginInteractive() {
1661
+ function triggerGhAuthLoginInteractive2() {
1620
1662
  log.info("M\u1EDF browser \u0111\u1EC3 switch GitHub account...");
1621
1663
  const r = spawnSync12("gh", ["auth", "login", "--web"], { stdio: "inherit" });
1622
1664
  if (r.status !== 0) {
@@ -1669,7 +1711,7 @@ async function handleRemoteAccessFailureWithAccountSwitch(args) {
1669
1711
  );
1670
1712
  }
1671
1713
  if (action === "switch") {
1672
- triggerGhAuthLoginInteractive();
1714
+ triggerGhAuthLoginInteractive2();
1673
1715
  }
1674
1716
  log.info("Verify remote l\u1EA1i...");
1675
1717
  const result = tryVerifyGitRemoteAccessible(args.url);
@@ -2176,7 +2218,7 @@ function buildScaffoldVariables(args) {
2176
2218
  }
2177
2219
 
2178
2220
  // src/commands/login.ts
2179
- import boxen2 from "boxen";
2221
+ import boxen3 from "boxen";
2180
2222
  import open from "open";
2181
2223
 
2182
2224
  // src/lib/google-oauth-device-flow.ts
@@ -2323,7 +2365,7 @@ async function runLogin(opts) {
2323
2365
  "",
2324
2366
  `Ho\u1EB7c Avatar t\u1EF1 m\u1EDF browser, click ${chalk.green("Allow")}...`
2325
2367
  ].join("\n");
2326
- process.stdout.write(`${boxen2(instructions, { padding: 1, borderStyle: "round" })}
2368
+ process.stdout.write(`${boxen3(instructions, { padding: 1, borderStyle: "round" })}
2327
2369
  `);
2328
2370
  void open(verificationUrl).catch(() => {
2329
2371
  log.dim("(Kh\xF4ng m\u1EDF \u0111\u01B0\u1EE3c browser t\u1EF1 \u0111\u1ED9ng \u2014 copy URL \u1EDF tr\xEAn)");
@@ -2482,7 +2524,8 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
2482
2524
  repoVisibility: opts.repoVisibility,
2483
2525
  repoOrg: opts.repoOrg,
2484
2526
  flow: "existing-remote",
2485
- aiSkip: opts.aiSkip
2527
+ aiSkip: opts.aiSkip,
2528
+ ssoEmail: ownerEmail
2486
2529
  });
2487
2530
  }
2488
2531
  async function runInitFromExistingFolder(opts, ownerEmail) {
@@ -2517,7 +2560,8 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
2517
2560
  repoVisibility: opts.repoVisibility,
2518
2561
  repoOrg: opts.repoOrg,
2519
2562
  flow: "existing-folder",
2520
- aiSkip: opts.aiSkip
2563
+ aiSkip: opts.aiSkip,
2564
+ ssoEmail: ownerEmail
2521
2565
  });
2522
2566
  }
2523
2567
  async function runInitFromScratch(opts, ownerEmail) {
@@ -2554,9 +2598,11 @@ async function runInitFromScratch(opts, ownerEmail) {
2554
2598
  await git(workspacePath).subModule(["add", urls.sshUrl, "src"]);
2555
2599
  let pinnedTag = "HEAD";
2556
2600
  if (!opts.skipTeamPack) {
2601
+ sp.stop();
2557
2602
  const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
2558
2603
  workspacePath,
2559
- opts.packVersion
2604
+ opts.packVersion,
2605
+ ownerEmail
2560
2606
  );
2561
2607
  pinnedTag = result.pinnedTag ?? "HEAD";
2562
2608
  sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
@@ -2627,9 +2673,11 @@ async function scaffoldWorkspaceWithSrcSubmodule(args) {
2627
2673
  await git(args.workspacePath).subModule(["add", args.srcRemoteUrl, "src"]);
2628
2674
  let pinnedTag = "HEAD";
2629
2675
  if (!args.skipTeamPack) {
2676
+ sp.stop();
2630
2677
  const result = await addTeamPackSubmoduleWithRetryOnNetworkFail(
2631
2678
  args.workspacePath,
2632
- args.packVersion
2679
+ args.packVersion,
2680
+ args.ssoEmail
2633
2681
  );
2634
2682
  pinnedTag = result.pinnedTag ?? "HEAD";
2635
2683
  sp.succeed(`Pin team-ai-pack v\xE0o ${pinnedTag}`);
@@ -2806,7 +2854,7 @@ function printInitSuccessBox(rootPath, flow, aiResult = null) {
2806
2854
  ` ${chalk.cyan("avatar sync")} Pull team-ai-pack m\u1EDBi`,
2807
2855
  ` ${chalk.cyan("avatar uninstall")} G\u1EE1 Avatar (gi\u1EEF code)`
2808
2856
  ];
2809
- process.stdout.write(`${boxen3(lines.join("\n"), { padding: 1, borderStyle: "round" })}
2857
+ process.stdout.write(`${boxen4(lines.join("\n"), { padding: 1, borderStyle: "round" })}
2810
2858
  `);
2811
2859
  }
2812
2860
 
@@ -2843,7 +2891,7 @@ function registerSecretsCommand(program2) {
2843
2891
  // src/commands/status.ts
2844
2892
  import { promises as fs8 } from "fs";
2845
2893
  import { join as join18 } from "path";
2846
- import boxen4 from "boxen";
2894
+ import boxen5 from "boxen";
2847
2895
 
2848
2896
  // src/lib/pack-backup-manager.ts
2849
2897
  import { promises as fs7 } from "fs";
@@ -2921,7 +2969,7 @@ function renderStatusBox(s) {
2921
2969
  `${chalk.dim("Backups:")} ${s.backupCount}`,
2922
2970
  `${chalk.dim("Tech stack:")} ${s.techStackSummary}`
2923
2971
  ];
2924
- process.stdout.write(`${boxen4(lines.join("\n"), { padding: 1, borderStyle: "round" })}
2972
+ process.stdout.write(`${boxen5(lines.join("\n"), { padding: 1, borderStyle: "round" })}
2925
2973
  `);
2926
2974
  }
2927
2975
 
@@ -2941,7 +2989,7 @@ function registerToolsCommand(program2) {
2941
2989
  // src/commands/uninstall.ts
2942
2990
  import { relative as relative3 } from "path";
2943
2991
  import { confirm as confirm4 } from "@inquirer/prompts";
2944
- import boxen5 from "boxen";
2992
+ import boxen6 from "boxen";
2945
2993
 
2946
2994
  // src/lib/create-uninstall-backup-snapshot.ts
2947
2995
  import { cp, mkdir, writeFile } from "fs/promises";
@@ -3095,7 +3143,7 @@ async function removeSubmoduleEntry(gitmodulesPath, submodulePath) {
3095
3143
  }
3096
3144
 
3097
3145
  // src/commands/uninstall.ts
3098
- var CLI_VERSION = "1.2.5";
3146
+ var CLI_VERSION = "1.2.6";
3099
3147
  function registerUninstallCommand(program2) {
3100
3148
  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) => {
3101
3149
  try {
@@ -3172,12 +3220,12 @@ function printUninstallSuccessBox(backupPath) {
3172
3220
  lines.push(` ${chalk.dim("Backup:")} ${backupPath}`);
3173
3221
  lines.push(` ${chalk.dim("Restore:")} ${chalk.cyan(`cp -r "${backupPath}"/* .`)}`);
3174
3222
  }
3175
- process.stdout.write(`${boxen5(lines.join("\n"), { padding: 1, borderStyle: "round" })}
3223
+ process.stdout.write(`${boxen6(lines.join("\n"), { padding: 1, borderStyle: "round" })}
3176
3224
  `);
3177
3225
  }
3178
3226
 
3179
3227
  // src/index.ts
3180
- var CLI_VERSION2 = "1.2.5";
3228
+ var CLI_VERSION2 = "1.2.6";
3181
3229
  var program = new Command();
3182
3230
  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(
3183
3231
  "beforeAll",