@nalvietnam/avatar-cli 1.10.0 → 1.11.0

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
@@ -2311,7 +2311,7 @@ function registerGitnexusCommand(program2) {
2311
2311
  }
2312
2312
 
2313
2313
  // src/commands/init.ts
2314
- import { basename, join as join24, relative as relative3, resolve } from "path";
2314
+ import { basename, join as join25, relative as relative3, resolve as resolve2 } from "path";
2315
2315
  import { confirm as confirm5, input as input5, select as select9 } from "@inquirer/prompts";
2316
2316
  import boxen6 from "boxen";
2317
2317
 
@@ -3747,8 +3747,43 @@ async function findAlternativeWorkspaceName(parent, desiredName, maxAttempts = 1
3747
3747
  return null;
3748
3748
  }
3749
3749
 
3750
+ // src/lib/read-cli-version-from-package-json.ts
3751
+ import { readFileSync as readFileSync5 } from "fs";
3752
+ import { dirname as dirname6, join as join24, resolve } from "path";
3753
+ import { fileURLToPath as fileURLToPath3 } from "url";
3754
+ var cachedVersion = null;
3755
+ function readCliVersion() {
3756
+ if (cachedVersion !== null) return cachedVersion;
3757
+ const here = dirname6(fileURLToPath3(import.meta.url));
3758
+ for (let i = 0; i < 5; i++) {
3759
+ const candidate = resolve(here, ...Array(i).fill(".."), "package.json");
3760
+ try {
3761
+ const raw = readFileSync5(candidate, "utf8");
3762
+ const pkg = JSON.parse(raw);
3763
+ if (pkg.name === "@nalvietnam/avatar-cli" && typeof pkg.version === "string") {
3764
+ cachedVersion = pkg.version;
3765
+ return cachedVersion;
3766
+ }
3767
+ } catch {
3768
+ }
3769
+ if (i === 0) {
3770
+ const sibling = join24(here, "..", "package.json");
3771
+ try {
3772
+ const raw = readFileSync5(sibling, "utf8");
3773
+ const pkg = JSON.parse(raw);
3774
+ if (pkg.name === "@nalvietnam/avatar-cli" && typeof pkg.version === "string") {
3775
+ cachedVersion = pkg.version;
3776
+ return cachedVersion;
3777
+ }
3778
+ } catch {
3779
+ }
3780
+ }
3781
+ }
3782
+ cachedVersion = "unknown";
3783
+ return cachedVersion;
3784
+ }
3785
+
3750
3786
  // src/commands/init-scaffold-variable-builders.ts
