@nalvietnam/avatar-cli 1.0.0 → 1.0.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
@@ -124,6 +124,7 @@ import { promises as fs2 } from "fs";
124
124
  import { join as join4 } from "path";
125
125
 
126
126
  // src/lib/template-bundle-loader.ts
127
+ import { existsSync } from "fs";
127
128
  import { dirname as dirname2, join as join3 } from "path";
128
129
  import { fileURLToPath } from "url";
129
130
 
@@ -139,8 +140,20 @@ function renderTemplate(source, variables) {
139
140
 
140
141
  // src/lib/template-bundle-loader.ts
141
142
  var HERE = dirname2(fileURLToPath(import.meta.url));
142
- var TEMPLATES_ROOT = join3(HERE, "..", "src", "templates");
143
- var HOOKS_ROOT = join3(HERE, "..", "src", "hooks");
143
+ var PACKAGE_ROOT = findPackageRoot(HERE);
144
+ var TEMPLATES_ROOT = join3(PACKAGE_ROOT, "src", "templates");
145
+ var HOOKS_ROOT = join3(PACKAGE_ROOT, "src", "hooks");
146
+ function findPackageRoot(startDir) {
147
+ let dir = startDir;
148
+ while (true) {
149
+ if (existsSync(join3(dir, "package.json"))) return dir;
150
+ const parent = dirname2(dir);
151
+ if (parent === dir) {
152
+ throw new Error(`Cannot locate package root from ${startDir}`);
153
+ }
154
+ dir = parent;
155
+ }
156
+ }
144
157
  async function loadTemplate(name) {
145
158
  return await readText(join3(TEMPLATES_ROOT, `${name}.tpl`));
146
159
  }
@@ -153,6 +166,28 @@ async function loadHook(name) {
153
166
  }
154
167
 
155
168
  // src/lib/project-tree-scaffolder.ts
169
+ var AVATAR_MANAGED_PATHS = [".claude", "CLAUDE.md", ".gitmodules"];
170
+ async function backupIfExists(path) {
171
+ if (!await pathExists(path)) return null;
172
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
173
+ const basePath = `${path}.avatar-backup-${ts}`;
174
+ let backupPath = basePath;
175
+ let counter = 1;
176
+ while (await pathExists(backupPath)) {
177
+ backupPath = `${basePath}-${counter}`;
178
+ counter++;
179
+ if (counter > 5) {
180
+ throw new Error(`Could not find free backup name for ${path}`);
181
+ }
182
+ }
183
+ await fs2.rename(path, backupPath);
184
+ return backupPath;
185
+ }
186
+ async function writeWithBackup(path, content, mode) {
187
+ const backup = await backupIfExists(path);
188
+ await writeTextAtomic(path, content, mode);
189
+ return backup;
190
+ }
156
191
  var CLAUDE_SUBDIRS = ["project", "state", "_pending", "_backup"];
157
192
  var PROJECT_KNOWLEDGE_TEMPLATES = [
158
193
  "project/tech-stack.md",
@@ -194,20 +229,23 @@ async function writeProjectKnowledgeFiles(projectRoot, vars) {
194
229
  primaryUseCases: "(ch\u01B0a scan)",
195
230
  domainGlossary: "(ch\u01B0a scan)"
196
231
  };
232
+ const backups = [];
197
233
  for (const tpl of PROJECT_KNOWLEDGE_TEMPLATES) {
198
234
  const content = await renderTemplateByName(tpl, baseVars);
199
- const relative2 = tpl.replace(/^project\//, "");
200
- const outPath = join4(projectRoot, ".claude", "project", relative2);
201
- await writeTextAtomic(outPath, content);
235
+ const relative3 = tpl.replace(/^project\//, "");
236
+ const outPath = join4(projectRoot, ".claude", "project", relative3);
237
+ const backup = await writeWithBackup(outPath, content);
238
+ if (backup) backups.push(backup);
202
239
  }
240
+ return backups;
203
241
  }
204
242
  async function writeRootClaudeMd(projectRoot, vars) {
205
243
  const content = await renderTemplateByName("CLAUDE.md", vars);
206
- await writeTextAtomic(join4(projectRoot, "CLAUDE.md"), content);
244
+ return await writeWithBackup(join4(projectRoot, "CLAUDE.md"), content);
207
245
  }
208
246
  async function writeProjectSettings(projectRoot, vars) {
209
247
  const content = await renderTemplateByName("settings.json", vars);
210
- await writeTextAtomic(join4(projectRoot, ".claude", "settings.json"), content);
248
+ return await writeWithBackup(join4(projectRoot, ".claude", "settings.json"), content);
211
249
  }
212
250
  async function appendGitignoreEntries(projectRoot) {
213
251
  const path = join4(projectRoot, ".gitignore");
@@ -441,7 +479,7 @@ async function applyFixes(checks) {
441
479
  }
442
480
 
443
481
  // src/commands/init.ts
444
- import { join as join8, resolve } from "path";
482
+ import { join as join9, relative as relative2, resolve } from "path";
445
483
  import { confirm, input, select } from "@inquirer/prompts";
446
484
  import boxen2 from "boxen";
447
485
 
@@ -461,7 +499,7 @@ async function appendAuditEntry(action, detail) {
461
499
 
462
500
  // src/lib/team-pack-submodule-manager.ts
463
501
  import { join as join7 } from "path";
464
- var TEAM_PACK_REPO_URL = "https://github.com/LukeNALS/team-ai-pack.git";
502
+ var TEAM_PACK_REPO_URL = process.env.AVATAR_TEAM_PACK_REPO_URL ?? "https://github.com/LukeNALS/team-ai-pack.git";
465
503
  var TEAM_PACK_RELATIVE_PATH = ".claude/pack";
466
504
  async function addTeamPackSubmodule(projectRoot, tag) {
467
505
  await addSubmodule(TEAM_PACK_REPO_URL, TEAM_PACK_RELATIVE_PATH, projectRoot);
@@ -482,10 +520,59 @@ async function readPinnedPackVersion(projectRoot) {
482
520
  return sha.slice(0, 7);
483
521
  }
484
522
 
523
+ // src/commands/init-conflict-detection-helpers.ts
524
+ import { readdir } from "fs/promises";
525
+ import { join as join8 } from "path";
526
+ async function isEmptyOrMissing(path) {
527
+ if (!await pathExists(path)) return true;
528
+ try {
529
+ const entries = await readdir(path);
530
+ const meaningful = entries.filter((e) => !e.startsWith(".") && e !== "Thumbs.db");
531
+ return meaningful.length === 0;
532
+ } catch {
533
+ return false;
534
+ }
535
+ }
536
+ async function detectAvatarConflicts(projectRoot) {
537
+ const found = [];
538
+ for (const rel of AVATAR_MANAGED_PATHS) {
539
+ if (await pathExists(join8(projectRoot, rel))) found.push(rel);
540
+ }
541
+ return found;
542
+ }
543
+ async function findAlternativeWorkspaceName(parent, desiredName, maxAttempts = 10) {
544
+ for (let i = 2; i < maxAttempts; i++) {
545
+ const candidate = join8(parent, `${desiredName}-${i}`);
546
+ if (await isEmptyOrMissing(candidate)) return candidate;
547
+ }
548
+ return null;
549
+ }
550
+
551
+ // src/commands/init-scaffold-variable-builders.ts
552
+ var AVATAR_CLI_VERSION = "1.0.1";
553
+ function projectNameOf(projectRoot) {
554
+ return projectRoot.split("/").filter(Boolean).pop() ?? "avatar-project";
555
+ }
556
+ function inferWorkspaceName(repoUrl) {
557
+ const m = repoUrl.match(/[/:]([^/]+?)(\.git)?$/);
558
+ const base = m?.[1] ?? "client";
559
+ return `avatar-${base}-workspace`;
560
+ }
561
+ function buildScaffoldVariables(args) {
562
+ return {
563
+ projectName: args.projectName,
564
+ projectDescription: args.projectDescription,
565
+ teamOwner: args.teamOwner,
566
+ avatarVersion: AVATAR_CLI_VERSION,
567
+ packVersion: args.packVersion,
568
+ lastScan: (/* @__PURE__ */ new Date()).toISOString(),
569
+ mode: args.mode
570
+ };
571
+ }
572
+
485
573
  // src/commands/init.ts
486
- var AVATAR_CLI_VERSION = "1.0.0";
487
574
  function registerInitCommand(program2) {
488
- program2.command("init").description("Kh\u1EDFi t\u1EA1o Avatar trong d\u1EF1 \xE1n (3 mode: internal/client/library)").option("--mode <mode>", "internal | client | library").option("--skip-scan", "B\u1ECF qua b\u01B0\u1EDBc project-scanner").option("--pack-version <tag>", "Pin team-ai-pack v\xE0o version c\u1EE5 th\u1EC3").option("--client-repo <url>", "URL git c\u1EE7a client repo (mode=client)").option("--workspace-name <name>", "T\xEAn workspace (mode=client)").option("--workspace-parent <path>", "Th\u01B0 m\u1EE5c cha t\u1EA1o workspace (mode=client)").action(async (opts) => {
575
+ program2.command("init").description("Kh\u1EDFi t\u1EA1o Avatar trong d\u1EF1 \xE1n (3 mode: internal/client/library)").option("--mode <mode>", "internal | client | library").option("--skip-scan", "B\u1ECF qua b\u01B0\u1EDBc project-scanner").option("--pack-version <tag>", "Pin team-ai-pack v\xE0o version c\u1EE5 th\u1EC3").option("--client-repo <url>", "URL git c\u1EE7a client repo (mode=client)").option("--workspace-name <name>", "T\xEAn workspace (mode=client)").option("--workspace-parent <path>", "Th\u01B0 m\u1EE5c cha t\u1EA1o workspace (mode=client)").option("--force", "B\u1ECF qua prompt x\xE1c nh\u1EADn khi backup file Avatar \u0111\xE3 t\u1ED3n t\u1EA1i").option("--team-owner <email>", "Email team owner (b\u1ECF qua prompt)").option("--description <text>", "M\xF4 t\u1EA3 1 d\xF2ng c\u1EE7a d\u1EF1 \xE1n (b\u1ECF qua prompt)").option("--yes", "Auto-confirm t\u1EA5t c\u1EA3 prompt (k\u1EC3 c\u1EA3 maybeCommit)").action(async (opts) => {
489
576
  try {
490
577
  await runInit(opts);
491
578
  } catch (err) {
@@ -525,14 +612,25 @@ async function runInitInternal(opts, ownerEmail) {
525
612
  if (!await isGitRepo(projectRoot)) {
526
613
  throw new Error("Mode internal c\u1EA7n d\u1EF1 \xE1n \u0111\xE3 l\xE0 git repo. Ch\u1EA1y 'git init' tr\u01B0\u1EDBc r\u1ED3i th\u1EED l\u1EA1i.");
527
614
  }
528
- if (await pathExists(join8(projectRoot, ".claude"))) {
529
- throw new Error(
530
- ".claude/ \u0111\xE3 t\u1ED3n t\u1EA1i. Avatar kh\xF4ng override. X\xF3a th\u1EE7 c\xF4ng ho\u1EB7c d\xF9ng mode kh\xE1c."
531
- );
615
+ const conflicts = await detectAvatarConflicts(projectRoot);
616
+ if (conflicts.length > 0) {
617
+ log.warn("C\xE1c path sau \u0111\xE3 t\u1ED3n t\u1EA1i \u2014 Avatar s\u1EBD backup t\u1EEBng file con b\u1ECB ghi \u0111\xE8:");
618
+ for (const c of conflicts) log.warn(` - ${c}`);
619
+ log.warn("File con c\u1EE7a b\u1EA1n kh\xF4ng b\u1ECB Avatar t\u1EA1o s\u1EBD \u0111\u01B0\u1EE3c gi\u1EEF nguy\xEAn.");
620
+ log.warn("Khuy\u1EBFn ngh\u1ECB mode 'client'/'library' n\u1EBFu mu\u1ED1n c\xF4 l\u1EADp ho\xE0n to\xE0n.");
621
+ if (!opts.force) {
622
+ const proceed = await confirm({
623
+ message: "Ti\u1EBFp t\u1EE5c backup-and-merge?",
624
+ default: true
625
+ });
626
+ if (!proceed) {
627
+ throw new Error("H\u1EE7y init. D\xF9ng mode 'client'/'library' ho\u1EB7c x\xF3a th\u1EE7 c\xF4ng c\xE1c file tr\xEAn.");
628
+ }
629
+ }
532
630
  }
533
- const teamOwner = await promptTeamOwner(ownerEmail);
631
+ const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
534
632
  const projectName = projectNameOf(projectRoot);
535
- const projectDescription = await input({
633
+ const projectDescription = opts.description ?? await input({
536
634
  message: "M\xF4 t\u1EA3 ng\u1EAFn 1 d\xF2ng c\u1EE7a d\u1EF1 \xE1n:",
537
635
  default: `Avatar-managed project: ${projectName}`
538
636
  });
@@ -554,18 +652,25 @@ async function runInitInternal(opts, ownerEmail) {
554
652
  mode: "internal"
555
653
  });
556
654
  await createClaudeDirTree(projectRoot);
557
- await writeProjectKnowledgeFiles(projectRoot, vars);
558
- await writeRootClaudeMd(projectRoot, vars);
559
- await writeProjectSettings(projectRoot, vars);
655
+ const allBackups = [];
656
+ allBackups.push(...await writeProjectKnowledgeFiles(projectRoot, vars));
657
+ const claudeMdBackup = await writeRootClaudeMd(projectRoot, vars);
658
+ if (claudeMdBackup) allBackups.push(claudeMdBackup);
659
+ const settingsBackup = await writeProjectSettings(projectRoot, vars);
660
+ if (settingsBackup) allBackups.push(settingsBackup);
560
661
  await appendGitignoreEntries(projectRoot);
561
- await installGitHook(join8(projectRoot, ".git"), "post-merge");
662
+ if (allBackups.length > 0) {
663
+ log.success(`\u0110\xE3 backup ${allBackups.length} file c\u1EE7a b\u1EA1n:`);
664
+ for (const b of allBackups) log.info(` \u2192 ${relative2(projectRoot, b) || b}`);
665
+ }
666
+ await installGitHook(join9(projectRoot, ".git"), "post-merge");
562
667
  log.success("C\xE0i git hook post-merge");
563
668
  await appendAuditEntry("init", `mode=internal,project=${projectName}`);
564
- await maybeCommit(projectRoot, "internal");
669
+ await maybeCommit(projectRoot, "internal", opts.yes);
565
670
  printInitSuccessBox(projectRoot, "internal");
566
671
  }
567
672
  async function runInitClientOrLibrary(opts, mode, ownerEmail) {
568
- const teamOwner = await promptTeamOwner(ownerEmail);
673
+ const teamOwner = opts.teamOwner ?? await promptTeamOwner(ownerEmail);
569
674
  const clientRepoUrl = opts.clientRepo ?? await input({
570
675
  message: "URL git c\u1EE7a client repo:",
571
676
  validate: (v) => v.length > 0 ? true : "URL b\u1EAFt bu\u1ED9c"
@@ -576,10 +681,7 @@ async function runInitClientOrLibrary(opts, mode, ownerEmail) {
576
681
  default: inferredName
577
682
  });
578
683
  const workspaceParent = resolve(opts.workspaceParent ?? "..");
579
- const workspacePath = join8(workspaceParent, workspaceName);
580
- if (await pathExists(workspacePath)) {
581
- throw new Error(`Workspace path \u0111\xE3 t\u1ED3n t\u1EA1i: ${workspacePath}`);
582
- }
684
+ const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);
583
685
  await ensureDir(workspacePath);
584
686
  await git(workspacePath).init();
585
687
  const sp = spinner("Add submodule client repo + team-ai-pack...");
@@ -599,26 +701,39 @@ async function runInitClientOrLibrary(opts, mode, ownerEmail) {
599
701
  await writeRootClaudeMd(workspacePath, vars);
600
702
  await writeProjectSettings(workspacePath, vars);
601
703
  await appendGitignoreEntries(workspacePath);
602
- await ensureDir(join8(workspacePath, "notes"));
603
- await ensureDir(join8(workspacePath, "scripts"));
604
- await installGitHook(join8(workspacePath, ".git"), "post-merge");
605
- await installGitHook(join8(workspacePath, "src", ".git"), "pre-push");
704
+ await ensureDir(join9(workspacePath, "notes"));
705
+ await ensureDir(join9(workspacePath, "scripts"));
706
+ await installGitHook(join9(workspacePath, ".git"), "post-merge");
707
+ await installGitHook(join9(workspacePath, ".git", "modules", "src"), "pre-push");
606
708
  log.success("C\xE0i post-merge (workspace) + pre-push (src/)");
607
709
  await appendAuditEntry("init", `mode=${mode},workspace=${workspaceName}`);
608
- await maybeCommitWorkspace(workspacePath);
710
+ await maybeCommitWorkspace(workspacePath, opts.yes);
609
711
  printInitSuccessBox(workspacePath, mode);
610
712
  } catch (err) {
611
713
  sp.fail("Init workspace th\u1EA5t b\u1EA1i");
612
714
  throw err;
613
715
  }
614
716
  }
615
- function projectNameOf(projectRoot) {
616
- return projectRoot.split("/").filter(Boolean).pop() ?? "avatar-project";
617
- }
618
- function inferWorkspaceName(repoUrl) {
619
- const m = repoUrl.match(/[/:]([^/]+?)(\.git)?$/);
620
- const base = m?.[1] ?? "client";
621
- return `avatar-${base}-workspace`;
717
+ async function resolveWorkspacePath(parent, desiredName, force) {
718
+ const desired = join9(parent, desiredName);
719
+ if (await isEmptyOrMissing(desired)) return desired;
720
+ const alternative = await findAlternativeWorkspaceName(parent, desiredName);
721
+ if (!alternative) {
722
+ throw new Error(`Kh\xF4ng t\xECm \u0111\u01B0\u1EE3c workspace path kh\u1EA3 d\u1EE5ng trong ${parent}`);
723
+ }
724
+ log.warn(`Workspace path "${desired}" \u0111\xE3 c\xF3 n\u1ED9i dung.`);
725
+ if (force) {
726
+ log.info(`--force: d\xF9ng ${alternative}`);
727
+ return alternative;
728
+ }
729
+ const useAlt = await confirm({
730
+ message: `D\xF9ng "${alternative}" thay th\u1EBF?`,
731
+ default: true
732
+ });
733
+ if (!useAlt) {
734
+ throw new Error("H\u1EE7y init. Ch\u1EA1y l\u1EA1i v\u1EDBi --workspace-name kh\xE1c.");
735
+ }
736
+ return alternative;
622
737
  }
623
738
  async function promptTeamOwner(currentUserEmail) {
624
739
  return await input({
@@ -626,19 +741,8 @@ async function promptTeamOwner(currentUserEmail) {
626
741
  default: currentUserEmail
627
742
  });
628
743
  }
629
- function buildScaffoldVariables(args) {
630
- return {
631
- projectName: args.projectName,
632
- projectDescription: args.projectDescription,
633
- teamOwner: args.teamOwner,
634
- avatarVersion: AVATAR_CLI_VERSION,
635
- packVersion: args.packVersion,
636
- lastScan: (/* @__PURE__ */ new Date()).toISOString(),
637
- mode: args.mode
638
- };
639
- }
640
- async function maybeCommit(projectRoot, mode) {
641
- const wantCommit = await confirm({
744
+ async function maybeCommit(projectRoot, mode, autoYes) {
745
+ const wantCommit = autoYes ?? await confirm({
642
746
  message: "Commit ngay c\xE1c file Avatar \u0111\xE3 t\u1EA1o?",
643
747
  default: true
644
748
  });
@@ -648,8 +752,8 @@ async function maybeCommit(projectRoot, mode) {
648
752
  await g.commit(`chore: initialize Avatar in ${mode} mode`);
649
753
  log.success("\u0110\xE3 commit");
650
754
  }
651
- async function maybeCommitWorkspace(workspacePath) {
652
- const wantCommit = await confirm({
755
+ async function maybeCommitWorkspace(workspacePath, autoYes) {
756
+ const wantCommit = autoYes ?? await confirm({
653
757
  message: "Commit workspace ngay?",
654
758
  default: true
655
759
  });
@@ -904,22 +1008,22 @@ function registerSecretsCommand(program2) {
904
1008
 
905
1009
  // src/commands/status.ts
906
1010
  import { promises as fs6 } from "fs";
907
- import { join as join10 } from "path";
1011
+ import { join as join11 } from "path";
908
1012
  import boxen4 from "boxen";
909
1013
 
910
1014
  // src/lib/pack-backup-manager.ts
911
1015
  import { promises as fs5 } from "fs";
912
- import { join as join9 } from "path";
1016
+ import { join as join10 } from "path";
913
1017
  var BACKUP_DIR_NAME = "_backup";
914
1018
  async function listBackups(projectRoot) {
915
- const dir = join9(projectRoot, ".claude", BACKUP_DIR_NAME);
1019
+ const dir = join10(projectRoot, ".claude", BACKUP_DIR_NAME);
916
1020
  if (!await pathExists(dir)) return [];
917
1021
  const entries = await fs5.readdir(dir, { withFileTypes: true });
918
1022
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
919
1023
  }
920
1024
 
921
1025
  // src/commands/status.ts
922
- var AVATAR_CLI_VERSION2 = "1.0.0";
1026
+ var AVATAR_CLI_VERSION2 = "1.0.1";
923
1027
  function registerStatusCommand(program2) {
924
1028
  program2.command("status").description("Snapshot t\u1EE9c th\xEC: project, pack version, pending, backup").option("--json", "Output JSON cho script").action(async (opts) => {
925
1029
  try {
@@ -938,7 +1042,7 @@ function registerStatusCommand(program2) {
938
1042
  }
939
1043
  async function gatherStatus(cwd) {
940
1044
  const projectName = cwd.split("/").filter(Boolean).pop() ?? "unknown";
941
- const claudeRoot = join10(cwd, ".claude");
1045
+ const claudeRoot = join11(cwd, ".claude");
942
1046
  const hasAvatar = await pathExists(claudeRoot);
943
1047
  if (!hasAvatar) {
944
1048
  return {
@@ -951,8 +1055,8 @@ async function gatherStatus(cwd) {
951
1055
  hasAvatar: false
952
1056
  };
953
1057
  }
954
- const packVersion = await isGitRepo(join10(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
955
- const pendingDir = join10(claudeRoot, "_pending");
1058
+ const packVersion = await isGitRepo(join11(claudeRoot, "pack")) ? await readPinnedPackVersion(cwd).catch(() => null) : null;
1059
+ const pendingDir = join11(claudeRoot, "_pending");
956
1060
  const pendingCount = await pathExists(pendingDir) ? (await fs6.readdir(pendingDir)).filter((n) => n.endsWith(".diff.md")).length : 0;
957
1061
  const backupCount = (await listBackups(cwd)).length;
958
1062
  const techStackSummary = await readTechStackFirstLine(claudeRoot);
@@ -967,7 +1071,7 @@ async function gatherStatus(cwd) {
967
1071
  };
968
1072
  }
969
1073
  async function readTechStackFirstLine(claudeRoot) {
970
- const techStackPath = join10(claudeRoot, "project", "tech-stack.md");
1074
+ const techStackPath = join11(claudeRoot, "project", "tech-stack.md");
971
1075
  if (!await pathExists(techStackPath)) return "(no tech-stack.md)";
972
1076
  const content = await readText(techStackPath);
973
1077
  const firstNonHeaderLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#") && !l.startsWith(">"));
@@ -1001,7 +1105,7 @@ function registerToolsCommand(program2) {
1001
1105
  }
1002
1106
 
1003
1107
  // src/index.ts
1004
- var CLI_VERSION = "1.0.0";
1108
+ var CLI_VERSION = "1.0.1";
1005
1109
  var program = new Command();
1006
1110
  program.name("avatar").description("AI harness CLI for NAL Vietnam engineering").version(CLI_VERSION, "-v, --version", "Hi\u1EC3n th\u1ECB phi\xEAn b\u1EA3n Avatar CLI");
1007
1111
  registerLoginCommand(program);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/lib/terminal-logger.ts","../src/lib/not-implemented-stub.ts","../src/commands/commit.ts","../src/commands/doctor.ts","../src/lib/filesystem-helpers.ts","../src/lib/git-operations.ts","../src/lib/project-tree-scaffolder.ts","../src/lib/template-bundle-loader.ts","../src/lib/mustache-template-engine.ts","../src/lib/user-config-store.ts","../src/types/config-schema.ts","../src/commands/init.ts","../src/lib/audit-log-appender.ts","../src/lib/team-pack-submodule-manager.ts","../src/commands/login.ts","../src/lib/google-oauth-device-flow.ts","../src/commands/mcp-run.ts","../src/commands/restore.ts","../src/commands/review.ts","../src/commands/scan.ts","../src/commands/secrets.ts","../src/commands/status.ts","../src/lib/pack-backup-manager.ts","../src/commands/sync.ts","../src/commands/tools.ts"],"sourcesContent":["// Bootstrap: load commander, register all 13 subcommands, parse argv.\n// Each command lives in src/commands/<name>.ts as a function that accepts the\n// commander Command instance and registers itself.\nimport { Command } from \"commander\";\nimport { registerCommitCommand } from \"./commands/commit.js\";\nimport { registerDoctorCommand } from \"./commands/doctor.js\";\nimport { registerInitCommand } from \"./commands/init.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerMcpRunCommand } from \"./commands/mcp-run.js\";\nimport { registerRestoreCommand } from \"./commands/restore.js\";\nimport { registerReviewCommand } from \"./commands/review.js\";\nimport { registerScanCommand } from \"./commands/scan.js\";\nimport { registerSecretsCommand } from \"./commands/secrets.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerSyncCommand } from \"./commands/sync.js\";\nimport { registerToolsCommand } from \"./commands/tools.js\";\n\nconst CLI_VERSION = \"1.0.0\";\n\nconst program = new Command();\n\nprogram\n .name(\"avatar\")\n .description(\"AI harness CLI for NAL Vietnam engineering\")\n .version(CLI_VERSION, \"-v, --version\", \"Hiển thị phiên bản Avatar CLI\");\n\n// Register all commands. Order matches Chapter 09 spec in implementation doc.\nregisterLoginCommand(program);\nregisterInitCommand(program);\nregisterSyncCommand(program);\nregisterScanCommand(program);\nregisterReviewCommand(program);\nregisterStatusCommand(program);\nregisterDoctorCommand(program);\nregisterRestoreCommand(program);\nregisterCommitCommand(program);\nregisterToolsCommand(program);\nregisterSecretsCommand(program);\nregisterMcpRunCommand(program);\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n // Top-level error sink. Individual commands should handle their own errors;\n // anything reaching here is unexpected.\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`✗ Lỗi không xử lý được: ${msg}\\n`);\n process.exit(1);\n});\n","// Terminal logging helpers — chalk for color, ora for spinners.\n// All Avatar CLI output should funnel through here for consistent UX.\nimport chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\ntype LogFn = (message: string) => void;\n\nexport const log: {\n info: LogFn;\n success: LogFn;\n warn: LogFn;\n error: LogFn;\n dim: LogFn;\n plain: LogFn;\n} = {\n info: (m) => process.stdout.write(`${chalk.blue(\"ℹ\")} ${m}\\n`),\n success: (m) => process.stdout.write(`${chalk.green(\"✓\")} ${m}\\n`),\n warn: (m) => process.stdout.write(`${chalk.yellow(\"⚠\")} ${m}\\n`),\n error: (m) => process.stderr.write(`${chalk.red(\"✗\")} ${m}\\n`),\n dim: (m) => process.stdout.write(`${chalk.dim(m)}\\n`),\n plain: (m) => process.stdout.write(`${m}\\n`),\n};\n\n// Spinner wrapper — handles non-TTY by degrading to plain output.\nexport function spinner(text: string): Ora {\n return ora({\n text,\n spinner: \"dots\",\n isEnabled: process.stdout.isTTY ?? false,\n }).start();\n}\n\n// Chalk re-export so commands don't all import chalk separately.\nexport { chalk };\n","// Shared stub action for commands wired into commander but not yet implemented.\n// Prints a consistent message + exit code so users know it's coming.\nimport { chalk } from \"./terminal-logger.js\";\n\nexport function notImplementedYet(commandName: string, milestone?: string): () => void {\n return () => {\n process.stdout.write(\n `${chalk.yellow(\"⏳\")} ${chalk.bold(`avatar ${commandName}`)} — chưa implement ở milestone hiện tại.\\n`,\n );\n if (milestone) {\n process.stdout.write(` Dự kiến: ${chalk.cyan(milestone)}\\n`);\n }\n process.stdout.write(\" Spec đã có trong avatar-cli-implementation_4.html.\\n\");\n process.exit(0);\n };\n}\n","// `avatar commit [--src|--avatar|--both] [-m <msg>] [--push]` — Command 09.\n// Only valid in client/library mode. Splits commits between the client repo\n// (src/) and the Avatar workspace.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerCommitCommand(program: Command): void {\n program\n .command(\"commit\")\n .description(\"Commit code khách (src/) hoặc Avatar state riêng — chỉ client mode (M07)\")\n .option(\"--src\", \"Commit src/ → client remote\")\n .option(\"--avatar\", \"Commit Avatar state → workspace remote\")\n .option(\"--both\", \"Commit cả hai (src trước, avatar sau)\")\n .option(\"-m, --message <msg>\", \"Commit message\")\n .option(\"--push\", \"Tự động push sau khi commit\")\n .action(notImplementedYet(\"commit\", \"Milestone 07\"));\n}\n","import { spawnSync } from \"node:child_process\";\n// `avatar doctor [--fix]` — Command 07 spec.\n// Run a series of health checks and surface ✓/✗ for each. --fix attempts\n// auto-fixes for the ones safe to fix automatically.\nimport { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { pathExists } from \"../lib/filesystem-helpers.js\";\nimport { isGitRepo } from \"../lib/git-operations.js\";\nimport { installGitHook } from \"../lib/project-tree-scaffolder.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\nimport { isTokenExpired, readUserConfig } from \"../lib/user-config-store.js\";\n\ninterface CheckResult {\n name: string;\n status: \"ok\" | \"warn\" | \"fail\";\n detail: string;\n fixable: boolean;\n fix?: () => Promise<void>;\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command(\"doctor\")\n .description(\"Chẩn đoán cài đặt Avatar: hooks, MCP, login, submodule, ...\")\n .option(\"--fix\", \"Tự động fix các issue có thể fix tự động\")\n .action(async (opts: { fix?: boolean }) => {\n try {\n const checks = await runChecks(process.cwd());\n renderChecks(checks);\n if (opts.fix) await applyFixes(checks);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runChecks(cwd: string): Promise<CheckResult[]> {\n const checks: CheckResult[] = [];\n\n // 1. Node.js version >= 18.17\n const nodeVer = process.versions.node;\n const [major, minor] = nodeVer.split(\".\").map((n) => Number.parseInt(n, 10));\n const nodeOk = (major ?? 0) > 18 || ((major ?? 0) === 18 && (minor ?? 0) >= 17);\n checks.push({\n name: \"Node.js version\",\n status: nodeOk ? \"ok\" : \"fail\",\n detail: `v${nodeVer}${nodeOk ? \"\" : \" (cần >= 18.17)\"}`,\n fixable: false,\n });\n\n // 2. Login.\n const config = await readUserConfig();\n if (!config) {\n checks.push({\n name: \"Login status\",\n status: \"fail\",\n detail: \"Chưa đăng nhập — chạy 'avatar login'\",\n fixable: false,\n });\n } else if (isTokenExpired(config)) {\n checks.push({\n name: \"Login status\",\n status: \"warn\",\n detail: `Token hết hạn (${config.email}) — chạy 'avatar login'`,\n fixable: false,\n });\n } else {\n checks.push({\n name: \"Login status\",\n status: \"ok\",\n detail: `Logged in: ${config.email}`,\n fixable: false,\n });\n }\n\n // 3. Git repo.\n const gitRepo = await isGitRepo(cwd);\n checks.push({\n name: \"Git repository\",\n status: gitRepo ? \"ok\" : \"warn\",\n detail: gitRepo ? cwd : \"Không phải git repo (cần cho 'avatar init')\",\n fixable: false,\n });\n\n // 4. .claude/pack/ submodule.\n const packPath = join(cwd, \".claude\", \"pack\");\n const hasPack = await pathExists(packPath);\n checks.push({\n name: \"team-ai-pack submodule\",\n status: hasPack ? \"ok\" : \"warn\",\n detail: hasPack ? packPath : \"Avatar chưa init — chạy 'avatar init'\",\n fixable: false,\n });\n\n // 5. CLAUDE.md present.\n const claudeMdPath = join(cwd, \"CLAUDE.md\");\n const hasClaudeMd = await pathExists(claudeMdPath);\n checks.push({\n name: \"CLAUDE.md\",\n status: hasClaudeMd ? \"ok\" : \"warn\",\n detail: hasClaudeMd ? \"tồn tại ở project root\" : \"thiếu — chạy 'avatar init'\",\n fixable: false,\n });\n\n // 6. post-merge hook.\n const hookPath = join(cwd, \".git\", \"hooks\", \"post-merge\");\n const hasHook = await pathExists(hookPath);\n if (gitRepo && hasPack) {\n checks.push({\n name: \"Git hook post-merge\",\n status: hasHook ? \"ok\" : \"fail\",\n detail: hasHook ? \"installed\" : \"missing — fixable\",\n fixable: !hasHook,\n fix: hasHook\n ? undefined\n : async () => {\n await installGitHook(join(cwd, \".git\"), \"post-merge\");\n },\n });\n }\n\n // 7. .gitignore has Avatar entries.\n const gitignorePath = join(cwd, \".gitignore\");\n if (gitRepo) {\n let gitignoreOk = false;\n if (await pathExists(gitignorePath)) {\n const content = await fs.readFile(gitignorePath, \"utf8\");\n gitignoreOk = content.includes(\".claude/_pending/\");\n }\n checks.push({\n name: \".gitignore Avatar entries\",\n status: gitignoreOk ? \"ok\" : hasPack ? \"fail\" : \"warn\",\n detail: gitignoreOk ? \"có .claude/_pending/, .claude/_backup/\" : \"thiếu entries\",\n fixable: false,\n });\n }\n\n // 8. Claude Code CLI installed (best-effort `which claude`).\n const which = spawnSync(\"which\", [\"claude\"]);\n const hasClaudeCli = which.status === 0;\n checks.push({\n name: \"Claude Code CLI\",\n status: hasClaudeCli ? \"ok\" : \"warn\",\n detail: hasClaudeCli ? which.stdout.toString().trim() : \"không tìm thấy 'claude' trên PATH\",\n fixable: false,\n });\n\n return checks;\n}\n\nfunction renderChecks(checks: CheckResult[]): void {\n const lines = [chalk.bold(\"Avatar Doctor\"), \"─\".repeat(48)];\n let passed = 0;\n let issues = 0;\n let fixable = 0;\n for (const c of checks) {\n const icon =\n c.status === \"ok\"\n ? chalk.green(\"✓\")\n : c.status === \"warn\"\n ? chalk.yellow(\"⚠\")\n : chalk.red(\"✗\");\n lines.push(`${icon} ${c.name.padEnd(28)} ${chalk.dim(c.detail)}`);\n if (c.status === \"ok\") passed += 1;\n else {\n issues += 1;\n if (c.fixable) fixable += 1;\n }\n }\n lines.push(\"─\".repeat(48));\n lines.push(\n `${passed} checks passed, ${issues} issue${issues === 1 ? \"\" : \"s\"}${fixable > 0 ? ` (${fixable} fixable — chạy 'avatar doctor --fix')` : \"\"}`,\n );\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n\nasync function applyFixes(checks: CheckResult[]): Promise<void> {\n let count = 0;\n for (const c of checks) {\n if (c.fixable && c.fix) {\n try {\n await c.fix();\n log.success(`Fixed: ${c.name}`);\n count += 1;\n } catch (err) {\n log.error(`Failed to fix ${c.name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n if (count === 0) log.dim(\"Không có gì để fix tự động.\");\n}\n","// Thin promise-based wrappers around node:fs/promises with the patterns Avatar\n// uses most: ensure-dir, read-text, write-text-atomic, copy-dir-recursive.\n// Atomic write writes to a temp file then renames to avoid corruption if Avatar\n// crashes mid-write.\nimport { constants, promises as fs } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await fs.access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(path: string): Promise<void> {\n await fs.mkdir(path, { recursive: true });\n}\n\nexport async function readText(path: string): Promise<string> {\n return await fs.readFile(path, \"utf8\");\n}\n\nexport async function readJson<T>(path: string): Promise<T> {\n return JSON.parse(await readText(path)) as T;\n}\n\n// Atomic write: write to temp file then rename. Prevents corruption if process\n// is killed between open() and close(). chmod is applied AFTER rename.\nexport async function writeTextAtomic(path: string, content: string, mode?: number): Promise<void> {\n await ensureDir(dirname(path));\n const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;\n await fs.writeFile(tmp, content, \"utf8\");\n if (mode !== undefined) {\n await fs.chmod(tmp, mode);\n }\n await fs.rename(tmp, path);\n}\n\nexport async function writeJsonAtomic(path: string, data: unknown, mode?: number): Promise<void> {\n await writeTextAtomic(path, `${JSON.stringify(data, null, 2)}\\n`, mode);\n}\n\n// Recursive copy. excludeNames filters by basename at any depth (e.g. \".git\").\nexport async function copyDirRecursive(\n source: string,\n destination: string,\n excludeNames: string[] = [],\n): Promise<void> {\n await ensureDir(destination);\n const entries = await fs.readdir(source, { withFileTypes: true });\n for (const entry of entries) {\n if (excludeNames.includes(entry.name)) continue;\n const src = join(source, entry.name);\n const dst = join(destination, entry.name);\n if (entry.isDirectory()) {\n await copyDirRecursive(src, dst, excludeNames);\n } else if (entry.isSymbolicLink()) {\n const target = await fs.readlink(src);\n await fs.symlink(target, dst);\n } else {\n await fs.copyFile(src, dst);\n }\n }\n}\n\nexport async function removeRecursive(path: string): Promise<void> {\n await fs.rm(path, { recursive: true, force: true });\n}\n\nexport function relativeFromCwd(path: string): string {\n return relative(process.cwd(), path);\n}\n","import { join } from \"node:path\";\n// Wrapper around simple-git for the operations Avatar uses repeatedly:\n// repo detection, submodule add/update, status, log, tag listing.\n// Centralised so error formatting and cwd handling stays consistent.\nimport { type SimpleGit, simpleGit } from \"simple-git\";\nimport { pathExists } from \"./filesystem-helpers.js\";\n\nexport function git(cwd: string = process.cwd()): SimpleGit {\n return simpleGit({ baseDir: cwd, binary: \"git\" });\n}\n\nexport async function isGitRepo(cwd: string = process.cwd()): Promise<boolean> {\n return await pathExists(join(cwd, \".git\"));\n}\n\nexport async function currentBranch(cwd: string = process.cwd()): Promise<string> {\n const result = await git(cwd).revparse([\"--abbrev-ref\", \"HEAD\"]);\n return result.trim();\n}\n\nexport async function addSubmodule(\n repoUrl: string,\n destPath: string,\n cwd: string = process.cwd(),\n): Promise<void> {\n await git(cwd).subModule([\"add\", repoUrl, destPath]);\n}\n\n// Checkout a tag inside a submodule. Used after `addSubmodule` to pin to a\n// specific team-ai-pack release.\nexport async function checkoutTagInSubmodule(\n submodulePath: string,\n tag: string,\n cwd: string = process.cwd(),\n): Promise<void> {\n const submoduleCwd = join(cwd, submodulePath);\n await git(submoduleCwd).fetch([\"--tags\"]);\n await git(submoduleCwd).checkout(tag);\n}\n\nexport async function listTags(cwd: string = process.cwd()): Promise<string[]> {\n const result = await git(cwd).tags();\n return result.all;\n}\n\nexport async function latestTag(cwd: string = process.cwd()): Promise<string | null> {\n const tags = await listTags(cwd);\n return tags.length > 0 ? (tags[tags.length - 1] ?? null) : null;\n}\n\nexport async function currentCommitSha(cwd: string = process.cwd()): Promise<string> {\n const result = await git(cwd).revparse([\"HEAD\"]);\n return result.trim();\n}\n\nexport async function workingTreeIsDirty(cwd: string = process.cwd()): Promise<boolean> {\n const status = await git(cwd).status();\n return !status.isClean();\n}\n","import { promises as fs } from \"node:fs\";\n// Scaffold the .claude/ directory tree inside a project after submodule add.\n// Used by `avatar init` for all three modes (internal/client/library).\n//\n// Spec source: Chapter 02 (folder map) + Chapter 09 Command 02 BEHAVIOR.\nimport { join } from \"node:path\";\nimport type { InitMode } from \"../types/config-schema.js\";\nimport { ensureDir, pathExists, writeTextAtomic } from \"./filesystem-helpers.js\";\nimport { type TemplateName, loadHook, renderTemplateByName } from \"./template-bundle-loader.js\";\n\n// Subdirectories Avatar creates inside .claude/ on init. `pack/` is added\n// separately by the submodule manager — listed here for reference only.\nconst CLAUDE_SUBDIRS = [\"project\", \"state\", \"_pending\", \"_backup\"] as const;\n\nconst PROJECT_KNOWLEDGE_TEMPLATES: TemplateName[] = [\n \"project/tech-stack.md\",\n \"project/conventions.md\",\n \"project/architecture.md\",\n \"project/domain.md\",\n \"project/gotchas.md\",\n];\n\nexport interface ScaffoldVariables {\n projectName: string;\n projectDescription: string;\n teamOwner: string;\n avatarVersion: string;\n packVersion: string;\n lastScan: string;\n mode: InitMode;\n}\n\n// Create .claude/{project,state,_pending,_backup}/ and a .gitkeep in each\n// so they survive a fresh checkout.\nexport async function createClaudeDirTree(projectRoot: string): Promise<void> {\n const claudeRoot = join(projectRoot, \".claude\");\n await ensureDir(claudeRoot);\n for (const sub of CLAUDE_SUBDIRS) {\n const dir = join(claudeRoot, sub);\n await ensureDir(dir);\n await writeTextAtomic(join(dir, \".gitkeep\"), \"\");\n }\n}\n\n// Render and write all 5 project knowledge files from templates.\n// All start with placeholder content — scanners fill them in later.\nexport async function writeProjectKnowledgeFiles(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<void> {\n const baseVars = {\n ...vars,\n primaryLanguage: \"(chưa scan)\",\n frameworks: \"(chưa scan)\",\n databases: \"(chưa scan)\",\n testStack: \"(chưa scan)\",\n buildStack: \"(chưa scan)\",\n toolVersions: \"(chưa scan)\",\n codeStyle: \"(chưa scan)\",\n namingConvention: \"(chưa scan)\",\n folderStructure: \"(chưa scan)\",\n commitConvention: \"(chưa scan)\",\n linterConfig: \"(chưa scan)\",\n architectureOverview: \"(chưa scan)\",\n moduleLayout: \"(chưa scan)\",\n dataFlow: \"(chưa scan)\",\n externalIntegrations: \"(chưa scan)\",\n deploymentTopology: \"(chưa scan)\",\n domainDescription: \"(chưa scan)\",\n coreEntities: \"(chưa scan)\",\n primaryUseCases: \"(chưa scan)\",\n domainGlossary: \"(chưa scan)\",\n };\n for (const tpl of PROJECT_KNOWLEDGE_TEMPLATES) {\n const content = await renderTemplateByName(tpl, baseVars);\n const relative = tpl.replace(/^project\\//, \"\");\n const outPath = join(projectRoot, \".claude\", \"project\", relative);\n await writeTextAtomic(outPath, content);\n }\n}\n\n// Write root CLAUDE.md from template — this is the entry point Claude Code reads.\nexport async function writeRootClaudeMd(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<void> {\n const content = await renderTemplateByName(\"CLAUDE.md\", vars);\n await writeTextAtomic(join(projectRoot, \"CLAUDE.md\"), content);\n}\n\n// Write .claude/settings.json from template.\nexport async function writeProjectSettings(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<void> {\n const content = await renderTemplateByName(\"settings.json\", vars);\n await writeTextAtomic(join(projectRoot, \".claude\", \"settings.json\"), content);\n}\n\n// Append Avatar entries to project .gitignore (or create if missing).\n// Idempotent — skips if our marker line already present.\nexport async function appendGitignoreEntries(projectRoot: string): Promise<void> {\n const path = join(projectRoot, \".gitignore\");\n const tpl = await renderTemplateByName(\"gitignore\", {});\n const marker = \"# Avatar — git-ignored entries injected on `avatar init`\";\n\n let existing = \"\";\n if (await pathExists(path)) {\n existing = await fs.readFile(path, \"utf8\");\n if (existing.includes(marker)) return;\n }\n\n const separator = existing.endsWith(\"\\n\") || existing.length === 0 ? \"\" : \"\\n\";\n await writeTextAtomic(path, `${existing}${separator}\\n${tpl}`);\n}\n\n// Install git hooks into <gitDir>/hooks/. `gitDir` lets caller point at\n// src/.git/hooks (client mode pre-push) or workspace .git/hooks (post-merge).\nexport async function installGitHook(\n gitDir: string,\n hookName: \"post-merge\" | \"pre-push\",\n): Promise<void> {\n const content = await loadHook(hookName);\n const hooksDir = join(gitDir, \"hooks\");\n await ensureDir(hooksDir);\n const dest = join(hooksDir, hookName);\n await writeTextAtomic(dest, content, 0o755);\n}\n","import { dirname, join } from \"node:path\";\n// Resolve template files bundled inside the Avatar CLI package. tsup bundles\n// src/ into dist/index.js but does NOT bundle template files — they ship as\n// loose files alongside dist/ in the npm tarball.\n//\n// At build/dev time templates live in <repo>/src/templates and <repo>/src/hooks.\n// At install time they live in <pkg>/src/templates and <pkg>/src/hooks (because\n// we also `files: [\"bin\", \"dist\", \"README.md\"]` — TODO: extend `files` to ship\n// templates with the package; for now resolve relative to import.meta.url).\nimport { fileURLToPath } from \"node:url\";\nimport { readText } from \"./filesystem-helpers.js\";\nimport { renderTemplate } from \"./mustache-template-engine.js\";\n\n// dist/index.js is the runtime location. Templates ship as siblings of dist/\n// under src/templates/ and src/hooks/ — see package.json `files` field.\nconst HERE = dirname(fileURLToPath(import.meta.url));\n\n// At dev time (`tsup --watch`) HERE points into dist/. At build time same.\n// Templates live at ../src/templates relative to the bundled file.\nconst TEMPLATES_ROOT = join(HERE, \"..\", \"src\", \"templates\");\nconst HOOKS_ROOT = join(HERE, \"..\", \"src\", \"hooks\");\n\nexport type TemplateName =\n | \"CLAUDE.md\"\n | \"settings.json\"\n | \"gitignore\"\n | \"project/tech-stack.md\"\n | \"project/conventions.md\"\n | \"project/architecture.md\"\n | \"project/domain.md\"\n | \"project/gotchas.md\";\n\nexport type HookName = \"post-merge\" | \"pre-push\";\n\nexport async function loadTemplate(name: TemplateName): Promise<string> {\n return await readText(join(TEMPLATES_ROOT, `${name}.tpl`));\n}\n\nexport async function renderTemplateByName(\n name: TemplateName,\n variables: Record<string, string | number | undefined>,\n): Promise<string> {\n const source = await loadTemplate(name);\n return renderTemplate(source, variables);\n}\n\nexport async function loadHook(name: HookName): Promise<string> {\n return await readText(join(HOOKS_ROOT, `${name}.sh.tpl`));\n}\n","// Minimal mustache-style {{variable}} replacement engine. Avoids pulling in\n// full mustache dependency — Avatar templates only need flat variable\n// substitution, no loops or conditionals.\n//\n// Unknown variables left as-is, NOT replaced with empty string. This makes\n// missing variables visible in output for easier debugging.\n\nconst TEMPLATE_PATTERN = /\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g;\n\nexport function renderTemplate(\n source: string,\n variables: Record<string, string | number | undefined>,\n): string {\n return source.replace(TEMPLATE_PATTERN, (match, key: string) => {\n const value = variables[key];\n if (value === undefined) return match;\n return String(value);\n });\n}\n\n// Extract the variable names referenced by a template — useful for\n// scaffolding tests and validating that callers pass every required key.\nexport function extractVariables(source: string): string[] {\n const found = new Set<string>();\n for (const match of source.matchAll(TEMPLATE_PATTERN)) {\n if (match[1]) found.add(match[1]);\n }\n return Array.from(found).sort();\n}\n","// Read/write ~/.avatar/config.json (OAuth credentials) and ~/.avatar/state.json\n// (non-credential user state). Validates with Zod, enforces chmod 600 on config.\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n type UserConfig,\n type UserState,\n userConfigSchema,\n userStateSchema,\n} from \"../types/config-schema.js\";\nimport { ensureDir, pathExists, readJson, writeJsonAtomic } from \"./filesystem-helpers.js\";\n\nexport const AVATAR_HOME = join(homedir(), \".avatar\");\nexport const USER_CONFIG_PATH = join(AVATAR_HOME, \"config.json\");\nexport const USER_STATE_PATH = join(AVATAR_HOME, \"state.json\");\nexport const AUDIT_LOG_PATH = join(AVATAR_HOME, \"audit.log\");\nexport const BACKUPS_DIR = join(AVATAR_HOME, \"backups\");\n\n// chmod 600 — owner-only read/write. Matches spec for credential files.\nconst SECRET_FILE_MODE = 0o600;\n\nexport async function ensureAvatarHome(): Promise<void> {\n await ensureDir(AVATAR_HOME);\n}\n\nexport async function readUserConfig(): Promise<UserConfig | null> {\n if (!(await pathExists(USER_CONFIG_PATH))) return null;\n const raw = await readJson<unknown>(USER_CONFIG_PATH);\n const parsed = userConfigSchema.safeParse(raw);\n if (!parsed.success) return null;\n return parsed.data;\n}\n\nexport async function writeUserConfig(config: UserConfig): Promise<void> {\n await ensureAvatarHome();\n await writeJsonAtomic(USER_CONFIG_PATH, config, SECRET_FILE_MODE);\n}\n\nexport async function clearUserConfig(): Promise<void> {\n if (await pathExists(USER_CONFIG_PATH)) {\n const { promises: fs } = await import(\"node:fs\");\n await fs.unlink(USER_CONFIG_PATH);\n }\n}\n\nexport async function readUserState(): Promise<UserState> {\n if (!(await pathExists(USER_STATE_PATH))) {\n return userStateSchema.parse({});\n }\n const raw = await readJson<unknown>(USER_STATE_PATH);\n const parsed = userStateSchema.safeParse(raw);\n if (!parsed.success) return userStateSchema.parse({});\n return parsed.data;\n}\n\nexport async function writeUserState(state: UserState): Promise<void> {\n await ensureAvatarHome();\n await writeJsonAtomic(USER_STATE_PATH, state);\n}\n\n// Token is considered expired if it would expire in the next 60 seconds.\n// 60s buffer accounts for slow network round-trip on the upcoming request.\nexport function isTokenExpired(config: UserConfig): boolean {\n const expiresAt = Date.parse(config.expires_at);\n return Number.isNaN(expiresAt) || expiresAt - Date.now() < 60_000;\n}\n","// Zod schemas + inferred TypeScript types for all on-disk Avatar config files.\n// Centralised so commands import from one place and validation matches storage.\nimport { z } from \"zod\";\n\n// ~/.avatar/config.json — OAuth credentials after `avatar login`.\n// chmod 600 enforced at write site.\nexport const userConfigSchema = z.object({\n email: z.string().email(),\n name: z.string(),\n access_token: z.string().min(1),\n refresh_token: z.string().min(1),\n expires_at: z.string().datetime(),\n id_token: z.string().min(1),\n});\nexport type UserConfig = z.infer<typeof userConfigSchema>;\n\n// ~/.avatar/state.json — non-credential user state (tool install records,\n// allowed-paths chosen for filesystem MCP, etc).\nexport const userStateSchema = z.object({\n installed_tools: z\n .record(\n z.string(),\n z.object({\n version: z.string().optional(),\n installed_at: z.string().datetime(),\n install_method: z.string(),\n }),\n )\n .default({}),\n tool_inputs: z.record(z.string(), z.unknown()).default({}),\n});\nexport type UserState = z.infer<typeof userStateSchema>;\n\n// .claude/settings.json — per-project Claude Code settings emitted by `avatar init`.\nexport const projectSettingsSchema = z.object({\n allowedTools: z.array(z.string()),\n hooks: z\n .object({\n PostToolUse: z.array(z.unknown()).optional(),\n })\n .partial()\n .optional(),\n env: z.record(z.string(), z.string()).default({}),\n});\nexport type ProjectSettings = z.infer<typeof projectSettingsSchema>;\n\n// Init mode — determines workspace topology (Chapter 02 + Chapter 07 spec).\nexport const initModeSchema = z.enum([\"internal\", \"client\", \"library\"]);\nexport type InitMode = z.infer<typeof initModeSchema>;\n","// `avatar init` — Command 02 spec.\n// Three modes: internal (Avatar files commit with code), client (Pattern A,\n// workspace + src/ submodule), library (same topology as client).\n//\n// This implementation focuses on the structural work: submodules, directory\n// tree, templates, git hooks. The scanner wizard step (Câu 3/3 — MCP tools)\n// is deferred — tools.ts is stubbed for this milestone.\n\nimport { join, resolve } from \"node:path\";\nimport { confirm, input, select } from \"@inquirer/prompts\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport { ensureDir, pathExists } from \"../lib/filesystem-helpers.js\";\nimport { git, isGitRepo } from \"../lib/git-operations.js\";\nimport {\n type ScaffoldVariables,\n appendGitignoreEntries,\n createClaudeDirTree,\n installGitHook,\n writeProjectKnowledgeFiles,\n writeProjectSettings,\n writeRootClaudeMd,\n} from \"../lib/project-tree-scaffolder.js\";\nimport { TEAM_PACK_REPO_URL, addTeamPackSubmodule } from \"../lib/team-pack-submodule-manager.js\";\nimport { chalk, log, spinner } from \"../lib/terminal-logger.js\";\nimport { isTokenExpired, readUserConfig } from \"../lib/user-config-store.js\";\nimport type { InitMode } from \"../types/config-schema.js\";\n\nconst AVATAR_CLI_VERSION = \"1.0.0\";\n\ninterface InitOptions {\n mode?: InitMode;\n skipScan?: boolean;\n packVersion?: string;\n clientRepo?: string;\n workspaceName?: string;\n workspaceParent?: string;\n}\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Khởi tạo Avatar trong dự án (3 mode: internal/client/library)\")\n .option(\"--mode <mode>\", \"internal | client | library\")\n .option(\"--skip-scan\", \"Bỏ qua bước project-scanner\")\n .option(\"--pack-version <tag>\", \"Pin team-ai-pack vào version cụ thể\")\n .option(\"--client-repo <url>\", \"URL git của client repo (mode=client)\")\n .option(\"--workspace-name <name>\", \"Tên workspace (mode=client)\")\n .option(\"--workspace-parent <path>\", \"Thư mục cha tạo workspace (mode=client)\")\n .action(async (opts: InitOptions) => {\n try {\n await runInit(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runInit(opts: InitOptions): Promise<void> {\n // Precondition: logged in.\n const userConfig = await readUserConfig();\n if (!userConfig || isTokenExpired(userConfig)) {\n log.error(\"Chưa đăng nhập hoặc token đã hết hạn. Chạy 'avatar login' trước.\");\n process.exit(1);\n }\n\n // Step 1: determine mode. If not provided, prompt.\n const mode = opts.mode ?? (await promptMode());\n\n // Step 2: route by mode.\n if (mode === \"internal\") {\n await runInitInternal(opts, userConfig.email);\n } else {\n await runInitClientOrLibrary(opts, mode, userConfig.email);\n }\n}\n\nasync function promptMode(): Promise<InitMode> {\n return (await select({\n message: \"Đây là loại dự án gì?\",\n choices: [\n {\n name: \"Nội bộ NAL (Avatar files commit cùng code)\",\n value: \"internal\" as const,\n },\n { name: \"Client (Pattern A — tách workspace)\", value: \"client\" as const },\n { name: \"Library/SDK public (tách workspace)\", value: \"library\" as const },\n ],\n })) as InitMode;\n}\n\n// ─── MODE: INTERNAL ─────────────────────────────────────────────────────────\nasync function runInitInternal(opts: InitOptions, ownerEmail: string): Promise<void> {\n const projectRoot = process.cwd();\n\n if (!(await isGitRepo(projectRoot))) {\n throw new Error(\"Mode internal cần dự án đã là git repo. Chạy 'git init' trước rồi thử lại.\");\n }\n if (await pathExists(join(projectRoot, \".claude\"))) {\n throw new Error(\n \".claude/ đã tồn tại. Avatar không override. Xóa thủ công hoặc dùng mode khác.\",\n );\n }\n\n const teamOwner = await promptTeamOwner(ownerEmail);\n\n const projectName = projectNameOf(projectRoot);\n const projectDescription = await input({\n message: \"Mô tả ngắn 1 dòng của dự án:\",\n default: `Avatar-managed project: ${projectName}`,\n });\n\n // Add team-ai-pack submodule.\n const sp = spinner(`Thêm submodule team-ai-pack từ ${TEAM_PACK_REPO_URL}...`);\n let pinnedTag: string | null = null;\n try {\n const result = await addTeamPackSubmodule(projectRoot, opts.packVersion);\n pinnedTag = result.pinnedTag;\n sp.succeed(`Đã pin team-ai-pack vào ${pinnedTag ?? \"HEAD\"}`);\n } catch (err) {\n sp.fail(\"Add submodule thất bại\");\n throw err;\n }\n\n // Scaffold .claude/ tree + templates.\n const vars = buildScaffoldVariables({\n projectName,\n projectDescription,\n teamOwner,\n packVersion: pinnedTag ?? \"HEAD\",\n mode: \"internal\",\n });\n await createClaudeDirTree(projectRoot);\n await writeProjectKnowledgeFiles(projectRoot, vars);\n await writeRootClaudeMd(projectRoot, vars);\n await writeProjectSettings(projectRoot, vars);\n await appendGitignoreEntries(projectRoot);\n\n // Install post-merge hook in project's .git/hooks/.\n await installGitHook(join(projectRoot, \".git\"), \"post-merge\");\n log.success(\"Cài git hook post-merge\");\n\n await appendAuditEntry(\"init\", `mode=internal,project=${projectName}`);\n await maybeCommit(projectRoot, \"internal\");\n printInitSuccessBox(projectRoot, \"internal\");\n}\n\n// ─── MODE: CLIENT / LIBRARY ─────────────────────────────────────────────────\nasync function runInitClientOrLibrary(\n opts: InitOptions,\n mode: \"client\" | \"library\",\n ownerEmail: string,\n): Promise<void> {\n // Collect required inputs (5 wizard questions per spec).\n const teamOwner = await promptTeamOwner(ownerEmail);\n const clientRepoUrl =\n opts.clientRepo ??\n (await input({\n message: \"URL git của client repo:\",\n validate: (v) => (v.length > 0 ? true : \"URL bắt buộc\"),\n }));\n const inferredName = inferWorkspaceName(clientRepoUrl);\n const workspaceName =\n opts.workspaceName ??\n (await input({\n message: \"Tên workspace:\",\n default: inferredName,\n }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n\n const workspacePath = join(workspaceParent, workspaceName);\n if (await pathExists(workspacePath)) {\n throw new Error(`Workspace path đã tồn tại: ${workspacePath}`);\n }\n\n // Create workspace + git init.\n await ensureDir(workspacePath);\n await git(workspacePath).init();\n\n // Add both submodules in parallel.\n const sp = spinner(\"Add submodule client repo + team-ai-pack...\");\n try {\n await git(workspacePath).subModule([\"add\", clientRepoUrl, \"src\"]);\n const result = await addTeamPackSubmodule(workspacePath, opts.packVersion);\n sp.succeed(`Workspace pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n\n // Scaffold workspace knowledge using projectName derived from workspace.\n const vars = buildScaffoldVariables({\n projectName: workspaceName,\n projectDescription: `Avatar ${mode} workspace for ${clientRepoUrl}`,\n teamOwner,\n packVersion: result.pinnedTag ?? \"HEAD\",\n mode,\n });\n\n await createClaudeDirTree(workspacePath);\n await writeProjectKnowledgeFiles(workspacePath, vars);\n await writeRootClaudeMd(workspacePath, vars);\n await writeProjectSettings(workspacePath, vars);\n await appendGitignoreEntries(workspacePath);\n\n // Extra dirs only client/library mode uses.\n await ensureDir(join(workspacePath, \"notes\"));\n await ensureDir(join(workspacePath, \"scripts\"));\n\n // Install post-merge in workspace, pre-push in src/ to block Avatar leaks.\n await installGitHook(join(workspacePath, \".git\"), \"post-merge\");\n await installGitHook(join(workspacePath, \"src\", \".git\"), \"pre-push\");\n log.success(\"Cài post-merge (workspace) + pre-push (src/)\");\n\n await appendAuditEntry(\"init\", `mode=${mode},workspace=${workspaceName}`);\n await maybeCommitWorkspace(workspacePath);\n printInitSuccessBox(workspacePath, mode);\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────────────────\n\nfunction projectNameOf(projectRoot: string): string {\n return projectRoot.split(\"/\").filter(Boolean).pop() ?? \"avatar-project\";\n}\n\nfunction inferWorkspaceName(repoUrl: string): string {\n // git@github.com:org/repo.git → repo\n const m = repoUrl.match(/[/:]([^/]+?)(\\.git)?$/);\n const base = m?.[1] ?? \"client\";\n return `avatar-${base}-workspace`;\n}\n\nasync function promptTeamOwner(currentUserEmail: string): Promise<string> {\n return await input({\n message: \"Team owner email:\",\n default: currentUserEmail,\n });\n}\n\nfunction buildScaffoldVariables(args: {\n projectName: string;\n projectDescription: string;\n teamOwner: string;\n packVersion: string;\n mode: InitMode;\n}): ScaffoldVariables {\n return {\n projectName: args.projectName,\n projectDescription: args.projectDescription,\n teamOwner: args.teamOwner,\n avatarVersion: AVATAR_CLI_VERSION,\n packVersion: args.packVersion,\n lastScan: new Date().toISOString(),\n mode: args.mode,\n };\n}\n\nasync function maybeCommit(projectRoot: string, mode: InitMode): Promise<void> {\n const wantCommit = await confirm({\n message: \"Commit ngay các file Avatar đã tạo?\",\n default: true,\n });\n if (!wantCommit) return;\n const g = git(projectRoot);\n await g.add([\".claude/\", \"CLAUDE.md\", \".gitignore\", \".gitmodules\"]);\n await g.commit(`chore: initialize Avatar in ${mode} mode`);\n log.success(\"Đã commit\");\n}\n\nasync function maybeCommitWorkspace(workspacePath: string): Promise<void> {\n const wantCommit = await confirm({\n message: \"Commit workspace ngay?\",\n default: true,\n });\n if (!wantCommit) return;\n const g = git(workspacePath);\n await g.add([\"CLAUDE.md\", \".claude/\", \".gitignore\", \".gitmodules\", \"notes/\", \"scripts/\"]);\n await g.commit(\"chore: initialize Avatar workspace for client\");\n log.success(\"Đã commit workspace\");\n}\n\nfunction printInitSuccessBox(rootPath: string, mode: InitMode): void {\n const lines: string[] = [];\n if (mode === \"internal\") {\n lines.push(`${chalk.green(\"✓\")} Avatar đã sẵn sàng trong dự án`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(\"claude\")} Mở Claude Code`);\n lines.push(` ${chalk.cyan(\"avatar status\")} Xem snapshot`);\n lines.push(` ${chalk.cyan(\"avatar sync\")} Pull team-ai-pack mới`);\n } else {\n lines.push(`${chalk.green(\"✓\")} Workspace sẵn sàng: ${rootPath}`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(`cd ${rootPath}`)}`);\n lines.push(` ${chalk.cyan(\"claude\")} Mở Claude Code ở workspace root`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(\"avatar commit --src\")} Commit code khách lên client remote`);\n lines.push(\n ` ${chalk.cyan(\"avatar commit --avatar\")} Commit Avatar state lên workspace remote`,\n );\n lines.push(` ${chalk.cyan(\"avatar sync\")} Sync team pack`);\n }\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","// Append-only audit log at ~/.avatar/audit.log. Records sensitive actions\n// (secrets set/rm, tool install/remove) WITHOUT logging secret values.\n// Used by `avatar doctor` to surface recent activity.\nimport { promises as fs } from \"node:fs\";\nimport { AUDIT_LOG_PATH, ensureAvatarHome } from \"./user-config-store.js\";\n\nexport type AuditAction =\n | \"login\"\n | \"login_reset\"\n | \"logout\"\n | \"secret_set\"\n | \"secret_rm\"\n | \"tool_install\"\n | \"tool_remove\"\n | \"init\"\n | \"sync\"\n | \"restore\";\n\nexport interface AuditEntry {\n timestamp: string;\n action: AuditAction;\n detail?: string;\n}\n\nexport async function appendAuditEntry(action: AuditAction, detail?: string): Promise<void> {\n await ensureAvatarHome();\n const entry: AuditEntry = {\n timestamp: new Date().toISOString(),\n action,\n ...(detail ? { detail } : {}),\n };\n const line = `${JSON.stringify(entry)}\\n`;\n await fs.appendFile(AUDIT_LOG_PATH, line, \"utf8\");\n}\n","// Manage the team-ai-pack git submodule lifecycle: add, update, pin-to-tag,\n// changelog extraction. Used by `avatar init` and `avatar sync`.\nimport { join } from \"node:path\";\nimport {\n addSubmodule,\n checkoutTagInSubmodule,\n currentCommitSha,\n latestTag,\n} from \"./git-operations.js\";\n\nexport const TEAM_PACK_REPO_URL = \"https://github.com/LukeNALS/team-ai-pack.git\";\nexport const TEAM_PACK_RELATIVE_PATH = \".claude/pack\";\n\n// Add the team-ai-pack submodule into a fresh project and pin it to a tag.\n// If `tag` is omitted, checks out the latest tag in the freshly-cloned submodule.\nexport async function addTeamPackSubmodule(\n projectRoot: string,\n tag?: string,\n): Promise<{ pinnedTag: string | null }> {\n await addSubmodule(TEAM_PACK_REPO_URL, TEAM_PACK_RELATIVE_PATH, projectRoot);\n\n // Resolve which tag to pin to. If caller passed one, honour it; otherwise\n // ask the just-cloned submodule for its latest tag.\n let target = tag ?? null;\n if (!target) {\n target = await latestTag(join(projectRoot, TEAM_PACK_RELATIVE_PATH));\n }\n\n if (target) {\n await checkoutTagInSubmodule(TEAM_PACK_RELATIVE_PATH, target, projectRoot);\n }\n return { pinnedTag: target };\n}\n\n// Read the current pinned version of the pack submodule. Returns the tag name\n// if HEAD matches a tag, otherwise the short SHA.\nexport async function readPinnedPackVersion(projectRoot: string): Promise<string> {\n const submoduleRoot = join(projectRoot, TEAM_PACK_RELATIVE_PATH);\n const tag = await latestTag(submoduleRoot);\n if (tag) return tag;\n const sha = await currentCommitSha(submoduleRoot);\n return sha.slice(0, 7);\n}\n","import boxen from \"boxen\";\n// `avatar login [--reset]` — Command 01 spec.\n// Implements the user-facing flow: announce verification URL, open browser,\n// poll Google until token returned, validate domain, persist credentials.\nimport type { Command } from \"commander\";\nimport open from \"open\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport {\n buildUserConfig,\n buildVerificationUrl,\n decodeIdToken,\n pollForToken,\n requestDeviceCode,\n revokeToken,\n verifyHostedDomain,\n} from \"../lib/google-oauth-device-flow.js\";\nimport { chalk, log, spinner } from \"../lib/terminal-logger.js\";\nimport {\n USER_CONFIG_PATH,\n clearUserConfig,\n isTokenExpired,\n readUserConfig,\n writeUserConfig,\n} from \"../lib/user-config-store.js\";\n\nexport function registerLoginCommand(program: Command): void {\n program\n .command(\"login\")\n .description(\"Đăng nhập Google SSO (workspace @nal.vn)\")\n .option(\"--reset\", \"Xóa credential cũ và đăng nhập lại\")\n .action(async (opts: { reset?: boolean }) => {\n try {\n await runLogin(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runLogin(opts: { reset?: boolean }): Promise<void> {\n // Step 1 of spec: short-circuit if already logged in and token is still good.\n if (opts.reset) {\n await clearUserConfig();\n await appendAuditEntry(\"login_reset\");\n } else {\n const existing = await readUserConfig();\n if (existing && !isTokenExpired(existing)) {\n log.success(`Đã đăng nhập: ${existing.email}`);\n return;\n }\n }\n\n // Step 2: request device + user code.\n const deviceSpinner = spinner(\"Đang yêu cầu device code từ Google...\");\n let deviceCode: Awaited<ReturnType<typeof requestDeviceCode>>;\n try {\n deviceCode = await requestDeviceCode();\n deviceSpinner.succeed(\"Nhận device code\");\n } catch (err) {\n deviceSpinner.fail(\"Không kết nối được Google\");\n throw err;\n }\n\n // Step 3: display instructions to user.\n const verificationUrl = buildVerificationUrl(deviceCode);\n const instructions = [\n `1. Truy cập: ${chalk.cyan(deviceCode.verification_url)}`,\n `2. Nhập code: ${chalk.bold.yellow(deviceCode.user_code)}`,\n \"\",\n `Hoặc Avatar tự mở browser, click ${chalk.green(\"Allow\")}...`,\n ].join(\"\\n\");\n process.stdout.write(`${boxen(instructions, { padding: 1, borderStyle: \"round\" })}\\n`);\n\n // Step 4: open browser. Failure here is non-fatal — user can copy URL manually.\n void open(verificationUrl).catch(() => {\n log.dim(\"(Không mở được browser tự động — copy URL ở trên)\");\n });\n\n // Step 5: poll token endpoint until success or expiry.\n const waitSpinner = spinner(\"Đang chờ xác nhận trong browser...\");\n const intervalMs = deviceCode.interval * 1000;\n const deadline = Date.now() + deviceCode.expires_in * 1000;\n\n let token = null;\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n try {\n token = await pollForToken(deviceCode.device_code);\n if (token) break;\n } catch (err) {\n waitSpinner.fail(\"Xác thực thất bại\");\n throw err;\n }\n }\n if (!token) {\n waitSpinner.fail(\"Hết hạn chờ (5 phút). Chạy lại 'avatar login'.\");\n process.exit(1);\n }\n waitSpinner.succeed(\"Đã nhận token từ Google\");\n\n // Step 6: verify hosted domain. Revoke token if claim is wrong.\n const claims = decodeIdToken(token.id_token);\n try {\n verifyHostedDomain(claims);\n } catch (err) {\n await revokeToken(token.access_token);\n throw err;\n }\n\n // Step 7: persist credentials with chmod 600.\n const userConfig = buildUserConfig(token, claims);\n await writeUserConfig(userConfig);\n await appendAuditEntry(\"login\", userConfig.email);\n\n log.success(`Xác thực thành công: ${userConfig.email}`);\n log.success(`Verify hosted domain: ${claims.hd} ✓`);\n log.success(`Lưu credential vào ${USER_CONFIG_PATH} (chmod 600)`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// Google OAuth 2.0 Device Authorization Grant (RFC 8628) implementation.\n//\n// Why Device Flow: Avatar is a terminal CLI with no browser redirect URL.\n// The user logs in via google.com/device on a browser and the CLI polls\n// Google for the resulting token.\n//\n// Why the client secret is bundled in source: Device Flow does not treat the\n// secret as a security boundary — the human Allow click in the browser is.\n// Google's TV/Limited Input docs explicitly permit this.\n//\n// To rotate: Google Cloud Console → APIs & Services → Credentials → click the\n// OAuth client → Reset Secret. Replace GOOGLE_CLIENT_SECRET below.\n\nimport type { UserConfig } from \"../types/config-schema.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuth client config (hardcoded — see file header for rationale).\n// To regenerate: Google Cloud Console → project \"avatar-cli\" → Clients.\n// Application type must be \"TV and Limited Input devices\".\n// ─────────────────────────────────────────────────────────────────────────────\nexport const GOOGLE_CLIENT_ID =\n \"1014766441755-i4jimivh5rd7vt8phuhmepmt45sovtph.apps.googleusercontent.com\";\nexport const GOOGLE_CLIENT_SECRET = \"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\";\n\n// Restrict to the NAL Workspace domain. Enforced at TWO layers:\n// 1. ?hd=nal.vn appended to the verification URL — Google filters the picker\n// 2. Verify id_token.hd === HOSTED_DOMAIN after token exchange (defense-in-depth)\nexport const HOSTED_DOMAIN = \"nal.vn\";\n\nexport const SCOPES = [\"openid\", \"email\", \"profile\"];\n\nconst DEVICE_CODE_URL = \"https://oauth2.googleapis.com/device/code\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nconst REVOKE_URL = \"https://oauth2.googleapis.com/revoke\";\n\nexport interface DeviceCodeResponse {\n device_code: string;\n user_code: string;\n verification_url: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface TokenResponse {\n access_token: string;\n refresh_token: string;\n id_token: string;\n expires_in: number;\n token_type: string;\n scope: string;\n}\n\nexport interface IdTokenClaims {\n email: string;\n email_verified: boolean;\n name?: string;\n hd?: string;\n exp: number;\n iss: string;\n aud: string;\n}\n\n// ── Step 2 of Command 01 spec: request device + user codes.\nexport async function requestDeviceCode(): Promise<DeviceCodeResponse> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n scope: SCOPES.join(\" \"),\n });\n const res = await fetch(DEVICE_CODE_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Device code request failed (${res.status}): ${text}`);\n }\n return (await res.json()) as DeviceCodeResponse;\n}\n\n// ── Step 5: poll the token endpoint until user authorises or expiry.\n// Returns the token response on success, null while still pending.\n// Throws on hard errors (access_denied, expired_token).\nexport async function pollForToken(deviceCode: string): Promise<TokenResponse | null> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n device_code: deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n });\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (res.ok) {\n return (await res.json()) as TokenResponse;\n }\n\n // Google returns 4xx with a JSON {error} field for both pending and fatal states.\n let errorCode = \"\";\n try {\n const data = (await res.json()) as { error?: string };\n errorCode = data.error ?? \"\";\n } catch {\n errorCode = \"\";\n }\n\n if (errorCode === \"authorization_pending\" || errorCode === \"slow_down\") {\n return null;\n }\n if (errorCode === \"access_denied\") {\n throw new Error(\"User từ chối quyền truy cập\");\n }\n if (errorCode === \"expired_token\") {\n throw new Error(\"Hết hạn chờ (5 phút). Chạy lại 'avatar login'.\");\n }\n throw new Error(`OAuth token endpoint trả lỗi: ${errorCode || res.status}`);\n}\n\n// Decode JWT payload WITHOUT verifying signature. Safe here because we receive\n// the token directly from Google over HTTPS — no MITM surface.\nexport function decodeIdToken(idToken: string): IdTokenClaims {\n const parts = idToken.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"id_token format không hợp lệ\");\n }\n const payload = parts[1];\n if (!payload) throw new Error(\"id_token thiếu payload\");\n // Convert base64url to base64.\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = Buffer.from(base64, \"base64\").toString(\"utf8\");\n return JSON.parse(json) as IdTokenClaims;\n}\n\n// ── Step 6: enforce hosted domain. Reject any non-@nal.vn account.\nexport function verifyHostedDomain(claims: IdTokenClaims): void {\n if (claims.hd !== HOSTED_DOMAIN) {\n throw new Error(\n `Email không thuộc workspace NAL (yêu cầu @${HOSTED_DOMAIN}). Nhận: ${claims.email}`,\n );\n }\n if (!claims.email_verified) {\n throw new Error(\"Email chưa được Google verify\");\n }\n}\n\n// Convert OAuth token response + decoded claims into the on-disk UserConfig shape.\nexport function buildUserConfig(token: TokenResponse, claims: IdTokenClaims): UserConfig {\n const expiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();\n return {\n email: claims.email,\n name: claims.name ?? claims.email,\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n expires_at: expiresAt,\n id_token: token.id_token,\n };\n}\n\n// Refresh flow: exchange refresh_token for a new access_token. Used by other\n// commands when they detect an expired token (see isTokenExpired).\nexport async function refreshAccessToken(refreshToken: string): Promise<{\n access_token: string;\n expires_in: number;\n id_token?: string;\n}> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: refreshToken,\n grant_type: \"refresh_token\",\n });\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Refresh token failed (${res.status}): ${text}`);\n }\n return (await res.json()) as { access_token: string; expires_in: number; id_token?: string };\n}\n\n// Revoke a token (used when login is rejected post-hoc, e.g. wrong domain).\nexport async function revokeToken(token: string): Promise<void> {\n const body = new URLSearchParams({ token });\n await fetch(REVOKE_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n }).catch(() => {\n // Best-effort revoke — don't fail the caller if revoke itself errors.\n });\n}\n\n// Build the verification URL with hd hint so Google pre-filters the account picker.\nexport function buildVerificationUrl(response: DeviceCodeResponse): string {\n const url = new URL(response.verification_url);\n url.searchParams.set(\"user_code\", response.user_code);\n url.searchParams.set(\"hd\", HOSTED_DOMAIN);\n return url.toString();\n}\n","// `avatar mcp-run <tool-id>` — hidden command from Chapter 13 roadmap.\n// Wrapper used by ~/.claude.json entries: Avatar injects secrets from keychain\n// then spawns the underlying MCP process with stdio piped to Claude Code.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerMcpRunCommand(program: Command): void {\n program\n .command(\"mcp-run <tool-id>\", { hidden: true })\n .description(\"[internal] Spawn MCP với secrets injected (M09)\")\n .action(notImplementedYet(\"mcp-run\", \"Milestone 09\"));\n}\n","// `avatar restore [--backup <name>] [--list]` — Command 08 spec.\n// Restore .claude/pack/ from a previous backup snapshot.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerRestoreCommand(program: Command): void {\n program\n .command(\"restore\")\n .description(\"Khôi phục .claude/pack/ từ backup (M08)\")\n .option(\"--backup <name>\", \"Tên backup folder trong .claude/_backup/\")\n .option(\"--list\", \"Liệt kê các backup hiện có\")\n .action(notImplementedYet(\"restore\", \"Milestone 08\"));\n}\n","// `avatar review [--accept-all|--reject-all]` — Command 05 spec.\n// Interactive review of .claude/_pending/*.diff.md proposals.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerReviewCommand(program: Command): void {\n program\n .command(\"review\")\n .description(\"Review pending proposals từ avatar scan (M08)\")\n .option(\"--accept-all\", \"Approve mọi pending không hỏi (CI mode)\")\n .option(\"--reject-all\", \"Xóa mọi pending không hỏi\")\n .action(notImplementedYet(\"review\", \"Milestone 08\"));\n}\n","// `avatar scan [--incremental|--full] [--scanners <list>]` — Command 04 spec.\n// Runs 5 project scanners and writes proposals to .claude/_pending/.\n// Implementation deferred — scanner files in src/scanners/ are stubbed.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerScanCommand(program: Command): void {\n program\n .command(\"scan\")\n .description(\"Chạy project scanner và đề xuất knowledge update (M06)\")\n .option(\"--incremental\", \"Chỉ scan các file thay đổi từ commit cuối\")\n .option(\"--full\", \"Scan toàn bộ dự án (default)\")\n .option(\"--scanners <list>\", \"tech-stack,conventions,architecture,domain,git-pattern\")\n .option(\"--quiet\", \"Chạy ngầm, ít output (dùng cho git hook)\")\n .action(notImplementedYet(\"scan\", \"Milestone 06\"));\n}\n","// `avatar secrets {list,set,get,rm,check}` — Command 13 spec.\n// Backed by OS keychain via @napi-rs/keyring. Service prefix: \"avatar\".\n// Audit log entries are written for set/rm; values NEVER logged.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerSecretsCommand(program: Command): void {\n const secrets = program.command(\"secrets\").description(\"Quản lý secrets trong OS keychain (M09)\");\n\n secrets\n .command(\"list\")\n .description(\"Liệt kê secrets đã set (chỉ tên, không value)\")\n .action(notImplementedYet(\"secrets list\", \"Milestone 09\"));\n\n secrets\n .command(\"set <service> <name>\")\n .description(\"Set/update secret (prompt ẩn)\")\n .action(notImplementedYet(\"secrets set\", \"Milestone 09\"));\n\n secrets\n .command(\"get <service> <name>\")\n .description(\"Lấy secret, copy clipboard, auto-xóa sau 30s\")\n .action(notImplementedYet(\"secrets get\", \"Milestone 09\"));\n\n secrets\n .command(\"rm <service> <name>\")\n .description(\"Xóa secret khỏi keychain\")\n .action(notImplementedYet(\"secrets rm\", \"Milestone 09\"));\n\n secrets\n .command(\"check\")\n .description(\"Verify mọi secret required bởi MCP đã enabled\")\n .action(notImplementedYet(\"secrets check\", \"Milestone 09\"));\n}\n","// `avatar status [--json]` — Command 06 spec.\n// Read-only snapshot: project name, CLI version, pack version, pending count,\n// backup count, tech-stack first-line. No mutations.\nimport { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { pathExists, readText } from \"../lib/filesystem-helpers.js\";\nimport { isGitRepo } from \"../lib/git-operations.js\";\nimport { listBackups } from \"../lib/pack-backup-manager.js\";\nimport { readPinnedPackVersion } from \"../lib/team-pack-submodule-manager.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\n\nconst AVATAR_CLI_VERSION = \"1.0.0\";\n\nexport function registerStatusCommand(program: Command): void {\n program\n .command(\"status\")\n .description(\"Snapshot tức thì: project, pack version, pending, backup\")\n .option(\"--json\", \"Output JSON cho script\")\n .action(async (opts: { json?: boolean }) => {\n try {\n const snapshot = await gatherStatus(process.cwd());\n if (opts.json) {\n process.stdout.write(`${JSON.stringify(snapshot, null, 2)}\\n`);\n } else {\n renderStatusBox(snapshot);\n }\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\ninterface StatusSnapshot {\n projectName: string;\n cliVersion: string;\n packVersion: string | null;\n pendingCount: number;\n backupCount: number;\n techStackSummary: string;\n hasAvatar: boolean;\n}\n\nasync function gatherStatus(cwd: string): Promise<StatusSnapshot> {\n const projectName = cwd.split(\"/\").filter(Boolean).pop() ?? \"unknown\";\n const claudeRoot = join(cwd, \".claude\");\n const hasAvatar = await pathExists(claudeRoot);\n if (!hasAvatar) {\n return {\n projectName,\n cliVersion: AVATAR_CLI_VERSION,\n packVersion: null,\n pendingCount: 0,\n backupCount: 0,\n techStackSummary: \"(Avatar chưa init)\",\n hasAvatar: false,\n };\n }\n\n const packVersion = (await isGitRepo(join(claudeRoot, \"pack\")))\n ? await readPinnedPackVersion(cwd).catch(() => null)\n : null;\n\n const pendingDir = join(claudeRoot, \"_pending\");\n const pendingCount = (await pathExists(pendingDir))\n ? (await fs.readdir(pendingDir)).filter((n) => n.endsWith(\".diff.md\")).length\n : 0;\n\n const backupCount = (await listBackups(cwd)).length;\n\n const techStackSummary = await readTechStackFirstLine(claudeRoot);\n\n return {\n projectName,\n cliVersion: AVATAR_CLI_VERSION,\n packVersion,\n pendingCount,\n backupCount,\n techStackSummary,\n hasAvatar: true,\n };\n}\n\nasync function readTechStackFirstLine(claudeRoot: string): Promise<string> {\n const techStackPath = join(claudeRoot, \"project\", \"tech-stack.md\");\n if (!(await pathExists(techStackPath))) return \"(no tech-stack.md)\";\n const content = await readText(techStackPath);\n const firstNonHeaderLine = content\n .split(\"\\n\")\n .find((l) => l.trim() && !l.startsWith(\"#\") && !l.startsWith(\">\"));\n return firstNonHeaderLine?.trim() ?? \"(empty)\";\n}\n\nfunction renderStatusBox(s: StatusSnapshot): void {\n const lines = [\n `${chalk.bold(\"Avatar Status\")} · ${chalk.cyan(s.projectName)}`,\n \"─\".repeat(48),\n `${chalk.dim(\"CLI version:\")} ${s.cliVersion}`,\n `${chalk.dim(\"Pack version:\")} ${s.packVersion ?? chalk.yellow(\"not installed\")}`,\n `${chalk.dim(\"Pending changes:\")} ${s.pendingCount}${s.pendingCount > 0 ? chalk.dim(\" (avatar review)\") : \"\"}`,\n `${chalk.dim(\"Backups:\")} ${s.backupCount}`,\n `${chalk.dim(\"Tech stack:\")} ${s.techStackSummary}`,\n ];\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","import { promises as fs } from \"node:fs\";\n// Backup .claude/pack/ before `avatar sync --force` so user can `avatar restore`\n// if the sync goes wrong. Naming convention: pack-{currentVersion}-{YYYYMMDD-HHmm}.\nimport { join } from \"node:path\";\nimport { copyDirRecursive, ensureDir, pathExists } from \"./filesystem-helpers.js\";\n\nexport const BACKUP_DIR_NAME = \"_backup\";\n\nfunction timestamp(): string {\n const now = new Date();\n const y = now.getFullYear();\n const m = String(now.getMonth() + 1).padStart(2, \"0\");\n const d = String(now.getDate()).padStart(2, \"0\");\n const h = String(now.getHours()).padStart(2, \"0\");\n const min = String(now.getMinutes()).padStart(2, \"0\");\n return `${y}${m}${d}-${h}${min}`;\n}\n\nexport function buildBackupName(currentVersion: string): string {\n // Strip any leading \"v\" so we don't get pack-vv1.2.3 if someone passes \"v1.2.3\".\n const cleanVersion = currentVersion.replace(/^v/, \"\");\n return `pack-v${cleanVersion}-${timestamp()}`;\n}\n\n// Backup .claude/pack/ to .claude/_backup/{name}/, excluding the submodule's\n// own .git directory (we restore content only, not git history).\nexport async function backupPack(projectRoot: string, currentVersion: string): Promise<string> {\n const name = buildBackupName(currentVersion);\n const srcPath = join(projectRoot, \".claude\", \"pack\");\n const dstPath = join(projectRoot, \".claude\", BACKUP_DIR_NAME, name);\n if (!(await pathExists(srcPath))) {\n throw new Error(\"Không tìm thấy .claude/pack/ để backup\");\n }\n await ensureDir(dstPath);\n await copyDirRecursive(srcPath, dstPath, [\".git\"]);\n return name;\n}\n\nexport async function listBackups(projectRoot: string): Promise<string[]> {\n const dir = join(projectRoot, \".claude\", BACKUP_DIR_NAME);\n if (!(await pathExists(dir))) return [];\n const entries = await fs.readdir(dir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort()\n .reverse();\n}\n","// `avatar sync [--force] [--version <tag>] [--dry-run]` — Command 03 spec.\n// Pulls latest team-ai-pack into .claude/pack. Implementation in next milestone.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerSyncCommand(program: Command): void {\n program\n .command(\"sync\")\n .description(\"Pull team-ai-pack mới nhất (M08)\")\n .option(\"--force\", \"Override .claude/pack/, backup trước\")\n .option(\"--version <tag>\", \"Pin vào version cụ thể\")\n .option(\"--dry-run\", \"Hiển thị changes, không apply\")\n .action(notImplementedYet(\"sync\", \"Milestone 08\"));\n}\n","// `avatar tools {list,install,remove}` — Commands 10/11/12 spec (Chapter 12 v4).\n// Lifecycle management for system dependencies (git, gh, node, uv, docker) and\n// MCP servers (gitnexus, context7, serena, github, filesystem, playwright).\n// Registry source: team-ai-pack/tools/registry.yaml.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerToolsCommand(program: Command): void {\n const tools = program.command(\"tools\").description(\"Quản lý system tools + MCP servers (M09)\");\n\n tools\n .command(\"list\")\n .description(\"Liệt kê tool đã cài / còn thiếu\")\n .option(\"--installed\", \"Chỉ liệt kê tool đã cài\")\n .option(\"--missing\", \"Chỉ liệt kê tool còn thiếu\")\n .option(\"--json\", \"Output JSON cho script\")\n .action(notImplementedYet(\"tools list\", \"Milestone 09\"));\n\n tools\n .command(\"install [tool-ids...]\")\n .description(\"Cài tool và đăng ký vào ~/.claude.json\")\n .option(\"--all-recommended\", \"Cài mọi MCP được recommend cho project type\")\n .option(\"--verify\", \"Chạy MCP thử để verify (mất ~30s/tool)\")\n .option(\"--no-secrets\", \"Skip prompt secrets, set sau qua 'avatar secrets'\")\n .action(notImplementedYet(\"tools install\", \"Milestone 09\"));\n\n tools\n .command(\"remove <tool-id>\")\n .description(\"Gỡ tool khỏi ~/.claude.json (optional uninstall binary)\")\n .option(\"--keep-secrets\", \"Không xóa secrets khỏi keychain\")\n .option(\"--keep-binary\", \"Không uninstall npm global binary\")\n .action(notImplementedYet(\"tools remove\", \"Milestone 09\"));\n}\n"],"mappings":"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","names":["program","fs","join","join","join","join","fs","join","dirname","join","dirname","join","join","relative","fs","join","join","fs","program","join","fs","join","boxen","fs","fs","join","join","program","join","boxen","boxen","program","boxen","resolve","program","program","program","program","program","fs","join","boxen","fs","join","join","fs","AVATAR_CLI_VERSION","program","join","fs","boxen","program","program"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/terminal-logger.ts","../src/lib/not-implemented-stub.ts","../src/commands/commit.ts","../src/commands/doctor.ts","../src/lib/filesystem-helpers.ts","../src/lib/git-operations.ts","../src/lib/project-tree-scaffolder.ts","../src/lib/template-bundle-loader.ts","../src/lib/mustache-template-engine.ts","../src/lib/user-config-store.ts","../src/types/config-schema.ts","../src/commands/init.ts","../src/lib/audit-log-appender.ts","../src/lib/team-pack-submodule-manager.ts","../src/commands/init-conflict-detection-helpers.ts","../src/commands/init-scaffold-variable-builders.ts","../src/commands/login.ts","../src/lib/google-oauth-device-flow.ts","../src/commands/mcp-run.ts","../src/commands/restore.ts","../src/commands/review.ts","../src/commands/scan.ts","../src/commands/secrets.ts","../src/commands/status.ts","../src/lib/pack-backup-manager.ts","../src/commands/sync.ts","../src/commands/tools.ts"],"sourcesContent":["// Bootstrap: load commander, register all 13 subcommands, parse argv.\n// Each command lives in src/commands/<name>.ts as a function that accepts the\n// commander Command instance and registers itself.\nimport { Command } from \"commander\";\nimport { registerCommitCommand } from \"./commands/commit.js\";\nimport { registerDoctorCommand } from \"./commands/doctor.js\";\nimport { registerInitCommand } from \"./commands/init.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerMcpRunCommand } from \"./commands/mcp-run.js\";\nimport { registerRestoreCommand } from \"./commands/restore.js\";\nimport { registerReviewCommand } from \"./commands/review.js\";\nimport { registerScanCommand } from \"./commands/scan.js\";\nimport { registerSecretsCommand } from \"./commands/secrets.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerSyncCommand } from \"./commands/sync.js\";\nimport { registerToolsCommand } from \"./commands/tools.js\";\n\nconst CLI_VERSION = \"1.0.1\";\n\nconst program = new Command();\n\nprogram\n .name(\"avatar\")\n .description(\"AI harness CLI for NAL Vietnam engineering\")\n .version(CLI_VERSION, \"-v, --version\", \"Hiển thị phiên bản Avatar CLI\");\n\n// Register all commands. Order matches Chapter 09 spec in implementation doc.\nregisterLoginCommand(program);\nregisterInitCommand(program);\nregisterSyncCommand(program);\nregisterScanCommand(program);\nregisterReviewCommand(program);\nregisterStatusCommand(program);\nregisterDoctorCommand(program);\nregisterRestoreCommand(program);\nregisterCommitCommand(program);\nregisterToolsCommand(program);\nregisterSecretsCommand(program);\nregisterMcpRunCommand(program);\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n // Top-level error sink. Individual commands should handle their own errors;\n // anything reaching here is unexpected.\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`✗ Lỗi không xử lý được: ${msg}\\n`);\n process.exit(1);\n});\n","// Terminal logging helpers — chalk for color, ora for spinners.\n// All Avatar CLI output should funnel through here for consistent UX.\nimport chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\n\ntype LogFn = (message: string) => void;\n\nexport const log: {\n info: LogFn;\n success: LogFn;\n warn: LogFn;\n error: LogFn;\n dim: LogFn;\n plain: LogFn;\n} = {\n info: (m) => process.stdout.write(`${chalk.blue(\"ℹ\")} ${m}\\n`),\n success: (m) => process.stdout.write(`${chalk.green(\"✓\")} ${m}\\n`),\n warn: (m) => process.stdout.write(`${chalk.yellow(\"⚠\")} ${m}\\n`),\n error: (m) => process.stderr.write(`${chalk.red(\"✗\")} ${m}\\n`),\n dim: (m) => process.stdout.write(`${chalk.dim(m)}\\n`),\n plain: (m) => process.stdout.write(`${m}\\n`),\n};\n\n// Spinner wrapper — handles non-TTY by degrading to plain output.\nexport function spinner(text: string): Ora {\n return ora({\n text,\n spinner: \"dots\",\n isEnabled: process.stdout.isTTY ?? false,\n }).start();\n}\n\n// Chalk re-export so commands don't all import chalk separately.\nexport { chalk };\n","// Shared stub action for commands wired into commander but not yet implemented.\n// Prints a consistent message + exit code so users know it's coming.\nimport { chalk } from \"./terminal-logger.js\";\n\nexport function notImplementedYet(commandName: string, milestone?: string): () => void {\n return () => {\n process.stdout.write(\n `${chalk.yellow(\"⏳\")} ${chalk.bold(`avatar ${commandName}`)} — chưa implement ở milestone hiện tại.\\n`,\n );\n if (milestone) {\n process.stdout.write(` Dự kiến: ${chalk.cyan(milestone)}\\n`);\n }\n process.stdout.write(\" Spec đã có trong avatar-cli-implementation_4.html.\\n\");\n process.exit(0);\n };\n}\n","// `avatar commit [--src|--avatar|--both] [-m <msg>] [--push]` — Command 09.\n// Only valid in client/library mode. Splits commits between the client repo\n// (src/) and the Avatar workspace.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerCommitCommand(program: Command): void {\n program\n .command(\"commit\")\n .description(\"Commit code khách (src/) hoặc Avatar state riêng — chỉ client mode (M07)\")\n .option(\"--src\", \"Commit src/ → client remote\")\n .option(\"--avatar\", \"Commit Avatar state → workspace remote\")\n .option(\"--both\", \"Commit cả hai (src trước, avatar sau)\")\n .option(\"-m, --message <msg>\", \"Commit message\")\n .option(\"--push\", \"Tự động push sau khi commit\")\n .action(notImplementedYet(\"commit\", \"Milestone 07\"));\n}\n","import { spawnSync } from \"node:child_process\";\n// `avatar doctor [--fix]` — Command 07 spec.\n// Run a series of health checks and surface ✓/✗ for each. --fix attempts\n// auto-fixes for the ones safe to fix automatically.\nimport { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { pathExists } from \"../lib/filesystem-helpers.js\";\nimport { isGitRepo } from \"../lib/git-operations.js\";\nimport { installGitHook } from \"../lib/project-tree-scaffolder.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\nimport { isTokenExpired, readUserConfig } from \"../lib/user-config-store.js\";\n\ninterface CheckResult {\n name: string;\n status: \"ok\" | \"warn\" | \"fail\";\n detail: string;\n fixable: boolean;\n fix?: () => Promise<void>;\n}\n\nexport function registerDoctorCommand(program: Command): void {\n program\n .command(\"doctor\")\n .description(\"Chẩn đoán cài đặt Avatar: hooks, MCP, login, submodule, ...\")\n .option(\"--fix\", \"Tự động fix các issue có thể fix tự động\")\n .action(async (opts: { fix?: boolean }) => {\n try {\n const checks = await runChecks(process.cwd());\n renderChecks(checks);\n if (opts.fix) await applyFixes(checks);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runChecks(cwd: string): Promise<CheckResult[]> {\n const checks: CheckResult[] = [];\n\n // 1. Node.js version >= 18.17\n const nodeVer = process.versions.node;\n const [major, minor] = nodeVer.split(\".\").map((n) => Number.parseInt(n, 10));\n const nodeOk = (major ?? 0) > 18 || ((major ?? 0) === 18 && (minor ?? 0) >= 17);\n checks.push({\n name: \"Node.js version\",\n status: nodeOk ? \"ok\" : \"fail\",\n detail: `v${nodeVer}${nodeOk ? \"\" : \" (cần >= 18.17)\"}`,\n fixable: false,\n });\n\n // 2. Login.\n const config = await readUserConfig();\n if (!config) {\n checks.push({\n name: \"Login status\",\n status: \"fail\",\n detail: \"Chưa đăng nhập — chạy 'avatar login'\",\n fixable: false,\n });\n } else if (isTokenExpired(config)) {\n checks.push({\n name: \"Login status\",\n status: \"warn\",\n detail: `Token hết hạn (${config.email}) — chạy 'avatar login'`,\n fixable: false,\n });\n } else {\n checks.push({\n name: \"Login status\",\n status: \"ok\",\n detail: `Logged in: ${config.email}`,\n fixable: false,\n });\n }\n\n // 3. Git repo.\n const gitRepo = await isGitRepo(cwd);\n checks.push({\n name: \"Git repository\",\n status: gitRepo ? \"ok\" : \"warn\",\n detail: gitRepo ? cwd : \"Không phải git repo (cần cho 'avatar init')\",\n fixable: false,\n });\n\n // 4. .claude/pack/ submodule.\n const packPath = join(cwd, \".claude\", \"pack\");\n const hasPack = await pathExists(packPath);\n checks.push({\n name: \"team-ai-pack submodule\",\n status: hasPack ? \"ok\" : \"warn\",\n detail: hasPack ? packPath : \"Avatar chưa init — chạy 'avatar init'\",\n fixable: false,\n });\n\n // 5. CLAUDE.md present.\n const claudeMdPath = join(cwd, \"CLAUDE.md\");\n const hasClaudeMd = await pathExists(claudeMdPath);\n checks.push({\n name: \"CLAUDE.md\",\n status: hasClaudeMd ? \"ok\" : \"warn\",\n detail: hasClaudeMd ? \"tồn tại ở project root\" : \"thiếu — chạy 'avatar init'\",\n fixable: false,\n });\n\n // 6. post-merge hook.\n const hookPath = join(cwd, \".git\", \"hooks\", \"post-merge\");\n const hasHook = await pathExists(hookPath);\n if (gitRepo && hasPack) {\n checks.push({\n name: \"Git hook post-merge\",\n status: hasHook ? \"ok\" : \"fail\",\n detail: hasHook ? \"installed\" : \"missing — fixable\",\n fixable: !hasHook,\n fix: hasHook\n ? undefined\n : async () => {\n await installGitHook(join(cwd, \".git\"), \"post-merge\");\n },\n });\n }\n\n // 7. .gitignore has Avatar entries.\n const gitignorePath = join(cwd, \".gitignore\");\n if (gitRepo) {\n let gitignoreOk = false;\n if (await pathExists(gitignorePath)) {\n const content = await fs.readFile(gitignorePath, \"utf8\");\n gitignoreOk = content.includes(\".claude/_pending/\");\n }\n checks.push({\n name: \".gitignore Avatar entries\",\n status: gitignoreOk ? \"ok\" : hasPack ? \"fail\" : \"warn\",\n detail: gitignoreOk ? \"có .claude/_pending/, .claude/_backup/\" : \"thiếu entries\",\n fixable: false,\n });\n }\n\n // 8. Claude Code CLI installed (best-effort `which claude`).\n const which = spawnSync(\"which\", [\"claude\"]);\n const hasClaudeCli = which.status === 0;\n checks.push({\n name: \"Claude Code CLI\",\n status: hasClaudeCli ? \"ok\" : \"warn\",\n detail: hasClaudeCli ? which.stdout.toString().trim() : \"không tìm thấy 'claude' trên PATH\",\n fixable: false,\n });\n\n return checks;\n}\n\nfunction renderChecks(checks: CheckResult[]): void {\n const lines = [chalk.bold(\"Avatar Doctor\"), \"─\".repeat(48)];\n let passed = 0;\n let issues = 0;\n let fixable = 0;\n for (const c of checks) {\n const icon =\n c.status === \"ok\"\n ? chalk.green(\"✓\")\n : c.status === \"warn\"\n ? chalk.yellow(\"⚠\")\n : chalk.red(\"✗\");\n lines.push(`${icon} ${c.name.padEnd(28)} ${chalk.dim(c.detail)}`);\n if (c.status === \"ok\") passed += 1;\n else {\n issues += 1;\n if (c.fixable) fixable += 1;\n }\n }\n lines.push(\"─\".repeat(48));\n lines.push(\n `${passed} checks passed, ${issues} issue${issues === 1 ? \"\" : \"s\"}${fixable > 0 ? ` (${fixable} fixable — chạy 'avatar doctor --fix')` : \"\"}`,\n );\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n\nasync function applyFixes(checks: CheckResult[]): Promise<void> {\n let count = 0;\n for (const c of checks) {\n if (c.fixable && c.fix) {\n try {\n await c.fix();\n log.success(`Fixed: ${c.name}`);\n count += 1;\n } catch (err) {\n log.error(`Failed to fix ${c.name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n if (count === 0) log.dim(\"Không có gì để fix tự động.\");\n}\n","// Thin promise-based wrappers around node:fs/promises with the patterns Avatar\n// uses most: ensure-dir, read-text, write-text-atomic, copy-dir-recursive.\n// Atomic write writes to a temp file then renames to avoid corruption if Avatar\n// crashes mid-write.\nimport { constants, promises as fs } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\n\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await fs.access(path, constants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(path: string): Promise<void> {\n await fs.mkdir(path, { recursive: true });\n}\n\nexport async function readText(path: string): Promise<string> {\n return await fs.readFile(path, \"utf8\");\n}\n\nexport async function readJson<T>(path: string): Promise<T> {\n return JSON.parse(await readText(path)) as T;\n}\n\n// Atomic write: write to temp file then rename. Prevents corruption if process\n// is killed between open() and close(). chmod is applied AFTER rename.\nexport async function writeTextAtomic(path: string, content: string, mode?: number): Promise<void> {\n await ensureDir(dirname(path));\n const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;\n await fs.writeFile(tmp, content, \"utf8\");\n if (mode !== undefined) {\n await fs.chmod(tmp, mode);\n }\n await fs.rename(tmp, path);\n}\n\nexport async function writeJsonAtomic(path: string, data: unknown, mode?: number): Promise<void> {\n await writeTextAtomic(path, `${JSON.stringify(data, null, 2)}\\n`, mode);\n}\n\n// Recursive copy. excludeNames filters by basename at any depth (e.g. \".git\").\nexport async function copyDirRecursive(\n source: string,\n destination: string,\n excludeNames: string[] = [],\n): Promise<void> {\n await ensureDir(destination);\n const entries = await fs.readdir(source, { withFileTypes: true });\n for (const entry of entries) {\n if (excludeNames.includes(entry.name)) continue;\n const src = join(source, entry.name);\n const dst = join(destination, entry.name);\n if (entry.isDirectory()) {\n await copyDirRecursive(src, dst, excludeNames);\n } else if (entry.isSymbolicLink()) {\n const target = await fs.readlink(src);\n await fs.symlink(target, dst);\n } else {\n await fs.copyFile(src, dst);\n }\n }\n}\n\nexport async function removeRecursive(path: string): Promise<void> {\n await fs.rm(path, { recursive: true, force: true });\n}\n\nexport function relativeFromCwd(path: string): string {\n return relative(process.cwd(), path);\n}\n","import { join } from \"node:path\";\n// Wrapper around simple-git for the operations Avatar uses repeatedly:\n// repo detection, submodule add/update, status, log, tag listing.\n// Centralised so error formatting and cwd handling stays consistent.\nimport { type SimpleGit, simpleGit } from \"simple-git\";\nimport { pathExists } from \"./filesystem-helpers.js\";\n\nexport function git(cwd: string = process.cwd()): SimpleGit {\n return simpleGit({ baseDir: cwd, binary: \"git\" });\n}\n\nexport async function isGitRepo(cwd: string = process.cwd()): Promise<boolean> {\n return await pathExists(join(cwd, \".git\"));\n}\n\nexport async function currentBranch(cwd: string = process.cwd()): Promise<string> {\n const result = await git(cwd).revparse([\"--abbrev-ref\", \"HEAD\"]);\n return result.trim();\n}\n\nexport async function addSubmodule(\n repoUrl: string,\n destPath: string,\n cwd: string = process.cwd(),\n): Promise<void> {\n await git(cwd).subModule([\"add\", repoUrl, destPath]);\n}\n\n// Checkout a tag inside a submodule. Used after `addSubmodule` to pin to a\n// specific team-ai-pack release.\nexport async function checkoutTagInSubmodule(\n submodulePath: string,\n tag: string,\n cwd: string = process.cwd(),\n): Promise<void> {\n const submoduleCwd = join(cwd, submodulePath);\n await git(submoduleCwd).fetch([\"--tags\"]);\n await git(submoduleCwd).checkout(tag);\n}\n\nexport async function listTags(cwd: string = process.cwd()): Promise<string[]> {\n const result = await git(cwd).tags();\n return result.all;\n}\n\nexport async function latestTag(cwd: string = process.cwd()): Promise<string | null> {\n const tags = await listTags(cwd);\n return tags.length > 0 ? (tags[tags.length - 1] ?? null) : null;\n}\n\nexport async function currentCommitSha(cwd: string = process.cwd()): Promise<string> {\n const result = await git(cwd).revparse([\"HEAD\"]);\n return result.trim();\n}\n\nexport async function workingTreeIsDirty(cwd: string = process.cwd()): Promise<boolean> {\n const status = await git(cwd).status();\n return !status.isClean();\n}\n","import { promises as fs } from \"node:fs\";\n// Scaffold the .claude/ directory tree inside a project after submodule add.\n// Used by `avatar init` for all three modes (internal/client/library).\n//\n// Spec source: Chapter 02 (folder map) + Chapter 09 Command 02 BEHAVIOR.\nimport { join } from \"node:path\";\nimport type { InitMode } from \"../types/config-schema.js\";\nimport { ensureDir, pathExists, writeTextAtomic } from \"./filesystem-helpers.js\";\nimport { type TemplateName, loadHook, renderTemplateByName } from \"./template-bundle-loader.js\";\n\n// Top-level paths Avatar will create or overwrite during init.\n// init.ts imports this for conflict detection so the list stays in sync.\n// `.gitmodules` is managed by git submodule add (not direct write), but it\n// gets modified during init so it's listed here for conflict warnings.\nexport const AVATAR_MANAGED_PATHS = [\".claude\", \"CLAUDE.md\", \".gitmodules\"] as const;\n\n// Rename `path` to `{path}.avatar-backup-{ISO timestamp}` if it exists.\n// Returns the backup path if created, null if source didn't exist.\n// Caller logs the backup so the user knows where their original file went.\n//\n// Collision handling: if the timestamped backup name already exists (two\n// backups within the same millisecond, or pre-existing leftover from prior\n// run), appends `-{counter}` until a free name is found. Prevents silent\n// overwrite of a previous backup.\nexport async function backupIfExists(path: string): Promise<string | null> {\n if (!(await pathExists(path))) return null;\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\");\n const basePath = `${path}.avatar-backup-${ts}`;\n let backupPath = basePath;\n let counter = 1;\n while (await pathExists(backupPath)) {\n backupPath = `${basePath}-${counter}`;\n counter++;\n if (counter > 5) {\n throw new Error(`Could not find free backup name for ${path}`);\n }\n }\n await fs.rename(path, backupPath);\n return backupPath;\n}\n\n// Backup-aware atomic write. Used by all scaffolder write functions so user's\n// pre-existing files in `.claude/` are preserved when Avatar re-scaffolds.\n// Returns the backup path if a backup was created, null otherwise.\nasync function writeWithBackup(\n path: string,\n content: string,\n mode?: number,\n): Promise<string | null> {\n const backup = await backupIfExists(path);\n await writeTextAtomic(path, content, mode);\n return backup;\n}\n\n// Subdirectories Avatar creates inside .claude/ on init. `pack/` is added\n// separately by the submodule manager — listed here for reference only.\nconst CLAUDE_SUBDIRS = [\"project\", \"state\", \"_pending\", \"_backup\"] as const;\n\nconst PROJECT_KNOWLEDGE_TEMPLATES: TemplateName[] = [\n \"project/tech-stack.md\",\n \"project/conventions.md\",\n \"project/architecture.md\",\n \"project/domain.md\",\n \"project/gotchas.md\",\n];\n\nexport interface ScaffoldVariables {\n projectName: string;\n projectDescription: string;\n teamOwner: string;\n avatarVersion: string;\n packVersion: string;\n lastScan: string;\n mode: InitMode;\n}\n\n// Create .claude/{project,state,_pending,_backup}/ and a .gitkeep in each\n// so they survive a fresh checkout.\nexport async function createClaudeDirTree(projectRoot: string): Promise<void> {\n const claudeRoot = join(projectRoot, \".claude\");\n await ensureDir(claudeRoot);\n for (const sub of CLAUDE_SUBDIRS) {\n const dir = join(claudeRoot, sub);\n await ensureDir(dir);\n await writeTextAtomic(join(dir, \".gitkeep\"), \"\");\n }\n}\n\n// Render and write all 5 project knowledge files from templates.\n// All start with placeholder content — scanners fill them in later.\n// Returns list of backup paths created for pre-existing user files.\nexport async function writeProjectKnowledgeFiles(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<string[]> {\n const baseVars = {\n ...vars,\n primaryLanguage: \"(chưa scan)\",\n frameworks: \"(chưa scan)\",\n databases: \"(chưa scan)\",\n testStack: \"(chưa scan)\",\n buildStack: \"(chưa scan)\",\n toolVersions: \"(chưa scan)\",\n codeStyle: \"(chưa scan)\",\n namingConvention: \"(chưa scan)\",\n folderStructure: \"(chưa scan)\",\n commitConvention: \"(chưa scan)\",\n linterConfig: \"(chưa scan)\",\n architectureOverview: \"(chưa scan)\",\n moduleLayout: \"(chưa scan)\",\n dataFlow: \"(chưa scan)\",\n externalIntegrations: \"(chưa scan)\",\n deploymentTopology: \"(chưa scan)\",\n domainDescription: \"(chưa scan)\",\n coreEntities: \"(chưa scan)\",\n primaryUseCases: \"(chưa scan)\",\n domainGlossary: \"(chưa scan)\",\n };\n const backups: string[] = [];\n for (const tpl of PROJECT_KNOWLEDGE_TEMPLATES) {\n const content = await renderTemplateByName(tpl, baseVars);\n const relative = tpl.replace(/^project\\//, \"\");\n const outPath = join(projectRoot, \".claude\", \"project\", relative);\n const backup = await writeWithBackup(outPath, content);\n if (backup) backups.push(backup);\n }\n return backups;\n}\n\n// Write root CLAUDE.md from template — this is the entry point Claude Code reads.\n// Returns backup path if a pre-existing CLAUDE.md was renamed.\nexport async function writeRootClaudeMd(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<string | null> {\n const content = await renderTemplateByName(\"CLAUDE.md\", vars);\n return await writeWithBackup(join(projectRoot, \"CLAUDE.md\"), content);\n}\n\n// Write .claude/settings.json from template. Returns backup path if existed.\nexport async function writeProjectSettings(\n projectRoot: string,\n vars: ScaffoldVariables,\n): Promise<string | null> {\n const content = await renderTemplateByName(\"settings.json\", vars);\n return await writeWithBackup(join(projectRoot, \".claude\", \"settings.json\"), content);\n}\n\n// Append Avatar entries to project .gitignore (or create if missing).\n// Idempotent — skips if our marker line already present.\nexport async function appendGitignoreEntries(projectRoot: string): Promise<void> {\n const path = join(projectRoot, \".gitignore\");\n const tpl = await renderTemplateByName(\"gitignore\", {});\n const marker = \"# Avatar — git-ignored entries injected on `avatar init`\";\n\n let existing = \"\";\n if (await pathExists(path)) {\n existing = await fs.readFile(path, \"utf8\");\n if (existing.includes(marker)) return;\n }\n\n const separator = existing.endsWith(\"\\n\") || existing.length === 0 ? \"\" : \"\\n\";\n await writeTextAtomic(path, `${existing}${separator}\\n${tpl}`);\n}\n\n// Install git hooks into <gitDir>/hooks/. `gitDir` lets caller point at\n// src/.git/hooks (client mode pre-push) or workspace .git/hooks (post-merge).\nexport async function installGitHook(\n gitDir: string,\n hookName: \"post-merge\" | \"pre-push\",\n): Promise<void> {\n const content = await loadHook(hookName);\n const hooksDir = join(gitDir, \"hooks\");\n await ensureDir(hooksDir);\n const dest = join(hooksDir, hookName);\n await writeTextAtomic(dest, content, 0o755);\n}\n","// Resolve template files bundled inside the Avatar CLI package. tsup bundles\n// src/ into dist/index.js but does NOT bundle template files — they ship as\n// loose files via package.json `files` field (src/templates, src/hooks).\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { readText } from \"./filesystem-helpers.js\";\nimport { renderTemplate } from \"./mustache-template-engine.js\";\n\n// Templates ship in src/templates/ and src/hooks/ at runtime (see\n// package.json `files` field — both src/templates and src/hooks are included\n// in the npm tarball). Locate them by walking up from this file to the\n// package root (folder containing package.json), then descending to src/.\n//\n// This works identically from dist/index.js (npm-installed) and from\n// src/lib/*.ts (vitest/tsx) without path string sniffing.\nconst HERE = dirname(fileURLToPath(import.meta.url));\nconst PACKAGE_ROOT = findPackageRoot(HERE);\nconst TEMPLATES_ROOT = join(PACKAGE_ROOT, \"src\", \"templates\");\nconst HOOKS_ROOT = join(PACKAGE_ROOT, \"src\", \"hooks\");\n\nfunction findPackageRoot(startDir: string): string {\n let dir = startDir;\n while (true) {\n if (existsSync(join(dir, \"package.json\"))) return dir;\n const parent = dirname(dir);\n if (parent === dir) {\n throw new Error(`Cannot locate package root from ${startDir}`);\n }\n dir = parent;\n }\n}\n\nexport type TemplateName =\n | \"CLAUDE.md\"\n | \"settings.json\"\n | \"gitignore\"\n | \"project/tech-stack.md\"\n | \"project/conventions.md\"\n | \"project/architecture.md\"\n | \"project/domain.md\"\n | \"project/gotchas.md\";\n\nexport type HookName = \"post-merge\" | \"pre-push\";\n\nexport async function loadTemplate(name: TemplateName): Promise<string> {\n return await readText(join(TEMPLATES_ROOT, `${name}.tpl`));\n}\n\nexport async function renderTemplateByName(\n name: TemplateName,\n variables: Record<string, string | number | undefined>,\n): Promise<string> {\n const source = await loadTemplate(name);\n return renderTemplate(source, variables);\n}\n\nexport async function loadHook(name: HookName): Promise<string> {\n return await readText(join(HOOKS_ROOT, `${name}.sh.tpl`));\n}\n","// Minimal mustache-style {{variable}} replacement engine. Avoids pulling in\n// full mustache dependency — Avatar templates only need flat variable\n// substitution, no loops or conditionals.\n//\n// Unknown variables left as-is, NOT replaced with empty string. This makes\n// missing variables visible in output for easier debugging.\n\nconst TEMPLATE_PATTERN = /\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g;\n\nexport function renderTemplate(\n source: string,\n variables: Record<string, string | number | undefined>,\n): string {\n return source.replace(TEMPLATE_PATTERN, (match, key: string) => {\n const value = variables[key];\n if (value === undefined) return match;\n return String(value);\n });\n}\n\n// Extract the variable names referenced by a template — useful for\n// scaffolding tests and validating that callers pass every required key.\nexport function extractVariables(source: string): string[] {\n const found = new Set<string>();\n for (const match of source.matchAll(TEMPLATE_PATTERN)) {\n if (match[1]) found.add(match[1]);\n }\n return Array.from(found).sort();\n}\n","// Read/write ~/.avatar/config.json (OAuth credentials) and ~/.avatar/state.json\n// (non-credential user state). Validates with Zod, enforces chmod 600 on config.\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n type UserConfig,\n type UserState,\n userConfigSchema,\n userStateSchema,\n} from \"../types/config-schema.js\";\nimport { ensureDir, pathExists, readJson, writeJsonAtomic } from \"./filesystem-helpers.js\";\n\nexport const AVATAR_HOME = join(homedir(), \".avatar\");\nexport const USER_CONFIG_PATH = join(AVATAR_HOME, \"config.json\");\nexport const USER_STATE_PATH = join(AVATAR_HOME, \"state.json\");\nexport const AUDIT_LOG_PATH = join(AVATAR_HOME, \"audit.log\");\nexport const BACKUPS_DIR = join(AVATAR_HOME, \"backups\");\n\n// chmod 600 — owner-only read/write. Matches spec for credential files.\nconst SECRET_FILE_MODE = 0o600;\n\nexport async function ensureAvatarHome(): Promise<void> {\n await ensureDir(AVATAR_HOME);\n}\n\nexport async function readUserConfig(): Promise<UserConfig | null> {\n if (!(await pathExists(USER_CONFIG_PATH))) return null;\n const raw = await readJson<unknown>(USER_CONFIG_PATH);\n const parsed = userConfigSchema.safeParse(raw);\n if (!parsed.success) return null;\n return parsed.data;\n}\n\nexport async function writeUserConfig(config: UserConfig): Promise<void> {\n await ensureAvatarHome();\n await writeJsonAtomic(USER_CONFIG_PATH, config, SECRET_FILE_MODE);\n}\n\nexport async function clearUserConfig(): Promise<void> {\n if (await pathExists(USER_CONFIG_PATH)) {\n const { promises: fs } = await import(\"node:fs\");\n await fs.unlink(USER_CONFIG_PATH);\n }\n}\n\nexport async function readUserState(): Promise<UserState> {\n if (!(await pathExists(USER_STATE_PATH))) {\n return userStateSchema.parse({});\n }\n const raw = await readJson<unknown>(USER_STATE_PATH);\n const parsed = userStateSchema.safeParse(raw);\n if (!parsed.success) return userStateSchema.parse({});\n return parsed.data;\n}\n\nexport async function writeUserState(state: UserState): Promise<void> {\n await ensureAvatarHome();\n await writeJsonAtomic(USER_STATE_PATH, state);\n}\n\n// Token is considered expired if it would expire in the next 60 seconds.\n// 60s buffer accounts for slow network round-trip on the upcoming request.\nexport function isTokenExpired(config: UserConfig): boolean {\n const expiresAt = Date.parse(config.expires_at);\n return Number.isNaN(expiresAt) || expiresAt - Date.now() < 60_000;\n}\n","// Zod schemas + inferred TypeScript types for all on-disk Avatar config files.\n// Centralised so commands import from one place and validation matches storage.\nimport { z } from \"zod\";\n\n// ~/.avatar/config.json — OAuth credentials after `avatar login`.\n// chmod 600 enforced at write site.\nexport const userConfigSchema = z.object({\n email: z.string().email(),\n name: z.string(),\n access_token: z.string().min(1),\n refresh_token: z.string().min(1),\n expires_at: z.string().datetime(),\n id_token: z.string().min(1),\n});\nexport type UserConfig = z.infer<typeof userConfigSchema>;\n\n// ~/.avatar/state.json — non-credential user state (tool install records,\n// allowed-paths chosen for filesystem MCP, etc).\nexport const userStateSchema = z.object({\n installed_tools: z\n .record(\n z.string(),\n z.object({\n version: z.string().optional(),\n installed_at: z.string().datetime(),\n install_method: z.string(),\n }),\n )\n .default({}),\n tool_inputs: z.record(z.string(), z.unknown()).default({}),\n});\nexport type UserState = z.infer<typeof userStateSchema>;\n\n// .claude/settings.json — per-project Claude Code settings emitted by `avatar init`.\nexport const projectSettingsSchema = z.object({\n allowedTools: z.array(z.string()),\n hooks: z\n .object({\n PostToolUse: z.array(z.unknown()).optional(),\n })\n .partial()\n .optional(),\n env: z.record(z.string(), z.string()).default({}),\n});\nexport type ProjectSettings = z.infer<typeof projectSettingsSchema>;\n\n// Init mode — determines workspace topology (Chapter 02 + Chapter 07 spec).\nexport const initModeSchema = z.enum([\"internal\", \"client\", \"library\"]);\nexport type InitMode = z.infer<typeof initModeSchema>;\n","// `avatar init` — Command 02 spec.\n// Three modes: internal (Avatar files commit with code), client (Pattern A,\n// workspace + src/ submodule), library (same topology as client).\n//\n// This implementation focuses on the structural work: submodules, directory\n// tree, templates, git hooks. The scanner wizard step (Câu 3/3 — MCP tools)\n// is deferred — tools.ts is stubbed for this milestone.\n\nimport { join, relative, resolve } from \"node:path\";\nimport { confirm, input, select } from \"@inquirer/prompts\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport { ensureDir } from \"../lib/filesystem-helpers.js\";\nimport { git, isGitRepo } from \"../lib/git-operations.js\";\nimport {\n appendGitignoreEntries,\n createClaudeDirTree,\n installGitHook,\n writeProjectKnowledgeFiles,\n writeProjectSettings,\n writeRootClaudeMd,\n} from \"../lib/project-tree-scaffolder.js\";\nimport { TEAM_PACK_REPO_URL, addTeamPackSubmodule } from \"../lib/team-pack-submodule-manager.js\";\nimport { chalk, log, spinner } from \"../lib/terminal-logger.js\";\nimport { isTokenExpired, readUserConfig } from \"../lib/user-config-store.js\";\nimport type { InitMode } from \"../types/config-schema.js\";\nimport {\n detectAvatarConflicts,\n findAlternativeWorkspaceName,\n isEmptyOrMissing,\n} from \"./init-conflict-detection-helpers.js\";\nimport {\n buildScaffoldVariables,\n inferWorkspaceName,\n projectNameOf,\n} from \"./init-scaffold-variable-builders.js\";\n\ninterface InitOptions {\n mode?: InitMode;\n skipScan?: boolean;\n packVersion?: string;\n clientRepo?: string;\n workspaceName?: string;\n workspaceParent?: string;\n force?: boolean;\n teamOwner?: string;\n description?: string;\n yes?: boolean;\n}\n\nexport function registerInitCommand(program: Command): void {\n program\n .command(\"init\")\n .description(\"Khởi tạo Avatar trong dự án (3 mode: internal/client/library)\")\n .option(\"--mode <mode>\", \"internal | client | library\")\n .option(\"--skip-scan\", \"Bỏ qua bước project-scanner\")\n .option(\"--pack-version <tag>\", \"Pin team-ai-pack vào version cụ thể\")\n .option(\"--client-repo <url>\", \"URL git của client repo (mode=client)\")\n .option(\"--workspace-name <name>\", \"Tên workspace (mode=client)\")\n .option(\"--workspace-parent <path>\", \"Thư mục cha tạo workspace (mode=client)\")\n .option(\"--force\", \"Bỏ qua prompt xác nhận khi backup file Avatar đã tồn tại\")\n .option(\"--team-owner <email>\", \"Email team owner (bỏ qua prompt)\")\n .option(\"--description <text>\", \"Mô tả 1 dòng của dự án (bỏ qua prompt)\")\n .option(\"--yes\", \"Auto-confirm tất cả prompt (kể cả maybeCommit)\")\n .action(async (opts: InitOptions) => {\n try {\n await runInit(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runInit(opts: InitOptions): Promise<void> {\n // Precondition: logged in.\n const userConfig = await readUserConfig();\n if (!userConfig || isTokenExpired(userConfig)) {\n log.error(\"Chưa đăng nhập hoặc token đã hết hạn. Chạy 'avatar login' trước.\");\n process.exit(1);\n }\n\n // Step 1: determine mode. If not provided, prompt.\n const mode = opts.mode ?? (await promptMode());\n\n // Step 2: route by mode.\n if (mode === \"internal\") {\n await runInitInternal(opts, userConfig.email);\n } else {\n await runInitClientOrLibrary(opts, mode, userConfig.email);\n }\n}\n\nasync function promptMode(): Promise<InitMode> {\n return (await select({\n message: \"Đây là loại dự án gì?\",\n choices: [\n {\n name: \"Nội bộ NAL (Avatar files commit cùng code)\",\n value: \"internal\" as const,\n },\n { name: \"Client (Pattern A — tách workspace)\", value: \"client\" as const },\n { name: \"Library/SDK public (tách workspace)\", value: \"library\" as const },\n ],\n })) as InitMode;\n}\n\n// ─── MODE: INTERNAL ─────────────────────────────────────────────────────────\nasync function runInitInternal(opts: InitOptions, ownerEmail: string): Promise<void> {\n const projectRoot = process.cwd();\n\n if (!(await isGitRepo(projectRoot))) {\n throw new Error(\"Mode internal cần dự án đã là git repo. Chạy 'git init' trước rồi thử lại.\");\n }\n\n const conflicts = await detectAvatarConflicts(projectRoot);\n if (conflicts.length > 0) {\n log.warn(\"Các path sau đã tồn tại — Avatar sẽ backup từng file con bị ghi đè:\");\n for (const c of conflicts) log.warn(` - ${c}`);\n log.warn(\"File con của bạn không bị Avatar tạo sẽ được giữ nguyên.\");\n log.warn(\"Khuyến nghị mode 'client'/'library' nếu muốn cô lập hoàn toàn.\");\n\n if (!opts.force) {\n const proceed = await confirm({\n message: \"Tiếp tục backup-and-merge?\",\n default: true,\n });\n if (!proceed) {\n throw new Error(\"Hủy init. Dùng mode 'client'/'library' hoặc xóa thủ công các file trên.\");\n }\n }\n }\n\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n\n const projectName = projectNameOf(projectRoot);\n const projectDescription =\n opts.description ??\n (await input({\n message: \"Mô tả ngắn 1 dòng của dự án:\",\n default: `Avatar-managed project: ${projectName}`,\n }));\n\n // Add team-ai-pack submodule.\n const sp = spinner(`Thêm submodule team-ai-pack từ ${TEAM_PACK_REPO_URL}...`);\n let pinnedTag: string | null = null;\n try {\n const result = await addTeamPackSubmodule(projectRoot, opts.packVersion);\n pinnedTag = result.pinnedTag;\n sp.succeed(`Đã pin team-ai-pack vào ${pinnedTag ?? \"HEAD\"}`);\n } catch (err) {\n sp.fail(\"Add submodule thất bại\");\n throw err;\n }\n\n // Scaffold .claude/ tree + templates.\n const vars = buildScaffoldVariables({\n projectName,\n projectDescription,\n teamOwner,\n packVersion: pinnedTag ?? \"HEAD\",\n mode: \"internal\",\n });\n await createClaudeDirTree(projectRoot);\n const allBackups: string[] = [];\n allBackups.push(...(await writeProjectKnowledgeFiles(projectRoot, vars)));\n const claudeMdBackup = await writeRootClaudeMd(projectRoot, vars);\n if (claudeMdBackup) allBackups.push(claudeMdBackup);\n const settingsBackup = await writeProjectSettings(projectRoot, vars);\n if (settingsBackup) allBackups.push(settingsBackup);\n await appendGitignoreEntries(projectRoot);\n\n if (allBackups.length > 0) {\n log.success(`Đã backup ${allBackups.length} file của bạn:`);\n for (const b of allBackups) log.info(` → ${relative(projectRoot, b) || b}`);\n }\n\n // Install post-merge hook in project's .git/hooks/.\n await installGitHook(join(projectRoot, \".git\"), \"post-merge\");\n log.success(\"Cài git hook post-merge\");\n\n await appendAuditEntry(\"init\", `mode=internal,project=${projectName}`);\n await maybeCommit(projectRoot, \"internal\", opts.yes);\n printInitSuccessBox(projectRoot, \"internal\");\n}\n\n// ─── MODE: CLIENT / LIBRARY ─────────────────────────────────────────────────\nasync function runInitClientOrLibrary(\n opts: InitOptions,\n mode: \"client\" | \"library\",\n ownerEmail: string,\n): Promise<void> {\n // Collect required inputs (5 wizard questions per spec).\n const teamOwner = opts.teamOwner ?? (await promptTeamOwner(ownerEmail));\n const clientRepoUrl =\n opts.clientRepo ??\n (await input({\n message: \"URL git của client repo:\",\n validate: (v) => (v.length > 0 ? true : \"URL bắt buộc\"),\n }));\n const inferredName = inferWorkspaceName(clientRepoUrl);\n const workspaceName =\n opts.workspaceName ??\n (await input({\n message: \"Tên workspace:\",\n default: inferredName,\n }));\n const workspaceParent = resolve(opts.workspaceParent ?? \"..\");\n\n const workspacePath = await resolveWorkspacePath(workspaceParent, workspaceName, opts.force);\n\n // Create workspace + git init.\n await ensureDir(workspacePath);\n await git(workspacePath).init();\n\n // Add both submodules in parallel.\n const sp = spinner(\"Add submodule client repo + team-ai-pack...\");\n try {\n await git(workspacePath).subModule([\"add\", clientRepoUrl, \"src\"]);\n const result = await addTeamPackSubmodule(workspacePath, opts.packVersion);\n sp.succeed(`Workspace pin team-ai-pack vào ${result.pinnedTag ?? \"HEAD\"}`);\n\n // Scaffold workspace knowledge using projectName derived from workspace.\n const vars = buildScaffoldVariables({\n projectName: workspaceName,\n projectDescription: `Avatar ${mode} workspace for ${clientRepoUrl}`,\n teamOwner,\n packVersion: result.pinnedTag ?? \"HEAD\",\n mode,\n });\n\n await createClaudeDirTree(workspacePath);\n await writeProjectKnowledgeFiles(workspacePath, vars);\n await writeRootClaudeMd(workspacePath, vars);\n await writeProjectSettings(workspacePath, vars);\n await appendGitignoreEntries(workspacePath);\n\n // Extra dirs only client/library mode uses.\n await ensureDir(join(workspacePath, \"notes\"));\n await ensureDir(join(workspacePath, \"scripts\"));\n\n // Install post-merge in workspace, pre-push in src/ to block Avatar leaks.\n // src/ is a submodule so its real git dir lives at workspace/.git/modules/src,\n // not src/.git (which is a gitlink file, not a directory).\n await installGitHook(join(workspacePath, \".git\"), \"post-merge\");\n await installGitHook(join(workspacePath, \".git\", \"modules\", \"src\"), \"pre-push\");\n log.success(\"Cài post-merge (workspace) + pre-push (src/)\");\n\n await appendAuditEntry(\"init\", `mode=${mode},workspace=${workspaceName}`);\n await maybeCommitWorkspace(workspacePath, opts.yes);\n printInitSuccessBox(workspacePath, mode);\n } catch (err) {\n sp.fail(\"Init workspace thất bại\");\n throw err;\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────────────────\n\n// Resolve a usable workspace path. Wraps the pure helper\n// findAlternativeWorkspaceName with the interactive prompt + --force handling.\n// Exported so tests can mock `confirm` from @inquirer/prompts and exercise\n// the interactive branches.\nexport async function resolveWorkspacePath(\n parent: string,\n desiredName: string,\n force?: boolean,\n): Promise<string> {\n const desired = join(parent, desiredName);\n if (await isEmptyOrMissing(desired)) return desired;\n\n const alternative = await findAlternativeWorkspaceName(parent, desiredName);\n if (!alternative) {\n throw new Error(`Không tìm được workspace path khả dụng trong ${parent}`);\n }\n\n log.warn(`Workspace path \"${desired}\" đã có nội dung.`);\n if (force) {\n log.info(`--force: dùng ${alternative}`);\n return alternative;\n }\n const useAlt = await confirm({\n message: `Dùng \"${alternative}\" thay thế?`,\n default: true,\n });\n if (!useAlt) {\n throw new Error(\"Hủy init. Chạy lại với --workspace-name khác.\");\n }\n return alternative;\n}\n\nasync function promptTeamOwner(currentUserEmail: string): Promise<string> {\n return await input({\n message: \"Team owner email:\",\n default: currentUserEmail,\n });\n}\n\nasync function maybeCommit(projectRoot: string, mode: InitMode, autoYes?: boolean): Promise<void> {\n const wantCommit =\n autoYes ??\n (await confirm({\n message: \"Commit ngay các file Avatar đã tạo?\",\n default: true,\n }));\n if (!wantCommit) return;\n const g = git(projectRoot);\n await g.add([\".claude/\", \"CLAUDE.md\", \".gitignore\", \".gitmodules\"]);\n await g.commit(`chore: initialize Avatar in ${mode} mode`);\n log.success(\"Đã commit\");\n}\n\nasync function maybeCommitWorkspace(workspacePath: string, autoYes?: boolean): Promise<void> {\n const wantCommit =\n autoYes ??\n (await confirm({\n message: \"Commit workspace ngay?\",\n default: true,\n }));\n if (!wantCommit) return;\n const g = git(workspacePath);\n await g.add([\"CLAUDE.md\", \".claude/\", \".gitignore\", \".gitmodules\", \"notes/\", \"scripts/\"]);\n await g.commit(\"chore: initialize Avatar workspace for client\");\n log.success(\"Đã commit workspace\");\n}\n\nfunction printInitSuccessBox(rootPath: string, mode: InitMode): void {\n const lines: string[] = [];\n if (mode === \"internal\") {\n lines.push(`${chalk.green(\"✓\")} Avatar đã sẵn sàng trong dự án`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(\"claude\")} Mở Claude Code`);\n lines.push(` ${chalk.cyan(\"avatar status\")} Xem snapshot`);\n lines.push(` ${chalk.cyan(\"avatar sync\")} Pull team-ai-pack mới`);\n } else {\n lines.push(`${chalk.green(\"✓\")} Workspace sẵn sàng: ${rootPath}`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(`cd ${rootPath}`)}`);\n lines.push(` ${chalk.cyan(\"claude\")} Mở Claude Code ở workspace root`);\n lines.push(\"\");\n lines.push(` ${chalk.cyan(\"avatar commit --src\")} Commit code khách lên client remote`);\n lines.push(\n ` ${chalk.cyan(\"avatar commit --avatar\")} Commit Avatar state lên workspace remote`,\n );\n lines.push(` ${chalk.cyan(\"avatar sync\")} Sync team pack`);\n }\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","// Append-only audit log at ~/.avatar/audit.log. Records sensitive actions\n// (secrets set/rm, tool install/remove) WITHOUT logging secret values.\n// Used by `avatar doctor` to surface recent activity.\nimport { promises as fs } from \"node:fs\";\nimport { AUDIT_LOG_PATH, ensureAvatarHome } from \"./user-config-store.js\";\n\nexport type AuditAction =\n | \"login\"\n | \"login_reset\"\n | \"logout\"\n | \"secret_set\"\n | \"secret_rm\"\n | \"tool_install\"\n | \"tool_remove\"\n | \"init\"\n | \"sync\"\n | \"restore\";\n\nexport interface AuditEntry {\n timestamp: string;\n action: AuditAction;\n detail?: string;\n}\n\nexport async function appendAuditEntry(action: AuditAction, detail?: string): Promise<void> {\n await ensureAvatarHome();\n const entry: AuditEntry = {\n timestamp: new Date().toISOString(),\n action,\n ...(detail ? { detail } : {}),\n };\n const line = `${JSON.stringify(entry)}\\n`;\n await fs.appendFile(AUDIT_LOG_PATH, line, \"utf8\");\n}\n","// Manage the team-ai-pack git submodule lifecycle: add, update, pin-to-tag,\n// changelog extraction. Used by `avatar init` and `avatar sync`.\nimport { join } from \"node:path\";\nimport {\n addSubmodule,\n checkoutTagInSubmodule,\n currentCommitSha,\n latestTag,\n} from \"./git-operations.js\";\n\n// Production default — overridable via AVATAR_TEAM_PACK_REPO_URL env for\n// testing (use file:// path to a local clone) or for self-hosted forks.\nexport const TEAM_PACK_REPO_URL =\n process.env.AVATAR_TEAM_PACK_REPO_URL ?? \"https://github.com/LukeNALS/team-ai-pack.git\";\nexport const TEAM_PACK_RELATIVE_PATH = \".claude/pack\";\n\n// Add the team-ai-pack submodule into a fresh project and pin it to a tag.\n// If `tag` is omitted, checks out the latest tag in the freshly-cloned submodule.\nexport async function addTeamPackSubmodule(\n projectRoot: string,\n tag?: string,\n): Promise<{ pinnedTag: string | null }> {\n await addSubmodule(TEAM_PACK_REPO_URL, TEAM_PACK_RELATIVE_PATH, projectRoot);\n\n // Resolve which tag to pin to. If caller passed one, honour it; otherwise\n // ask the just-cloned submodule for its latest tag.\n let target = tag ?? null;\n if (!target) {\n target = await latestTag(join(projectRoot, TEAM_PACK_RELATIVE_PATH));\n }\n\n if (target) {\n await checkoutTagInSubmodule(TEAM_PACK_RELATIVE_PATH, target, projectRoot);\n }\n return { pinnedTag: target };\n}\n\n// Read the current pinned version of the pack submodule. Returns the tag name\n// if HEAD matches a tag, otherwise the short SHA.\nexport async function readPinnedPackVersion(projectRoot: string): Promise<string> {\n const submoduleRoot = join(projectRoot, TEAM_PACK_RELATIVE_PATH);\n const tag = await latestTag(submoduleRoot);\n if (tag) return tag;\n const sha = await currentCommitSha(submoduleRoot);\n return sha.slice(0, 7);\n}\n","// Pure helpers for `avatar init` conflict detection and workspace path\n// resolution. Extracted from init.ts so they're unit-testable without\n// triggering inquirer prompts or git operations.\n\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { pathExists } from \"../lib/filesystem-helpers.js\";\nimport { AVATAR_MANAGED_PATHS } from \"../lib/project-tree-scaffolder.js\";\n\n// True if path doesn't exist OR is an empty directory (ignoring dotfiles and\n// Windows Thumbs.db noise). Safe to scaffold into. macOS .DS_Store and\n// .localized are already filtered by the dotfile rule.\nexport async function isEmptyOrMissing(path: string): Promise<boolean> {\n if (!(await pathExists(path))) return true;\n try {\n const entries = await readdir(path);\n const meaningful = entries.filter((e) => !e.startsWith(\".\") && e !== \"Thumbs.db\");\n return meaningful.length === 0;\n } catch {\n return false;\n }\n}\n\n// Return Avatar-managed top-level paths that already exist in projectRoot.\n// Caller decides whether to abort, prompt, or auto-backup.\nexport async function detectAvatarConflicts(projectRoot: string): Promise<string[]> {\n const found: string[] = [];\n for (const rel of AVATAR_MANAGED_PATHS) {\n if (await pathExists(join(projectRoot, rel))) found.push(rel);\n }\n return found;\n}\n\n// Find first numbered alternative path (e.g. \"foo-2\", \"foo-3\", ...) under\n// `parent` that is empty or missing. Returns null if exhausted.\n// maxAttempts defaults to 10; if a user has 9+ workspaces with the same\n// base name, something else is wrong.\nexport async function findAlternativeWorkspaceName(\n parent: string,\n desiredName: string,\n maxAttempts = 10,\n): Promise<string | null> {\n for (let i = 2; i < maxAttempts; i++) {\n const candidate = join(parent, `${desiredName}-${i}`);\n if (await isEmptyOrMissing(candidate)) return candidate;\n }\n return null;\n}\n","// Pure transformation helpers used by `avatar init` to derive project names,\n// workspace names from git URLs, and build the ScaffoldVariables struct for\n// template rendering. Extracted from init.ts for testability — no IO,\n// no prompts, no git calls.\n\nimport type { ScaffoldVariables } from \"../lib/project-tree-scaffolder.js\";\nimport type { InitMode } from \"../types/config-schema.js\";\n\nconst AVATAR_CLI_VERSION = \"1.0.1\";\n\n// Last path segment of an absolute project root, used as fallback project\n// name when user doesn't supply one explicitly. Handles trailing slashes.\nexport function projectNameOf(projectRoot: string): string {\n return projectRoot.split(\"/\").filter(Boolean).pop() ?? \"avatar-project\";\n}\n\n// Infer workspace folder name from a git remote URL.\n// \"git@github.com:org/repo.git\" → \"avatar-repo-workspace\"\n// \"https://github.com/org/repo.git\" → \"avatar-repo-workspace\"\n// \"https://github.com/org/repo\" → \"avatar-repo-workspace\"\n// fallback when match fails → \"avatar-client-workspace\"\nexport function inferWorkspaceName(repoUrl: string): string {\n const m = repoUrl.match(/[/:]([^/]+?)(\\.git)?$/);\n const base = m?.[1] ?? \"client\";\n return `avatar-${base}-workspace`;\n}\n\n// Build the template-rendering variable bag. lastScan stamps \"now\" — tests\n// that need a deterministic value should freeze time via vi.useFakeTimers().\nexport function buildScaffoldVariables(args: {\n projectName: string;\n projectDescription: string;\n teamOwner: string;\n packVersion: string;\n mode: InitMode;\n}): ScaffoldVariables {\n return {\n projectName: args.projectName,\n projectDescription: args.projectDescription,\n teamOwner: args.teamOwner,\n avatarVersion: AVATAR_CLI_VERSION,\n packVersion: args.packVersion,\n lastScan: new Date().toISOString(),\n mode: args.mode,\n };\n}\n","import boxen from \"boxen\";\n// `avatar login [--reset]` — Command 01 spec.\n// Implements the user-facing flow: announce verification URL, open browser,\n// poll Google until token returned, validate domain, persist credentials.\nimport type { Command } from \"commander\";\nimport open from \"open\";\nimport { appendAuditEntry } from \"../lib/audit-log-appender.js\";\nimport {\n buildUserConfig,\n buildVerificationUrl,\n decodeIdToken,\n pollForToken,\n requestDeviceCode,\n revokeToken,\n verifyHostedDomain,\n} from \"../lib/google-oauth-device-flow.js\";\nimport { chalk, log, spinner } from \"../lib/terminal-logger.js\";\nimport {\n USER_CONFIG_PATH,\n clearUserConfig,\n isTokenExpired,\n readUserConfig,\n writeUserConfig,\n} from \"../lib/user-config-store.js\";\n\nexport function registerLoginCommand(program: Command): void {\n program\n .command(\"login\")\n .description(\"Đăng nhập Google SSO (workspace @nal.vn)\")\n .option(\"--reset\", \"Xóa credential cũ và đăng nhập lại\")\n .action(async (opts: { reset?: boolean }) => {\n try {\n await runLogin(opts);\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\nasync function runLogin(opts: { reset?: boolean }): Promise<void> {\n // Step 1 of spec: short-circuit if already logged in and token is still good.\n if (opts.reset) {\n await clearUserConfig();\n await appendAuditEntry(\"login_reset\");\n } else {\n const existing = await readUserConfig();\n if (existing && !isTokenExpired(existing)) {\n log.success(`Đã đăng nhập: ${existing.email}`);\n return;\n }\n }\n\n // Step 2: request device + user code.\n const deviceSpinner = spinner(\"Đang yêu cầu device code từ Google...\");\n let deviceCode: Awaited<ReturnType<typeof requestDeviceCode>>;\n try {\n deviceCode = await requestDeviceCode();\n deviceSpinner.succeed(\"Nhận device code\");\n } catch (err) {\n deviceSpinner.fail(\"Không kết nối được Google\");\n throw err;\n }\n\n // Step 3: display instructions to user.\n const verificationUrl = buildVerificationUrl(deviceCode);\n const instructions = [\n `1. Truy cập: ${chalk.cyan(deviceCode.verification_url)}`,\n `2. Nhập code: ${chalk.bold.yellow(deviceCode.user_code)}`,\n \"\",\n `Hoặc Avatar tự mở browser, click ${chalk.green(\"Allow\")}...`,\n ].join(\"\\n\");\n process.stdout.write(`${boxen(instructions, { padding: 1, borderStyle: \"round\" })}\\n`);\n\n // Step 4: open browser. Failure here is non-fatal — user can copy URL manually.\n void open(verificationUrl).catch(() => {\n log.dim(\"(Không mở được browser tự động — copy URL ở trên)\");\n });\n\n // Step 5: poll token endpoint until success or expiry.\n const waitSpinner = spinner(\"Đang chờ xác nhận trong browser...\");\n const intervalMs = deviceCode.interval * 1000;\n const deadline = Date.now() + deviceCode.expires_in * 1000;\n\n let token = null;\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n try {\n token = await pollForToken(deviceCode.device_code);\n if (token) break;\n } catch (err) {\n waitSpinner.fail(\"Xác thực thất bại\");\n throw err;\n }\n }\n if (!token) {\n waitSpinner.fail(\"Hết hạn chờ (5 phút). Chạy lại 'avatar login'.\");\n process.exit(1);\n }\n waitSpinner.succeed(\"Đã nhận token từ Google\");\n\n // Step 6: verify hosted domain. Revoke token if claim is wrong.\n const claims = decodeIdToken(token.id_token);\n try {\n verifyHostedDomain(claims);\n } catch (err) {\n await revokeToken(token.access_token);\n throw err;\n }\n\n // Step 7: persist credentials with chmod 600.\n const userConfig = buildUserConfig(token, claims);\n await writeUserConfig(userConfig);\n await appendAuditEntry(\"login\", userConfig.email);\n\n log.success(`Xác thực thành công: ${userConfig.email}`);\n log.success(`Verify hosted domain: ${claims.hd} ✓`);\n log.success(`Lưu credential vào ${USER_CONFIG_PATH} (chmod 600)`);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","// Google OAuth 2.0 Device Authorization Grant (RFC 8628) implementation.\n//\n// Why Device Flow: Avatar is a terminal CLI with no browser redirect URL.\n// The user logs in via google.com/device on a browser and the CLI polls\n// Google for the resulting token.\n//\n// Why the client secret is bundled in source: Device Flow does not treat the\n// secret as a security boundary — the human Allow click in the browser is.\n// Google's TV/Limited Input docs explicitly permit this.\n//\n// To rotate: Google Cloud Console → APIs & Services → Credentials → click the\n// OAuth client → Reset Secret. Replace GOOGLE_CLIENT_SECRET below.\n\nimport type { UserConfig } from \"../types/config-schema.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// OAuth client config (hardcoded — see file header for rationale).\n// To regenerate: Google Cloud Console → project \"avatar-cli\" → Clients.\n// Application type must be \"TV and Limited Input devices\".\n// ─────────────────────────────────────────────────────────────────────────────\nexport const GOOGLE_CLIENT_ID =\n \"1014766441755-i4jimivh5rd7vt8phuhmepmt45sovtph.apps.googleusercontent.com\";\nexport const GOOGLE_CLIENT_SECRET = \"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\";\n\n// Restrict to the NAL Workspace domain. Enforced at TWO layers:\n// 1. ?hd=nal.vn appended to the verification URL — Google filters the picker\n// 2. Verify id_token.hd === HOSTED_DOMAIN after token exchange (defense-in-depth)\nexport const HOSTED_DOMAIN = \"nal.vn\";\n\nexport const SCOPES = [\"openid\", \"email\", \"profile\"];\n\nconst DEVICE_CODE_URL = \"https://oauth2.googleapis.com/device/code\";\nconst TOKEN_URL = \"https://oauth2.googleapis.com/token\";\nconst REVOKE_URL = \"https://oauth2.googleapis.com/revoke\";\n\nexport interface DeviceCodeResponse {\n device_code: string;\n user_code: string;\n verification_url: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface TokenResponse {\n access_token: string;\n refresh_token: string;\n id_token: string;\n expires_in: number;\n token_type: string;\n scope: string;\n}\n\nexport interface IdTokenClaims {\n email: string;\n email_verified: boolean;\n name?: string;\n hd?: string;\n exp: number;\n iss: string;\n aud: string;\n}\n\n// ── Step 2 of Command 01 spec: request device + user codes.\nexport async function requestDeviceCode(): Promise<DeviceCodeResponse> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n scope: SCOPES.join(\" \"),\n });\n const res = await fetch(DEVICE_CODE_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Device code request failed (${res.status}): ${text}`);\n }\n return (await res.json()) as DeviceCodeResponse;\n}\n\n// ── Step 5: poll the token endpoint until user authorises or expiry.\n// Returns the token response on success, null while still pending.\n// Throws on hard errors (access_denied, expired_token).\nexport async function pollForToken(deviceCode: string): Promise<TokenResponse | null> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n device_code: deviceCode,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n });\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n\n if (res.ok) {\n return (await res.json()) as TokenResponse;\n }\n\n // Google returns 4xx with a JSON {error} field for both pending and fatal states.\n let errorCode = \"\";\n try {\n const data = (await res.json()) as { error?: string };\n errorCode = data.error ?? \"\";\n } catch {\n errorCode = \"\";\n }\n\n if (errorCode === \"authorization_pending\" || errorCode === \"slow_down\") {\n return null;\n }\n if (errorCode === \"access_denied\") {\n throw new Error(\"User từ chối quyền truy cập\");\n }\n if (errorCode === \"expired_token\") {\n throw new Error(\"Hết hạn chờ (5 phút). Chạy lại 'avatar login'.\");\n }\n throw new Error(`OAuth token endpoint trả lỗi: ${errorCode || res.status}`);\n}\n\n// Decode JWT payload WITHOUT verifying signature. Safe here because we receive\n// the token directly from Google over HTTPS — no MITM surface.\nexport function decodeIdToken(idToken: string): IdTokenClaims {\n const parts = idToken.split(\".\");\n if (parts.length !== 3) {\n throw new Error(\"id_token format không hợp lệ\");\n }\n const payload = parts[1];\n if (!payload) throw new Error(\"id_token thiếu payload\");\n // Convert base64url to base64.\n const base64 = payload.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const json = Buffer.from(base64, \"base64\").toString(\"utf8\");\n return JSON.parse(json) as IdTokenClaims;\n}\n\n// ── Step 6: enforce hosted domain. Reject any non-@nal.vn account.\nexport function verifyHostedDomain(claims: IdTokenClaims): void {\n if (claims.hd !== HOSTED_DOMAIN) {\n throw new Error(\n `Email không thuộc workspace NAL (yêu cầu @${HOSTED_DOMAIN}). Nhận: ${claims.email}`,\n );\n }\n if (!claims.email_verified) {\n throw new Error(\"Email chưa được Google verify\");\n }\n}\n\n// Convert OAuth token response + decoded claims into the on-disk UserConfig shape.\nexport function buildUserConfig(token: TokenResponse, claims: IdTokenClaims): UserConfig {\n const expiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();\n return {\n email: claims.email,\n name: claims.name ?? claims.email,\n access_token: token.access_token,\n refresh_token: token.refresh_token,\n expires_at: expiresAt,\n id_token: token.id_token,\n };\n}\n\n// Refresh flow: exchange refresh_token for a new access_token. Used by other\n// commands when they detect an expired token (see isTokenExpired).\nexport async function refreshAccessToken(refreshToken: string): Promise<{\n access_token: string;\n expires_in: number;\n id_token?: string;\n}> {\n const body = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: refreshToken,\n grant_type: \"refresh_token\",\n });\n const res = await fetch(TOKEN_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Refresh token failed (${res.status}): ${text}`);\n }\n return (await res.json()) as { access_token: string; expires_in: number; id_token?: string };\n}\n\n// Revoke a token (used when login is rejected post-hoc, e.g. wrong domain).\nexport async function revokeToken(token: string): Promise<void> {\n const body = new URLSearchParams({ token });\n await fetch(REVOKE_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body,\n }).catch(() => {\n // Best-effort revoke — don't fail the caller if revoke itself errors.\n });\n}\n\n// Build the verification URL with hd hint so Google pre-filters the account picker.\nexport function buildVerificationUrl(response: DeviceCodeResponse): string {\n const url = new URL(response.verification_url);\n url.searchParams.set(\"user_code\", response.user_code);\n url.searchParams.set(\"hd\", HOSTED_DOMAIN);\n return url.toString();\n}\n","// `avatar mcp-run <tool-id>` — hidden command from Chapter 13 roadmap.\n// Wrapper used by ~/.claude.json entries: Avatar injects secrets from keychain\n// then spawns the underlying MCP process with stdio piped to Claude Code.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerMcpRunCommand(program: Command): void {\n program\n .command(\"mcp-run <tool-id>\", { hidden: true })\n .description(\"[internal] Spawn MCP với secrets injected (M09)\")\n .action(notImplementedYet(\"mcp-run\", \"Milestone 09\"));\n}\n","// `avatar restore [--backup <name>] [--list]` — Command 08 spec.\n// Restore .claude/pack/ from a previous backup snapshot.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerRestoreCommand(program: Command): void {\n program\n .command(\"restore\")\n .description(\"Khôi phục .claude/pack/ từ backup (M08)\")\n .option(\"--backup <name>\", \"Tên backup folder trong .claude/_backup/\")\n .option(\"--list\", \"Liệt kê các backup hiện có\")\n .action(notImplementedYet(\"restore\", \"Milestone 08\"));\n}\n","// `avatar review [--accept-all|--reject-all]` — Command 05 spec.\n// Interactive review of .claude/_pending/*.diff.md proposals.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerReviewCommand(program: Command): void {\n program\n .command(\"review\")\n .description(\"Review pending proposals từ avatar scan (M08)\")\n .option(\"--accept-all\", \"Approve mọi pending không hỏi (CI mode)\")\n .option(\"--reject-all\", \"Xóa mọi pending không hỏi\")\n .action(notImplementedYet(\"review\", \"Milestone 08\"));\n}\n","// `avatar scan [--incremental|--full] [--scanners <list>]` — Command 04 spec.\n// Runs 5 project scanners and writes proposals to .claude/_pending/.\n// Implementation deferred — scanner files in src/scanners/ are stubbed.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerScanCommand(program: Command): void {\n program\n .command(\"scan\")\n .description(\"Chạy project scanner và đề xuất knowledge update (M06)\")\n .option(\"--incremental\", \"Chỉ scan các file thay đổi từ commit cuối\")\n .option(\"--full\", \"Scan toàn bộ dự án (default)\")\n .option(\"--scanners <list>\", \"tech-stack,conventions,architecture,domain,git-pattern\")\n .option(\"--quiet\", \"Chạy ngầm, ít output (dùng cho git hook)\")\n .action(notImplementedYet(\"scan\", \"Milestone 06\"));\n}\n","// `avatar secrets {list,set,get,rm,check}` — Command 13 spec.\n// Backed by OS keychain via @napi-rs/keyring. Service prefix: \"avatar\".\n// Audit log entries are written for set/rm; values NEVER logged.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerSecretsCommand(program: Command): void {\n const secrets = program.command(\"secrets\").description(\"Quản lý secrets trong OS keychain (M09)\");\n\n secrets\n .command(\"list\")\n .description(\"Liệt kê secrets đã set (chỉ tên, không value)\")\n .action(notImplementedYet(\"secrets list\", \"Milestone 09\"));\n\n secrets\n .command(\"set <service> <name>\")\n .description(\"Set/update secret (prompt ẩn)\")\n .action(notImplementedYet(\"secrets set\", \"Milestone 09\"));\n\n secrets\n .command(\"get <service> <name>\")\n .description(\"Lấy secret, copy clipboard, auto-xóa sau 30s\")\n .action(notImplementedYet(\"secrets get\", \"Milestone 09\"));\n\n secrets\n .command(\"rm <service> <name>\")\n .description(\"Xóa secret khỏi keychain\")\n .action(notImplementedYet(\"secrets rm\", \"Milestone 09\"));\n\n secrets\n .command(\"check\")\n .description(\"Verify mọi secret required bởi MCP đã enabled\")\n .action(notImplementedYet(\"secrets check\", \"Milestone 09\"));\n}\n","// `avatar status [--json]` — Command 06 spec.\n// Read-only snapshot: project name, CLI version, pack version, pending count,\n// backup count, tech-stack first-line. No mutations.\nimport { promises as fs } from \"node:fs\";\nimport { join } from \"node:path\";\nimport boxen from \"boxen\";\nimport type { Command } from \"commander\";\nimport { pathExists, readText } from \"../lib/filesystem-helpers.js\";\nimport { isGitRepo } from \"../lib/git-operations.js\";\nimport { listBackups } from \"../lib/pack-backup-manager.js\";\nimport { readPinnedPackVersion } from \"../lib/team-pack-submodule-manager.js\";\nimport { chalk, log } from \"../lib/terminal-logger.js\";\n\nconst AVATAR_CLI_VERSION = \"1.0.1\";\n\nexport function registerStatusCommand(program: Command): void {\n program\n .command(\"status\")\n .description(\"Snapshot tức thì: project, pack version, pending, backup\")\n .option(\"--json\", \"Output JSON cho script\")\n .action(async (opts: { json?: boolean }) => {\n try {\n const snapshot = await gatherStatus(process.cwd());\n if (opts.json) {\n process.stdout.write(`${JSON.stringify(snapshot, null, 2)}\\n`);\n } else {\n renderStatusBox(snapshot);\n }\n } catch (err) {\n log.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n });\n}\n\ninterface StatusSnapshot {\n projectName: string;\n cliVersion: string;\n packVersion: string | null;\n pendingCount: number;\n backupCount: number;\n techStackSummary: string;\n hasAvatar: boolean;\n}\n\nasync function gatherStatus(cwd: string): Promise<StatusSnapshot> {\n const projectName = cwd.split(\"/\").filter(Boolean).pop() ?? \"unknown\";\n const claudeRoot = join(cwd, \".claude\");\n const hasAvatar = await pathExists(claudeRoot);\n if (!hasAvatar) {\n return {\n projectName,\n cliVersion: AVATAR_CLI_VERSION,\n packVersion: null,\n pendingCount: 0,\n backupCount: 0,\n techStackSummary: \"(Avatar chưa init)\",\n hasAvatar: false,\n };\n }\n\n const packVersion = (await isGitRepo(join(claudeRoot, \"pack\")))\n ? await readPinnedPackVersion(cwd).catch(() => null)\n : null;\n\n const pendingDir = join(claudeRoot, \"_pending\");\n const pendingCount = (await pathExists(pendingDir))\n ? (await fs.readdir(pendingDir)).filter((n) => n.endsWith(\".diff.md\")).length\n : 0;\n\n const backupCount = (await listBackups(cwd)).length;\n\n const techStackSummary = await readTechStackFirstLine(claudeRoot);\n\n return {\n projectName,\n cliVersion: AVATAR_CLI_VERSION,\n packVersion,\n pendingCount,\n backupCount,\n techStackSummary,\n hasAvatar: true,\n };\n}\n\nasync function readTechStackFirstLine(claudeRoot: string): Promise<string> {\n const techStackPath = join(claudeRoot, \"project\", \"tech-stack.md\");\n if (!(await pathExists(techStackPath))) return \"(no tech-stack.md)\";\n const content = await readText(techStackPath);\n const firstNonHeaderLine = content\n .split(\"\\n\")\n .find((l) => l.trim() && !l.startsWith(\"#\") && !l.startsWith(\">\"));\n return firstNonHeaderLine?.trim() ?? \"(empty)\";\n}\n\nfunction renderStatusBox(s: StatusSnapshot): void {\n const lines = [\n `${chalk.bold(\"Avatar Status\")} · ${chalk.cyan(s.projectName)}`,\n \"─\".repeat(48),\n `${chalk.dim(\"CLI version:\")} ${s.cliVersion}`,\n `${chalk.dim(\"Pack version:\")} ${s.packVersion ?? chalk.yellow(\"not installed\")}`,\n `${chalk.dim(\"Pending changes:\")} ${s.pendingCount}${s.pendingCount > 0 ? chalk.dim(\" (avatar review)\") : \"\"}`,\n `${chalk.dim(\"Backups:\")} ${s.backupCount}`,\n `${chalk.dim(\"Tech stack:\")} ${s.techStackSummary}`,\n ];\n process.stdout.write(`${boxen(lines.join(\"\\n\"), { padding: 1, borderStyle: \"round\" })}\\n`);\n}\n","import { promises as fs } from \"node:fs\";\n// Backup .claude/pack/ before `avatar sync --force` so user can `avatar restore`\n// if the sync goes wrong. Naming convention: pack-{currentVersion}-{YYYYMMDD-HHmm}.\nimport { join } from \"node:path\";\nimport { copyDirRecursive, ensureDir, pathExists } from \"./filesystem-helpers.js\";\n\nexport const BACKUP_DIR_NAME = \"_backup\";\n\nfunction timestamp(): string {\n const now = new Date();\n const y = now.getFullYear();\n const m = String(now.getMonth() + 1).padStart(2, \"0\");\n const d = String(now.getDate()).padStart(2, \"0\");\n const h = String(now.getHours()).padStart(2, \"0\");\n const min = String(now.getMinutes()).padStart(2, \"0\");\n return `${y}${m}${d}-${h}${min}`;\n}\n\nexport function buildBackupName(currentVersion: string): string {\n // Strip any leading \"v\" so we don't get pack-vv1.2.3 if someone passes \"v1.2.3\".\n const cleanVersion = currentVersion.replace(/^v/, \"\");\n return `pack-v${cleanVersion}-${timestamp()}`;\n}\n\n// Backup .claude/pack/ to .claude/_backup/{name}/, excluding the submodule's\n// own .git directory (we restore content only, not git history).\nexport async function backupPack(projectRoot: string, currentVersion: string): Promise<string> {\n const name = buildBackupName(currentVersion);\n const srcPath = join(projectRoot, \".claude\", \"pack\");\n const dstPath = join(projectRoot, \".claude\", BACKUP_DIR_NAME, name);\n if (!(await pathExists(srcPath))) {\n throw new Error(\"Không tìm thấy .claude/pack/ để backup\");\n }\n await ensureDir(dstPath);\n await copyDirRecursive(srcPath, dstPath, [\".git\"]);\n return name;\n}\n\nexport async function listBackups(projectRoot: string): Promise<string[]> {\n const dir = join(projectRoot, \".claude\", BACKUP_DIR_NAME);\n if (!(await pathExists(dir))) return [];\n const entries = await fs.readdir(dir, { withFileTypes: true });\n return entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort()\n .reverse();\n}\n","// `avatar sync [--force] [--version <tag>] [--dry-run]` — Command 03 spec.\n// Pulls latest team-ai-pack into .claude/pack. Implementation in next milestone.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerSyncCommand(program: Command): void {\n program\n .command(\"sync\")\n .description(\"Pull team-ai-pack mới nhất (M08)\")\n .option(\"--force\", \"Override .claude/pack/, backup trước\")\n .option(\"--version <tag>\", \"Pin vào version cụ thể\")\n .option(\"--dry-run\", \"Hiển thị changes, không apply\")\n .action(notImplementedYet(\"sync\", \"Milestone 08\"));\n}\n","// `avatar tools {list,install,remove}` — Commands 10/11/12 spec (Chapter 12 v4).\n// Lifecycle management for system dependencies (git, gh, node, uv, docker) and\n// MCP servers (gitnexus, context7, serena, github, filesystem, playwright).\n// Registry source: team-ai-pack/tools/registry.yaml.\nimport type { Command } from \"commander\";\nimport { notImplementedYet } from \"../lib/not-implemented-stub.js\";\n\nexport function registerToolsCommand(program: Command): void {\n const tools = program.command(\"tools\").description(\"Quản lý system tools + MCP servers (M09)\");\n\n tools\n .command(\"list\")\n .description(\"Liệt kê tool đã cài / còn thiếu\")\n .option(\"--installed\", \"Chỉ liệt kê tool đã cài\")\n .option(\"--missing\", \"Chỉ liệt kê tool còn thiếu\")\n .option(\"--json\", \"Output JSON cho script\")\n .action(notImplementedYet(\"tools list\", \"Milestone 09\"));\n\n tools\n .command(\"install [tool-ids...]\")\n .description(\"Cài tool và đăng ký vào ~/.claude.json\")\n .option(\"--all-recommended\", \"Cài mọi MCP được recommend cho project type\")\n .option(\"--verify\", \"Chạy MCP thử để verify (mất ~30s/tool)\")\n .option(\"--no-secrets\", \"Skip prompt secrets, set sau qua 'avatar secrets'\")\n .action(notImplementedYet(\"tools install\", \"Milestone 09\"));\n\n tools\n .command(\"remove <tool-id>\")\n .description(\"Gỡ tool khỏi ~/.claude.json (optional uninstall binary)\")\n .option(\"--keep-secrets\", \"Không xóa secrets khỏi keychain\")\n .option(\"--keep-binary\", \"Không uninstall npm global binary\")\n .action(notImplementedYet(\"tools remove\", \"Milestone 09\"));\n}\n"],"mappings":"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","names":["program","fs","join","join","join","join","fs","join","dirname","join","dirname","join","fs","join","relative","join","join","fs","program","join","fs","join","relative","boxen","fs","fs","join","join","join","join","program","relative","join","boxen","boxen","program","boxen","resolve","program","program","program","program","program","fs","join","boxen","fs","join","join","fs","AVATAR_CLI_VERSION","program","join","fs","boxen","program","program"]}
package/package.json CHANGED
@@ -1,46 +1,55 @@
1
1
  {
2
2
  "name": "@nalvietnam/avatar-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "AI harness CLI for NAL Vietnam engineering",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "avatar": "./bin/avatar.js"
8
8
  },
9
- "files": ["bin", "dist", "src/templates", "src/hooks", "README.md"],
9
+ "files": [
10
+ "bin",
11
+ "dist",
12
+ "src/templates",
13
+ "src/hooks",
14
+ "README.md"
15
+ ],
10
16
  "engines": {
11
17
  "node": ">=18.17.0"
12
18
  },
13
19
  "scripts": {
14
20
  "build": "tsup",
15
21
  "dev": "tsup --watch",
16
- "test": "vitest run --passWithNoTests",
17
- "test:watch": "vitest",
22
+ "test": "vitest run --passWithNoTests --exclude 'test/integration/**'",
23
+ "test:watch": "vitest --exclude 'test/integration/**'",
24
+ "test:e2e": "npm run build && vitest run --passWithNoTests test/integration",
25
+ "test:all": "npm run build && vitest run --passWithNoTests",
18
26
  "lint": "biome check src test",
19
27
  "format": "biome format --write src test",
20
- "prepublishOnly": "npm run build && npm run test"
28
+ "prepublishOnly": "npm run build && npm run test:all"
21
29
  },
22
30
  "dependencies": {
23
- "commander": "^12.0.0",
24
- "@inquirer/prompts": "^5.0.0",
31
+ "@inquirer/prompts": "^8.4.3",
32
+ "@napi-rs/keyring": "^1.1.0",
33
+ "boxen": "^8.0.0",
25
34
  "chalk": "^5.3.0",
26
- "ora": "^8.0.0",
35
+ "cli-highlight": "^2.1.11",
27
36
  "cli-table3": "^0.6.3",
28
- "boxen": "^8.0.0",
37
+ "clipboardy": "^4.0.0",
38
+ "commander": "^12.0.0",
29
39
  "diff": "^5.2.0",
30
- "cli-highlight": "^2.1.11",
31
- "simple-git": "^3.22.0",
32
40
  "fast-glob": "^3.3.2",
33
- "zod": "^3.22.4",
34
- "@napi-rs/keyring": "^1.1.0",
35
41
  "open": "^10.0.0",
36
- "clipboardy": "^4.0.0"
42
+ "ora": "^8.0.0",
43
+ "simple-git": "^3.22.0",
44
+ "zod": "^3.22.4"
37
45
  },
38
46
  "devDependencies": {
39
47
  "@biomejs/biome": "^1.5.0",
40
- "@types/node": "^20.11.0",
41
48
  "@types/diff": "^5.0.9",
49
+ "@types/node": "^20.11.0",
50
+ "@vitest/coverage-v8": "^4.1.7",
42
51
  "tsup": "^8.0.0",
43
52
  "typescript": "^5.3.0",
44
- "vitest": "^1.2.0"
53
+ "vitest": "^4.1.7"
45
54
  }
46
55
  }