3751
- var AVATAR_CLI_VERSION = "1.0.1";
3752
3787
  function inferWorkspaceName(repoUrl) {
3753
3788
  const trimmed = repoUrl.trim().replace(/\/+$/, "");
3754
3789
  const lastSegment = trimmed.split(/[/:]/).pop() ?? "";
@@ -3794,7 +3829,7 @@ function buildScaffoldVariables(args) {
3794
3829
  projectName: args.projectName,
3795
3830
  projectDescription: args.projectDescription,
3796
3831
  teamOwner: args.teamOwner,
3797
- avatarVersion: AVATAR_CLI_VERSION,
3832
+ avatarVersion: readCliVersion(),
3798
3833
  packVersion: args.packVersion,
3799
3834
  lastScan: (/* @__PURE__ */ new Date()).toISOString(),
3800
3835
  mode: args.mode,
@@ -3989,7 +4024,7 @@ async function runLogin(opts) {
3989
4024
  log.success(`L\u01B0u credential v\xE0o ${USER_CONFIG_PATH} (chmod 600)`);
3990
4025
  }
3991
4026
  function sleep(ms) {
3992
- return new Promise((resolve2) => setTimeout(resolve2, ms));
4027
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
3993
4028
  }
3994
4029
 
3995
4030
  // src/commands/init.ts
@@ -4097,7 +4132,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
4097
4132
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
4098
4133
  const inferredName = inferWorkspaceName(remoteUrl);
4099
4134
  const workspaceName = opts.workspaceName ?? await input5({ message: "T\xEAn workspace:", default: inferredName });
4100
- const workspaceParent = resolve(opts.workspaceParent ?? ".");
4135
+ const workspaceParent = resolve2(opts.workspaceParent ?? ".");
4101
4136
  const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
4102
4137
  await scaffoldWorkspaceWithSrcSubmodule({
4103
4138
  workspacePath,
@@ -4120,7 +4155,7 @@ async function runInitFromExistingRemote(opts, ownerEmail) {
4120
4155
  });
4121
4156
  }
4122
4157
  async function runInitFromExistingFolder(opts, ownerEmail) {
4123
- const folderPath = resolve(
4158
+ const folderPath = resolve2(
4124
4159
  opts.folderPath ?? await input5({
4125
4160
  message: "\u0110\u01B0\u1EDDng d\u1EABn folder hi\u1EC7n c\xF3:",
4126
4161
  validate: (v) => v.length > 0 ? true : "Path b\u1EAFt bu\u1ED9c"
@@ -4134,7 +4169,7 @@ async function runInitFromExistingFolder(opts, ownerEmail) {
4134
4169
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
4135
4170
  const inferredName = opts.workspaceName ?? `${basename(folderPath)}-avatar-workspace`;
4136
4171
  const workspaceName = opts.workspaceName ?? await input5({ message: "T\xEAn workspace:", default: inferredName });
4137
- const workspaceParent = resolve(opts.workspaceParent ?? ".");
4172
+ const workspaceParent = resolve2(opts.workspaceParent ?? ".");
4138
4173
  const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
4139
4174
  await scaffoldWorkspaceWithSrcSubmodule({
4140
4175
  workspacePath,
@@ -4171,9 +4206,9 @@ async function runInitFromScratch(opts, ownerEmail) {
4171
4206
  ]
4172
4207
  });
4173
4208
  const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
4174
- const workspaceParent = resolve(opts.workspaceParent ?? ".");
4209
+ const workspaceParent = resolve2(opts.workspaceParent ?? ".");
4175
4210
  const workspacePath = await resolveWorkspacePath(workspaceParent, projectName, opts.force);
4176
- const srcPath = join24(workspacePath, "src");
4211
+ const srcPath = join25(workspacePath, "src");
4177
4212
  await ensureDir(workspacePath);
4178
4213
  await ensureDir(srcPath);
4179
4214
  await safeBootstrapGitInFolder(srcPath, { autoYes: true });
@@ -4315,10 +4350,10 @@ async function finalizeWorkspaceScaffold(args) {
4315
4350
  await writeRootClaudeMd(args.workspacePath, vars);
4316
4351
  await writeProjectSettings(args.workspacePath, vars);
4317
4352
  await appendGitignoreEntries(args.workspacePath);
4318
- await ensureDir(join24(args.workspacePath, "notes"));
4319
- await ensureDir(join24(args.workspacePath, "scripts"));
4320
- await installGitHook(join24(args.workspacePath, ".git"), "post-merge");
4321
- await installGitHook(join24(args.workspacePath, ".git", "modules", "src"), "pre-push");
4353
+ await ensureDir(join25(args.workspacePath, "notes"));
4354
+ await ensureDir(join25(args.workspacePath, "scripts"));
4355
+ await installGitHook(join25(args.workspacePath, ".git"), "post-merge");
4356
+ await installGitHook(join25(args.workspacePath, ".git", "modules", "src"), "pre-push");
4322
4357
  log.success("C\xE0i post-merge (workspace) + pre-push (src/)");
4323
4358
  await autoSyncPackOnInit(args.workspacePath);
4324
4359
  await appendAuditEntry("init", `flow=${args.flow},workspace=${args.workspaceName}`);
@@ -4356,12 +4391,12 @@ async function finalizeWorkspaceScaffold(args) {
4356
4391
  await printInitSuccessBox(args.workspacePath, args.flow, aiResult, gitnexusResult);
4357
4392
  }
4358
4393
  async function autoSyncPackOnInit(workspacePath) {
4359
- const packDir = join24(workspacePath, TEAM_PACK_RELATIVE_PATH);
4394
+ const packDir = join25(workspacePath, TEAM_PACK_RELATIVE_PATH);
4360
4395
  if (!await pathExists(packDir)) {
4361
4396
  log.dim("Pack submodule kh\xF4ng t\u1ED3n t\u1EA1i (skip auto-sync). C\xF3 th\u1EC3 ch\u1EA1y `avatar sync` sau.");
4362
4397
  return;
4363
4398
  }
4364
- const claudeDir = join24(workspacePath, ".claude");
4399
+ const claudeDir = join25(workspacePath, ".claude");
4365
4400
  log.info("Auto-sync pack content v\xE0o .claude/ (symlinks + settings merge)...");
4366
4401
  try {
4367
4402
  const results = await syncAllMountDirs(packDir, claudeDir, false);
@@ -4475,7 +4510,7 @@ async function maybeCreateWorkspaceRemote(args) {
4475
4510
  }
4476
4511
  }
4477
4512
  async function resolveWorkspacePath(parent, desiredName, force) {
4478
- const desired = join24(parent, desiredName);
4513
+ const desired = join25(parent, desiredName);
4479
4514
  if (await isEmptyOrMissing(desired)) return desired;
4480
4515
  log.warn(`Workspace path "${desired}" \u0111\xE3 c\xF3 n\u1ED9i dung.`);
4481
4516
  while (true) {
@@ -4506,7 +4541,7 @@ async function resolveWorkspacePath(parent, desiredName, force) {
4506
4541
  message: "T\xEAn workspace m\u1EDBi:",
4507
4542
  validate: (v) => v.trim().length > 0 ? true : "T\xEAn kh\xF4ng \u0111\u01B0\u1EE3c r\u1ED7ng"
4508
4543
  });
4509
- const newPath = join24(parent, newName.trim());
4544
+ const newPath = join25(parent, newName.trim());
4510
4545
  if (await isEmptyOrMissing(newPath)) return newPath;
4511
4546
  log.warn(`"${newPath}" c\u0169ng \u0111\xE3 c\xF3 n\u1ED9i dung. Th\u1EED t\xEAn kh\xE1c.`);
4512
4547
  }
@@ -4563,7 +4598,7 @@ async function printInitSuccessBox(rootPath, flow, aiResult = null, gitnexusResu
4563
4598
  ];
4564
4599
  process.stdout.write(`${boxen6(lines.join("\n"), { padding: 1, borderStyle: "round" })}
4565
4600
  `);
4566
- const packDir = join24(rootPath, TEAM_PACK_RELATIVE_PATH);
4601
+ const packDir = join25(rootPath, TEAM_PACK_RELATIVE_PATH);
4567
4602
  if (await pathExists(packDir)) {
4568
4603
  process.stdout.write(`
4569
4604
  ${formatPackCommandsCheatsheetBox()}
@@ -4592,6 +4627,91 @@ function registerMcpRunCommand(program2) {
4592
4627
  program2.command("mcp-run <tool-id>", { hidden: true }).description("[internal] Spawn MCP v\u1EDBi secrets injected (M09)").action(notImplementedYet("mcp-run", "Milestone 09"));
4593
4628
  }
4594
4629
 
4630
+ // src/commands/pack-status-and-version-check.ts
4631
+ import { join as join26 } from "path";
4632
+ import boxen7 from "boxen";
4633
+ var PACK_RELATIVE_PATH = ".claude/pack";
4634
+ function registerPackCommand(program2) {
4635
+ const pack = program2.command("pack").description("Qu\u1EA3n l\xFD team-ai-pack submodule (status, ...)");
4636
+ pack.command("status").description("Hi\u1EC3n th\u1ECB tag pack \u0111ang pin + tag m\u1EDBi nh\u1EA5t t\u1EEB remote").option("--json", "Output JSON cho script").option("--no-fetch", "B\u1ECF qua fetch remote (ch\u1EC9 \u0111\u1ECDc local tags)").action(async (opts) => {
4637
+ try {
4638
+ const snap = await gatherPackStatus(process.cwd(), opts.fetch !== false);
4639
+ if (opts.json) {
4640
+ process.stdout.write(`${JSON.stringify(snap, null, 2)}
4641
+ `);
4642
+ } else {
4643
+ renderPackStatus(snap);
4644
+ }
4645
+ } catch (err) {
4646
+ log.error(err instanceof Error ? err.message : String(err));
4647
+ process.exit(1);
4648
+ }
4649
+ });
4650
+ }
4651
+ async function gatherPackStatus(cwd, doFetch) {
4652
+ const packDir = join26(cwd, PACK_RELATIVE_PATH);
4653
+ if (!await pathExists(packDir) || !await isGitRepo(packDir)) {
4654
+ return {
4655
+ installed: false,
4656
+ currentTag: null,
4657
+ latestTag: null,
4658
+ upToDate: null,
4659
+ fetched: false
4660
+ };
4661
+ }
4662
+ const currentTag = await readPinnedPackVersion(cwd).catch(() => null);
4663
+ let fetched = false;
4664
+ if (doFetch) {
4665
+ try {
4666
+ await git(packDir).fetch(["--tags", "origin"]);
4667
+ fetched = true;
4668
+ } catch {
4669
+ fetched = false;
4670
+ }
4671
+ }
4672
+ const allTags = await listTags(packDir).catch(() => []);
4673
+ const latestTag = pickLatestStableSemVerTag(allTags);
4674
+ const upToDate = currentTag && latestTag ? currentTag === latestTag : null;
4675
+ return {
4676
+ installed: true,
4677
+ currentTag,
4678
+ latestTag,
4679
+ upToDate,
4680
+ fetched
4681
+ };
4682
+ }
4683
+ function renderPackStatus(s) {
4684
+ if (!s.installed) {
4685
+ const lines2 = [
4686
+ `${chalk.bold("team-ai-pack")} \xB7 ${chalk.yellow("ch\u01B0a c\xE0i")}`,
4687
+ "\u2500".repeat(48),
4688
+ chalk.dim("Ch\u1EA1y `avatar init` ho\u1EB7c `avatar sync` \u0111\u1EC3 c\xE0i pack.")
4689
+ ];
4690
+ process.stdout.write(`${boxen7(lines2.join("\n"), { padding: 1, borderStyle: "round" })}
4691
+ `);
4692
+ return;
4693
+ }
4694
+ const current = s.currentTag ?? chalk.yellow("(unknown)");
4695
+ const latest = s.latestTag ?? chalk.dim(s.fetched ? "(no tags)" : "(kh\xF4ng fetch)");
4696
+ let verdict;
4697
+ if (s.upToDate === true) {
4698
+ verdict = chalk.green("\u2713 \u0110ang d\xF9ng tag m\u1EDBi nh\u1EA5t");
4699
+ } else if (s.upToDate === false) {
4700
+ verdict = `${chalk.yellow("\u26A0 C\xF3 version m\u1EDBi")} ${chalk.dim("\u2192 avatar sync")}`;
4701
+ } else {
4702
+ verdict = chalk.dim("(kh\xF4ng so s\xE1nh \u0111\u01B0\u1EE3c)");
4703
+ }
4704
+ const lines = [
4705
+ `${chalk.bold("team-ai-pack status")}`,
4706
+ "\u2500".repeat(48),
4707
+ `${chalk.dim("Tag hi\u1EC7n t\u1EA1i:")} ${current}`,
4708
+ `${chalk.dim("Tag m\u1EDBi nh\u1EA5t:")} ${latest}`,
4709
+ `${chalk.dim("Tr\u1EA1ng th\xE1i:")} ${verdict}`
4710
+ ];
4711
+ process.stdout.write(`${boxen7(lines.join("\n"), { padding: 1, borderStyle: "round" })}
4712
+ `);
4713
+ }
4714
+
4595
4715
  // src/commands/restore.ts
4596
4716
  function registerRestoreCommand(program2) {
4597
4717
  program2.command("restore").description("Kh\xF4i ph\u1EE5c .claude/pack/ t\u1EEB backup (M08)").option("--backup <name>", "T\xEAn backup folder trong .claude/_backup/").option("--list", "Li\u1EC7t k\xEA c\xE1c backup hi\u1EC7n c\xF3").action(notImplementedYet("restore", "Milestone 08"));
@@ -4619,22 +4739,21 @@ function registerSecretsCommand(program2) {
4619
4739
 
4620
4740
  // src/commands/status.ts
4621
4741
  import { promises as fs13 } from "fs";
4622
- import { join as join26 } from "path";
4623
- import boxen7 from "boxen";
4742
+ import { join as join28 } from "path";
4743
+ import boxen8 from "boxen";
4624
4744
 
4625
4745
  // src/lib/pack-backup-manager.ts
4626
4746
  import { promises as fs12 } from "fs";
4627
- import { join as join25 } from "path";
4747
+ import { join as join27 } from "path";
4628
4748
  var BACKUP_DIR_NAME = "_backup";
4629
4749
  async function listBackups(projectRoot) {
4630
- const dir = join25(projectRoot, ".claude", BACKUP_DIR_NAME);
4750
+ const dir = join27(projectRoot, ".claude", BACKUP_DIR_NAME);
4631
4751
  if (!await pathExists(dir)) return [];
4632
4752
  const entries = await fs12.readdir(dir, { withFileTypes: true });
4633
4753
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
4634
4754
  }
4635
4755
 
4636
4756
  // src/commands/status.ts
4637
- var AVATAR_CLI_VERSION2 = "1.0.1";
4638
4757
  function registerStatusCommand(program2) {
4639
4758
  program2.command("status").description("Snapshot t\u1EE9c th\xEC: project, pack version, pending, backup").option("--json", "Output JSON cho script").action(async (opts) => {
4640
4759
  try {
@@ -4653,12 +4772,12 @@ function registerStatusCommand(program2) {
4653
4772
  }
4654
4773
  async function gatherStatus(cwd) {
4655
4774
  const projectName = cwd.split("/").filter(Boolean).pop() ?? "unknown";
4656
- const claudeRoot = join26(cwd, ".claude");
4775
+ const claudeRoot = join28(cwd, ".claude");
4657
4776
  const hasAvatar = await pathExists(claudeRoot);
4658
4777
  if (!hasAvatar) {
4659
4778
  return {
4660
4779
  projectName,
4661
- cliVersion: AVATAR_CLI_VERSION2,
4780
+ cliVersion: readCliVersion(),
4662
4781
  packVersion: null,
4663
4782
  pendingCount: 0,
4664
4783
  backupCount: 0,
@@ -4666,14 +4785,14 @@ async function gatherStatus(cwd) {
4666
4785
  hasAvatar: false
4667
4786
  };
4668
4787
  }
4669
- const packVersion = await isGitRepo(join26(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
4670
- const pendingDir = join26(claudeRoot, "_pending");
4788
+ const packVersion = await isGitRepo(join28(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
4789
+ const pendingDir = join28(claudeRoot, "_pending");
4671
4790
  const pendingCount = await pathExists(pendingDir) ? (await fs13.readdir(pendingDir)).filter((n) => n.endsWith(".diff.md")).length : 0;
4672
4791
  const backupCount = (await listBackups(cwd)).length;
4673
4792
  const techStackSummary = await readTechStackFirstLine(claudeRoot);
4674
4793
  return {
4675
4794
  projectName,
4676
- cliVersion: AVATAR_CLI_VERSION2,
4795
+ cliVersion: AVATAR_CLI_VERSION,
4677
4796
  packVersion,
4678
4797
  pendingCount,
4679
4798
  backupCount,
@@ -4682,7 +4801,7 @@ async function gatherStatus(cwd) {
4682
4801
  };
4683
4802
  }
4684
4803
  async function readTechStackFirstLine(claudeRoot) {
4685
- const techStackPath = join26(claudeRoot, "project", "tech-stack.md");
4804
+ const techStackPath = join28(claudeRoot, "project", "tech-stack.md");
4686
4805
  if (!await pathExists(techStackPath)) return "(no tech-stack.md)";
4687
4806
  const content = await readText(techStackPath);
4688
4807
  const firstNonHeaderLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#") && !l.startsWith(">"));
@@ -4698,18 +4817,18 @@ function renderStatusBox(s) {
4698
4817
  `${chalk.dim("Backups:")} ${s.backupCount}`,
4699
4818
  `${chalk.dim("Tech stack:")} ${s.techStackSummary}`
4700
4819
  ];
4701
- process.stdout.write(`${boxen7(lines.join("\n"), { padding: 1, borderStyle: "round" })}
4820
+ process.stdout.write(`${boxen8(lines.join("\n"), { padding: 1, borderStyle: "round" })}
4702
4821
  `);
4703
4822
  }
4704
4823
 
4705
4824
  // src/commands/sync.ts
4706
- import { join as join28 } from "path";
4825
+ import { join as join30 } from "path";
4707
4826
 
4708
4827
  // src/lib/preview-team-pack-sync-changes-for-dry-run.ts
4709
- import { join as join27 } from "path";
4828
+ import { join as join29 } from "path";
4710
4829
  async function inspectMountDir(packDir, claudeDir, dir) {
4711
- const source = join27(packDir, dir);
4712
- const dest = join27(claudeDir, dir);
4830
+ const source = join29(packDir, dir);
4831
+ const dest = join29(claudeDir, dir);
4713
4832
  if (!await pathExists(source)) return "source-missing";
4714
4833
  if (!await pathExists(dest)) return "needs-creation";
4715
4834
  const { promises: fs14 } = await import("fs");
@@ -4751,8 +4870,8 @@ async function buildSyncPreview(packDir, claudeDir, targetVersion) {
4751
4870
  var DEFAULT_PACK_BRANCH2 = "main";
4752
4871
  async function syncAction(opts) {
4753
4872
  const projectRoot = process.cwd();
4754
- const claudeDir = join28(projectRoot, ".claude");
4755
- const packDir = join28(projectRoot, TEAM_PACK_RELATIVE_PATH);
4873
+ const claudeDir = join30(projectRoot, ".claude");
4874
+ const packDir = join30(projectRoot, TEAM_PACK_RELATIVE_PATH);
4756
4875
  if (!await pathExists(packDir)) {
4757
4876
  log.error(
4758
4877
  `team-ai-pack submodule ch\u01B0a \u0111\u01B0\u1EE3c kh\u1EDFi t\u1EA1o \u1EDF ${TEAM_PACK_RELATIVE_PATH}/.
@@ -4890,32 +5009,32 @@ function registerToolsCommand(program2) {
4890
5009
  // src/commands/uninstall.ts
4891
5010
  import { relative as relative4 } from "path";
4892
5011
  import { confirm as confirm6 } from "@inquirer/prompts";
4893
- import boxen8 from "boxen";
5012
+ import boxen9 from "boxen";
4894
5013
 
4895
5014
  // src/lib/create-uninstall-backup-snapshot.ts
4896
5015
  import { cp, mkdir, writeFile } from "fs/promises";
4897
5016
  import { homedir as homedir4 } from "os";
4898
- import { basename as basename2, join as join29 } from "path";
4899
- var UNINSTALL_BACKUPS_DIR = join29(homedir4(), ".avatar", "uninstall-backups");
5017
+ import { basename as basename2, join as join31 } from "path";
5018
+ var UNINSTALL_BACKUPS_DIR = join31(homedir4(), ".avatar", "uninstall-backups");
4900
5019
  async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersion) {
4901
5020
  const projectName = basename2(projectRoot);
4902
5021
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4903
- const backupDir = join29(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp2}`);
5022
+ const backupDir = join31(UNINSTALL_BACKUPS_DIR, `${projectName}-${timestamp2}`);
4904
5023
  await mkdir(backupDir, { recursive: true, mode: 448 });
4905
5024
  if (artifacts.claudeDir) {
4906
- await cp(artifacts.claudeDir, join29(backupDir, ".claude"), { recursive: true });
5025
+ await cp(artifacts.claudeDir, join31(backupDir, ".claude"), { recursive: true });
4907
5026
  }
4908
5027
  if (artifacts.claudeMd) {
4909
- await cp(artifacts.claudeMd, join29(backupDir, "CLAUDE.md"));
5028
+ await cp(artifacts.claudeMd, join31(backupDir, "CLAUDE.md"));
4910
5029
  }
4911
5030
  if (artifacts.postMergeHook || artifacts.prePushHook) {
4912
- const hooksBackupDir = join29(backupDir, "hooks");
5031
+ const hooksBackupDir = join31(backupDir, "hooks");
4913
5032
  await mkdir(hooksBackupDir, { recursive: true });
4914
5033
  if (artifacts.postMergeHook) {
4915
- await cp(artifacts.postMergeHook, join29(hooksBackupDir, "post-merge"));
5034
+ await cp(artifacts.postMergeHook, join31(hooksBackupDir, "post-merge"));
4916
5035
  }
4917
5036
  if (artifacts.prePushHook) {
4918
- await cp(artifacts.prePushHook, join29(hooksBackupDir, "pre-push"));
5037
+ await cp(artifacts.prePushHook, join31(hooksBackupDir, "pre-push"));
4919
5038
  }
4920
5039
  }
4921
5040
  const manifest = {
@@ -4930,27 +5049,27 @@ async function createUninstallBackupSnapshot(projectRoot, artifacts, avatarVersi
4930
5049
  prePushHook: !!artifacts.prePushHook
4931
5050
  }
4932
5051
  };
4933
- await writeFile(join29(backupDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
5052
+ await writeFile(join31(backupDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
4934
5053
  return backupDir;
4935
5054
  }
4936
5055
 
4937
5056
  // src/lib/detect-avatar-project-artifacts.ts
4938
5057
  import { existsSync as existsSync9 } from "fs";
4939
- import { join as join30 } from "path";
5058
+ import { join as join32 } from "path";
4940
5059
  function existsOrNull(path) {
4941
5060
  return existsSync9(path) ? path : null;
4942
5061
  }
4943
5062
  function detectAvatarProjectArtifacts(projectRoot) {
4944
- const claudeDir = existsOrNull(join30(projectRoot, ".claude"));
4945
- const claudeMd = existsOrNull(join30(projectRoot, "CLAUDE.md"));
4946
- const postMergeHook = existsOrNull(join30(projectRoot, ".git", "hooks", "post-merge"));
5063
+ const claudeDir = existsOrNull(join32(projectRoot, ".claude"));
5064
+ const claudeMd = existsOrNull(join32(projectRoot, "CLAUDE.md"));
5065
+ const postMergeHook = existsOrNull(join32(projectRoot, ".git", "hooks", "post-merge"));
4947
5066
  const prePushHook = existsOrNull(
4948
- join30(projectRoot, ".git", "modules", "src", "hooks", "pre-push")
5067
+ join32(projectRoot, ".git", "modules", "src", "hooks", "pre-push")
4949
5068
  );
4950
- const gitignorePath = existsOrNull(join30(projectRoot, ".gitignore"));
4951
- const gitmodulesPath = existsOrNull(join30(projectRoot, ".gitmodules"));
4952
- const notesDir = existsOrNull(join30(projectRoot, "notes"));
4953
- const scriptsDir = existsOrNull(join30(projectRoot, "scripts"));
5069
+ const gitignorePath = existsOrNull(join32(projectRoot, ".gitignore"));
5070
+ const gitmodulesPath = existsOrNull(join32(projectRoot, ".gitmodules"));
5071
+ const notesDir = existsOrNull(join32(projectRoot, "notes"));
5072
+ const scriptsDir = existsOrNull(join32(projectRoot, "scripts"));
4954
5073
  const hasAnyArtifact = !!(claudeDir || claudeMd || postMergeHook || prePushHook);
4955
5074
  return {
4956
5075
  hasAnyArtifact,
@@ -4971,11 +5090,11 @@ async function executeUninstallDeletion(artifacts, flags) {
4971
5090
  if (artifacts.claudeDir) {
4972
5091
  if (flags.keepSubmodule) {
4973
5092
  const { readdir: readdir2 } = await import("fs/promises");
4974
- const { join: join31 } = await import("path");
5093
+ const { join: join33 } = await import("path");
4975
5094
  const entries = await readdir2(artifacts.claudeDir);
4976
5095
  for (const entry of entries) {
4977
5096
  if (entry === "pack") continue;
4978
- await rm(join31(artifacts.claudeDir, entry), { recursive: true, force: true });
5097
+ await rm(join33(artifacts.claudeDir, entry), { recursive: true, force: true });
4979
5098
  }
4980
5099
  } else {
4981
5100
  await rm(artifacts.claudeDir, { recursive: true, force: true });
@@ -5121,12 +5240,12 @@ function printUninstallSuccessBox(backupPath) {
5121
5240
  lines.push(` ${chalk.dim("Backup:")} ${backupPath}`);
5122
5241
  lines.push(` ${chalk.dim("Restore:")} ${chalk.cyan(`cp -r "${backupPath}"/* .`)}`);
5123
5242
  }
5124
- process.stdout.write(`${boxen8(lines.join("\n"), { padding: 1, borderStyle: "round" })}
5243
+ process.stdout.write(`${boxen9(lines.join("\n"), { padding: 1, borderStyle: "round" })}
5125
5244
  `);
5126
5245
  }
5127
5246
 
5128
5247
  // src/index.ts
5129
- var CLI_VERSION2 = "1.10.0";
5248
+ var CLI_VERSION2 = readCliVersion();
5130
5249
  var program = new Command();
5131
5250
  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(
5132
5251
  "beforeAll",
@@ -5154,6 +5273,7 @@ registerSecretsCommand(program);
5154
5273
  registerMcpRunCommand(program);
5155
5274
  registerAiCommand(program);
5156
5275
  registerGitnexusCommand(program);
5276
+ registerPackCommand(program);
5157
5277
  registerUninstallCommand(program);
5158
5278
  program.parseAsync(process.argv).catch((err) => {
5159
5279
  const msg = err instanceof Error ? err.message : String(err);